diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -175,9 +175,7 @@ bool isShift() const { return isShift(getOpcode()); } bool isCast() const { return isCast(getOpcode()); } bool isFuncletPad() const { return isFuncletPad(getOpcode()); } - bool isExceptionalTerminator() const { - return isExceptionalTerminator(getOpcode()); - } + bool isSpecialTerminator() const { return isSpecialTerminator(getOpcode()); } /// It checks if this instruction is the only user of at least one of /// its operands. @@ -235,14 +233,16 @@ return Opcode >= FuncletPadOpsBegin && Opcode < FuncletPadOpsEnd; } - /// Returns true if the Opcode is a terminator related to exception handling. - static inline bool isExceptionalTerminator(unsigned Opcode) { + /// Returns true if the Opcode is a "special" terminator that does more than + /// branch to a successor (e.g. have a side effect or return a value). + static inline bool isSpecialTerminator(unsigned Opcode) { switch (Opcode) { case Instruction::CatchSwitch: case Instruction::CatchRet: case Instruction::CleanupRet: case Instruction::Invoke: case Instruction::Resume: + case Instruction::CallBr: return true; default: return false; diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp --- a/llvm/lib/IR/BasicBlock.cpp +++ b/llvm/lib/IR/BasicBlock.cpp @@ -396,8 +396,9 @@ // If the block has no successors, there can be no instructions to hoist. assert(Term->getNumSuccessors() > 0); - // Instructions should not be hoisted across exception handling boundaries. - return !Term->isExceptionalTerminator(); + // Instructions should not be hoisted across special terminators, which may + // have side effects or return values. + return !Term->isSpecialTerminator(); } bool BasicBlock::isEntryBlock() const { diff --git a/llvm/lib/Transforms/Coroutines/CoroElide.cpp b/llvm/lib/Transforms/Coroutines/CoroElide.cpp --- a/llvm/lib/Transforms/Coroutines/CoroElide.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroElide.cpp @@ -227,7 +227,7 @@ PotentiallyEscaped |= EscapingBBs.count(BB); if (TIs.count(BB)) { - if (!BB->getTerminator()->isExceptionalTerminator() || PotentiallyEscaped) + if (isa(BB->getTerminator()) || PotentiallyEscaped) return true; // If the function ends with the exceptional terminator, the memory used diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -1368,7 +1368,7 @@ LoadInst *Load) { // For simplicity we handle a Pred has 2 successors only. auto *Term = Pred->getTerminator(); - if (Term->getNumSuccessors() != 2 || Term->isExceptionalTerminator()) + if (Term->getNumSuccessors() != 2 || Term->isSpecialTerminator()) return nullptr; auto *SuccBB = Term->getSuccessor(0); if (SuccBB == LoadBB) diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp --- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp +++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp @@ -1899,7 +1899,7 @@ return false; const Instruction *TI = SinglePred->getTerminator(); - if (TI->isExceptionalTerminator() || TI->getNumSuccessors() != 1 || + if (TI->isSpecialTerminator() || TI->getNumSuccessors() != 1 || SinglePred == BB || hasAddressTakenAndUsed(BB)) return false; diff --git a/llvm/lib/Transforms/Scalar/Sink.cpp b/llvm/lib/Transforms/Scalar/Sink.cpp --- a/llvm/lib/Transforms/Scalar/Sink.cpp +++ b/llvm/lib/Transforms/Scalar/Sink.cpp @@ -67,9 +67,8 @@ assert(Inst && "Instruction to be sunk is null"); assert(SuccToSinkTo && "Candidate sink target is null"); - // It's never legal to sink an instruction into a block which terminates in an - // EH-pad. - if (SuccToSinkTo->getTerminator()->isExceptionalTerminator()) + // It's never legal to sink an instruction into an EH-pad block. + if (SuccToSinkTo->isEHPad()) return false; // If the block has multiple predecessors, this would introduce computation diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -194,7 +194,7 @@ // Don't break unwinding instructions or terminators with other side-effects. Instruction *PTI = PredBB->getTerminator(); - if (PTI->isExceptionalTerminator() || PTI->mayHaveSideEffects()) + if (PTI->isSpecialTerminator() || PTI->mayHaveSideEffects()) return false; // Can't merge if there are multiple distinct successors. diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -1029,8 +1029,9 @@ return; } - // Unwinding instructions successors are always executable. - if (TI.isExceptionalTerminator()) { + // We cannot analyze special terminators, so consider all successors + // executable. + if (TI.isSpecialTerminator()) { Succs.assign(TI.getNumSuccessors(), true); return; } @@ -1098,13 +1099,6 @@ return; } - // In case of callbr, we pessimistically assume that all successors are - // feasible. - if (isa(&TI)) { - Succs.assign(TI.getNumSuccessors(), true); - return; - } - LLVM_DEBUG(dbgs() << "Unknown terminator instruction: " << TI << '\n'); llvm_unreachable("SCCP: Don't know how to handle this terminator!"); } diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -5676,7 +5676,7 @@ for (Instruction &I : CaseDest->instructionsWithoutDebug(false)) { if (I.isTerminator()) { // If the terminator is a simple branch, continue to the next block. - if (I.getNumSuccessors() != 1 || I.isExceptionalTerminator()) + if (I.getNumSuccessors() != 1 || I.isSpecialTerminator()) return false; Pred = CaseDest; CaseDest = I.getSuccessor(0); diff --git a/llvm/test/Transforms/JumpThreading/pr46857-callbr.ll b/llvm/test/Transforms/JumpThreading/callbr.ll rename from llvm/test/Transforms/JumpThreading/pr46857-callbr.ll rename to llvm/test/Transforms/JumpThreading/callbr.ll --- a/llvm/test/Transforms/JumpThreading/pr46857-callbr.ll +++ b/llvm/test/Transforms/JumpThreading/callbr.ll @@ -47,3 +47,17 @@ bb11: ret i1 %i9 } + +define i32 @callbr_no_block_merge() { +; CHECK-LABEL: @callbr_no_block_merge( +; CHECK-NEXT: [[X:%.*]] = callbr i32 asm sideeffect "", "=r"() +; CHECK-NEXT: to label [[BB:%.*]] [] +; CHECK: bb: +; CHECK-NEXT: ret i32 [[X]] +; + %x = callbr i32 asm sideeffect "", "=r"() + to label %bb [] + +bb: + ret i32 %x +} diff --git a/llvm/test/Transforms/LICM/callbr-crash.ll b/llvm/test/Transforms/LICM/callbr-crash.ll --- a/llvm/test/Transforms/LICM/callbr-crash.ll +++ b/llvm/test/Transforms/LICM/callbr-crash.ll @@ -35,3 +35,27 @@ %phi = phi i32 [ %asmresult1.i.i, %cond.true.i ], [ undef, %for.cond ] ret i32 %phi } + +declare void @use_i32(i32) + +define void @pr64215() { +; CHECK-LABEL: @pr64215( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[X:%.*]] = callbr i32 asm sideeffect "", "=r"() +; CHECK-NEXT: to label [[LOOP_PREHEADER:%.*]] [] +; CHECK: loop.preheader: +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], 1 +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: call void @use_i32(i32 [[ADD]]) +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %x = callbr i32 asm sideeffect "", "=r"() + to label %loop [] + +loop: + %add = add i32 %x, 1 + call void @use_i32(i32 %add) + br label %loop +}