-
-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DI: Don't use llvm.dbg.declare with non-alloca storage #2294
Conversation
It becomes more and more evident that LLVM doesn't like non-allocas for debuginfo params and locals. That's especially proving to be problematic for Win64 with its ExplicitByvalRewrite, where the storage for a parameter is an alloca of the caller and that pointer is passed as LL pointer parameter (and used directly for the callee's DI local). |
Presumably you've considered this already, but at some point, wouldn't it be easier to tackle the problem in upstream LLVM? We would presumably make life better for a whole host of other compilers as well. |
Does this work for optimized compilation with debug info ( |
This definitely sounds interesting. We have the same issue with nested variables, those aren't allocas either. |
Yeah, I am working on trying with llvm.dbg.value now. |
I hoped all that needed to be constant is the storage/lvalue of the variable. The official DI docs are, hmm, crappy; it shouldn't be that hard to associate a debuginfo variable with its (constant) LL lvalue?! Edit: I'm reading the docs like you, so it may work for reference params here, but not for nested and rewritten ones. 'Declaring' seems the way to connect the DI variable with its LL lvalue, but here LLVM seems to expect local allocas. Our attempts to work around this for nested variables, by using the frame alloca and indexing it via DWARF expressions, didn't fully work out IIRC. We may want to check what clang does in similar circumstances (captured vars in lambdas or so). |
When the value is constant, it's storage (LLVM value) is also constant and all is well. When a value is not constant, on every write you'd get a new LLVM value (because of SSA), and then you'd have to retag that new value with llvm.dbg.value (because the LLVM value associated with the D variable has changed). But for non-constant things, we always use an alloca (right?), and we can use llvm.dbg.declare. |
…oreach statement, where it _is_ an alloca.
Now based on reworked #2292, and the first results are fantastic, no issues with struct params > 64 bit anymore on Win64 (with LLVM 5.0). I still have to check the nested vars, but I'm very optimistic. |
gen/dibuilder.h
Outdated
@@ -141,6 +141,8 @@ class DIBuilder { | |||
DIScope GetCurrentScope(); | |||
void Declare(const Loc &loc, llvm::Value *var, ldc::DILocalVariable divar, | |||
ldc::DIExpression diexpr); | |||
void DbgValue(const Loc &loc, llvm::Value *var, ldc::DILocalVariable divar, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be just Value
in line with Declare
(or rename both)?
gen/dibuilder.cpp
Outdated
const bool storedInAlloca = llvm::dyn_cast<llvm::AllocaInst>(ll); | ||
bool useDbgValueIntrinsic = false; | ||
if (!storedInAlloca || vd->isRef() || vd->isOut()) { | ||
useDbgValueIntrinsic = !storedInAlloca; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this a bit confusing to read, especially given the comment – is there a situation where ref
and out
actually do use an alloca
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See Johan's original comment in #2292 (comment). Apparently used for special-ref loop variables. It's a bit smelly and should be definitely looked into sometime.
Edit: 'Declaring' these special-ref vars seems to make perfect sense, as their reference is advanced with each iteration and not const as in the other cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, they are fun – I called them SpecialRefVar
s in our code precisely because they don't map to anything else in the D language. In terms of looking into things, I've been wondering for a while how much of the extra DValue
metadata we could do away with given that we have LLVM IR type information and frontend type information already. The current design seems needlessly complicated, although I can't really pinpoint any specific issues yet.
Represent the local or parameter as DI reference instead, using llvm.dbg.value to set the constant storage/lvalue. Affects parameters rewritten by ExplicitByvalRewrite and nested variables, possibly some more.
bc5b1e8
to
ff0af8c
Compare
|
Unfortunately, this approach apparently doesn't work for nested vars at all, neither on Win64 nor on Linux x64. I checked the IR manually and it looks okay to me. The nested vars don't show up in the VS debugger anymore (previously they were shown, although each with some attached error message); in gdb the same (symbols still known, but not listed in |
An example from tests/debuginfo/nested.d: %arg0 = getelementptr inbounds %nest.encloser, %nest.encloser* %2, i32 0, i32 0 ; [#uses = 1, type = i32*]
call void @llvm.dbg.value(metadata i32* %arg0, i64 0, metadata !28, metadata !16), !dbg !34 ; [debug line = 1:6] [debug variable = arg0]
!11 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !12, size: 64, align: 32)
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!16 = !DIExpression()
!28 = !DILocalVariable(name: "arg0", scope: !24, file: !3, line: 1, type: !11) This results in the following naked DWARF var without any value on Linux x64 (via
|
I tried using
Result on Linux x64: no change at all, the vars still only feature a name, type and source location. |
Not sure if it is relevant to your approach, but non-trivial DWARF expressions were unsupported on Windows for ages until a month ago llvm-mirror/llvm@2cd77a8 added "rudimentary DwarfExpression support to CodeView". |
Thanks, but I guess an expression shouldn't be really necessary here; I mean it's working for immediate pointers from parameters (ref/out params, params rewritten by ExplicitByvalRewrite and params with the LLVM |
Now trying to avoid all GEPs and using an alloca + more complex DWARF expression instead. Seems to work fine on Linux x64; the VS debugger on Win64 displays closure parameters and locals correctly, but the captured ones (in nested functions) are missing, possibly due to a too complex DWARF expression for LLVM 5. |
a0aebeb
to
e1538bc
Compare
For both closure and captured vars. Both llvm.dbg.declare() and llvm.dbg.value() intrinsics apparently don't like GEP values.
Related discussion on LLVM-dev, pointing out some of the limitations of the current debuginfo LLVM code: https://lists.llvm.org/pipermail/llvm-dev/2017-September/117141.html |
Yeah with the alloca+expression approach, it's #1933 all over again; LLVM < 3.9 fails for our testcase. We speculated about it being an LLVM bug back then, and apparently later other testcases came up which also failed with LLVM 3.9, so that we went ahead with the GEP + weird additional dereference approach. Also considering that LLVM < 5 misused The original issue triggering this again, LLVM 5 complaining about the LL pointer param as DI storage for non-nested ref/out params, was definitely a bug on our side, as it was declared as reference, but we passed the reference rvalue instead of its lvalue. I'll probably split off the fix as a separate PR, as this is still clearly WIP and I'm not sure the alloca+expression route is the proper way of handling this, or whether LLVM is supposed to swallow GEPs just fine and we're just having a hard time trying to work around it 'properly'. |
Sounds good. |
Obsoleted by #2315. |
An alternative to #2292.