[llvm-commits] [126210] Fix handling of arrays with var-sized elements

dpatel at apple.com dpatel at apple.com
Mon Apr 16 13:01:42 PDT 2007


Revision: 126210
Author:   dpatel
Date:     2007-04-16 13:01:41 -0700 (Mon, 16 Apr 2007)

Log Message:
-----------
Fix handling of arrays with var-sized elements
Patch by Duncan Sands.

Modified Paths:
--------------
    apple-local/branches/llvm/gcc/llvm-abi.h
    apple-local/branches/llvm/gcc/llvm-convert.cpp
    apple-local/branches/llvm/gcc/llvm-debug.cpp
    apple-local/branches/llvm/gcc/llvm-internal.h
    apple-local/branches/llvm/gcc/llvm-types.cpp

Modified: apple-local/branches/llvm/gcc/llvm-abi.h
===================================================================
--- apple-local/branches/llvm/gcc/llvm-abi.h	2007-04-16 18:55:33 UTC (rev 126209)
+++ apple-local/branches/llvm/gcc/llvm-abi.h	2007-04-16 20:01:41 UTC (rev 126210)
@@ -110,7 +110,7 @@
       }
     return FoundField ? isSingleElementStructOrArray(FoundField) : 0;
   case ARRAY_TYPE:
-    if (TREE_CODE(TYPE_SIZE(type)) != INTEGER_CST)
+    if (!isArrayCompatible(type))
       return 0;
     tree length = arrayLength(type);
     if (!length || !integer_onep(length))

Modified: apple-local/branches/llvm/gcc/llvm-convert.cpp
===================================================================
--- apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-04-16 18:55:33 UTC (rev 126209)
+++ apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-04-16 20:01:41 UTC (rev 126210)
@@ -253,6 +253,35 @@
   }
 }
 
+/// isInt64 - Return true if t is an INTEGER_CST that fits in a 64 bit integer.
+/// If Unsigned is false, returns whether it fits in a int64_t.  If Unsigned is
+/// true, returns whether the value is non-negative and fits in a uint64_t.
+/// Always returns false for overflowed constants.
+bool isInt64(tree_node *t, bool Unsigned) {
+  if (HOST_BITS_PER_WIDE_INT == 64)
+    return host_integerp(t, Unsigned);
+  else {
+    assert(HOST_BITS_PER_WIDE_INT == 32 &&
+           "Only 32- and 64-bit hosts supported!");
+    return
+      (TREE_CODE (t) == INTEGER_CST && !TREE_OVERFLOW (t))
+      && ((TYPE_UNSIGNED(TREE_TYPE(t)) == Unsigned) ||
+          // If the constant is signed and we want an unsigned result, check
+          // that the value is non-negative.  If the constant is unsigned and
+          // we want a signed result, check it fits in 63 bits.
+          (HOST_WIDE_INT)TREE_INT_CST_HIGH(t) >= 0);
+  }
+}
+
+/// getInt64 - Extract the value of an INTEGER_CST as a 64 bit integer.  If
+/// Unsigned is false, the value must fit in a int64_t.  If Unsigned is true,
+/// the value must be non-negative and fit in a uint64_t.  Must not be used on
+/// overflowed constants.  These conditions can be checked by calling isInt64.
+uint64_t getInt64(tree_node *t, bool Unsigned) {
+  assert(isInt64(t, Unsigned) && "invalid constant!");
+  return getINTEGER_CSTVal(t);
+}
+
 //===----------------------------------------------------------------------===//
 //                         ... High-Level Methods ...
 //===----------------------------------------------------------------------===//
@@ -1374,16 +1403,18 @@
     tree length;
 
     // Dynamic-size object: must push space on the stack.
-    if (TREE_CODE(type) == ARRAY_TYPE && (length = arrayLength(type))) {
+    if (TREE_CODE(type) == ARRAY_TYPE &&
+        isSequentialCompatible(type) &&
+        (length = arrayLength(type))) {
       Ty = ConvertType(TREE_TYPE(type));  // Get array element type.
       // Compute the number of elements in the array.
       Size = Emit(length, 0);
-      Size = CastToUIntType(Size, Size->getType());
     } else {
       // Compute the variable's size in bytes.
-      Size = CastToUIntType(Emit(DECL_SIZE_UNIT(decl), 0), Type::Int32Ty);
+      Size = Emit(DECL_SIZE_UNIT(decl), 0);
       Ty = Type::Int8Ty;
     }
+    Size = CastToUIntType(Size, Type::Int32Ty);
   }
   
   const char *Name;      // Name of variable
@@ -4645,94 +4676,71 @@
 }
 
 LValue TreeToLLVM::EmitLV_ARRAY_REF(tree exp) {
+  // The result type is an ElementTy* in the case of an ARRAY_REF, an array
+  // of ElementTy in the case of ARRAY_RANGE_REF.
+
   tree Array = TREE_OPERAND(exp, 0);
+  tree ArrayType = TREE_TYPE(Array);
   tree Index = TREE_OPERAND(exp, 1);
-  assert((TREE_CODE (TREE_TYPE(Array)) == ARRAY_TYPE ||
-          TREE_CODE (TREE_TYPE(Array)) == POINTER_TYPE ||
-          TREE_CODE (TREE_TYPE(Array)) == REFERENCE_TYPE) &&
+  tree IndexType = TREE_TYPE(Index);
+  tree ElementType = TREE_TYPE(ArrayType);
+
+  assert((TREE_CODE (ArrayType) == ARRAY_TYPE ||
+          TREE_CODE (ArrayType) == POINTER_TYPE ||
+          TREE_CODE (ArrayType) == REFERENCE_TYPE) &&
          "Unknown ARRAY_REF!");
-  
+
   // As an LLVM extension, we allow ARRAY_REF with a pointer as the first
   // operand.  This construct maps directly to a getelementptr instruction.
   Value *ArrayAddr;
-  
-  if (TREE_CODE(TREE_TYPE(Array)) == ARRAY_TYPE) {
+
+  if (TREE_CODE(ArrayType) == ARRAY_TYPE) {
     // First subtract the lower bound, if any, in the type of the index.
     tree LowerBound = array_ref_low_bound(exp);
     if (!integer_zerop(LowerBound))
-      Index = fold(build2(MINUS_EXPR, TREE_TYPE(Index), Index, LowerBound));
-    
+      Index = fold(build2(MINUS_EXPR, IndexType, Index, LowerBound));
+
     LValue ArrayAddrLV = EmitLV(Array);
     assert(!ArrayAddrLV.isBitfield() && "Arrays cannot be bitfields!");
     ArrayAddr = ArrayAddrLV.Ptr;
   } else {
     ArrayAddr = Emit(Array, 0);
   }
-  
+
   Value *IndexVal = Emit(Index, 0);
 
-  // FIXME: If UnitSize is a variable, or if it disagrees with the LLVM array
-  // element type, insert explicit pointer arithmetic here.
-  //tree ElementSizeInBytes = array_ref_element_size(exp);
-  
   const Type *IntPtrTy = getTargetData().getIntPtrType();
-  if (IndexVal->getType() != IntPtrTy) {
-    if (TYPE_UNSIGNED(TREE_TYPE(Index))) // if the index is unsigned
-      // ZExt it to retain its value in the larger type
-      IndexVal = CastToUIntType(IndexVal, IntPtrTy);
-    else
-      // SExt it to retain its value in the larger type
-      IndexVal = CastToSIntType(IndexVal, IntPtrTy);
-  }
+  if (TYPE_UNSIGNED(IndexType)) // if the index is unsigned
+    // ZExt it to retain its value in the larger type
+    IndexVal = CastToUIntType(IndexVal, IntPtrTy);
+  else
+    // SExt it to retain its value in the larger type
+    IndexVal = CastToSIntType(IndexVal, IntPtrTy);
 
-  // If this is an index into an array, codegen as a GEP.
-  if (TREE_CODE(TREE_TYPE(Array)) == ARRAY_TYPE) {
-    Value *Ptr;
-
-    // Check for variable sized array reference.
-    tree length = arrayLength(TREE_TYPE(Array));
-    if (length && !host_integerp(length, 1)) {
-      // Make sure that ArrayAddr is of type ElementTy*, then do a 2-index gep.
-      ArrayAddr = BitCastToType(ArrayAddr, PointerType::get(Type::Int8Ty));
-      Value *Scale = Emit(array_ref_element_size(exp), 0);
-      if (Scale->getType() != IntPtrTy)
-        Scale = CastToUIntType(Scale, IntPtrTy);
-
-      IndexVal = BinaryOperator::createMul(IndexVal, Scale, "tmp", CurBB);
-      Ptr = new GetElementPtrInst(ArrayAddr, IndexVal, "tmp", CurBB);
-    } else {
-      // Otherwise, this is not a variable-sized array, use a GEP to index.
-      Ptr = new GetElementPtrInst(ArrayAddr, ConstantInt::get(Type::Int32Ty, 0),
-                                  IndexVal, "tmp", CurBB);
-    }
-
-    // The result type is an ElementTy* in the case of an ARRAY_REF, an array
-    // of ElementTy in the case of ARRAY_RANGE_REF.  Return the correct type.
+  // If this is an index into an LLVM array, codegen as a GEP.
+  if (isArrayCompatible(ArrayType)) {
+    Value *Ptr = new GetElementPtrInst(ArrayAddr,
+                                       ConstantInt::get(Type::Int32Ty, 0),
+                                       IndexVal, "tmp", CurBB);
     return BitCastToType(Ptr, PointerType::get(ConvertType(TREE_TYPE(exp))));
   }
 
-  // Otherwise, this is an index off a pointer, codegen as a 2-idx GEP.
-  assert(TREE_CODE(TREE_TYPE(Array)) == POINTER_TYPE ||
-         TREE_CODE(TREE_TYPE(Array)) == REFERENCE_TYPE);
-  tree IndexedType = TREE_TYPE(TREE_TYPE(Array));
-  
   // If we are indexing over a fixed-size type, just use a GEP.
-  if (TREE_CODE(TYPE_SIZE(IndexedType)) == INTEGER_CST) {
-    const Type *PtrIndexedTy = PointerType::get(ConvertType(IndexedType));
-    ArrayAddr = BitCastToType(ArrayAddr, PtrIndexedTy);
-    return new GetElementPtrInst(ArrayAddr, IndexVal, "tmp", CurBB);
+  if (isSequentialCompatible(ArrayType)) {
+    const Type *PtrElementTy = PointerType::get(ConvertType(ElementType));
+    ArrayAddr = BitCastToType(ArrayAddr, PtrElementTy);
+    Value *Ptr = new GetElementPtrInst(ArrayAddr, IndexVal, "tmp", CurBB);
+    return BitCastToType(Ptr, PointerType::get(ConvertType(TREE_TYPE(exp))));
   }
-  
+
   // Otherwise, just do raw, low-level pointer arithmetic.  FIXME: this could be
   // much nicer in cases like:
   //   float foo(int w, float A[][w], int g) { return A[g][0]; }
-  
+
   ArrayAddr = BitCastToType(ArrayAddr, PointerType::get(Type::Int8Ty));
   Value *TypeSize = Emit(array_ref_element_size(exp), 0);
+  TypeSize = CastToUIntType(TypeSize, IntPtrTy);
 
-  if (TypeSize->getType() != IntPtrTy)
-    TypeSize = CastToUIntType(TypeSize, IntPtrTy);
-  
   IndexVal = BinaryOperator::createMul(IndexVal, TypeSize, "tmp", CurBB);
   Value *Ptr = new GetElementPtrInst(ArrayAddr, IndexVal, "tmp", CurBB);
 
@@ -5790,44 +5798,41 @@
 
 Constant *TreeConstantToLLVM::EmitLV_ARRAY_REF(tree exp) {
   tree Array = TREE_OPERAND(exp, 0);
+  tree ArrayType = TREE_TYPE(Array);
   tree Index = TREE_OPERAND(exp, 1);
-  assert((TREE_CODE (TREE_TYPE(Array)) == ARRAY_TYPE ||
-          TREE_CODE (TREE_TYPE(Array)) == POINTER_TYPE ||
-          TREE_CODE (TREE_TYPE(Array)) == REFERENCE_TYPE) &&
+  tree IndexType = TREE_TYPE(Index);
+  assert((TREE_CODE (ArrayType) == ARRAY_TYPE ||
+          TREE_CODE (ArrayType) == POINTER_TYPE ||
+          TREE_CODE (ArrayType) == REFERENCE_TYPE) &&
          "Unknown ARRAY_REF!");
-  
+
+  // Check for variable sized reference.
+  // FIXME: add support for array types where the size doesn't fit into 64 bits
+  assert(isArrayCompatible(ArrayType) || isSequentialCompatible(ArrayType)
+         && "Cannot have globals with variable size!");
+
   // As an LLVM extension, we allow ARRAY_REF with a pointer as the first
   // operand.  This construct maps directly to a getelementptr instruction.
   Constant *ArrayAddr;
-  if (TREE_CODE (TREE_TYPE(Array)) == ARRAY_TYPE) {
+  if (TREE_CODE (ArrayType) == ARRAY_TYPE) {
     // First subtract the lower bound, if any, in the type of the index.
     tree LowerBound = array_ref_low_bound(exp);
     if (!integer_zerop(LowerBound))
-      Index = fold(build2(MINUS_EXPR, TREE_TYPE(Index), Index, LowerBound));
+      Index = fold(build2(MINUS_EXPR, IndexType, Index, LowerBound));
     ArrayAddr = EmitLV(Array);
   } else {
     ArrayAddr = Convert(Array);
   }
   
   Constant *IndexVal = Convert(Index);
-  
-  // FIXME: If UnitSize is a variable, or if it disagrees with the LLVM array
-  // element type, insert explicit pointer arithmetic here.
-  //tree ElementSizeInBytes = array_ref_element_size(exp);
-  
-  if (IndexVal->getType() != Type::Int32Ty &&
-      IndexVal->getType() != Type::Int64Ty)
-    IndexVal = ConstantExpr::getSExtOrBitCast(IndexVal, Type::Int64Ty);
-  
-  // Check for variable sized array reference.
-  if (TREE_CODE(TREE_TYPE(Array)) == ARRAY_TYPE) {
-    tree length = arrayLength(TREE_TYPE(Array));
-    assert(!length || host_integerp(length, 1) &&
-           "Cannot have globals with variable size!");
-  }
 
+  const Type *IntPtrTy = getTargetData().getIntPtrType();
+  if (IndexVal->getType() != IntPtrTy)
+    IndexVal = ConstantExpr::getIntegerCast(IndexVal, IntPtrTy,
+                                            !TYPE_UNSIGNED(IndexType));
+
   std::vector<Value*> Idx;
-  if (TREE_CODE (TREE_TYPE(Array)) == ARRAY_TYPE)
+  if (isArrayCompatible(ArrayType))
     Idx.push_back(ConstantInt::get(Type::Int32Ty, 0));
   Idx.push_back(IndexVal);
   return ConstantExpr::getGetElementPtr(ArrayAddr, &Idx[0], Idx.size());

Modified: apple-local/branches/llvm/gcc/llvm-debug.cpp
===================================================================
--- apple-local/branches/llvm/gcc/llvm-debug.cpp	2007-04-16 18:55:33 UTC (rev 126209)
+++ apple-local/branches/llvm/gcc/llvm-debug.cpp	2007-04-16 20:01:41 UTC (rev 126210)
@@ -582,8 +582,6 @@
           tree MinValue = TYPE_MIN_VALUE(Domain);
           tree MaxValue = TYPE_MAX_VALUE(Domain);
           if (MinValue && MaxValue &&
-              TREE_CODE(MinValue) == INTEGER_CST &&
-              TREE_CODE(MaxValue) == INTEGER_CST &&
               host_integerp(MinValue, 0) &&
               host_integerp(MaxValue, 0)) {
             Subrange->setLo(tree_low_cst(MinValue, 0));

Modified: apple-local/branches/llvm/gcc/llvm-internal.h
===================================================================
--- apple-local/branches/llvm/gcc/llvm-internal.h	2007-04-16 18:55:33 UTC (rev 126209)
+++ apple-local/branches/llvm/gcc/llvm-internal.h	2007-04-16 20:01:41 UTC (rev 126210)
@@ -144,11 +144,32 @@
   return TheTypeConverter->ConvertType(type);
 }
 
+/// isInt64 - Return true if t is an INTEGER_CST that fits in a 64 bit integer.
+/// If Unsigned is false, returns whether it fits in a int64_t.  If Unsigned is
+/// true, returns whether the value is non-negative and fits in a uint64_t.
+/// Always returns false for overflowed constants.
+bool isInt64(tree_node *t, bool Unsigned);
+
+/// getInt64 - Extract the value of an INTEGER_CST as a 64 bit integer.  If
+/// Unsigned is false, the value must fit in a int64_t.  If Unsigned is true,
+/// the value must be non-negative and fit in a uint64_t.  Must not be used on
+/// overflowed constants.  These conditions can be checked by calling isInt64.
+uint64_t getInt64(tree_node *t, bool Unsigned);
+
 /// isPassedByInvisibleReference - Return true if the specified type should be
 /// passed by 'invisible reference'. In other words, instead of passing the
 /// thing by value, pass the address of a temporary.
 bool isPassedByInvisibleReference(tree_node *type);
 
+/// isSequentialCompatible - Return true if the specified gcc array or pointer
+/// type and the corresponding LLVM SequentialType lay out their components
+/// identically in memory.
+bool isSequentialCompatible(tree_node *type);
+
+/// isArrayCompatible - Return true if the specified gcc array or pointer type
+/// corresponds to an LLVM array type.
+bool isArrayCompatible(tree_node *type);
+
 /// arrayLength - Return a tree expressing the number of elements in an array
 /// of the specified type, or NULL if the type does not specify the length.
 tree_node *arrayLength(tree_node *type);

Modified: apple-local/branches/llvm/gcc/llvm-types.cpp
===================================================================
--- apple-local/branches/llvm/gcc/llvm-types.cpp	2007-04-16 18:55:33 UTC (rev 126209)
+++ apple-local/branches/llvm/gcc/llvm-types.cpp	2007-04-16 20:01:41 UTC (rev 126210)
@@ -73,6 +73,10 @@
 // Note down LLVM type for GCC tree node.
 static const Type * llvm_set_type(tree Tr, const Type *Ty) {
 
+  assert(!TYPE_SIZE(Tr) || !Ty->isSized() || !isInt64(TYPE_SIZE(Tr), true) ||
+         getInt64(TYPE_SIZE(Tr), true) == getTargetData().getTypeSizeInBits(Ty)
+         && "LLVM type size doesn't match GCC type size!");
+
   unsigned &TypeSlot = LTypesMap[Ty];
   if (TypeSlot) {
     // Already in map.
@@ -273,6 +277,39 @@
   return Prefix + ContextStr + Name;
 }
 
+/// isSequentialCompatible - Return true if the specified gcc array or pointer
+/// type and the corresponding LLVM SequentialType lay out their components
+/// identically in memory.
+bool isSequentialCompatible(tree_node *type) {
+  assert((TREE_CODE (type) == ARRAY_TYPE ||
+          TREE_CODE (type) == POINTER_TYPE ||
+          TREE_CODE (type) == REFERENCE_TYPE) && "not a sequential type!");
+  // This relies on gcc types with constant size mapping to LLVM types with the
+  // same size.
+  return isInt64(TYPE_SIZE(TREE_TYPE(type)), true);
+}
+
+/// isArrayCompatible - Return true if the specified gcc array or pointer type
+/// corresponds to an LLVM array type.
+bool isArrayCompatible(tree_node *type) {
+  assert((TREE_CODE (type) == ARRAY_TYPE ||
+          TREE_CODE (type) == POINTER_TYPE ||
+          TREE_CODE (type) == REFERENCE_TYPE) && "not a sequential type!");
+  return
+    (TREE_CODE (type) == ARRAY_TYPE) && (
+      // Arrays with no size are fine as long as their components are layed out
+      // the same way in memory by LLVM.  For example "int X[]" -> "[0 x int]".
+      (!TYPE_SIZE(type) && isSequentialCompatible(type)) ||
+
+      // Arrays with constant size map to LLVM arrays.  If the array has zero
+      // size then there can be two exotic cases: (1) the array might have zero
+      // length and a component type of variable size; or (2) the array could
+      // have variable length and a component type with zero size.  In both
+      // cases we convert to a zero length LLVM array.
+      (TYPE_SIZE(type) && isInt64(TYPE_SIZE(type), true))
+    );
+}
+
 /// arrayLength - Return a tree expressing the number of elements in an array
 /// of the specified type, or NULL if the type does not specify the length.
 tree_node *arrayLength(tree_node *type) {
@@ -582,30 +619,37 @@
     if (const Type *Ty = GET_TYPE_LLVM(type))
       return Ty;
 
-    unsigned NumElements;
-    tree length = arrayLength(type);
-    if (length) {
-      if (host_integerp(length, 1)) {
+    if (isArrayCompatible(type)) {
+      uint64_t NumElements;
+      tree length = arrayLength(type);
+
+      if (!length) {
+        // We get here if we have something that is globally declared as an
+        // array with no dimension, this becomes just a zero size array of the
+        // element type so that: int X[] becomes *'%X = external global [0 x int]'
+        //
+        // Note that this also affects new expressions, which return a pointer to
+        // an unsized array of elements.
+        NumElements = 0;
+      } else if (!isInt64(length, true)) {
+        // A variable length array where the element type has size zero.  Turn
+        // it into a zero length array of the element type.
+        assert(integer_zerop(TYPE_SIZE(TREE_TYPE(type)))
+               && "variable length array has constant size!");
+        NumElements = 0;
+      } else {
         // Normal array.
-        NumElements = tree_low_cst(length, 1);
-      } else {
-        // This handles cases like "int A[n]" which have a runtime constant
-        // number of elements, but is a compile-time variable.  Since these are
-        // variable sized, we just represent them as the element themself.
-        return TypeDB.setType(type, ConvertType(TREE_TYPE(type)));
+        NumElements = getInt64(length, true);
       }
-    } else {
-      // We get here is if they have something that is globally declared as an
-      // array with no dimension, this becomes just a zero size array of the
-      // element type so that: int X[] becomes *'%X = external global [0 x int]'
-      //
-      // Note that this also affects new expressions, which return a pointer to
-      // an unsized array of elements.
-      NumElements = 0;
+
+      return TypeDB.setType(type, ArrayType::get(ConvertType(TREE_TYPE(type)),
+                                                 NumElements));
     }
-    
-    return TypeDB.setType(type, ArrayType::get(ConvertType(TREE_TYPE(type)),
-                                                NumElements));
+
+    // This handles cases like "int A[n]" which have a runtime constant
+    // number of elements, but is a compile-time variable.  Since these are
+    // variable sized, we just represent them as the element themself.
+    return TypeDB.setType(type, ConvertType(TREE_TYPE(type)));
   }
   case OFFSET_TYPE:
     // Handle OFFSET_TYPE specially.  This is used for pointers to members,





More information about the llvm-commits mailing list