Permalink
Browse files

Debuginfo: Fix Win64 CodeView emission for some by-value params (#2908)

Don't declare some special params as DI references, but as DI locals
instead.

Also enable some Win32 checks in the args_cdb test, as they are working
now with LLVM 7.
  • Loading branch information...
kinke committed Nov 15, 2018
1 parent 0d02aa5 commit 0a6125352d6eff73d494faed3b8e56851c68cfc9
Showing with 74 additions and 50 deletions.
  1. +18 −17 gen/dibuilder.cpp
  2. +56 −33 tests/debuginfo/args_cdb.d
@@ -1224,14 +1224,24 @@ void DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd,
return; // unsupported
const bool isRefOrOut = vd->isRef() || vd->isOut(); // incl. special-ref vars
// For MSVC x64 targets, declare params rewritten by IndirectByvalRewrite as
// DI references, as if they were ref parameters.
const bool isPassedExplicitlyByval =
isTargetMSVCx64 && !isRefOrOut && isaArgument(ll) && addr.empty();
const bool isParameter = vd->isParameter();
// For MSVC x64, some by-value parameters need to be declared as DI locals to
// work around garbage for both cdb and VS debuggers.
if (isParameter && !forceAsLocal && isTargetMSVCx64 && !isRefOrOut) {
// 1) params rewritten by IndirectByvalRewrite
if (isaArgument(ll) && addr.empty()) {
forceAsLocal = true;
} else {
// 2) dynamic arrays and vectors
TY ty = type->toBasetype()->ty;
if (ty == Tarray || ty == Tvector)
forceAsLocal = true;
}
}
bool useDbgValueIntrinsic = false;
if (isRefOrOut || isPassedExplicitlyByval) {
if (isRefOrOut) {
// DW_TAG_reference_type sounds like the correct tag for `this`, but member
// function calls won't work with GDB unless `this` gets declared as
// DW_TAG_pointer_type.
@@ -1243,22 +1253,13 @@ void DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd,
// itself is constant. So we don't have to attach the debug information to a
// memory location and can use llvm.dbg.value to set the constant pointer
// for the DI reference.
useDbgValueIntrinsic =
isPassedExplicitlyByval || (!isSpecialRefVar(vd) && isRefRVal);
useDbgValueIntrinsic = !isSpecialRefVar(vd) && isRefRVal;
// Note: createReferenceType expects the size to be the size of a pointer,
// not the size of the type the reference refers to.
TD = DBuilder.createReferenceType(
Tag, TD,
gDataLayout->getPointerSizeInBits(), // size (bits)
DtoAlignment(type) * 8); // align (bits)
} else {
// FIXME: For MSVC x64 targets, declare dynamic array and vector parameters
// as DI locals to work around garbage for both cdb and VS debuggers.
if (isTargetMSVCx64) {
TY ty = type->toBasetype()->ty;
if (ty == Tarray || ty == Tvector)
forceAsLocal = true;
}
}
// get variable description
@@ -1274,7 +1275,7 @@ void DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd,
: DIFlags::FlagArtificial | DIFlags::FlagObjectPointer;
DILocalVariable debugVariable;
if (!forceAsLocal && vd->isParameter()) {
if (!forceAsLocal && isParameter) {
FuncDeclaration *fd = vd->parent->isFuncDeclaration();
assert(fd);
size_t argNo = 0;
@@ -46,30 +46,32 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// CHECK: float f = 5
// CHECK: double d = 6
// CHECK: double r = 7
// x64: cdouble * c =
// x86: cdouble c =
// x64: int delegate() * dg =
// x86: int delegate() dg =
// CHECK: <function> * fun = {{0x[0-9a-f`]*}}
// CHECK-G-x86: struct int[] slice =
// CHECK-GC-x86: struct slice<int> slice =
// CHECK-G: struct float[int] aa =
// CHECK-GC: struct associative_array<int, float> aa =
// x64: unsigned char (*)[16] fa
// x86: unsigned char [16] fa
// x86: float [4] f4 = float [4]
// x86: double [4] d4 = double [4]
// CHECK: args_cdb::Small small
// x64: args_cdb::Large * large
// x86: args_cdb::Large large
// CHECK: struct object::TypeInfo_Class * ti = {{0x[0-9a-f`]*}}
// CHECK: void * np = {{0x[0`]*}}
// params emitted as locals (listed after params) for Win64:
// CHECK-G-x64: struct int[] slice =
// CHECK-GC-x64: struct slice<int> slice =
// x64: float [4] f4 = float [4]
// x64: double [4] d4 = double [4]
/*
* On Win64, by-value params > 64-bits are declared as DI locals, not parameters.
* Locals are listed after params in cdb's `dv /t`, so don't check the order (-DAG).
*/
// CHECK-DAG: cdouble c =
// CHECK-DAG: int delegate() dg =
// CHECK-DAG: <function> * fun = {{0x[0-9a-f`]*}}
// CHECK-G-DAG: struct int[] slice =
// CHECK-GC-DAG: struct slice<int> slice =
// CHECK-G-DAG: struct float[int] aa =
// CHECK-GC-DAG: struct associative_array<int, float> aa =
// CHECK-DAG: unsigned char [16] fa =
// CHECK-DAG: float [4] f4 =
// CHECK-DAG: double [4] d4 =
// CHECK-DAG: struct args_cdb::Small small =
/*
* On Win64, `large` is emitted as reference, according to CodeViewDebug::calculateRanges()
* in llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp because of a CodeView limitation, as
* the pointer to the hidden copy is on the stack and not in a register.
* See the byValueShort() test below.
*/
// x86-DAG: struct args_cdb::Large large =
// x64-DAG: struct args_cdb::Large * large =
// CHECK-DAG: struct object::TypeInfo_Class * ti = {{0x[0-9a-f`]*}}
// CHECK-DAG: void * np = {{0x[0`]*}}
// check arguments with indirections
// CDB: ?? c
@@ -90,9 +92,8 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// CHECK-NEXT: ptr :
// CHECK-SAME: 0n10
// CDB: ?? (*fa)[1]
// x64: unsigned char 0x0e
// no-x86: would be fa[1], but displays garbage anyway
// CDB: ?? fa[1]
// CHECK: unsigned char 0x0e
// CDB: ?? f4[1]
// CHECK: float 16
@@ -102,20 +103,38 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// CDB: ?? small
// CHECK: args_cdb::Small
// x64-NEXT: val : 0x12
// no-x86-NEXT: val : 0x12 (displays garbage)
// CHECK-NEXT: val : 0x12
// CDB: ?? large
// CHECK: args_cdb::Large
// x64-NEXT: a : 0x13
// no-x86-NEXT: a : 0x13 (displays garbage)
// CHECK-NEXT: a : 0x13
// CDB: ?? ti
// CHECK: object::TypeInfo_Class
// CHECK-G-NEXT: m_init : byte[]
// CHECK-GC-NEXT: m_init : slice<byte>
}
/*
* On Win64, it makes a difference whether an argument > 64 bits (rewritten by
* ImplicitByvalRewrite) is one the first 4 LL args (pointer to hidden copy in
* register or spilled to stack).
* The latter case is tested above, this tests the register case.
*/
size_t byValueShort(Large large)
{
// CDB: bp `args_cdb.d:126`
// CDB: g
return large.a;
// CHECK: !args_cdb::byValueShort
// CDB: dv /t
// CHECK: struct args_cdb::Large large = struct args_cdb::Large
// CDB: ?? large
// CHECK: args_cdb::Large
// CHECK-NEXT: a : 0x13
}
int byPtr(ubyte* ub, ushort* us, uint* ui, ulong* ul,
float* f, double* d, real* r,
cdouble* c, int delegate()* dg, int function()* fun,
@@ -124,7 +143,7 @@ int byPtr(ubyte* ub, ushort* us, uint* ui, ulong* ul,
Small* small, Large* large,
TypeInfo_Class* ti, typeof(null)* np)
{
// CDB: bp `args_cdb.d:127`
// CDB: bp `args_cdb.d:146`
// CDB: g
return 3;
// CHECK: !args_cdb::byPtr
@@ -192,7 +211,7 @@ int byRef(ref ubyte ub, ref ushort us, ref uint ui, ref ulong ul,
ref Small small, ref Large large,
ref TypeInfo_Class ti, ref typeof(null) np)
{
// CDB: bp `args_cdb.d:195`
// CDB: bp `args_cdb.d:214`
// CDB: g
// CHECK: !args_cdb::byRef
@@ -249,7 +268,10 @@ int byRef(ref ubyte ub, ref ushort us, ref uint ui, ref ulong ul,
// CHECK-G-NEXT: m_init : byte[]
// CHECK-GC-NEXT: m_init : slice<byte>
// CDB: ?? *np
// no-CHECK: void * {{0x[0`]*}} (not available)
/*
* On Win32, `np` is somehow not available.
*/
// x64: void * {{0x[0`]*}}
// needs access to references to actually generate debug info
float cim = c.im + fa[7] + dg() + fun() + slice.length + aa.length + f4[0] + d4[1] +
@@ -300,6 +322,7 @@ int main()
typeof(null) np = null;
byValue(ub, us, ui, ul, f, d, r, c, dg, fun, slice, aa, fa, f4, d4, small, large, ti, np);
byValueShort(large);
byPtr(&ub, &us, &ui, &ul, &f, &d, &r, &c, &dg, &fun, &slice, &aa, &fa, &f4, &d4, &small, &large, &ti, &np);
byRef(ub, us, ui, ul, f, d, r, c, dg, fun, slice, aa, fa, f4, d4, small, large, ti, np);

0 comments on commit 0a61253

Please sign in to comment.