First Last Prev Next    No search results available
Details
: C front-end doesn't emit getelementptr for address of arr...
Bug#: 84
: tools
: llvm-gcc
Status: RESOLVED
Resolution: FIXED
: PC
: Linux
: 1.0
: P2
: minor
: 1.1

:
: quality-of-implementation
:
:
  Show dependency tree - Show dependency graph
People
Reporter: Chris Lattner <sabre@nondot.org>
Assigned To: Chris Lattner <sabre@nondot.org>

Attachments
Patch to fix latest problem. (2.24 KB, patch)
2003-11-13 01:11, Chris Lattner
Details


Note

You need to log in before you can comment on or make changes to this bug.

Related actions


Description:   Opened: 2003-11-03 15:37
The following code:

---
char Global[100];
char *test1(unsigned i) {
  return &Global[i];
}
---

Turns into the following LLVM code:
---
        %tmp.1 = cast uint %i.1 to [100 x sbyte]*
        %tmp.4 = cast [100 x sbyte]* %tmp.1 to long
        %tmp.5 = add long %tmp.4, cast ([100 x sbyte]* %Global to long)
        %tmp.6 = cast long %tmp.5 to sbyte*
        ret sbyte* %tmp.6
---

This is disgusting, it should use a getelementptr instruction.  Testcase is:
test/Regression/CFrontend/2003-11-03-AddrArrayElement.c.tr


-Chris
------- Comment #1 From Chris Lattner 2003-11-07 00:21:54 -------
This turned out to be easier than I thought.  I wish there was some way to find
ALL of them though.  :(

$ diff -u c-typeck.c~ c-typeck.c
--- c-typeck.c~ 2003-08-19 17:22:52.000000000 -0500
+++ c-typeck.c  2003-11-07 00:11:04.000000000 -0600
@@ -2379,7 +2379,7 @@
        }
 
       /* For &x[y], return x+y */
-      if (TREE_CODE (arg) == ARRAY_REF)
+      if (TREE_CODE (arg) == ARRAY_REF && !EMIT_LLVM)
        {
          if (!c_mark_addressable (TREE_OPERAND (arg, 0)))
            return error_mark_node;

---

The testcase now produces:
sbyte* %test1(uint %i.1) {
        %tmp.1 = cast uint %i.1 to long        
        %tmp.2 = getelementptr [100 x sbyte]* %Global, long 0, long %tmp.1     
        
        ret sbyte* %tmp.2
}

... as expected.  Long live uncollapsed DSNodes!

-Chris
------- Comment #2 From Chris Lattner 2003-11-08 17:01:29 -------
Here's another horrible case, this one found in the pool allocator:

struct foo {
  int array[100];
  void *getAddr(unsigned i);
};

void *foo::getAddr(unsigned i) { 
  return &array[i];
}

This becomes:
sbyte* %_ZN3foo7getAddrEj(%struct.foo* %this.1, uint %i.1) {
entry:          ; No predecessors!
        %tmp.4 = shl uint %i.1, ubyte 2         ; <uint> [#uses=1]
        %tmp.5 = cast uint %tmp.4 to int*               ; <int*> [#uses=1]
        %tmp.6 = cast %struct.foo* %this.1 to long              ; <long> [#uses=1]
        %tmp.7 = cast int* %tmp.5 to long               ; <long> [#uses=1]
        %tmp.8 = add long %tmp.7, %tmp.6                ; <long> [#uses=1]
        %tmp.9 = cast long %tmp.8 to sbyte*             ; <sbyte*> [#uses=1]
        ret sbyte* %tmp.9
}

Testcase here: test/Regression/C++Frontend/2003-11-08-ArrayAddress.cpp.tr

Ugh.

-Chris
------- Comment #3 From Chris Lattner 2003-11-08 17:07:49 -------
Wow, this is certainly QUITE amazing.  The reason that the problem resurfaced in
the C++ front-end is that it contains a COPY of a huge amount of code from the C
frontend, including this hunk of code.  Nasty nasty nasty.  At least this one
was easy to fix.

Here's the patch:

$ diff -u cp/typeck.c~ cp/typeck.c
--- typeck.c~   2003-08-27 16:50:48.000000000 -0500
+++ typeck.c    2003-11-08 17:05:24.000000000 -0600
@@ -3882,7 +3882,7 @@
        }
 
       /* For &x[y], return x+y */
-      if (TREE_CODE (arg) == ARRAY_REF)
+      if (TREE_CODE (arg) == ARRAY_REF && !EMIT_LLVM)
        {
          if (!cxx_mark_addressable (TREE_OPERAND (arg, 0)))
            return error_mark_node;


The second testcase now generates the following code:
sbyte* %_ZN3foo7getAddrEj(%struct.foo* %this.1, uint %i.1) {
entry:          ; No predecessors!
        %tmp.3 = cast uint %i.1 to long         ; <long> [#uses=1]
        %tmp.4 = getelementptr %struct.foo* %this.1, long 0, ubyte 0, long
%tmp.3               ; <int*> [#uses=1]
        %tmp.5 = cast int* %tmp.4 to sbyte*             ; <sbyte*> [#uses=1]
        ret sbyte* %tmp.5
}
------- Comment #4 From Chris Lattner 2003-11-08 18:15:37 -------
Here's another testcase:
char *test(char* C) {
  return C-1;   // Should turn into a GEP
}

This turns into:

sbyte* %test(sbyte* %C.1) {
entry:          ; No predecessors!
        %tmp.4 = cast sbyte* %C.1 to long               ; <long> [#uses=1]
        %tmp.6 = add long %tmp.4, -1            ; <long> [#uses=1]
        %tmp.7 = cast long %tmp.6 to sbyte*             ; <sbyte*> [#uses=1]
        ret sbyte* %tmp.7
}

This testcase is: CFrontend/2003-11-08-PointerSubNotGetelementptr.c.tr

-Chris
------- Comment #5 From Chris Lattner 2003-11-08 18:23:36 -------
Fixing this one was more challenging:

$ diff -u llvm-expand.c~ llvm-expand.c
--- llvm-expand.c~      2003-11-06 10:21:33.000000000 -0600
+++ llvm-expand.c       2003-11-08 18:21:08.000000000 -0600
@@ -2902,13 +2902,58 @@
   /*return 0;    // Used to disabled addsub transformation */
 
   assert(DestTy->ID == PointerTyID && op0->Ty->ID == PointerTyID);
-  if (!isAdd) return 0;   /* Only handle add so far */
 
   /* Dig through constant-expr casts. */
   if (op1->VTy == ConstantExpr &&
       ((llvm_constant_expr*)op1)->Inst->Opcode == O_Cast)
     op1 = ((llvm_constant_expr*)op1)->Inst->Operands[0];
 
+  /* Handle array indexing cases... */
+  if (DestTy == op0->Ty) {
+    llvm_type *ETy = GET_POINTER_TYPE_ELEMENT(DestTy);
+    unsigned ETySize = llvm_type_get_size(ETy);
+
+    /* If we are indexing through a one byte element types, turn it directly
+     * into a getelementptr instruction.
+     */
+    if (ETySize == 1) {
+      llvm_instruction *I = llvm_instruction_new(DestTy, "tmp",
+                                                 O_GetElementPtr, 2);
+      I->Operands[0] = op0;
+      I->Operands[1] = cast_if_type_not_equal(Fn, op1, LongTy);
+      if (!isAdd) {
+        /* If this is a subtract, negate the index! */
+        I->Operands[1] = append_inst(Fn, create_binary_inst("tmp", O_Sub,
+                                    llvm_constant_long_0, I->Operands[1]));
+      }
+
+      return append_inst(Fn, I);
+    }
+
+    /* Otherwise this was not a one byte element.  See if the constant we are
+     * adding or subtracting is a multiple of the type size.
+     */
+    if (op1->VTy != Constant) return 0;  /* Only works for constants */
+    if (V2C(op1)->Repr[0] < '0' || V2C(op1)->Repr[0] > '9')
+      return 0; /* Not a positive numeric constant? */
+    Value = llvm_constant_get_integer_val(V2C(op1)); /* Get the constant value*
/
+
+    /* We can only do this if it is an exact multiple! */
+    if (Value % ETySize == 0) {
+      long long GEPIdx = Value/ETySize;
+      llvm_instruction *I = llvm_instruction_new(DestTy, "tmp",
+                                                 O_GetElementPtr, 2);
+
+      if (!isAdd) GEPIdx *= -1;
+
+      I->Operands[0] = op0;
+      I->Operands[1] = llvm_constant_new_integral(LongTy, GEPIdx);
+      return append_inst(Fn, I);
+    }
+  }
+
+  if (!isAdd) return 0;   /* Only handle add so far */
+
   if (op1->VTy != Constant) return 0;  /* Only works for constants */
   if (V2C(op1)->Repr[0] < '0' || V2C(op1)->Repr[0] > '9')
     return 0; /* Not a positive numeric constant? */
------- Comment #6 From Chris Lattner 2003-11-08 22:22:57 -------
Division by zero is bad:

$ diff -u llvm-expand.c~ llvm-expand.c
--- llvm-expand.c~      2003-11-08 18:21:08.000000000 -0600
+++ llvm-expand.c       2003-11-08 22:13:27.000000000 -0600
@@ -2939,7 +2939,7 @@
     Value = llvm_constant_get_integer_val(V2C(op1)); /* Get the constant value*/
 
     /* We can only do this if it is an exact multiple! */
-    if (Value % ETySize == 0) {
+    if (ETySize && Value % ETySize == 0) {
       long long GEPIdx = Value/ETySize;
       llvm_instruction *I = llvm_instruction_new(DestTy, "tmp",
                                                  O_GetElementPtr, 2);
------- Comment #7 From Chris Lattner 2003-11-09 11:38:06 -------
Here's another patch.  The corresponding fix was already in the C frontend. 
Ugh.

* Fix address of structure element:

$ diff -u typeck.c~ typeck.c
--- typeck.c~   2003-11-08 17:05:24.000000000 -0600
+++ typeck.c    2003-11-09 11:34:20.000000000 -0600
@@ -4000,7 +4000,8 @@
          }
        else
          {
-           /* Unfortunately we cannot just build an address
+            tree addrLLVMTmp;
+           /* Unfortunately we cxannot just build an address
               expression here, because we would not handle
               address-constant-expressions or offsetof correctly.  */
            tree field = TREE_OPERAND (arg, 1);
@@ -4013,6 +4014,15 @@
            rval = build_nop (argtype, rval);
            addr = fold (build (PLUS_EXPR, argtype, rval,
                                cp_convert (argtype, byte_position (field))));
+
+            addrLLVMTmp = fold(build1(ADDR_EXPR, argtype, arg));
+            if (EMIT_LLVM &&
+                (TREE_CONSTANT(addrLLVMTmp) || !TREE_CONSTANT(addr))) {
+              /* For LLVM, don't fold pointer arithmetic unless doing so
+               * produces a constant!
+               */
+              addr = addrLLVMTmp;
+            }
          }

        if (TREE_CODE (argtype) == POINTER_TYPE
------- Comment #8 From Chris Lattner 2003-11-13 00:18:18 -------
Here's yet another testcase which is causing bad things to happen.  :(  This
equally affects the C and C++ front-ends.

#include <stdlib.h>

int *test(int Y) {
  int *X = (int*)malloc(4);

  return X + Y;
}
---
Generates:

int* %_Z4testi(int %Y.1) {
entry:          ; No predecessors!
        %tmp.0 = malloc int             ; <int*> [#uses=1]
        %tmp.4 = cast int %Y.1 to uint          ; <uint> [#uses=1]
        %tmp.5 = shl uint %tmp.4, ubyte 2               ; <uint> [#uses=1]
        %tmp.6 = cast uint %tmp.5 to int*               ; <int*> [#uses=1]
        %tmp.7 = cast int* %tmp.0 to long               ; <long> [#uses=1]
        %tmp.8 = cast int* %tmp.6 to long               ; <long> [#uses=1]
        %tmp.9 = add long %tmp.7, %tmp.8                ; <long> [#uses=1]
        %tmp.10 = cast long %tmp.9 to int*              ; <int*> [#uses=1]
        ret int* %tmp.10
}

------- Comment #9 From Chris Lattner 2003-11-13 01:10:54 -------
Testcase here:
llvm/test/Regression/CFrontend/2003-11-13-TypeSafety.c.tr
------- Comment #10 From Chris Lattner 2003-11-13 01:11:42 -------
Created an attachment (id=21) [details]
Patch to fix latest problem.

This should fix the (ptr) + (intval) problem.

-Chris

First Last Prev Next    No search results available