1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

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 d72d8c83a3
commit c1f65c8564
12 changed files with 174 additions and 7 deletions

View File

@ -9427,6 +9427,46 @@ Semantics:
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -277,6 +277,10 @@ def int_pcmarker : Intrinsic<[], [llvm_i32_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
// guard to the correct place on the stack frame.
def int_stackprotector : Intrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;

View File

@ -471,10 +471,7 @@ namespace {
const Location &Loc) override;
ModRefResult getModRefInfo(ImmutableCallSite CS1,
ImmutableCallSite CS2) override {
// The AliasAnalysis base class has some smarts, lets use them.
return AliasAnalysis::getModRefInfo(CS1, CS2);
}
ImmutableCallSite CS2) override;
/// pointsToConstantMemory - Chase pointers until we find a (constant
/// global) or not.
@ -788,6 +785,14 @@ BasicAliasAnalysis::getArgLocation(ImmutableCallSite CS, unsigned ArgIdx,
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
/// 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
@ -840,10 +845,29 @@ BasicAliasAnalysis::getModRefInfo(ImmutableCallSite CS,
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.
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
/// against another pointer. We know that V1 is a GEP, but we don't know
/// 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.
return TCC_Basic;
case Intrinsic::assume:
case Intrinsic::dbg_declare:
case Intrinsic::dbg_value:
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::fma: 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_end:
return 0;

View File

@ -459,9 +459,10 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) {
CI->replaceAllUsesWith(CI->getOperand(0));
break;
case Intrinsic::assume:
case Intrinsic::var_annotation:
break; // Strip out annotate intrinsic
break; // Strip out these intrinsics
case Intrinsic::memcpy: {
Type *IntPtr = DL.getIntPtrType(Context);
Value *Size = Builder.CreateIntCast(CI->getArgOperand(2), IntPtr,

View File

@ -5304,8 +5304,9 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
// Drop the intrinsic, but forward the value
setValue(&I, getValue(I.getOperand(0)));
return nullptr;
case Intrinsic::assume:
case Intrinsic::var_annotation:
// Discard annotate attributes
// Discard annotate attributes and assumptions
return nullptr;
case Intrinsic::init_trampoline: {

View File

@ -301,6 +301,14 @@ bool llvm::isInstructionTriviallyDead(Instruction *I,
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
II->getIntrinsicID() == Intrinsic::lifetime_end)
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;
@ -1183,6 +1191,26 @@ static bool markAliveBlocks(BasicBlock *BB,
// instructions into LLVM unreachable insts. The instruction combining pass
// canonicalizes unreachable insts into stores to null or undef.
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 (CI->doesNotReturn()) {
// 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