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:
Hal Finkel 2014-07-25 21:13:35 +00:00
parent c888757a2d
commit 930469107d
12 changed files with 174 additions and 7 deletions

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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], []>;

View File

@ -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),

View File

@ -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:

View File

@ -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;

View File

@ -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);

View File

@ -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: {

View File

@ -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

View File

@ -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 }

View File

@ -0,0 +1,9 @@
; RUN: llc < %s
define void @main() {
call void @llvm.assume(i1 1)
ret void
}
declare void @llvm.assume(i1) nounwind

View File

@ -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

View File

@ -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