Skip to content

Commit

Permalink
[IPSCCP] Remove calls without side effects
Browse files Browse the repository at this point in the history
Summary:
When performing constant propagation for call instructions we have historically replaced all uses of the return from a call, but not removed the call itself. This is required for correctness if the calls have side effects, however the compiler should be able to safely remove calls that don't have side effects.

This allows the compiler to completely fold away calls to functions that have no side effects if the inputs are constant and the output can be determined at compile time.

Reviewers: davide, sanjoy, bruno, dberlin

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D38856

llvm-svn: 322125
  • Loading branch information
llvm-beanz committed Jan 9, 2018
1 parent 1712700 commit abdea26
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 4 deletions.
8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/Instruction.h
Expand Up @@ -535,6 +535,14 @@ class Instruction : public User,
/// matters, isSafeToSpeculativelyExecute may be more appropriate.
bool mayHaveSideEffects() const { return mayWriteToMemory() || mayThrow(); }

/// Return true if the instruction can be removed if the result is unused.
///
/// When constant folding some instructions cannot be removed even if their
/// results are unused. Specifically terminator instructions and calls that
/// may have side effects cannot be removed without semantically changing the
/// generated program.
bool isSafeToRemove() const;

/// Return true if the instruction is a variety of EH-block.
bool isEHPad() const {
switch (getOpcode()) {
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/IR/Instruction.cpp
Expand Up @@ -589,6 +589,11 @@ bool Instruction::mayThrow() const {
return isa<ResumeInst>(this);
}

bool Instruction::isSafeToRemove() const {
return (!isa<CallInst>(this) || !this->mayHaveSideEffects()) &&
!isa<TerminatorInst>(this);
}

bool Instruction::isAssociative() const {
unsigned Opcode = getOpcode();
if (isAssociative(Opcode))
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/Scalar/SCCP.cpp
Expand Up @@ -1900,7 +1900,7 @@ static bool runIPSCCP(Module &M, const DataLayout &DL,
if (Inst->getType()->isVoidTy())
continue;
if (tryToReplaceWithConstant(Solver, Inst)) {
if (!isa<CallInst>(Inst) && !isa<TerminatorInst>(Inst))
if (Inst->isSafeToRemove())
Inst->eraseFromParent();
// Hey, we just changed something!
MadeChanges = true;
Expand Down
33 changes: 33 additions & 0 deletions llvm/test/Transforms/IPConstantProp/remove-call-inst.ll
@@ -0,0 +1,33 @@
; RUN: opt < %s -S -ipsccp | FileCheck %s
; PR5596

; IPSCCP should propagate the 0 argument, eliminate the switch, and propagate
; the result.

; CHECK: define i32 @main() #0 {
; CHECK-NEXT: entry:
; CHECK-NOT: call
; CHECK-NEXT: ret i32 123

define i32 @main() noreturn nounwind {
entry:
%call2 = tail call i32 @wwrite(i64 0) nounwind
ret i32 %call2
}

define internal i32 @wwrite(i64 %i) nounwind readnone {
entry:
switch i64 %i, label %sw.default [
i64 3, label %return
i64 10, label %return
]

sw.default:
ret i32 123

return:
ret i32 0
}

; CHECK: attributes #0 = { noreturn nounwind }
; CHECK: attributes #1 = { nounwind readnone }
Expand Up @@ -15,7 +15,7 @@ entry:
ret i32 %call2
}

define internal i32 @wwrite(i64 %i) nounwind readnone {
define internal i32 @wwrite(i64 %i) nounwind {
entry:
switch i64 %i, label %sw.default [
i64 3, label %return
Expand All @@ -30,5 +30,4 @@ return:
}

; CHECK: attributes #0 = { noreturn nounwind }
; CHECK: attributes #1 = { nounwind readnone }
; CHECK: attributes [[NUW]] = { nounwind }
; CHECK: attributes #1 = { nounwind }

0 comments on commit abdea26

Please sign in to comment.