This patch applies on top of the previously posted patch "llvm-gcc: use component_ref_field_offset in component references", http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20070226/045399.html.
This fixes wrong handling of structs containing more than one variable sized field. Two C testcases are attached. Consider for example the struct from VarSizeInStruct1: struct f { char w; char x[n]; char z[]; }; DecodeStructFields does the following: it creates a field for w; it skips x; it doesn't skip z because it is the last field, however it thinks it starts at byte offset 0 because it starts at the variable offset n+1, and so pops w out of the struct and replaces it with z, leading to the LLVM struct { [0 * i8] }. This causes an assertion failure later on in EmitLV_COMPONENT_REF. The other testcase is similar, and leads to a different assertion failure. I first prepared a minimal fix, but later noticed that the code for handling variable sized struct fields was unnecessarily complicated and came up with this more involved patch, which removes a bunch of special casing. The existing code seems rather fixated on whether TYPE_SIZE is constant or not, when what really matters is whether the field starts at a constant offset. I simply skip over fields if and only if they don't start at a constant offset, and let variable sized fields be emitted and indexed like any others (no more ~0U field indices). I remove the special casing for ~0U field indices everywhere in favour of the generic code, for example in EmitLV_COMPONENT_REF. The only place where something still needs to be done is in ConvertRecordCONSTRUCTOR, but there too the code simplifies. I see no additional failures in the regression test suite or in MultiSource with these changes. Enjoy! Duncan.
Index: gcc.llvm.master/gcc/llvm-convert.cpp =================================================================== --- gcc.llvm.master.orig/gcc/llvm-convert.cpp 2007-03-05 13:05:41.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-convert.cpp 2007-03-05 14:55:33.000000000 +0100 @@ -4635,12 +4635,6 @@ assert(DECL_LLVM_SET_P(FieldDecl) && "Struct not laid out for LLVM?"); ConstantInt *CI = cast<ConstantInt>(DECL_LLVM(FieldDecl)); uint32_t MemberIndex = CI->getZExtValue(); - if (MemberIndex == ~0U) { - assert(isStructWithVarSizeArrayAtEnd(StructTy) && - "Isn't var sized array access!"); - CI = ConstantInt::get(Type::Int32Ty, StructTy->getNumContainedTypes()-1); - MemberIndex = CI->getZExtValue(); - } assert(MemberIndex < StructTy->getNumContainedTypes() && "Field Idx out of range!"); FieldPtr = new GetElementPtrInst(StructAddrLV.Ptr, @@ -5451,35 +5445,32 @@ // If not, things are much simpler. assert(DECL_LLVM_SET_P(Field) && "Struct not laid out for LLVM?"); unsigned FieldNo = cast<ConstantInt>(DECL_LLVM(Field))->getZExtValue(); - + assert(FieldNo < ResultElts.size() && "Invalid struct field number!"); + + // Example: struct X { int A; char C[]; } x = { 4, "foo" }; + assert(TYPE_SIZE(TREE_TYPE(Field)) || + (FieldNo == ResultElts.size()-1 && + isStructWithVarSizeArrayAtEnd(STy)) + && "field with no size is not array at end of struct!"); + // If this is an initialization of a global that ends with a variable // sized array at its end, and the initializer has a non-zero number of - // elements, we must handle this case now. In this case, FieldNo is ~0U - // and Val contains the actual type for the array. - if (FieldNo == ~0U) { - // Handle: struct X { int A; char C[]; } x = { 4, "foo" }; - assert(isStructWithVarSizeArrayAtEnd(STy) && - "Struct doesn't end with variable sized array!"); - FieldNo = STy->getNumElements()-1; - ResultElts[FieldNo] = Val; - } else { - assert(FieldNo < ResultElts.size() && "Invalid struct field number!"); - - // Otherwise, we know that the initializer has to match the element type - // of the LLVM structure field. If not, then there is something that is - // not straight-forward going on. For example, we could be initializing - // an unaligned integer field (e.g. due to attribute packed) with an - // integer. The struct field will have type [4 x ubyte] instead of - // "int" for example. If we ignored this, we would lay out the - // initializer wrong. - if (Val->getType() != STy->getElementType(FieldNo)) - Val = ConvertStructFieldInitializerToType(Val, + // elements, then Val contains the actual type for the array. Otherwise, + // we know that the initializer has to match the element type of the LLVM + // structure field. If not, then there is something that is not + // straight-forward going on. For example, we could be initializing an + // unaligned integer field (e.g. due to attribute packed) with an + // integer. The struct field will have type [4 x ubyte] instead of + // "int" for example. If we ignored this, we would lay out the + // initializer wrong. + if (TYPE_SIZE(TREE_TYPE(Field)) && + Val->getType() != STy->getElementType(FieldNo)) + Val = ConvertStructFieldInitializerToType(Val, STy->getElementType(FieldNo)); - ResultElts[FieldNo] = Val; - } + ResultElts[FieldNo] = Val; } - + NextField = TREE_CHAIN(Field); } @@ -5691,31 +5682,18 @@ ConstantInt *CI = cast<ConstantInt>(DECL_LLVM(FieldDecl)); uint64_t MemberIndex = CI->getZExtValue(); - if (MemberIndex != ~0U) { - std::vector<Value*> Idxs; - Idxs.push_back(Constant::getNullValue(Type::Int32Ty)); - Idxs.push_back(CI); - FieldPtr = ConstantExpr::getGetElementPtr(StructAddrLV, &Idxs[0], - Idxs.size()); - - // Now that we did an offset from the start of the struct, subtract off - // the offset from BitStart. - if (MemberIndex) { - const StructLayout *SL = TD.getStructLayout(cast<StructType>(StructTy)); - BitStart -= SL->getElementOffset(MemberIndex) * 8; - } - } else { - // We were unable to make a nice offset, emit an ugly one. - Constant *Offset = Convert(field_offset); - FieldPtr = ConstantExpr::getPtrToInt(StructAddrLV, Offset->getType()); - FieldPtr = ConstantExpr::getAdd(FieldPtr, Offset); - FieldPtr = ConstantExpr::getIntToPtr(FieldPtr, PointerType::get(FieldTy)); - - // Do horrible pointer arithmetic to get the address of the field. - unsigned ByteOffset = TREE_INT_CST_LOW(field_offset); - BitStart -= ByteOffset * 8; - } + std::vector<Value*> Idxs; + Idxs.push_back(Constant::getNullValue(Type::Int32Ty)); + Idxs.push_back(CI); + FieldPtr = ConstantExpr::getGetElementPtr(StructAddrLV, &Idxs[0], + Idxs.size()); + // Now that we did an offset from the start of the struct, subtract off + // the offset from BitStart. + if (MemberIndex) { + const StructLayout *SL = TD.getStructLayout(cast<StructType>(StructTy)); + BitStart -= SL->getElementOffset(MemberIndex) * 8; + } } else { Constant *Offset = Convert(field_offset); Constant *Ptr = ConstantExpr::getPtrToInt(StructAddrLV, Offset->getType()); Index: gcc.llvm.master/gcc/llvm-types.cpp =================================================================== --- gcc.llvm.master.orig/gcc/llvm-types.cpp 2007-03-05 13:05:20.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-types.cpp 2007-03-05 15:11:38.000000000 +0100 @@ -972,8 +972,11 @@ while (CurFieldNo < ElementOffsetInBytes.size() && getFieldEndOffsetInBytes(CurFieldNo)*8 <= FieldOffsetInBits) ++CurFieldNo; - if (CurFieldNo >= ElementOffsetInBytes.size()) return ~0U; - return CurFieldNo; + if (CurFieldNo < ElementOffsetInBytes.size()) + return CurFieldNo; + // Otherwise, we couldn't find the field! + assert(0 && "Could not find field!"); + return ~0U; } // Handle zero sized fields now. If the next field is zero sized, return @@ -1019,28 +1022,15 @@ return Result; } -/// isNotLastField - Return true if this is not the last field in its record. -/// -static bool isNotLastField(tree Field) { - for (Field = TREE_CHAIN(Field); Field; Field = TREE_CHAIN(Field)) - if (TREE_CODE(Field) == FIELD_DECL) - return true; - return false; -} - - /// DecodeStructFields - This method decodes the specified field, if it is a /// FIELD_DECL, adding or updating the specified StructTypeConversionInfo to /// reflect it. void TypeConverter::DecodeStructFields(tree Field, StructTypeConversionInfo &Info) { if (TREE_CODE(Field) != FIELD_DECL || - // Don't include variable sized fields, doing so would break anything - // after them... unless this is the last field. - ((TYPE_SIZE(TREE_TYPE(Field)) == 0 || - TREE_CODE(TYPE_SIZE(TREE_TYPE(Field))) != INTEGER_CST) && - isNotLastField(Field))) return; - + TREE_CODE(DECL_FIELD_OFFSET(Field)) != INTEGER_CST) + return; + // Handle bit-fields specially. if (DECL_BIT_FIELD_TYPE(Field)) { DecodeStructBitField(Field, Info); @@ -1228,12 +1218,8 @@ // variable offset. unsigned CurFieldNo = 0; for (tree Field = TYPE_FIELDS(type); Field; Field = TREE_CHAIN(Field)) - if (TREE_CODE(Field) == FIELD_DECL) { - // If this field comes after a variable sized element, stop trying to - // assign DECL_LLVM's. - if (TREE_CODE(DECL_FIELD_OFFSET(Field)) != INTEGER_CST) - break; - + if (TREE_CODE(Field) == FIELD_DECL && + TREE_CODE(DECL_FIELD_OFFSET(Field)) == INTEGER_CST) { unsigned FieldOffsetInBits = getFieldOffsetInBits(Field); tree FieldType = TREE_TYPE(Field); @@ -1251,10 +1237,9 @@ // Figure out if this field is zero bits wide, e.g. {} or [0 x int]. Do // not include variable sized fields here. - bool isZeroSizeField = - TYPE_SIZE(FieldType) && TREE_CODE(TYPE_SIZE(FieldType)) == INTEGER_CST&& - TREE_INT_CST_LOW(TYPE_SIZE(FieldType)) == 0; - + bool isZeroSizeField = !TYPE_SIZE(FieldType) || + integer_zerop(TYPE_SIZE(FieldType)); + unsigned FieldNo = Info.getLLVMFieldFor(FieldOffsetInBits, CurFieldNo, isZeroSizeField); SET_DECL_LLVM(Field, ConstantInt::get(Type::Int32Ty, FieldNo));
// RUN: %llvmgcc %s -S -o - void* p (int n) { struct f { char w; char x[n]; char z[]; } F; F.x[0]='x'; return &F; }
// RUN: %llvmgcc %s -S -o - char p (int n) { struct f { char w; char x[n]; char y[n]; } F; return F.x[0]; }
_______________________________________________ llvm-commits mailing list llvm-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits