diff --git a/llvm/include/llvm/Analysis/InstructionSimplify.h b/llvm/include/llvm/Analysis/InstructionSimplify.h --- a/llvm/include/llvm/Analysis/InstructionSimplify.h +++ b/llvm/include/llvm/Analysis/InstructionSimplify.h @@ -259,6 +259,10 @@ /// Given a callsite, fold the result or return null. Value *SimplifyCall(CallBase *Call, const SimplifyQuery &Q); +/// Given an operand for a Freeze, see if we can fold the result. +/// If not, this returns null. +Value *SimplifyFreezeInst(Value *Op, const SimplifyQuery &Q); + /// See if we can compute a simplified version of this instruction. If not, /// return null. Value *SimplifyInstruction(Instruction *I, const SimplifyQuery &Q, diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -561,6 +561,10 @@ /// the parent of I. bool programUndefinedIfFullPoison(const Instruction *PoisonI); + /// Return true if this function can prove that V is never undef value + /// or poison value. + bool isGuaranteedNotToBeUndefOrPoison(const Value *V); + /// Specific patterns of select instructions we can match. enum SelectPatternFlavor { SPF_UNKNOWN = 0, diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5241,6 +5241,19 @@ return ConstantFoldCall(Call, F, ConstantArgs, Q.TLI); } +/// Given operands for a Freeze, see if we can fold the result. +static Value *SimplifyFreezeInst(Value *Op0) { + // Use a utility function defined in ValueTracking. + if (llvm::isGuaranteedNotToBeUndefOrPoison(Op0)) + return Op0; + // We have room for improvement. + return nullptr; +} + +Value *llvm::SimplifyFreezeInst(Value *Op0, const SimplifyQuery &Q) { + return ::SimplifyFreezeInst(Op0); +} + /// See if we can compute a simplified version of this instruction. /// If not, this returns null. @@ -5383,6 +5396,9 @@ Result = SimplifyCall(cast(I), Q); break; } + case Instruction::Freeze: + Result = SimplifyFreezeInst(I->getOperand(0), Q); + break; #define HANDLE_CAST_INST(num, opc, clas) case Instruction::opc: #include "llvm/IR/Instruction.def" #undef HANDLE_CAST_INST diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -4221,6 +4221,20 @@ return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch); } +bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V) { + // If the value is a freeze instruction, then it can never + // be undef or poison. + if (isa(V)) + return true; + // TODO: Some instructions are guaranteed to return neither undef + // nor poison if their arguments are not poison/undef. + + // TODO: Deal with other Constant subclasses. + if (isa(V) || isa(V)) + return true; + + return false; +} OverflowResult llvm::computeOverflowForSignedAdd(const AddOperator *Add, const DataLayout &DL, diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -446,6 +446,7 @@ Instruction *visitLandingPadInst(LandingPadInst &LI); Instruction *visitVAStartInst(VAStartInst &I); Instruction *visitVACopyInst(VACopyInst &I); + Instruction *visitFreeze(FreezeInst &I); /// Specify what to return for unhandled instructions. Instruction *visitInstruction(Instruction &I) { return nullptr; } diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3131,6 +3131,15 @@ return nullptr; } +Instruction *InstCombiner::visitFreeze(FreezeInst &I) { + Value *Op0 = I.getOperand(0); + + if (Value *V = SimplifyFreezeInst(Op0, SQ.getWithInstruction(&I))) + return replaceInstUsesWith(I, V); + + return nullptr; +} + /// Try to move the specified instruction from its current block into the /// beginning of DestBlock, which can only happen if it's safe to move the /// instruction past all of the instructions between it and the end of its diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/freeze.ll @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +define i32 @fold(i32 %x) { +; CHECK-LABEL: @fold( +; CHECK-NEXT: [[Y:%.*]] = freeze i32 [[X:%.*]] +; CHECK-NEXT: ret i32 [[Y]] +; + %y = freeze i32 %x + %z = freeze i32 %y + ret i32 %z +} + +define i32 @make_const() { +; CHECK-LABEL: @make_const( +; CHECK-NEXT: ret i32 10 +; + %x = freeze i32 10 + ret i32 %x +} diff --git a/llvm/test/Transforms/InstSimplify/freeze.ll b/llvm/test/Transforms/InstSimplify/freeze.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/freeze.ll @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s + +define i32 @fold(i32 %x) { +; CHECK-LABEL: @fold( +; CHECK-NEXT: [[Y:%.*]] = freeze i32 [[X:%.*]] +; CHECK-NEXT: ret i32 [[Y]] +; + %y = freeze i32 %x + %z = freeze i32 %y + ret i32 %z +} + +define i32 @make_const() { +; CHECK-LABEL: @make_const( +; CHECK-NEXT: ret i32 10 +; + %x = freeze i32 10 + ret i32 %x +}