Skip to content
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

constexpr auto causes Assertion failed: (!V->hasBrokenDebugInfo() && "Module contains invalid debug info"), function doFinalization, file /Users/thakis/src/llvm-rw/lib/IR/Verifier.cpp, line 4435. #29492

Closed
nico opened this issue Aug 24, 2016 · 25 comments
Assignees
Labels
bugzilla Issues migrated from bugzilla c++

Comments

@nico
Copy link
Contributor

nico commented Aug 24, 2016

Bugzilla Link 29122
Resolution FIXED
Resolved on Jul 29, 2019 15:49
Version trunk
OS All
Depends On #29710
CC @adrian-prantl,@dwblaikie,@dexonsmith,@DougGregor,@zmodem,@pogo59,@pcc,@rnk
Fixed by commit(s) r281284

Extended Description

$ cat test.cc
template
constexpr int some_int() {
return I;
}

constexpr auto five = some_int<5>;

int f() {
return five();
}

$ ~/src/llvm-build/bin/clang -g -std=c++14 -c test.cc
invalid global varaible ref
!​4 = distinct !DIGlobalVariable(name: "five", scope: !​0, file: !​1, line: 6, type: !​5, isLocal: true, isDefinition: true, variable: i32 ()* @​_Z8some_intILi5EEiv)
i32 ()* @​_Z8some_intILi5EEiv
Assertion failed: (!V->hasBrokenDebugInfo() && "Module contains invalid debug info"), function doFinalization, file /Users/thakis/src/llvm-rw/lib/IR/Verifier.cpp, line 4435.
Stack dump:
0. Program arguments: /Users/thakis/src/llvm-build/bin/clang-3.5 -cc1 -triple x86_64-apple-macosx10.10.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -main-file-name test.cc -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu core2 -target-linker-version 253.3 -dwarf-column-info -debug-info-kind=standalone -dwarf-version=2 -debugger-tuning=lldb -coverage-file /Users/thakis/src/chrome/src/test.cc -resource-dir /Users/thakis/src/llvm-build/bin/../lib/clang/4.0.0 -stdlib=libc++ -std=c++14 -fdeprecated-macro -fdebug-compilation-dir /Users/thakis/src/chrome/src -ferror-limit 19 -fmessage-length 248 -stack-protector 1 -fblocks -fobjc-runtime=macosx-10.10.0 -fencode-extended-block-signature -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o test.o -x c++ test.cc

  1. parser at end of file
  2. Per-function optimization
    clang-3.5: error: unable to execute command: Abort trap: 6
    clang-3.5: error: clang frontend command failed due to signal (use -v to see invocation)
    clang version 4.0.0 (trunk 279499)
    Target: x86_64-apple-darwin14.5.0
    Thread model: posix
    InstalledDir: /Users/thakis/src/llvm-build/bin
    clang-3.5: note: diagnostic msg: PLEASE submit a bug report to http://llvm.org/bugs/ and include the crash backtrace, preprocessed source, and associated run script.
    clang-3.5: note: diagnostic msg:

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang-3.5: note: diagnostic msg: /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-c942d1.cpp
clang-3.5: note: diagnostic msg: /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-c942d1.sh
clang-3.5: note: diagnostic msg:


(also typo "varaible")

@nico
Copy link
Contributor Author

nico commented Aug 24, 2016

assigned to @dexonsmith

@nico
Copy link
Contributor Author

nico commented Aug 24, 2016

+dblaikie 'cause debug info

@dwblaikie
Copy link
Collaborator

Reproduces without auto and templates, in C++11

void f1();
constexpr void (*f2)() = f1;
void f3() {
f2();
}

Continuing to investigate.

@dwblaikie
Copy link
Collaborator

Hey Duncan, looks like you added this check in r233389 - any idea why? Looks like it's reasonable/possible that a global variable would be initialized with a global function.

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Aug 24, 2016

Hey Duncan, looks like you added this check in r233389 - any idea why? Looks
like it's reasonable/possible that a global variable would be initialized
with a global function.

I agree, this seems reasonable. For a moment I wondered if I cargo-culted it from DIGlobalVariable::Verify, but no, it looks like I was just wrong.

@nico
Copy link
Contributor Author

nico commented Aug 24, 2016

Duncan: Are you removing the assert / adding a test?

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Aug 24, 2016

If this is blocking you

Duncan: Are you removing the assert / adding a test?

If this is blocking you, please go ahead. Otherwise I should get to it next week.

@nico
Copy link
Contributor Author

nico commented Sep 3, 2016

Friendly ping :-)

@nico
Copy link
Contributor Author

nico commented Sep 9, 2016

Duncan: Ping?

1 similar comment
@nico
Copy link
Contributor Author

nico commented Sep 15, 2016

Duncan: Ping?

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Sep 15, 2016

Building ToT now so I can try to reproduce.

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Sep 15, 2016

--
$ clang -g -x c++ -std=gnu++11 - -S -emit-llvm -o -
invalid global varaible ref
!​4 = distinct !DIGlobalVariable(name: "five", scope: !​0, file: !​5, line: 6, type: !​6, isLocal: true, isDefinition: true, variable: i32 ()* @​_Z8some_intILi5EEiv)
i32 ()* @​_Z8some_intILi5EEiv
Assertion failed: (!V->hasBrokenDebugInfo() && "Module contains invalid debug info"), function doFinalization, file /Users/dexonsmith/data/llvm/staging/lib/IR/Verifier.cpp, line 4435.

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Sep 15, 2016

Had the fix (hadn't actually built against ToT before, used a clang from the weekend). Tried to rebase, and then hit a conflict with r281284. I'm not sure what happens now for this testcase (rebuilding at real ToT), but DIGlobalVariable will certainly never reference a Function...

+pcc: can an llvm::Function have multiple !dbg attachments? Can one of them be a DIGlobalVariable?

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Sep 15, 2016

patch that applied before pcc changed the world in r281284
Attaching the patch I was going to test for posterity as pr29122-before-r281284.patch.

@dexonsmith
Copy link
Mannequin

dexonsmith mannequin commented Sep 15, 2016

Had the fix (hadn't actually built against ToT before, used a clang from the
weekend). Tried to rebase, and then hit a conflict with r281284. I'm not
sure what happens now for this testcase (rebuilding at real ToT), but
DIGlobalVariable will certainly never reference a Function...

Confirmed that this was inadvertently fixed by r281284.

+pcc: can an llvm::Function have multiple !dbg attachments? Can one of them
be a DIGlobalVariable?

Confirmed that the link from the Function to the DIGlobalVariable does not exist (gets dropped the floor). Not surprising, given that there was no coverage (and the Verifier failed).

Adrian, David, etc.; I'll leave it for you to sort out whether this is an important regression.

@adrian-prantl
Copy link
Collaborator

This is a regression on the LLVM IR side, but also we never produced useful DWARF for this example (the variable never had DW_AT_location or other constant value).

I see two interesting design problems here:

  1. How to represent a function constant in IR?
    For function definitions, we could add a !dbg attachment to the function.
    define void @​_Z1f3v() #​0 !dbg !​1, !dbg2 {
    ...
    }

    !​1 = distinct !DIGlobalVariable(name: "f2", type: , ..., expr: ???)
    !​2 = !DISubroutineType(types: !​3)

    But what to do if this is a forward declaration like f1 in David's example?
    Should we allow symbols to appear in DIExpressions, at the risk that they
    might get optimized away?

    How do we encode that the constant value is the function's address
    (as opposed to the data stored at the address like with global variables)?

  2. How to represent a function constant in DWARF?
    What if the target is a Harvard architecture with separate
    address spaces for code and data?

@pogo59
Copy link
Collaborator

pogo59 commented Oct 3, 2016

  1. How to represent a function constant in DWARF?
    What if the target is a Harvard architecture with separate
    address spaces for code and data?

Regarding constexpr functions in general, DWARF 4 section 3.3.8.2
says you give the concrete inlined instance a DW_AT_const_expr flag
and a DW_AT_const_value describing the value returned by the instance.
It's a concrete inlined instance because a 'constexpr' function/method
is implicitly 'inline'. There's an example in appendix D.8.

A variable that is a function pointer would normally have a location,
which is the storage for the variable, which would contain the address
of the function it points to.
A constexpr variable would instead have a DW_AT_const_value describing
the constant address of the function it points to (and not have a
DW_AT_location attribute).

A constexpr variable pointing to a constexpr function that is in fact
evaluated at compile time... uh.

Maybe give the variable the correct function-pointer type, and the
DW_AT_const_expr flag, and then use DW_AT_location to provide an
expression that calls a DIE describing the concrete inlined instance
(which in turn would be DW_AT_const_expr with DW_AT_const_value).
This is slightly perverting DW_AT_location, I'd like to get the
opinion of somebody who has worked on a debugger more recently than
I have.

A less faithful DWARF description would give the variable the result
type of the function (not a pointer-to-function type) and use
DW_AT_const_value to describe that value. This elides the function
from the debug info entirely, and so is not very satisfactory in terms
of describing the source (although it would correctly describe the
translation of the source).

In either case I don't think Harvard architectures would be special.
In one case the variable is still defined in terms of the function
(which just happens not to have an actual object address) and in the
other case you've eliminated the function from the DWARF entirely.

@dwblaikie
Copy link
Collaborator

  1. How to represent a function constant in DWARF?
    What if the target is a Harvard architecture with separate
    address spaces for code and data?

Regarding constexpr functions in general, DWARF 4 section 3.3.8.2
says you give the concrete inlined instance a DW_AT_const_expr flag
and a DW_AT_const_value describing the value returned by the instance.
It's a concrete inlined instance because a 'constexpr' function/method
is implicitly 'inline'. There's an example in appendix D.8.

For now I don't think we're worried about that - we wouldn't describe this inlining at all (since there are no instructions associated with the inlining). One day, perhaps, we'll have a sufficiently expressive representation to explain this frontend constant evaluation to the backend to produce an inlined subroutine over zero instructions.

A variable that is a function pointer would normally have a location,
which is the storage for the variable, which would contain the address
of the function it points to.
A constexpr variable would instead have a DW_AT_const_value describing
the constant address of the function it points to (and not have a
DW_AT_location attribute).

A constexpr variable just means it's guaranteed to be initialized at compile time (not link time - so there's no initialization order fiasco), the variable can still have storage (you can take the variable's address, pass it somewhere, etc). But in some cases the storage may be optimized away, leaving only the value and you'd want to use a DW_AT_const_value as you mentioned.

A constexpr variable pointing to a constexpr function that is in fact
evaluated at compile time... uh.

At that point I'd expect we would, ideally (again, where nowhere near doing any of this today) describe the function that was actually called as inlined with a const_value/constexpr.

Maybe give the variable the correct function-pointer type, and the
DW_AT_const_expr flag, and then use DW_AT_location to provide an
expression that calls a DIE describing the concrete inlined instance
(which in turn would be DW_AT_const_expr with DW_AT_const_value).

I'm not sure I follow. If we're giving any value for the global constexpr variable, it should be for the function it points to, or nothing if the function's been optimized away. If the function's been fully inlined (ie: has no concrete out of line instance) then I think we just leave it at that and provide no value for the global variable. I suppose in theory we could provide a DWARF expression that could evaluate to the value of the function call (& put that expression in the DW_AT_const_value attribute) - but how would we access the function's parameters from that DWARF expression? (can we actually map between those domains at all) Again - /far/ off future stuff.

The function may take parameters - so pointing to a concrete inlined instance wouldn't be suitable (since each concrete inlined instance may produce a different value because it may be given different arguments).

This is slightly perverting DW_AT_location, I'd like to get the
opinion of somebody who has worked on a debugger more recently than
I have.

A less faithful DWARF description would give the variable the result
type of the function (not a pointer-to-function type) and use
DW_AT_const_value to describe that value. This elides the function
from the debug info entirely, and so is not very satisfactory in terms
of describing the source (although it would correctly describe the
translation of the source).

In either case I don't think Harvard architectures would be special.
In one case the variable is still defined in terms of the function
(which just happens not to have an actual object address) and in the
other case you've eliminated the function from the DWARF entirely.

@pogo59
Copy link
Collaborator

pogo59 commented Oct 3, 2016

I was trying to arrive at a DWARF construct that is able to

  • describe a constexpr pointer to a constexpr function
  • where the constexpr function is evaluated at compile time, and
  • that allows the debugger to evaluate an expression calling the
    function through that pointer. ("five()" in the original example.)

Whether Clang is able to get there from here is a separate question,
but it seems like a prerequisite would be knowing where 'there' is.

Situations where the constexpr function is not evaluated at compile
time would behave more like normal function-pointer cases and didn't
seem worth spending time on.

(Also, in my admittedly aging copy of C++11, 'constexpr' does not
guarantee compile-time initialization (variable) or evaluation (function).
On a variable it's static-initialization which is required to occur prior
to all dynamic-initialization, which might or might not be compile-time.
Maybe this has changed in subsequent revisions of C++11, but I remember
being in a lecture at CPPcon that carefully made that distinction.)

@dwblaikie
Copy link
Collaborator

I was trying to arrive at a DWARF construct that is able to

  • describe a constexpr pointer to a constexpr function
  • where the constexpr function is evaluated at compile time, and
  • that allows the debugger to evaluate an expression calling the
    function through that pointer. ("five()" in the original example.)

Whether Clang is able to get there from here is a separate question,
but it seems like a prerequisite would be knowing where 'there' is.

Fair - with that in mind, I don't think your description accounts for the case where a constexpr function has parameters:

constexpr int add(int X, int Y) {
return X + Y;
}

Even before we get to the function pointer case - if we inline all calls to this function, I'm not sure how we'd describe it so a user could call it/do useful things with it. This is the state of things even before we get to constexpr (we can still inline all calls away to a non-constexpr function).

Any /specific/ call that was inlined into a constant could have a DW_AT_const_value with the value of evaluating the constexpr function call, but this doesn't help the user invoke it for other arguments (or even for the arguments used in the inlined instances - since the arguments wouldn't necessarily be encoded in any way the debugger could retrieve and thus map to the user's expression in the debugger)

So without a solution to that, I'm not sure there's anything we can do for the function pointer to such a function.

Situations where the constexpr function is not evaluated at compile
time would behave more like normal function-pointer cases and didn't
seem worth spending time on.

(Also, in my admittedly aging copy of C++11, 'constexpr' does not
guarantee compile-time initialization (variable) or evaluation (function).
On a variable it's static-initialization which is required to occur prior
to all dynamic-initialization, which might or might not be compile-time.
Maybe this has changed in subsequent revisions of C++11, but I remember
being in a lecture at CPPcon that carefully made that distinction.)

Oh, fair enough - good point. Yes s/compile-time/static-initialization/ in my prior statements. Equivalent for our purposes, perhaps.

@pogo59
Copy link
Collaborator

pogo59 commented Oct 3, 2016

Fair - with that in mind, I don't think your description accounts for the
case where a constexpr function has parameters:

constexpr int add(int X, int Y) {
return X + Y;
}

Even before we get to the function pointer case - if we inline all calls to
this function, I'm not sure how we'd describe it so a user could call it/do
useful things with it. This is the state of things even before we get to
constexpr (we can still inline all calls away to a non-constexpr function).

Ah, true, the original example didn't have parameters and that may
have led me down the garden path. Probably not worth making a special
case for the zero-parameters case.

So, the constexpr pointer-to-function would try to point to an instance
if there is one, which might disappear because that happens sometimes.
And if the pointed-to function is constexpr, probably happens a lot.
Oh well.

So you don't get to evaluate "five()" in the debugger after all.

@nico
Copy link
Contributor Author

nico commented Dec 17, 2016

If doing the Right Thing is difficult (or maybe not even expressible in dwarf), can we do something Not Right That At Least Doesn't Crash in the meantime?

@zmodem
Copy link
Collaborator

zmodem commented Dec 13, 2018

Is this still an issue or something that got fixed in the meantime?

@rnk
Copy link
Collaborator

rnk commented Jul 29, 2019

pcc fixed it in r281284.

@pcc
Copy link
Contributor

pcc commented Nov 26, 2021

mentioned in issue #29710

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla c++
Projects
None yet
Development

No branches or pull requests

7 participants