Rust: Rustc 1.12.0 Windows build of `ethcore` crate fails with LLVM error

Created on 3 Oct 2016  路  23Comments  路  Source: rust-lang/rust

Error produced:

EH pad must be jumped to via an unwind edge
  %cleanuppad13 = cleanuppad within none []
  br i1 %1927, label %bb99, label %bb102_cleanup_trampoline_bb99, !dbg !719592

As seen here: https://ci.appveyor.com/project/NikolayVolf/parity-g802m/build/1.3.0+1997#L640
Triggered by this update from 1.10.0 to 1.12.0: https://github.com/ethcore/parity/pull/2423

Either LLVM is broken or the generated IR is.
Feel free to change the title to something more descriptive. Unfortunately I haven't got a windows machine for testing, just the one broken appveyor build.

A-mir P-high T-compiler regression-from-stable-to-stable

Most helpful comment

The following patch fixes the reduced testcase, could you please test it?

diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp
index 90ce672..2702b76 100644
--- a/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4503,7 +4503,8 @@ GetCaseResults(SwitchInst *SI, ConstantInt *CaseVal, BasicBlock *CaseDest,
        ++I) {
     if (TerminatorInst *T = dyn_cast<TerminatorInst>(I)) {
       // If the terminator is a simple branch, continue to the next block.
-      if (T->getNumSuccessors() != 1)
+      const auto *BI = dyn_cast<BranchInst>(T);
+      if (!BI || !BI->isUnconditional())
         return false;
       Pred = CaseDest;
       CaseDest = T->getSuccessor(0);

All 23 comments

Historically most of these have been LLVM bugs, unfortunately, which are notoriously hard to track down. @rphmeier do you know if it'd be possible to minimize the test case at all?

@alexcrichton yikes, I have no idea to be honest. Like I said, I haven't got a windows machine to test with and the test case as it stands is about 10-15k LoC on its own...

This may well be related to MIR, as well.

cc @rust-lang/compiler

Confirmed a MIR issue: build succeeds with RUSTFLAGS= -Zorbit=off
https://ci.appveyor.com/project/NikolayVolf/parity-g802m/build/1.3.0+2055

@rphmeier I am trying to get a windows setup up and going to test this and other reported problems (my previous Windows VM recently mysteriously lost its Network Driver and I've never been able to figure out how to get it back...). Any tips you can give me for reproducing it? I guess just a vanilla x86_64 MSVC configuration?

@nikomatsakis Thanks for looking into this. A vanilla x86_64 MSVC setup sounds right. The appveyor.yml contains a few additional setup steps which you might find useful: https://github.com/ethcore/parity/blob/master/appveyor.yml

So I am able to reproduce this. Interestingly, debug builds don't seem to encounter the issue.

Huh, somewhat frustratingly, this...stopped reproducing for now. Not sure what is different now!

Ah, I see, cargo build must be run from the parity directory, not parity/parity. This probably affects some relevant Cargo.toml settings...

Compiling parity takes a very long amount of time and does not seem to reproduce the bug - are there any good STR?

@arielb1 and I discussed on IRC -- I am definitely able to reproduce, but I think @arielb1 was testing with 1.14, not 1.12. So this is quite possibly a bug that has been fixed in the meantime.

The problematic function seems to be _ZN93_$LT$ethcore..evm..interpreter..Interpreter$LT$Cost$GT$$u20$as$u20$ethcore..evm..evm..Evm$GT$4exec17h1655a9f21f79ac34E"(%"2.std::result::Result<evm::evm::GasLeft, evm::evm::Error>"* noalias nocapture sret dereferenceable(64), %"evm::interpreter::Interpreter<usize>, and LLVM seems to be chocking on it. I'm running a dump of simplifycfg to see whether something pops up (should have done that earlier).

I think dropping zeroing might have fixedhidden it - it makes some of the control flow simpler (after inlining).

The code that is optimized wrong looks like this, basically:

bb111:                                            ; preds = %bb112
  store i8 0, i8* %tmp191
  cleanupret from %cleanuppad7 unwind label %bb110

bb112:                                            ; preds = %bb113, %bb113, %bb113
  %497 = load i8, i8* %tmp191, !range !3
  %498 = trunc i8 %497 to i1
  br i1 %498, label %bb111, label %bb112_cleanup_trampoline_bb110

bb113:                                            ; preds = %bb114
  %499 = getelementptr inbounds %"evm::interpreter::InstructionResult<usize>", %"evm::interpreter::InstructionResult<usize>"* %result, i32 0, i32 0
  %500 = load i64, i64* %499, !range !12
  switch i64 %500, label %bb113_cleanup_trampoline_bb110 [
    i64 0, label %bb112
    i64 1, label %bb112
    i64 6, label %bb112
  ]

Somehow, it is optimized into this (that is not only simplifycfg, but also a fair bunch of other passes):

bb113:                                            ; preds = %bb114
  %1427 = icmp ult i64 %result.sroa.0.1, 7
  br i1 %1427, label %bb110, label %bb113_cleanup_trampoline_bb110

Where bb110 is a cleanup pad:

bb110:                                            ; preds = %bb112_cleanup_trampoline_bb110, %bb113_cleanup_trampoline_bb110, %bb114_cleanup_trampoline_bb110, %panic, %bb111, %bb97, %bb59, %bb56, %bb54, %bb51, %bb50, %bb48, %bb47, %bb44, %bb43, %bb41, %bb37, %bb36, %bb34, %bb32, %bb31, %bb29, %bb28, %bb27, %bb25, %bb24, %bb23, %bb22, %bb21, %bb20
  %cleanuppad8 = cleanuppad within none []
  ...

cc @majnemer.

See that %bb110 has _no_

Crashing IR available at https://dl.dropboxusercontent.com/u/35340625/ethcore-rust-1.12.ll.xz (huge, un-minified). opt output available at https://dl.dropboxusercontent.com/u/35340625/verify-report.xz.

Reduced IR:

target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"

declare void @throw()

define void @bad_fn(i64 %val) personality i32 (...)* @__CxxFrameHandler3 {
entry:
  invoke void @throw()
          to label %unreachable unwind label %cleanup1

unreachable:
  unreachable

cleanup1:
  %cleanuppad1 = cleanuppad within none []
  switch i64 %val, label %cleanupdone2 [
    i64 0, label %cleanupdone1
    i64 1, label %cleanupdone1
    i64 6, label %cleanupdone1
  ]

cleanupdone1:
  cleanupret from %cleanuppad1 unwind label %cleanup2

cleanupdone2:
  cleanupret from %cleanuppad1 unwind label %cleanup2

cleanup2:
  %phi = phi i1 [ true, %cleanupdone1 ], [ true, %cleanupdone2 ]
  %cleanuppad2 = cleanuppad within none []
  call void @throw() [ "funclet"(token %cleanuppad2) ]
  unreachable
}

declare i32 @__CxxFrameHandler3(...)

The bug can be reproduced using opt -simplifycfg

The following patch fixes the reduced testcase, could you please test it?

diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp
index 90ce672..2702b76 100644
--- a/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4503,7 +4503,8 @@ GetCaseResults(SwitchInst *SI, ConstantInt *CaseVal, BasicBlock *CaseDest,
        ++I) {
     if (TerminatorInst *T = dyn_cast<TerminatorInst>(I)) {
       // If the terminator is a simple branch, continue to the next block.
-      if (T->getNumSuccessors() != 1)
+      const auto *BI = dyn_cast<BranchInst>(T);
+      if (!BI || !BI->isUnconditional())
         return false;
       Pred = CaseDest;
       CaseDest = T->getSuccessor(0);

@majnemer

Is there an LLVM issue/PR for it?

Patch appears to fix problem (on Linux at least).

Fixed in LLVM r283517.

This should be fixed on nightly builds now, right? I think we're just waiting on a backport?

Sure.

I believe https://github.com/rust-lang/rust/pull/37030 is the PR that fixes this. Not backported yet.

This is fixed by the SimplifyCfg PR, which _was_ backported (that got merged into rustc PR #37030).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

withoutboats picture withoutboats  路  202Comments

Leo1003 picture Leo1003  路  898Comments

withoutboats picture withoutboats  路  308Comments

GuillaumeGomez picture GuillaumeGomez  路  300Comments

nikomatsakis picture nikomatsakis  路  331Comments