Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

LLVM emits extra jump instructions when invokes share an unreachable normal destination #44253

Open Quuxplusone opened 4 years ago

Quuxplusone commented 4 years ago
Bugzilla Link PR45283
Status NEW
Importance P enhancement
Reported by Reid Kleckner (rnk@google.com)
Reported on 2020-03-23 12:17:18 -0700
Last modified on 2020-03-24 14:18:29 -0700
Version trunk
Hardware PC All
CC aeubanks@google.com, akhuang@google.com, efriedma@quicinc.com, hans@chromium.org, llvm-bugs@lists.llvm.org, matze@braunis.de, nicolasweber@gmx.de, quentin.colombet@gmail.com
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also

Consider this example:

$ cat t.cpp struct HasCleanup { ~HasCleanup(); }; extern "C" void mythrow1(); extern "C" void mythrow2(); extern "C" void mythrow3(); extern "C" int multi_throw(bool c1, bool c2, bool c3) { HasCleanup obj; if (c1) { mythrow1(); goto unreachable; } if (c2) { mythrow2(); goto unreachable; } if (c3) { mythrow3(); goto unreachable; } return 0; unreachable: __builtin_unreachable(); }

Clang emits extra branches after each call to mythrowN: $ clang -S -O1 t.cpp -o - | grep -A3 mythrow callq mythrow1 .Ltmp5: jmp .LBB0_7 .LBB0_4: # %if.then4

    callq   mythrow2

.Ltmp3: jmp .LBB0_7 .LBB0_6: # %if.then8

    callq   mythrow3

.Ltmp1: .LBB0_7: # %unreachable .LBB0_2: # %lpad

I encountered this issue because Clang uses the same IR pattern to emit calls to C++ throw expressions. See the code that uses getUnreachableBlock here: https://github.com/llvm/llvm-project/blob/master/clang/lib/CodeGen/CGCall.cpp#L3794

C++ source & asm:

$ cat b.cpp struct HasCleanup { ~HasCleanup(); }; extern "C" int multi_throw(bool c1, bool c2, bool c3) { HasCleanup obj; if (c1) throw 1; if (c2) throw 2; if (c3) throw 3; return 0; }

$ clang -S -O1 b.cpp -o - | grep -A3 call.*throw callq __cxa_throw .Ltmp5: jmp .LBB0_7 .LBB0_4: # %if.then4

    callq   __cxa_throw

.Ltmp3: jmp .LBB0_7 .LBB0_6: # %if.then8

    callq   __cxa_throw

.Ltmp1: .LBB0_7: # %unreachable .LBB0_2: # %lpad

We could teach clang not to use this IR pattern, but the first example shows that this is an LLVM issue too.

There are a few possible places to fix this:

See also https://llvm.org/PR45064#c4, where this code pattern caused issues with the win64 unwinder.

Quuxplusone commented 4 years ago

Could we handle this after isel, in TailDup?

Quuxplusone commented 4 years ago
(In reply to Eli Friedman from comment #1)
> Could we handle this after isel, in TailDup?

It's possible, but as I read the code, it could still hit the same issues as
handling it in ISel.

Currently I think tail duplication is structured to clone instructions from a
common tail block into the preceding blocks. Currently it is restricted from
doing this if the predecessor has any EHPad successors. If we remove that
restriction, we may run into the problems I hit with the ISel solution, where
downstream passes get upset when they see a BB with exactly one exceptional
successor. If we fix those issues, then the ISel solution seems better to me
(fewer MBBs earlier).

We could teach tail duplication to create new MBBs, but that would be a larger
change. It might be generally useful, though.
Quuxplusone commented 4 years ago

Oh, I see, we expect two successors, so we can't do conventional taildup.

Taildup that keeps the cloned code in a separate BB might be useful in other situations.