diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1776,14 +1776,49 @@ break; } case Intrinsic::stackrestore: { - // If the save is right next to the restore, remove the restore. This can - // happen when variable allocas are DCE'd. + enum class ClassifyResult { + None, + Alloca, + StackRestore, + CallWithSideEffects, + }; + auto Classify = [](const Instruction *I) { + if (isa(I)) + return ClassifyResult::Alloca; + + if (auto *CI = dyn_cast(I)) { + if (auto *II = dyn_cast(CI)) { + if (II->getIntrinsicID() == Intrinsic::stackrestore) + return ClassifyResult::StackRestore; + + if (II->mayHaveSideEffects()) + return ClassifyResult::CallWithSideEffects; + } else { + // Consider all non-intrinsic calls to be side effects + return ClassifyResult::CallWithSideEffects; + } + } + + return ClassifyResult::None; + }; + + // If there is no intervening call/alloca between the save and the restore, + // remove the restore. This can happen when variable allocas are DCE'd. if (IntrinsicInst *SS = dyn_cast(II->getArgOperand(0))) { - if (SS->getIntrinsicID() == Intrinsic::stacksave) { - // Skip over debug info. - if (SS->getNextNonDebugInstruction() == II) { - return eraseInstFromFunction(CI); + if (SS->getIntrinsicID() == Intrinsic::stacksave && + SS->getParent() == II->getParent()) { + BasicBlock::iterator BI(SS); + bool CannotRemove = false; + for (++BI; &*BI != II; ++BI) { + if (Classify(&*BI) == ClassifyResult::None) + continue; + + CannotRemove = true; + break; } + + if (!CannotRemove) + return eraseInstFromFunction(CI); } } @@ -1793,29 +1828,24 @@ Instruction *TI = II->getParent()->getTerminator(); bool CannotRemove = false; for (++BI; &*BI != TI; ++BI) { - if (isa(BI)) { + switch (Classify(&*BI)) { + case ClassifyResult::None: + break; + + case ClassifyResult::StackRestore: + // If there is a stackrestore below this one, remove this one. + return eraseInstFromFunction(CI); + + case ClassifyResult::Alloca: + case ClassifyResult::CallWithSideEffects: + // If we found an alloca, a non-intrinsic call, or an intrinsic call + // with side effects (such as llvm.stacksave and llvm.read_register), + // we can't remove the stack restore. CannotRemove = true; break; } - if (CallInst *BCI = dyn_cast(BI)) { - if (auto *II2 = dyn_cast(BCI)) { - // If there is a stackrestore below this one, remove this one. - if (II2->getIntrinsicID() == Intrinsic::stackrestore) - return eraseInstFromFunction(CI); - - // Bail if we cross over an intrinsic with side effects, such as - // llvm.stacksave, or llvm.read_register. - if (II2->mayHaveSideEffects()) { - CannotRemove = true; - break; - } - } else { - // If we found a non-intrinsic call, we can't remove the stack - // restore. - CannotRemove = true; - break; - } - } + if (CannotRemove) + break; } // If the stack restore is in a return, resume, or unwind block and if there diff --git a/llvm/test/Transforms/InstCombine/stacksaverestore.ll b/llvm/test/Transforms/InstCombine/stacksaverestore.ll --- a/llvm/test/Transforms/InstCombine/stacksaverestore.ll +++ b/llvm/test/Transforms/InstCombine/stacksaverestore.ll @@ -110,3 +110,27 @@ ; CHECK: call void @llvm.stackrestore(i8* %save1) ; CHECK: br i1 %done, label %loop, label %return ; CHECK: ret void + +define i32 @test4(i32 %m, i32* nocapture readonly %a) { +entry: + br label %for.body + +for.body: + %x.012 = phi i32 [ 0, %entry ], [ %add, %for.body ] + %i.011 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %0 = call i8* @llvm.stacksave() + %1 = load i32, i32* %a, align 4 + %mul = mul nsw i32 %1, %m + %add = add nsw i32 %mul, %x.012 + call void @llvm.stackrestore(i8* %0) + %inc = add nuw nsw i32 %i.011, 1 + %exitcond.not = icmp eq i32 %inc, 100 + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: + ret i32 %add +} + +; CHECK-LABEL: define i32 @test4( +; CHECK-NOT: call void @llvm.stackrestore +; CHECK: ret i32 %add