Problem with InsertPointGuard ABI?

Hi,

I have problem with IRBuilderBase::InsertPointGuard class that simply does not work in the release build of my project. The class does not restore the IRBuilder’s insert point correctly when NDEBUG macro is set. It happens on OSX system only, trunk version of the LLVM built with brew.

I suspect it is the ABI problem. InsertPointGuard uses AssertingVT for debug builds.

LLDB gets confused also. The first listing shows that the instance of InsertPointGuard is messed up:

96 auto check = _builder.GetInsertBlock();
97 {
98 llvm::IRBuilderBase::InsertPointGuard guard{_builder};
→ 99 _builder.SetInsertPoint(checkBB);
100 }
101
102 if (_builder.GetInsertBlock() != check)
(lldb) p _builder
(llvm::IRBuilder<true, llvm::ConstantFolder, llvm::IRBuilderDefaultInserter >) $4 = {
llvm::IRBuilderBase = {
CurDbgLocation = {
Loc = {
Ref = {
MD = 0x0000000000000000
}
}
}
BB = 0x000000010642ecf0
InsertPt = {
NodePtr = 0x000000010642ed00
}
Context = 0x000000010642d170
DefaultFPMathTag = 0x0000000000000000
FMF = (Flags = 0)
}
Folder = {}
}
(lldb) p guard
(llvm::IRBuilderBase::InsertPointGuard) $5 = {
Builder = 0x00007fff5fbf46d8
Block = {
ThePtr = 0x0000000107800398
}
Point = {
NodePtr = 0x0000000000000000
}
DbgLoc = {
Loc = {
Ref = {
MD = 0x000000010642ecf0
}
}
}
}

Moreover, if I start printing the guard from InsertPointGuard constructor it shows different data layout. LLDB also crashes after step out and printing the guard again.

195 public:
196 InsertPointGuard(IRBuilderBase &B)
197 : Builder(B), Block(B.GetInsertBlock()), Point(B.GetInsertPoint()),
→ 198 DbgLoc(B.getCurrentDebugLocation()) {}
199
200 ~InsertPointGuard() {
201 Builder.restoreIP(InsertPoint(Block, Point));
(lldb) p *this
(llvm::IRBuilderBase::InsertPointGuard) $6 = {
Builder = 0x00007fff5fbf4440
Block = {
llvm::ValueHandleBase = {
PrevPair = (Value = 4337803886)
Next = 0x000000010615b0a8
V = 0x000000010615b170
}
}
Point = {
NodePtr = 0x000000010615b140
}
DbgLoc = {
Loc = {
Ref = {
MD = 0x000000010324444b
}
}
}
}
(lldb) n
Process 1498 stopped

  • thread #1: tid = 0x493aa8, 0x0000000102920b22 libevmjit.0.0.dylibdev::eth::jit::test(_builder=0x00007fff5fbf46d8) + 258 at RuntimeManager.cpp:99, queue = 'com.apple.main-thread', stop reason = step over frame #0: 0x0000000102920b22 libevmjit.0.0.dylibdev::eth::jit::test(_builder=0x00007fff5fbf46d8) + 258 at RuntimeManager.cpp:99
    96 auto check = _builder.GetInsertBlock();
    97 {
    98 llvm::IRBuilderBase::InsertPointGuard guard{_builder};
    → 99 _builder.SetInsertPoint(checkBB);
    100 }
    101
    102 if (_builder.GetInsertBlock() != check)
    (lldb) p guard
    Assertion failed: (field_idx < record_layout.getFieldCount()), function GetChildClangTypeAtIndex, file /SourceCache/lldb/lldb-320.4.156/source/Symbol/ClangASTType.cpp, line 6615.
    Abort trap: 6

It’s hard to create a small executable where the problem can be reproduced.

Cheers,

  • Paweł Bylica

The layout of AssertingVH has depended on NDEBUG since 2009, which predates any of our efforts to make LLVM’s ABI resilient to mismatched NDEBUG definitions between LLVM and its users.

For now, make sure your definition of NDEBUG matches LLVM’s. In the long run, we could conceivably do something with LLVM_ENABLE_ABI_BREAKING_CHECKS to allow this mismatch.

I can confirm that the issue has been caused by NDEBUG flag.

Paweł Bylica <chfast@gmail.com> writes:

I can confirm that the issue has been caused by NDEBUG flag.

    The layout of AssertingVH has depended on NDEBUG since 2009, which
    predates any of our efforts to make LLVM's ABI resilient to mismatched
    NDEBUG definitions between LLVM and its users.
   
    For now, make sure your definition of NDEBUG matches LLVM's. In the long
    run, we could conceivably do something
    with LLVM_ENABLE_ABI_BREAKING_CHECKS to allow this mismatch.

In practice it is very hard to make NDEBUG flag match configs of your project
and LLVM project. You often need to build debug and release versions of your
project and LLVM is installed as a debian package or with homebrew. Moreover,
there is not reliable way of checking if LLVM has been built with or without
NDEBUG.

FWIW, `llvm-config --assertion-mode` will tell you whether or not your
LLVM was built with or without NDEBUG.

Paweł Bylica <chfast@gmail.com> writes:

I can confirm that the issue has been caused by NDEBUG flag.

The layout of AssertingVH has depended on NDEBUG since 2009, which
predates any of our efforts to make LLVM’s ABI resilient to mismatched
NDEBUG definitions between LLVM and its users.

For now, make sure your definition of NDEBUG matches LLVM’s. In the long
run, we could conceivably do something
with LLVM_ENABLE_ABI_BREAKING_CHECKS to allow this mismatch.

In practice it is very hard to make NDEBUG flag match configs of your project
and LLVM project. You often need to build debug and release versions of your
project and LLVM is installed as a debian package or with homebrew. Moreover,
there is not reliable way of checking if LLVM has been built with or without
NDEBUG.

FWIW, llvm-config --assertion-mode will tell you whether or not your
LLVM was built with or without NDEBUG.

That’s not true in all cases. In case CMAKE_BUILD_TYPE=Release, LLVM_ENABLE_ASSERTIONS=Off and CMAKE_CXX_FLAGS_RELEASE=“” llvm-config reports asserts as off but NDEBUG flag is not set.

Paweł Bylica <chfast@gmail.com> writes:

    Paweł Bylica <chfast@gmail.com> writes:
    > I can confirm that the issue has been caused by NDEBUG flag.
    >
    >
    > The layout of AssertingVH has depended on NDEBUG since 2009, which
    > predates any of our efforts to make LLVM's ABI resilient to
    mismatched
    > NDEBUG definitions between LLVM and its users.
    >
    > For now, make sure your definition of NDEBUG matches LLVM's. In the
    long
    > run, we could conceivably do something
    > with LLVM_ENABLE_ABI_BREAKING_CHECKS to allow this mismatch.
    >
    > In practice it is very hard to make NDEBUG flag match configs of your
    project
    > and LLVM project. You often need to build debug and release versions of
    your
    > project and LLVM is installed as a debian package or with homebrew.
    Moreover,
    > there is not reliable way of checking if LLVM has been built with or
    without
    > NDEBUG.
   
    FWIW, `llvm-config --assertion-mode` will tell you whether or not your
    LLVM was built with or without NDEBUG.

That's not true in all cases. In case CMAKE_BUILD_TYPE=Release,
LLVM_ENABLE_ASSERTIONS=Off and CMAKE_CXX_FLAGS_RELEASE="" llvm-config reports
asserts as off but NDEBUG flag is not set.

Um, okay, but why would you set CMAKE_CXX_FLAGS_RELEASE=""? That doesn't
make any sense...

Paweł Bylica <chfast@gmail.com> writes:

Paweł Bylica <chfast@gmail.com> writes:

I can confirm that the issue has been caused by NDEBUG flag.

The layout of AssertingVH has depended on NDEBUG since 2009, which
predates any of our efforts to make LLVM’s ABI resilient to
mismatched
NDEBUG definitions between LLVM and its users.

For now, make sure your definition of NDEBUG matches LLVM’s. In the
long
run, we could conceivably do something
with LLVM_ENABLE_ABI_BREAKING_CHECKS to allow this mismatch.

In practice it is very hard to make NDEBUG flag match configs of your
project
and LLVM project. You often need to build debug and release versions of
your
project and LLVM is installed as a debian package or with homebrew.
Moreover,
there is not reliable way of checking if LLVM has been built with or
without
NDEBUG.

FWIW, llvm-config --assertion-mode will tell you whether or not your
LLVM was built with or without NDEBUG.

That’s not true in all cases. In case CMAKE_BUILD_TYPE=Release,
LLVM_ENABLE_ASSERTIONS=Off and CMAKE_CXX_FLAGS_RELEASE=“” llvm-config reports
asserts as off but NDEBUG flag is not set.

Um, okay, but why would you set CMAKE_CXX_FLAGS_RELEASE=“”? That doesn’t
make any sense…

I agree, it make no sense. But homebrew actually does that:
https://github.com/Homebrew/homebrew/blob/master/Library/Homebrew/formula.rb#L615

There are many possible solutions for this case:

  1. Force NDEBUG flag
  2. Report a cmake error.
  3. Get rid of LLVM_ENABLE_ASSERTIONS flag and relay on NDEBUG flag only.

Paweł Bylica <chfast@gmail.com> writes:

Paweł Bylica <chfast@gmail.com> writes:

FWIW, `llvm-config --assertion-mode` will tell you whether or not your
LLVM was built with or without NDEBUG.

That's not true in all cases. In case CMAKE_BUILD_TYPE=Release,
LLVM_ENABLE_ASSERTIONS=Off and CMAKE_CXX_FLAGS_RELEASE=""
llvm-config reports asserts as off but NDEBUG flag is not set.

Um, okay, but why would you set CMAKE_CXX_FLAGS_RELEASE=""? That
doesn't make any sense...

I agree, it make no sense. But homebrew actually does that:
https://github.com/Homebrew/homebrew/blob/master/Library/Homebrew/formula.rb#L615

That sounds very broken - maybe ask them to fix it?

In any case, `llvm-config` seems to correctly report asserts as ON in
this case, despite the configuration being completely bogus:

  % cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=Off -DCMAKE_CXX_FLAGS_RELEASE=""
  ...
  % ninja llvm-config
  [94/94] Linking CXX executable bin/llvm-config
   % ./bin/llvm-config --assertion-mode
  ON
  
The code that prints this just checks NDEBUG:

llvm-config.cpp:320:

Paweł Bylica <chfast@gmail.com> writes:

Paweł Bylica <chfast@gmail.com> writes:

FWIW, llvm-config --assertion-mode will tell you whether or not your
LLVM was built with or without NDEBUG.

That’s not true in all cases. In case CMAKE_BUILD_TYPE=Release,
LLVM_ENABLE_ASSERTIONS=Off and CMAKE_CXX_FLAGS_RELEASE=“”
llvm-config reports asserts as off but NDEBUG flag is not set.

Um, okay, but why would you set CMAKE_CXX_FLAGS_RELEASE=“”? That
doesn’t make any sense…

I agree, it make no sense. But homebrew actually does that:
https://github.com/Homebrew/homebrew/blob/master/Library/Homebrew/formula.rb#L615

That sounds very broken - maybe ask them to fix it?

I’ve fixed that for the LLVM formula.
https://github.com/Homebrew/homebrew/blob/master/Library/Formula/llvm.rb#L140

In any case, llvm-config seems to correctly report asserts as ON in
this case, despite the configuration being completely bogus:

In the same time the shared cmake file LLVMConfig.cmake will have LLVM_ENABLE_ASSERTIONS set to On.