forked from OSchip/llvm-project
Add @llvm.assume, lowering, and some basic properties
This is the first commit in a series that add an @llvm.assume intrinsic which can be used to provide the optimizer with a condition it may assume to be true (when the control flow would hit the intrinsic call). Some basic properties are added here: - llvm.invariant(true) is dead. - llvm.invariant(false) is unreachable (this directly corresponds to the documented behavior of MSVC's __assume(0)), so is llvm.invariant(undef). The intrinsic is tagged as writing arbitrarily, in order to maintain control dependencies. BasicAA has been updated, however, to return NoModRef for any particular location-based query so that we don't unnecessarily block code motion. llvm-svn: 213973
This commit is contained in:
parent
c888757a2d
commit
930469107d
|
@ -9427,6 +9427,46 @@ Semantics:
|
||||||
|
|
||||||
This intrinsic is lowered to the ``val``.
|
This intrinsic is lowered to the ``val``.
|
||||||
|
|
||||||
|
'``llvm.assume``' Intrinsic
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
"""""""
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
declare void @llvm.assume(i1 %cond)
|
||||||
|
|
||||||
|
Overview:
|
||||||
|
"""""""""
|
||||||
|
|
||||||
|
The ``llvm.assume`` allows the optimizer to assume that the provided
|
||||||
|
condition is true. This information can then be used in simplifying other parts
|
||||||
|
of the code.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
The condition which the optimizer may assume is always true.
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
The intrinsic allows the optimizer to assume that the provided condition is
|
||||||
|
always true whenever the control flow reaches the intrinsic call. No code is
|
||||||
|
generated for this intrinsic, and instructions that contribute only to the
|
||||||
|
provided condition are not used for code generation. If the condition is
|
||||||
|
violated during execution, the behavior is undefined.
|
||||||
|
|
||||||
|
Please note that optimizer might limit the transformations performed on values
|
||||||
|
used by the ``llvm.assume`` intrinsic in order to preserve the instructions
|
||||||
|
only used to form the intrinsic's input argument. This might prove undesirable
|
||||||
|
if the extra information provided by the ``llvm.assume`` intrinsic does cause
|
||||||
|
sufficient overall improvement in code quality. For this reason,
|
||||||
|
``llvm.assume`` should not be used to document basic mathematical invariants
|
||||||
|
that the optimizer can otherwise deduce or facts that are of little use to the
|
||||||
|
optimizer.
|
||||||
|
|
||||||
'``llvm.donothing``' Intrinsic
|
'``llvm.donothing``' Intrinsic
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -277,6 +277,10 @@ def int_pcmarker : Intrinsic<[], [llvm_i32_ty]>;
|
||||||
|
|
||||||
def int_readcyclecounter : Intrinsic<[llvm_i64_ty]>;
|
def int_readcyclecounter : Intrinsic<[llvm_i64_ty]>;
|
||||||
|
|
||||||
|
// The assume intrinsic is marked as arbitrarily writing so that proper
|
||||||
|
// control dependencies will be maintained.
|
||||||
|
def int_assume : Intrinsic<[], [llvm_i1_ty], []>;
|
||||||
|
|
||||||
// Stack Protector Intrinsic - The stackprotector intrinsic writes the stack
|
// Stack Protector Intrinsic - The stackprotector intrinsic writes the stack
|
||||||
// guard to the correct place on the stack frame.
|
// guard to the correct place on the stack frame.
|
||||||
def int_stackprotector : Intrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;
|
def int_stackprotector : Intrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;
|
||||||
|
|
|
@ -471,10 +471,7 @@ namespace {
|
||||||
const Location &Loc) override;
|
const Location &Loc) override;
|
||||||
|
|
||||||
ModRefResult getModRefInfo(ImmutableCallSite CS1,
|
ModRefResult getModRefInfo(ImmutableCallSite CS1,
|
||||||
ImmutableCallSite CS2) override {
|
ImmutableCallSite CS2) override;
|
||||||
// The AliasAnalysis base class has some smarts, lets use them.
|
|
||||||
return AliasAnalysis::getModRefInfo(CS1, CS2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
||||||
/// global) or not.
|
/// global) or not.
|
||||||
|
@ -788,6 +785,14 @@ BasicAliasAnalysis::getArgLocation(ImmutableCallSite CS, unsigned ArgIdx,
|
||||||
return Loc;
|
return Loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isAssumeIntrinsic(ImmutableCallSite CS) {
|
||||||
|
const IntrinsicInst *II = dyn_cast<IntrinsicInst>(CS.getInstruction());
|
||||||
|
if (II && II->getIntrinsicID() == Intrinsic::assume)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// getModRefInfo - Check to see if the specified callsite can clobber the
|
/// getModRefInfo - Check to see if the specified callsite can clobber the
|
||||||
/// specified memory object. Since we only look at local properties of this
|
/// specified memory object. Since we only look at local properties of this
|
||||||
/// function, we really can't say much about this query. We do, however, use
|
/// function, we really can't say much about this query. We do, however, use
|
||||||
|
@ -840,10 +845,29 @@ BasicAliasAnalysis::getModRefInfo(ImmutableCallSite CS,
|
||||||
return NoModRef;
|
return NoModRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// While the assume intrinsic is marked as arbitrarily writing so that
|
||||||
|
// proper control dependencies will be maintained, it never aliases any
|
||||||
|
// particular memory location.
|
||||||
|
if (isAssumeIntrinsic(CS))
|
||||||
|
return NoModRef;
|
||||||
|
|
||||||
// The AliasAnalysis base class has some smarts, lets use them.
|
// The AliasAnalysis base class has some smarts, lets use them.
|
||||||
return AliasAnalysis::getModRefInfo(CS, Loc);
|
return AliasAnalysis::getModRefInfo(CS, Loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::ModRefResult
|
||||||
|
BasicAliasAnalysis::getModRefInfo(ImmutableCallSite CS1,
|
||||||
|
ImmutableCallSite CS2) {
|
||||||
|
// While the assume intrinsic is marked as arbitrarily writing so that
|
||||||
|
// proper control dependencies will be maintained, it never aliases any
|
||||||
|
// particular memory location.
|
||||||
|
if (isAssumeIntrinsic(CS1) || isAssumeIntrinsic(CS2))
|
||||||
|
return NoModRef;
|
||||||
|
|
||||||
|
// The AliasAnalysis base class has some smarts, lets use them.
|
||||||
|
return AliasAnalysis::getModRefInfo(CS1, CS2);
|
||||||
|
}
|
||||||
|
|
||||||
/// aliasGEP - Provide a bunch of ad-hoc rules to disambiguate a GEP instruction
|
/// aliasGEP - Provide a bunch of ad-hoc rules to disambiguate a GEP instruction
|
||||||
/// against another pointer. We know that V1 is a GEP, but we don't know
|
/// against another pointer. We know that V1 is a GEP, but we don't know
|
||||||
/// anything about V2. UnderlyingV1 is GetUnderlyingObject(GEP1, DL),
|
/// anything about V2. UnderlyingV1 is GetUnderlyingObject(GEP1, DL),
|
||||||
|
|
|
@ -385,6 +385,7 @@ struct NoTTI final : ImmutablePass, TargetTransformInfo {
|
||||||
// FIXME: This is wrong for libc intrinsics.
|
// FIXME: This is wrong for libc intrinsics.
|
||||||
return TCC_Basic;
|
return TCC_Basic;
|
||||||
|
|
||||||
|
case Intrinsic::assume:
|
||||||
case Intrinsic::dbg_declare:
|
case Intrinsic::dbg_declare:
|
||||||
case Intrinsic::dbg_value:
|
case Intrinsic::dbg_value:
|
||||||
case Intrinsic::invariant_start:
|
case Intrinsic::invariant_start:
|
||||||
|
|
|
@ -572,6 +572,7 @@ unsigned BasicTTI::getIntrinsicInstrCost(Intrinsic::ID IID, Type *RetTy,
|
||||||
case Intrinsic::pow: ISD = ISD::FPOW; break;
|
case Intrinsic::pow: ISD = ISD::FPOW; break;
|
||||||
case Intrinsic::fma: ISD = ISD::FMA; break;
|
case Intrinsic::fma: ISD = ISD::FMA; break;
|
||||||
case Intrinsic::fmuladd: ISD = ISD::FMA; break;
|
case Intrinsic::fmuladd: ISD = ISD::FMA; break;
|
||||||
|
// FIXME: We should return 0 whenever getIntrinsicCost == TCC_Free.
|
||||||
case Intrinsic::lifetime_start:
|
case Intrinsic::lifetime_start:
|
||||||
case Intrinsic::lifetime_end:
|
case Intrinsic::lifetime_end:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -459,8 +459,9 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) {
|
||||||
CI->replaceAllUsesWith(CI->getOperand(0));
|
CI->replaceAllUsesWith(CI->getOperand(0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Intrinsic::assume:
|
||||||
case Intrinsic::var_annotation:
|
case Intrinsic::var_annotation:
|
||||||
break; // Strip out annotate intrinsic
|
break; // Strip out these intrinsics
|
||||||
|
|
||||||
case Intrinsic::memcpy: {
|
case Intrinsic::memcpy: {
|
||||||
Type *IntPtr = DL.getIntPtrType(Context);
|
Type *IntPtr = DL.getIntPtrType(Context);
|
||||||
|
|
|
@ -5304,8 +5304,9 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||||
// Drop the intrinsic, but forward the value
|
// Drop the intrinsic, but forward the value
|
||||||
setValue(&I, getValue(I.getOperand(0)));
|
setValue(&I, getValue(I.getOperand(0)));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
case Intrinsic::assume:
|
||||||
case Intrinsic::var_annotation:
|
case Intrinsic::var_annotation:
|
||||||
// Discard annotate attributes
|
// Discard annotate attributes and assumptions
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
case Intrinsic::init_trampoline: {
|
case Intrinsic::init_trampoline: {
|
||||||
|
|
|
@ -301,6 +301,14 @@ bool llvm::isInstructionTriviallyDead(Instruction *I,
|
||||||
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
|
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
|
||||||
II->getIntrinsicID() == Intrinsic::lifetime_end)
|
II->getIntrinsicID() == Intrinsic::lifetime_end)
|
||||||
return isa<UndefValue>(II->getArgOperand(1));
|
return isa<UndefValue>(II->getArgOperand(1));
|
||||||
|
|
||||||
|
// Assumptions are dead if their condition is trivially true.
|
||||||
|
if (II->getIntrinsicID() == Intrinsic::assume) {
|
||||||
|
if (ConstantInt *Cond = dyn_cast<ConstantInt>(II->getArgOperand(0)))
|
||||||
|
return !Cond->isZero();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAllocLikeFn(I, TLI)) return true;
|
if (isAllocLikeFn(I, TLI)) return true;
|
||||||
|
@ -1183,6 +1191,26 @@ static bool markAliveBlocks(BasicBlock *BB,
|
||||||
// instructions into LLVM unreachable insts. The instruction combining pass
|
// instructions into LLVM unreachable insts. The instruction combining pass
|
||||||
// canonicalizes unreachable insts into stores to null or undef.
|
// canonicalizes unreachable insts into stores to null or undef.
|
||||||
for (BasicBlock::iterator BBI = BB->begin(), E = BB->end(); BBI != E;++BBI){
|
for (BasicBlock::iterator BBI = BB->begin(), E = BB->end(); BBI != E;++BBI){
|
||||||
|
// Assumptions that are known to be false are equivalent to unreachable.
|
||||||
|
// Also, if the condition is undefined, then we make the choice most
|
||||||
|
// beneficial to the optimizer, and choose that to also be unreachable.
|
||||||
|
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(BBI))
|
||||||
|
if (II->getIntrinsicID() == Intrinsic::assume) {
|
||||||
|
bool MakeUnreachable = false;
|
||||||
|
if (isa<UndefValue>(II->getArgOperand(0)))
|
||||||
|
MakeUnreachable = true;
|
||||||
|
else if (ConstantInt *Cond =
|
||||||
|
dyn_cast<ConstantInt>(II->getArgOperand(0)))
|
||||||
|
MakeUnreachable = Cond->isZero();
|
||||||
|
|
||||||
|
if (MakeUnreachable) {
|
||||||
|
// Don't insert a call to llvm.trap right before the unreachable.
|
||||||
|
changeToUnreachable(BBI, false);
|
||||||
|
Changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CallInst *CI = dyn_cast<CallInst>(BBI)) {
|
if (CallInst *CI = dyn_cast<CallInst>(BBI)) {
|
||||||
if (CI->doesNotReturn()) {
|
if (CI->doesNotReturn()) {
|
||||||
// If we found a call to a no-return function, insert an unreachable
|
// If we found a call to a no-return function, insert an unreachable
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
; RUN: opt < %s -basicaa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||||
|
target datalayout = "e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:64-v128:32:128-a0:0:32-n32"
|
||||||
|
|
||||||
|
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) #0
|
||||||
|
declare void @llvm.assume(i1) #0
|
||||||
|
|
||||||
|
define void @test1(i8* %P, i8* %Q) nounwind ssp {
|
||||||
|
tail call void @llvm.assume(i1 true)
|
||||||
|
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %P, i8* %Q, i64 12, i32 1, i1 false)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
; CHECK-LABEL: Function: test1:
|
||||||
|
|
||||||
|
; CHECK: MayAlias: i8* %P, i8* %Q
|
||||||
|
; CHECK: NoModRef: Ptr: i8* %P <-> tail call void @llvm.assume(i1 true)
|
||||||
|
; CHECK: NoModRef: Ptr: i8* %Q <-> tail call void @llvm.assume(i1 true)
|
||||||
|
; CHECK: Both ModRef: Ptr: i8* %P <-> tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %P, i8* %Q, i64 12, i32 1, i1 false)
|
||||||
|
; CHECK: Both ModRef: Ptr: i8* %Q <-> tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %P, i8* %Q, i64 12, i32 1, i1 false)
|
||||||
|
; CHECK: NoModRef: tail call void @llvm.assume(i1 true) <-> tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %P, i8* %Q, i64 12, i32 1, i1 false)
|
||||||
|
; CHECK: NoModRef: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %P, i8* %Q, i64 12, i32 1, i1 false) <-> tail call void @llvm.assume(i1 true)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
|
@ -0,0 +1,9 @@
|
||||||
|
; RUN: llc < %s
|
||||||
|
|
||||||
|
define void @main() {
|
||||||
|
call void @llvm.assume(i1 1)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.assume(i1) nounwind
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
; RUN: opt -instsimplify -S < %s | FileCheck %s
|
||||||
|
|
||||||
|
define void @test1() {
|
||||||
|
call void @llvm.assume(i1 1)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test1
|
||||||
|
; CHECK-NOT: llvm.assume
|
||||||
|
; CHECK: ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.assume(i1) nounwind
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
; RUN: opt -simplifycfg -S < %s | FileCheck %s
|
||||||
|
|
||||||
|
define void @test1() {
|
||||||
|
call void @llvm.assume(i1 0)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test1
|
||||||
|
; CHECK-NOT: llvm.assume
|
||||||
|
; CHECK: unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @test2() {
|
||||||
|
call void @llvm.assume(i1 undef)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test2
|
||||||
|
; CHECK-NOT: llvm.assume
|
||||||
|
; CHECK: unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.assume(i1) nounwind
|
||||||
|
|
Loading…
Reference in New Issue