From 32b6e49a654a3eb7d19e57f99fa2021ca759464c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 20 Nov 2018 20:44:47 +0100 Subject: [PATCH] Debuginfo: Fix nested variables (primarily for CodeView) (#2909) We've been going back and forth between GEPs and complex DWARF expressions based on the context pointer; this goes back to the latter, as that fixes simple nested variables for CodeView with LLVM >= 6. I guess it also helps for debuginfos of nested vars with enabled optimizations. --- gen/dibuilder.h | 4 ++++ gen/nested.cpp | 33 +++++++++++++++++++++------------ tests/debuginfo/nested.d | 31 ------------------------------- tests/debuginfo/nested_cdb.d | 20 +++++++++----------- 4 files changed, 34 insertions(+), 54 deletions(-) delete mode 100644 tests/debuginfo/nested.d diff --git a/gen/dibuilder.h b/gen/dibuilder.h index 9c31040c89..54ec7f1b27 100644 --- a/gen/dibuilder.h +++ b/gen/dibuilder.h @@ -206,7 +206,11 @@ public: uint64_t offset = gDataLayout->getStructLayout(type)->getElementOffset(index); +#if LDC_LLVM_VER >= 500 + addr.push_back(llvm::dwarf::DW_OP_plus_uconst); +#else addr.push_back(llvm::dwarf::DW_OP_plus); +#endif addr.push_back(offset); } diff --git a/gen/nested.cpp b/gen/nested.cpp index 3050587c7d..61e56e3761 100644 --- a/gen/nested.cpp +++ b/gen/nested.cpp @@ -93,6 +93,21 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, IF_LOG { Logger::cout() << "casting to: " << *irfunc->frameType << '\n'; } LLValue *val = DtoBitCast(ctx, frameType); + // Make the DWARF variable address relative to the context pointer (ctx); + // register all ops (offsetting, dereferencing) required to get there in the + // following list. + LLSmallVector dwarfAddrOps; + + const auto offsetToNthField = [&val, &dwarfAddrOps](unsigned fieldIndex, + const char *name = "") { + gIR->DBuilder.OpOffset(dwarfAddrOps, val, fieldIndex); + val = DtoGEPi(val, 0, fieldIndex, name); + }; + const auto dereference = [&val, &dwarfAddrOps](const char *name = "") { + gIR->DBuilder.OpDeref(dwarfAddrOps); + val = DtoAlignedLoad(val, name); + }; + IrLocal *const irLocal = getIrLocal(vd); const auto vardepth = irLocal->nestedDepth; const auto funcdepth = irfunc->depth; @@ -111,20 +126,16 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, } else { // Load frame pointer and index that... IF_LOG Logger::println("Lower depth"); - val = DtoGEPi(val, 0, vardepth); + offsetToNthField(vardepth); IF_LOG Logger::cout() << "Frame index: " << *val << '\n'; - val = DtoAlignedLoad( - val, (std::string(".frame.") + vdparent->toChars()).c_str()); + dereference((std::string(".frame.") + vdparent->toChars()).c_str()); IF_LOG Logger::cout() << "Frame: " << *val << '\n'; } const auto idx = irLocal->nestedIndex; assert(idx != -1 && "Nested context not yet resolved for variable."); - LLSmallVector dwarfAddrOps; - - LLValue *gep = DtoGEPi(val, 0, idx, vd->toChars()); - val = gep; + offsetToNthField(idx, vd->toChars()); IF_LOG { Logger::cout() << "Addr: " << *val << '\n'; Logger::cout() << "of type: " << *val->getType() << '\n'; @@ -135,8 +146,8 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, // storage of pointer (reference lvalue). } else if (byref || isRefOrOut) { val = DtoAlignedLoad(val); - // ref/out variables get a reference-debuginfo-type in EmitLocalVariable(); - // pass the GEP as reference lvalue in that case. + // ref/out variables get a reference-debuginfo-type in EmitLocalVariable() + // => don't dereference, use reference lvalue as address if (!isRefOrOut) gIR->DBuilder.OpDeref(dwarfAddrOps); IF_LOG { @@ -147,11 +158,9 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, if (!skipDIDeclaration && global.params.symdebug) { #if LDC_LLVM_VER < 500 - // Because we are passing a GEP instead of an alloca to - // llvm.dbg.declare, we have to make the address dereference explicit. gIR->DBuilder.OpDeref(dwarfAddrOps); #endif - gIR->DBuilder.EmitLocalVariable(gep, vd, nullptr, false, + gIR->DBuilder.EmitLocalVariable(ctx, vd, nullptr, false, /*forceAsLocal=*/true, false, dwarfAddrOps); } diff --git a/tests/debuginfo/nested.d b/tests/debuginfo/nested.d deleted file mode 100644 index a811483110..0000000000 --- a/tests/debuginfo/nested.d +++ /dev/null @@ -1,31 +0,0 @@ -// Tests debug info generation for nested functions -// RUN: %ldc -g -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll - -// CHECK: define {{.*}} @{{.*}}_D6nested8encloserFiiZv -// CHECK-SAME: !dbg -void encloser(int arg0, int arg1) -{ - // CHECK: @llvm.dbg.declare{{.*}}%enc_n{{.*}}enc_n - int enc_n; - - // CHECK-LABEL: define {{.*}}_D6nested8encloserFiiZQuMFNaNbNiNfiZv - void nested(int nes_i) - { - // CHECK: %arg0 = getelementptr inbounds %nest.encloser - // CHECK: @llvm.dbg.declare{{.*}}%arg0 - // CHECK: %arg1 = getelementptr inbounds %nest.encloser - // CHECK: @llvm.dbg.declare{{.*}}%arg1 - // CHECK: %enc_n = getelementptr inbounds %nest.encloser - // CHECK: @llvm.dbg.declare{{.*}}%enc_n - arg0 = arg1 = enc_n = nes_i; // accessing arg0, arg1 and enc_n from a nested function turns them into closure variables - - // nes_i and arg1 have the same parameter index in the generated IR, if both get declared as - // function parameters this triggers off an assert in LLVM >=3.8 (see Github PR #1598) - } -} - -// CHECK-LABEL: !DISubprogram(name:{{.*}}nested" -// CHECK: !DILocalVariable{{.*}}nes_i -// CHECK: !DILocalVariable{{.*}}arg1 -// CHECK-NOT: arg: -// CHECK-SAME: ){{$}} diff --git a/tests/debuginfo/nested_cdb.d b/tests/debuginfo/nested_cdb.d index 7b389529d9..d4080200fc 100644 --- a/tests/debuginfo/nested_cdb.d +++ b/tests/debuginfo/nested_cdb.d @@ -1,11 +1,10 @@ // REQUIRES: atleast_llvm500 -// REQUIRES: atmost_llvm501 // REQUIRES: Windows // REQUIRES: cdb // RUN: %ldc -g -of=%t.exe %s // RUN: sed -e "/^\\/\\/ CDB:/!d" -e "s,// CDB:,," %s \ // RUN: | %cdb -snul -lines -y . %t.exe >%t.out -// RUN: FileCheck %s -check-prefix=CHECK -check-prefix=%arch < %t.out +// RUN: FileCheck %s < %t.out // CDB: ld /f nested_cdb* // enable case sensitive symbol lookup @@ -14,7 +13,7 @@ void encloser(int arg0, ref int arg1) { int enc_n = 123; -// CDB: bp `nested_cdb.d:17` +// CDB: bp `nested_cdb.d:16` // CDB: g // CDB: dv /t // CHECK: int arg0 = 0n1 @@ -28,7 +27,7 @@ void encloser(int arg0, ref int arg1) void nested(int nes_i) { int blub = arg0 + arg1 + enc_n; -// CDB: bp `nested_cdb.d:31` +// CDB: bp `nested_cdb.d:30` // CDB: g // CDB: dv /t // CHECK: int arg0 = 0n1 @@ -37,7 +36,7 @@ void encloser(int arg0, ref int arg1) // CDB: ?? *arg1 // CHECK: int 0n2 arg0 = arg1 = enc_n = nes_i; -// CDB: bp `nested_cdb.d:40` +// CDB: bp `nested_cdb.d:39` // CDB: g // CDB: dv /t // CHECK: int arg0 = 0n456 @@ -48,15 +47,14 @@ void encloser(int arg0, ref int arg1) } nested(456); -// CDB: bp `nested_cdb.d:51` +// CDB: bp `nested_cdb.d:50` // CDB: g // CDB: dv /t -// the following values are garbage on Win32... -// x64: int arg0 = 0n456 -// x64-NEXT: int * arg1 = {{0x[0-9a-f`]*}} -// x64-NEXT: int enc_n = 0n456 +// CHECK: int arg0 = 0n456 +// CHECK-NEXT: int * arg1 = {{0x[0-9a-f`]*}} +// CHECK-NEXT: int enc_n = 0n456 // CDB: ?? *arg1 -// x64: int 0n456 +// CHECK: int 0n456 } void main()