<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - LLVM emits extra jump instructions when invokes share an unreachable normal destination"
   href="https://bugs.llvm.org/show_bug.cgi?id=45283">45283</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>LLVM emits extra jump instructions when invokes share an unreachable normal destination
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libraries
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>trunk
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>enhancement
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>Common Code Generator Code
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>rnk@google.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>aeubanks@google.com, akhuang@google.com, hans@chromium.org, llvm-bugs@lists.llvm.org, matze@braunis.de, nicolasweber@gmx.de, quentin.colombet@gmail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>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:
<a href="https://github.com/llvm/llvm-project/blob/master/clang/lib/CodeGen/CGCall.cpp#L3794">https://github.com/llvm/llvm-project/blob/master/clang/lib/CodeGen/CGCall.cpp#L3794</a>

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:

- Clang: Most users probably don't write code like example 1, more like example
2, so we could change the generated IR and declare victory.

- SimplifyCFG: This pass already does a lot of unreachable handling. However,
this would increase the BB count and instruction count, so it could be
considered non-canonical. Then again, we don't canonicalize in the opposite
direction by tail merging in the IR. I did have a patch for this
(<a href="https://reviews.llvm.org/D29428">https://reviews.llvm.org/D29428</a>), but the results were not promising.

- CodeGenPrepare: We do some CFG adjustment here already, but we'd need a new
loop over all BBs just for this. It doesn't fit into the existing transforms.
This would be limited to, duplicate unreachable BBs after invokes. We'd have to
think about dbg.values as well.

- ISel: We already have handling for switch () { default: unreachable; }, so we
could handle invoke unreachable. I tried this locally, but it seems to cause
bugs in machine CFG passes like branch folding. We end up with an MBB with one
successor, but no terminator instruction, and passes try to merge the landing
pad into the throwing block. Also, it would need to be done in SDISel and
GlobalISel. Maybe these issues are related to <a href="https://llvm.org/pr27659">https://llvm.org/pr27659</a>.

See also <a href="https://llvm.org/PR45064#c4">https://llvm.org/PR45064#c4</a>, where this code pattern caused issues
with the win64 unwinder.</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>