diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -118,6 +118,7 @@ - Improve the dump format, dump both bitwidth(if its a bitfield) and field value. - Remove anonymous tag locations. - Beautify dump format, add indent for nested struct and struct members. + - Support constant array in struct and union. New Compiler Flags ------------------ diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2043,21 +2043,9 @@ return RValue::get(Overflow); } -static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, - LValue RecordLV, CharUnits Align, - llvm::FunctionCallee Func, int Lvl) { - ASTContext &Context = CGF.getContext(); - RecordDecl *RD = RType->castAs()->getDecl()->getDefinition(); - std::string Pad = std::string(Lvl * 4, ' '); - std::string ElementPad = std::string((Lvl + 1) * 4, ' '); - - PrintingPolicy Policy(Context.getLangOpts()); - Policy.AnonymousTagLocations = false; - Value *GString = CGF.Builder.CreateGlobalStringPtr( - llvm::Twine(Pad).concat(RType.getAsString(Policy)).concat(" {\n").str()); - Value *Res = CGF.Builder.CreateCall(Func, {GString}); - - static llvm::DenseMap Types; +static StringRef getDumpStructFormatSpecifier(ASTContext &Context, + QualType Type) { + static llvm::DenseMap Types; if (Types.empty()) { Types[Context.CharTy] = "%c"; Types[Context.BoolTy] = "%d"; @@ -2079,10 +2067,156 @@ Types[Context.getPointerType(Context.getConstType(Context.CharTy))] = "%s"; } + if (Types.find(Type) == Types.end()) + return Types[Context.VoidPtrTy]; + return Types[Type]; +} + +static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, + LValue RecordLV, CharUnits Align, bool inArray, + llvm::FunctionCallee Func, int Lvl); + +static llvm::Value *dumpArray(CodeGenFunction &CGF, QualType ArrayType, + LValue LV, CharUnits Align, + llvm::FunctionCallee Func, int Lvl) { + assert(ArrayType->isConstantArrayType() && "required an array type"); + auto &Builder = CGF.Builder; + ASTContext &Context = CGF.getContext(); + std::string Pad = std::string(Lvl * 4, ' '); + std::string ElementPad = std::string((Lvl + 1) * 4, ' '); + std::string &LastElementPad = Pad; + std::string Name = "__builtin_dump_struct.array."; + + auto GetConstInt = [&CGF](int64_t Val) -> llvm::ConstantInt * { + return llvm::ConstantInt::get(CGF.IntPtrTy, Val, true); + }; + + auto GetInstName = [&Name, &Lvl](StringRef Suffix) -> std::string { + return llvm::Twine(Name) + .concat(Suffix) + .concat(".") + .concat(llvm::Twine(Lvl)) + .str(); + }; + + const ConstantArrayType *CAT = Context.getAsConstantArrayType(ArrayType); + QualType ElementType = CAT->getElementType(); + Address ArrayAddress = LV.getAddress(CGF); + + CharUnits ElementAlign = ArrayAddress.getAlignment().alignmentOfArrayElement( + Context.getTypeSizeInChars(ArrayType)); + + const llvm::ArrayType *LLVMArrayType = + dyn_cast(ArrayAddress.getElementType()); + + // The length of an array in elements, as well as the base element type and a + // properly-typed first element pointer. + llvm::Value *Length = GetConstInt(LLVMArrayType->getNumElements()); + + // Normally we have to check whether the array is zero-length. + bool CheckZeroLength = true; + // But if the array length is constant, we can suppress that. + if (llvm::ConstantInt *CL = dyn_cast(Length)) { + // ...and if it's constant zero, we can just skip the entire thing. + if (!CL->isZero()) + CheckZeroLength = false; + } + + llvm::Value *Begin = ArrayAddress.getPointer(); + llvm::Value *End = + Builder.CreateInBoundsGEP(ArrayAddress.getElementType(), Begin, Length); + + Address IndexAddr = CGF.CreateTempAlloca(CGF.IntPtrTy, CGF.getSizeAlign(), + "index", GetConstInt(1), nullptr); + llvm::Value *Index = Builder.CreateStore(GetConstInt(0), IndexAddr); + + // The basic structure here is a do-while loop, because we don't + // need to check for the zero-element case. + llvm::BasicBlock *BodyBB = CGF.createBasicBlock(GetInstName("body")); + llvm::BasicBlock *DoneBB = CGF.createBasicBlock(GetInstName("done")); + + if (CheckZeroLength) { + llvm::Value *isEmpty = + Builder.CreateICmpEQ(Begin, End, GetInstName("isempty")); + CGF.Builder.CreateCondBr(isEmpty, DoneBB, BodyBB); + } + + llvm::Value *GString = Builder.CreateGlobalStringPtr("[\n"); + llvm::Value *Res = Builder.CreateCall(Func, {GString}); + llvm::Value *TmpRes = nullptr; + + CGF.EmitBlock(BodyBB); + Index = Builder.CreateLoad(IndexAddr); + llvm::Value *Element = Builder.CreateInBoundsGEP( + ArrayAddress.getElementType(), ArrayAddress.getPointer(), + {CGF.CGM.getSize(CharUnits::Zero()), Index}); + Address ElementAddr = + Address(Element, LLVMArrayType->getElementType(), ElementAlign); + LValue ElementLV = CGF.MakeAddrLValue(ElementAddr, ElementType); + + GString = Builder.CreateGlobalStringPtr( + llvm::Twine(ElementPad).concat("[%ld] = ").str()); + TmpRes = CGF.Builder.CreateCall(Func, {GString, Index}); + Res = CGF.Builder.CreateAdd(Res, TmpRes); + + if (ElementType->isConstantArrayType()) { + TmpRes = + dumpArray(CGF, ElementType, ElementLV, ElementAlign, Func, Lvl + 1); + Res = Builder.CreateAdd(Res, TmpRes); + } else { + if (ElementType->isRecordType()) { + TmpRes = dumpRecord(CGF, ElementType, ElementLV, ElementAlign, true, Func, + Lvl + 1); + } else { + StringRef Format = getDumpStructFormatSpecifier(Context, ElementType); + GString = + Builder.CreateGlobalStringPtr(llvm::Twine(Format).concat("\n").str()); + RValue RV = CGF.EmitLoadOfLValue(ElementLV, SourceLocation()); + TmpRes = CGF.Builder.CreateCall(Func, {GString, RV.getScalarVal()}); + } + Res = CGF.Builder.CreateAdd(Res, TmpRes); + } + + Index = Builder.CreateAdd(Index, GetConstInt(1)); + Builder.CreateStore(Index, IndexAddr); + + llvm::Value *Done = Builder.CreateICmpEQ(Index, Length); + Builder.CreateCondBr(Done, DoneBB, BodyBB); + + CGF.EmitBlock(DoneBB); + GString = Builder.CreateGlobalStringPtr( + llvm::Twine(LastElementPad).concat("]\n").str()); + TmpRes = Builder.CreateCall(Func, {GString}); + Res = Builder.CreateAdd(TmpRes, Res); + return Res; +} + +static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, + LValue RecordLV, CharUnits Align, bool inArray, + llvm::FunctionCallee Func, int Lvl) { + ASTContext &Context = CGF.getContext(); + RecordDecl *RD = RType->castAs()->getDecl()->getDefinition(); + std::string Pad = std::string(Lvl * 4, ' '); + std::string FieldPad = std::string((Lvl + 1) * 4, ' '); + + Value *GString = nullptr; + if (inArray) { + GString = CGF.Builder.CreateGlobalStringPtr("{\n"); + } else { + PrintingPolicy Policy(Context.getLangOpts()); + Policy.AnonymousTagLocations = false; + GString = + CGF.Builder.CreateGlobalStringPtr(llvm::Twine(Pad) + .concat(RType.getAsString(Policy)) + .concat(" {\n") + .str()); + } + Value *Res = CGF.Builder.CreateCall(Func, {GString}); + for (const auto *FD : RD->fields()) { Value *TmpRes = nullptr; - std::string Format = llvm::Twine(ElementPad) + std::string Format = llvm::Twine(FieldPad) .concat(FD->getType().getAsString()) .concat(llvm::Twine(' ')) .concat(FD->getNameAsString()) @@ -2114,15 +2248,24 @@ // We check whether we are in a recursive type if (CanonicalType->isRecordType()) { - TmpRes = dumpRecord(CGF, CanonicalType, FieldLV, Align, Func, Lvl + 1); + TmpRes = + dumpRecord(CGF, CanonicalType, FieldLV, Align, false, Func, Lvl + 1); + Res = CGF.Builder.CreateAdd(TmpRes, Res); + continue; + } + + if (CanonicalType->isConstantArrayType()) { + GString = CGF.Builder.CreateGlobalStringPtr( + llvm::Twine(Format).concat(" = ").str()); + TmpRes = CGF.Builder.CreateCall(Func, {GString}); + Res = CGF.Builder.CreateAdd(Res, TmpRes); + TmpRes = dumpArray(CGF, CanonicalType, FieldLV, Align, Func, Lvl + 1); Res = CGF.Builder.CreateAdd(TmpRes, Res); continue; } // We try to determine the best format to print the current field - const char *TypeFormat = Types.find(CanonicalType) == Types.end() - ? Types[Context.VoidPtrTy] - : Types[CanonicalType]; + StringRef TypeFormat = getDumpStructFormatSpecifier(Context, CanonicalType); GString = CGF.Builder.CreateGlobalStringPtr(llvm::Twine(Format) .concat(" = ") @@ -2681,7 +2824,7 @@ Value *RecordPtr = EmitScalarExpr(Arg0); LValue RecordLV = MakeAddrLValue(RecordPtr, Arg0Type, Arg0Align); - Value *Res = dumpRecord(*this, Arg0Type, RecordLV, Arg0Align, + Value *Res = dumpRecord(*this, Arg0Type, RecordLV, Arg0Align, false, {LLVMFuncType, Func}, 0); return RValue::get(Res); } diff --git a/clang/test/CodeGen/dump-struct-builtin.c b/clang/test/CodeGen/dump-struct-builtin.c --- a/clang/test/CodeGen/dump-struct-builtin.c +++ b/clang/test/CodeGen/dump-struct-builtin.c @@ -76,7 +76,11 @@ // CHECK: @__const.unit15.a = private unnamed_addr constant %struct.U15A { [3 x i32] [i32 1, i32 2, i32 3] }, align 4 // CHECK-NEXT: [[STRUCT_STR_U15:@[0-9]+]] = private unnamed_addr constant [15 x i8] c"struct U15A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" int[3] a = %p\0A\00", align 1 +// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" int[3] a = \00", align 1 +// CHECK-NEXT: [[ARRAY_L_SQUARE_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_INDEX_U15:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" [%ld] = \00", align 1 +// CHECK-NEXT: [[ARRAY_ELEMENT_U15:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_R_SQUARE_U15:@[0-9]+]] = private unnamed_addr constant [7 x i8] c" ]\0A\00", align 1 // CHECK-NEXT: [[END_STRUCT_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1 // CHECK: @__const.unit16.a = private unnamed_addr constant %struct.U16A { i8 12 }, align 1 @@ -94,6 +98,18 @@ // CHECK-NEXT: [[FIELD_U18:@[0-9]+]] = private unnamed_addr constant [25 x i8] c" long double a = %Lf\0A\00", align 1 // CHECK-NEXT: [[END_STRUCT_U18:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1 +// CHECK: @__const.unit19.a = private unnamed_addr constant %struct.T19A { {{.*}} }, align 4 +// CHECK-NEXT: [[STRUCT_STR_U19:@[0-9]+]] = private unnamed_addr constant [15 x i8] c"struct T19A {\0A\00", align 1 +// CHECK-NEXT: [[FIELD_U19:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" int[2][2] b = \00", align 1 +// CHECK-NEXT: [[ARRAY_L_SQUARE_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_INDEX_U19:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" [%ld] = \00", align 1 +// CHECK-NEXT: [[ARRAY_L_SQUARE2_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_INDEX2_U19:@[0-9]+]] = private unnamed_addr constant [21 x i8] c" [%ld] = \00", align 1 +// CHECK-NEXT: [[ARRAY_ELEMENT_U19:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_R_SQUARE2_U19:@[0-9]+]] = private unnamed_addr constant [11 x i8] c" ]\0A\00", align 1 +// CHECK-NEXT: [[ARRAY_R_SQUARE_U19:@[0-9]+]] = private unnamed_addr constant [7 x i8] c" ]\0A\00", align 1 +// CHECK-NEXT: [[END_STRUCT_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1 + int printf(const char *fmt, ...) { return 0; } @@ -107,8 +123,8 @@ .a = 12, }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U1]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U1]], i32 0, i32 0), i16 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U1]], i32 0, i32 0)) __builtin_dump_struct(&a, &printf); @@ -124,8 +140,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U2]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* [[FIELD_U2]], i32 0, i32 0), i16 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U2]], i32 0, i32 0)) __builtin_dump_struct(&a, &printf); @@ -141,8 +157,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U3]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U3]], i32 0, i32 0), i32 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U3]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -158,8 +174,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U4]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* [[FIELD_U4]], i32 0, i32 0), i32 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U4]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -175,8 +191,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U5]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* [[FIELD_U5]], i32 0, i32 0), i64 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U5]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -192,8 +208,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U6]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* [[FIELD_U6]], i32 0, i32 0), i64 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U6]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -209,8 +225,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U7]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[FIELD_U7]], i32 0, i32 0), i64 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U7]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -226,8 +242,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U8]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([33 x i8], [33 x i8]* [[FIELD_U8]], i32 0, i32 0), i64 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U8]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -243,8 +259,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U9]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i8, i8* [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[FIELD_U9]], i32 0, i32 0), i8 [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U9]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -260,8 +276,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U10]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U10]], i32 0, i32 0), i8* [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U10]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -277,8 +293,8 @@ }; // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U11]], i32 0, i32 0)) - // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]], + // CHECK: [[RES1:%[−a−zA−Z._0-9]+]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U11]], i32 0, i32 0), i8* [[LOAD1]]) // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U11]], i32 0, i32 0) __builtin_dump_struct(&a, &printf); @@ -347,10 +363,26 @@ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U15]], i32 0, i32 0)) // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0 - // CHECK: [[LOAD1:%.*]] = load [3 x i32], [3 x i32]* [[RES1]], - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U15]], i32 0, i32 0), [3 x i32] [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U15]], i32 0, i32 0) - __builtin_dump_struct(&a, &printf); + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U15]], i32 0, i32 0)) + // CHECK: [[ARRAY_PTR:%.*]] = getelementptr inbounds [3 x i32], [3 x i32]* [[RES1]], i64 3 + // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1, + // CHECK: store i64 0, i64* [[ALLOC_INDEX]], + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE_U15]], i32 0, i32 0)) + // CHECK: br label %__builtin_dump_struct.array.body.1 + + // CHECK: __builtin_dump_struct.array.body.1: + // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]] + // CHECK: [[RES2:%.*]] = getelementptr inbounds [3 x i32], [3 x i32]* [[RES1]], i64 0, i64 [[INDEX]] + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[ARRAY_INDEX_U15]], i32 0, i32 0), i64 [[INDEX]]) + // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES2]], + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[ARRAY_ELEMENT_U15]], i32 0, i32 0), i32 [[LOAD1]]) + // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1 + // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]], + // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 3 + // CHECK: br i1 [[DONE]], label %__builtin_dump_struct.array.done.1, label %__builtin_dump_struct.array.body.1 + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[ARRAY_R_SQUARE_U15]], i32 0, i32 0)) + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U15]], i32 0, i32 0)) + __builtin_dump_struct(&a, &printf); } void unit16(void) { @@ -404,6 +436,57 @@ __builtin_dump_struct(&a, &printf); } +void unit19(void) { + struct T19A { + int b[2][2]; + }; + struct T19A a = { + .b = {{1, 2}, {3, 4}} + }; + + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U19]], i32 0, i32 0)) + // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T19A, %struct.T19A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U19]], i32 0, i32 0)) + // CHECK: [[RES2:%.*]] = getelementptr inbounds [2 x [2 x i32]], [2 x [2 x i32]]* [[RES1]], i64 2 + // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1, + // CHECK: store i64 0, i64* [[ALLOC_INDEX]], + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE_U19]], i32 0, i32 0)) + // CHECK: br label %__builtin_dump_struct.array.body.1 + + // CHECK: __builtin_dump_struct.array.body.1: + // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]] + // CHECK: [[RES3:%.*]] = getelementptr inbounds [2 x [2 x i32]], [2 x [2 x i32]]* [[RES1]], i64 0, i64 [[INDEX]] + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[ARRAY_INDEX_U19]], i32 0, i32 0), i64 [[INDEX]]) + // CHECK: [[RES4:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[RES3]], i64 2 + // CHECK: [[ALLOC_INDEX1:%.*]] = alloca i64, i64 1, + // CHECK: store i64 0, i64* [[ALLOC_INDEX1]], + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE2_U19]], i32 0, i32 0)) + // CHECK: br label %__builtin_dump_struct.array.body.2 + + // CHECK: __builtin_dump_struct.array.body.2: + // CHECK: [[INDEX1:%.*]] = load i64, i64* [[ALLOC_INDEX1]], + // CHECK: [[RES5:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[RES3]], i64 0, i64 [[INDEX1]] + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[ARRAY_INDEX2_U19]], i32 0, i32 0), i64 [[INDEX1]]) + // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES5]], + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[ARRAY_ELEMENT_U19]], i32 0, i32 0), i32 [[LOAD1]]) + // CHECK: [[NEXT_INDEX1:%.*]] = add i64 [[INDEX1]], 1 + // CHECK: store i64 [[NEXT_INDEX1]], i64* [[ALLOC_INDEX1]], + // CHECK: [[DONE1:%.*]] = icmp eq i64 [[NEXT_INDEX1]], 2 + // CHECK: br i1 %20, label %__builtin_dump_struct.array.done.2, label %__builtin_dump_struct.array.body.2 + + // CHECK: __builtin_dump_struct.array.done.2: + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[ARRAY_R_SQUARE2_U19]], i32 0, i32 0)) + // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1 + // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]], + // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 2 + // CHECK: br i1 %25, label %__builtin_dump_struct.array.done.1, label %__builtin_dump_struct.array.body.1 + + // CHECK: __builtin_dump_struct.array.done.1: + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[ARRAY_R_SQUARE_U19]], i32 0, i32 0)) + // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U19]], i32 0, i32 0)) + __builtin_dump_struct(&a, &printf); +} + void test1(void) { struct T1A { int a; @@ -475,8 +558,24 @@ // CHECK: [[LOAD1:%.*]] = load i32, i32* [[BC1]], // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) // CHECK: [[BC2:%.*]] = bitcast %union.anon* [[RES1]] to [4 x i8]* - // CHECK: [[LOAD2:%.*]] = load [4 x i8], [4 x i8]* [[BC2]], - // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]]) + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES2:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* [[BC2]], i64 4 + // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1, + // CHECK: store i64 0, i64* [[ALLOC_INDEX]], + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: br label %__builtin_dump_struct.array.body.2 + // CHECK: __builtin_dump_struct.array.body.2: + // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]], + // CHECK: [[RES3:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* [[BC2]], i64 0, i64 [[INDEX]] + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[INDEX]]) + // CHECK: [[LOAD2:%.*]] = load i8, i8* [[RES3]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8 [[LOAD2]]) + // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1 + // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]], + // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 4 + // CHECK: br i1 %19, label %__builtin_dump_struct.array.done.2, label %__builtin_dump_struct.array.body.2 + // CHECK: __builtin_dump_struct.array.done.2: + // CHECK: call i32 (i8*, ...) @printf( // CHECK: call i32 (i8*, ...) @printf( // CHECK: call i32 (i8*, ...) @printf( __builtin_dump_struct(&a, &printf); @@ -619,20 +718,20 @@ }; // CHECK: call i32 (i8*, ...) @printf( - // CHECK: [[BC1:%.*]] = bitcast %struct.T8A* %a to i8* - // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]], - // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1 - // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32 + // CHECK: [[BC1:%[−a−zA−Z._0-9]+]] = bitcast %struct.T8A* %a to i8* + // CHECK: [[LOAD1:%[-a-zA-Z._0-9]+]] = load i8, i8* [[BC1]], + // CHECK: [[CLEAR1:%[-a-zA-Z._0-9]+]] = and i8 [[LOAD1]], 1 + // CHECK: [[CAST1:%[-a-zA-Z._0-9]+]] = zext i8 [[CLEAR1]] to i32 // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST1]]) - // CHECK: [[BC2:%.*]] = bitcast %struct.T8A* %a to i8* - // CHECK: [[LOAD2:%.*]] = load i8, i8* [[BC2]], - // CHECK: [[LSHR2:%.*]] = lshr i8 [[LOAD2]], 1 - // CHECK: [[CLEAR2:%.*]] = and i8 [[LSHR2]], 7 - // CHECK: [[CAST2:%.*]] = zext i8 [[CLEAR2]] to i32 + // CHECK: [[BC2:%[−a−zA−Z._0-9]+]] = bitcast %struct.T8A* %a to i8* + // CHECK: [[LOAD2:%[-a-zA-Z._0-9]+]] = load i8, i8* [[BC2]], + // CHECK: [[LSHR2:%[0-9a-z.]+]] = lshr i8 [[LOAD2]], 1 + // CHECK: [[CLEAR2:%[-a-zA-Z._0-9]+]] = and i8 [[LSHR2]], 7 + // CHECK: [[CAST2:%[-a-zA-Z._0-9]+]] = zext i8 [[CLEAR2]] to i32 // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST2]]) // CHECK: call i32 (i8*, ...) @printf( - // CHECK: [[RES3:%.*]] = getelementptr inbounds %struct.T8A, %struct.T8A* %a, i32 0, i32 1 - // CHECK: [[LOAD3:%.*]] = load i32, i32* [[RES3]], + // CHECK: [[RES3:%[-a-zA-Z._0-9]+]] = getelementptr inbounds %struct.T8A, %struct.T8A* %a, i32 0, i32 1 + // CHECK: [[LOAD3:%[-a-zA-Z._0-9]+]] = load i32, i32* [[RES3]], // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* {{.*}}, i32 0, i32 0), i32 [[LOAD3]]) // CHECK: call i32 (i8*, ...) @printf( __builtin_dump_struct(&a, &printf);