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 @@ -1802,14 +1802,40 @@ return ClassifyResult::None; }; - // If the save is right next to the restore, remove the restore. This can - // happen when variable allocas are DCE'd. + // If the stacksave and the stackrestore are in the same BB, and there is + // no intervening call, alloca, or stackrestore of a different stacksave, + // 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) { + switch (Classify(&*BI)) { + case ClassifyResult::None: + // So far so good, look at next instructions. + break; + + case ClassifyResult::StackRestore: + // If we found an intervening stackrestore for a different + // stacksave, we can't remove the stackrestore. Otherwise, continue. + if (cast(*BI).getArgOperand(0) != SS) + CannotRemove = true; + break; + + case ClassifyResult::Alloca: + case ClassifyResult::CallWithSideEffects: + // If we found an alloca, a non-intrinsic call, or an intrinsic + // call with side effects, we can't remove the stackrestore. + CannotRemove = true; + break; + } + if (CannotRemove) + break; } + + if (!CannotRemove) + return eraseInstFromFunction(CI); } } 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,31 @@ ; CHECK: call void @llvm.stackrestore(i8* %save1) ; CHECK: br i1 %done, label %loop, label %return ; CHECK: ret void + +define i32 @test4(i32 %m, i32* %a, i32* %b) { +entry: + br label %for.body + +for.body: + %x.012 = phi i32 [ 0, %entry ], [ %add2, %for.body ] + %i.011 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %0 = call i8* @llvm.stacksave() + %load1 = load i32, i32* %a, align 4 + %mul1 = mul nsw i32 %load1, %m + %add1 = add nsw i32 %mul1, %x.012 + call void @llvm.stackrestore(i8* %0) + %load2 = load i32, i32* %b, align 4 + %mul2 = mul nsw i32 %load2, %m + %add2 = add nsw i32 %mul2, %add1 + 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 %add2 +} + +; CHECK-LABEL: define i32 @test4( +; CHECK-NOT: call void @llvm.stackrestore +; CHECK: ret i32 %add2