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, _______________________________________________ llvm-commits mailing list llvm-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits