forked from OSchip/llvm-project
Implement strip.invariant.group
Summary: This patch introduce new intrinsic - strip.invariant.group that was described in the RFC: Devirtualization v2 Reviewers: rsmith, hfinkel, nlopes, sanjoy, amharc, kuhar Subscribers: arsenm, nhaehnle, JDevlieghere, hiraditya, xbolva00, llvm-commits Differential Revision: https://reviews.llvm.org/D47103 Co-authored-by: Krzysztof Pszeniczny <krzysztof.pszeniczny@gmail.com> llvm-svn: 336073
This commit is contained in:
parent
53054141a7
commit
5b3db45e8f
|
@ -13350,16 +13350,17 @@ Overview:
|
||||||
"""""""""
|
"""""""""
|
||||||
|
|
||||||
The '``llvm.launder.invariant.group``' intrinsic can be used when an invariant
|
The '``llvm.launder.invariant.group``' intrinsic can be used when an invariant
|
||||||
established by invariant.group metadata no longer holds, to obtain a new pointer
|
established by ``invariant.group`` metadata no longer holds, to obtain a new
|
||||||
value that does not carry the invariant information. It is an experimental
|
pointer value that carries fresh invariant group information. It is an
|
||||||
intrinsic, which means that its semantics might change in the future.
|
experimental intrinsic, which means that its semantics might change in the
|
||||||
|
future.
|
||||||
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
""""""""""
|
""""""""""
|
||||||
|
|
||||||
The ``llvm.launder.invariant.group`` takes only one argument, which is
|
The ``llvm.launder.invariant.group`` takes only one argument, which is a pointer
|
||||||
the pointer to the memory for which the ``invariant.group`` no longer holds.
|
to the memory.
|
||||||
|
|
||||||
Semantics:
|
Semantics:
|
||||||
""""""""""
|
""""""""""
|
||||||
|
@ -13368,6 +13369,43 @@ Returns another pointer that aliases its argument but which is considered differ
|
||||||
for the purposes of ``load``/``store`` ``invariant.group`` metadata.
|
for the purposes of ``load``/``store`` ``invariant.group`` metadata.
|
||||||
It does not read any accessible memory and the execution can be speculated.
|
It does not read any accessible memory and the execution can be speculated.
|
||||||
|
|
||||||
|
'``llvm.strip.invariant.group``' Intrinsic
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
"""""""
|
||||||
|
This is an overloaded intrinsic. The memory object can belong to any address
|
||||||
|
space. The returned pointer must belong to the same address space as the
|
||||||
|
argument.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8* <ptr>)
|
||||||
|
|
||||||
|
Overview:
|
||||||
|
"""""""""
|
||||||
|
|
||||||
|
The '``llvm.strip.invariant.group``' intrinsic can be used when an invariant
|
||||||
|
established by ``invariant.group`` metadata no longer holds, to obtain a new pointer
|
||||||
|
value that does not carry the invariant information. It is an experimental
|
||||||
|
intrinsic, which means that its semantics might change in the future.
|
||||||
|
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
The ``llvm.strip.invariant.group`` takes only one argument, which is a pointer
|
||||||
|
to the memory.
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
Returns another pointer that aliases its argument but which has no associated
|
||||||
|
``invariant.group`` metadata.
|
||||||
|
It does not read any memory and can be speculated.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _constrainedfp:
|
.. _constrainedfp:
|
||||||
|
|
||||||
Constrained Floating-Point Intrinsics
|
Constrained Floating-Point Intrinsics
|
||||||
|
|
|
@ -2022,6 +2022,7 @@ public:
|
||||||
Value *CreateLaunderInvariantGroup(Value *Ptr) {
|
Value *CreateLaunderInvariantGroup(Value *Ptr) {
|
||||||
assert(isa<PointerType>(Ptr->getType()) &&
|
assert(isa<PointerType>(Ptr->getType()) &&
|
||||||
"launder.invariant.group only applies to pointers.");
|
"launder.invariant.group only applies to pointers.");
|
||||||
|
// FIXME: we could potentially avoid casts to/from i8*.
|
||||||
auto *PtrType = Ptr->getType();
|
auto *PtrType = Ptr->getType();
|
||||||
auto *Int8PtrTy = getInt8PtrTy(PtrType->getPointerAddressSpace());
|
auto *Int8PtrTy = getInt8PtrTy(PtrType->getPointerAddressSpace());
|
||||||
if (PtrType != Int8PtrTy)
|
if (PtrType != Int8PtrTy)
|
||||||
|
@ -2042,6 +2043,34 @@ public:
|
||||||
return Fn;
|
return Fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Create a strip.invariant.group intrinsic call. If Ptr type is
|
||||||
|
/// different from pointer to i8, it's casted to pointer to i8 in the same
|
||||||
|
/// address space before call and casted back to Ptr type after call.
|
||||||
|
Value *CreateStripInvariantGroup(Value *Ptr) {
|
||||||
|
assert(isa<PointerType>(Ptr->getType()) &&
|
||||||
|
"strip.invariant.group only applies to pointers.");
|
||||||
|
|
||||||
|
// FIXME: we could potentially avoid casts to/from i8*.
|
||||||
|
auto *PtrType = Ptr->getType();
|
||||||
|
auto *Int8PtrTy = getInt8PtrTy(PtrType->getPointerAddressSpace());
|
||||||
|
if (PtrType != Int8PtrTy)
|
||||||
|
Ptr = CreateBitCast(Ptr, Int8PtrTy);
|
||||||
|
Module *M = BB->getParent()->getParent();
|
||||||
|
Function *FnStripInvariantGroup = Intrinsic::getDeclaration(
|
||||||
|
M, Intrinsic::strip_invariant_group, {Int8PtrTy});
|
||||||
|
|
||||||
|
assert(FnStripInvariantGroup->getReturnType() == Int8PtrTy &&
|
||||||
|
FnStripInvariantGroup->getFunctionType()->getParamType(0) ==
|
||||||
|
Int8PtrTy &&
|
||||||
|
"StripInvariantGroup should take and return the same type");
|
||||||
|
|
||||||
|
CallInst *Fn = CreateCall(FnStripInvariantGroup, {Ptr});
|
||||||
|
|
||||||
|
if (PtrType != Int8PtrTy)
|
||||||
|
return CreateBitCast(Fn, PtrType);
|
||||||
|
return Fn;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a vector value that contains \arg V broadcasted to \p
|
/// Return a vector value that contains \arg V broadcasted to \p
|
||||||
/// NumElts elements.
|
/// NumElts elements.
|
||||||
Value *CreateVectorSplat(unsigned NumElts, Value *V, const Twine &Name = "") {
|
Value *CreateVectorSplat(unsigned NumElts, Value *V, const Twine &Name = "") {
|
||||||
|
|
|
@ -728,6 +728,11 @@ def int_launder_invariant_group : Intrinsic<[llvm_anyptr_ty],
|
||||||
[LLVMMatchType<0>],
|
[LLVMMatchType<0>],
|
||||||
[IntrInaccessibleMemOnly, IntrSpeculatable]>;
|
[IntrInaccessibleMemOnly, IntrSpeculatable]>;
|
||||||
|
|
||||||
|
|
||||||
|
def int_strip_invariant_group : Intrinsic<[llvm_anyptr_ty],
|
||||||
|
[LLVMMatchType<0>],
|
||||||
|
[IntrSpeculatable, IntrNoMem]>;
|
||||||
|
|
||||||
//===------------------------ Stackmap Intrinsics -------------------------===//
|
//===------------------------ Stackmap Intrinsics -------------------------===//
|
||||||
//
|
//
|
||||||
def int_experimental_stackmap : Intrinsic<[],
|
def int_experimental_stackmap : Intrinsic<[],
|
||||||
|
|
|
@ -431,13 +431,15 @@ bool BasicAAResult::DecomposeGEPExpression(const Value *V,
|
||||||
const GEPOperator *GEPOp = dyn_cast<GEPOperator>(Op);
|
const GEPOperator *GEPOp = dyn_cast<GEPOperator>(Op);
|
||||||
if (!GEPOp) {
|
if (!GEPOp) {
|
||||||
if (auto CS = ImmutableCallSite(V)) {
|
if (auto CS = ImmutableCallSite(V)) {
|
||||||
// Note: getArgumentAliasingToReturnedPointer keeps it in sync with
|
// CaptureTracking can know about special capturing properties of some
|
||||||
// CaptureTracking, which is needed for correctness. This is because
|
// intrinsics like launder.invariant.group, that can't be expressed with
|
||||||
// some intrinsics like launder.invariant.group returns pointers that
|
// the attributes, but have properties like returning aliasing pointer.
|
||||||
// are aliasing it's argument, which is known to CaptureTracking.
|
// Because some analysis may assume that nocaptured pointer is not
|
||||||
// If AliasAnalysis does not use the same information, it could assume
|
// returned from some special intrinsic (because function would have to
|
||||||
// that pointer returned from launder does not alias it's argument
|
// be marked with returns attribute), it is crucial to use this function
|
||||||
// because launder could not return it if the pointer was not captured.
|
// because it should be in sync with CaptureTracking. Not using it may
|
||||||
|
// cause weird miscompilations where 2 aliasing pointers are assumed to
|
||||||
|
// noalias.
|
||||||
if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) {
|
if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) {
|
||||||
V = RP;
|
V = RP;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1393,6 +1393,7 @@ bool llvm::canConstantFoldCallTo(ImmutableCallSite CS, const Function *F) {
|
||||||
case Intrinsic::fmuladd:
|
case Intrinsic::fmuladd:
|
||||||
case Intrinsic::copysign:
|
case Intrinsic::copysign:
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
case Intrinsic::round:
|
case Intrinsic::round:
|
||||||
case Intrinsic::masked_load:
|
case Intrinsic::masked_load:
|
||||||
case Intrinsic::sadd_with_overflow:
|
case Intrinsic::sadd_with_overflow:
|
||||||
|
@ -1596,14 +1597,16 @@ Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, Type *Ty,
|
||||||
return Constant::getNullValue(Ty);
|
return Constant::getNullValue(Ty);
|
||||||
if (IntrinsicID == Intrinsic::bswap ||
|
if (IntrinsicID == Intrinsic::bswap ||
|
||||||
IntrinsicID == Intrinsic::bitreverse ||
|
IntrinsicID == Intrinsic::bitreverse ||
|
||||||
IntrinsicID == Intrinsic::launder_invariant_group)
|
IntrinsicID == Intrinsic::launder_invariant_group ||
|
||||||
|
IntrinsicID == Intrinsic::strip_invariant_group)
|
||||||
return Operands[0];
|
return Operands[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isa<ConstantPointerNull>(Operands[0]) &&
|
if (isa<ConstantPointerNull>(Operands[0]) &&
|
||||||
Operands[0]->getType()->getPointerAddressSpace() == 0) {
|
Operands[0]->getType()->getPointerAddressSpace() == 0) {
|
||||||
// launder(null) == null iff in addrspace 0
|
// launder(null) == null == strip(null) iff in addrspace 0
|
||||||
if (IntrinsicID == Intrinsic::launder_invariant_group)
|
if (IntrinsicID == Intrinsic::launder_invariant_group ||
|
||||||
|
IntrinsicID == Intrinsic::strip_invariant_group)
|
||||||
return Operands[0];
|
return Operands[0];
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3404,8 +3404,9 @@ const Value *llvm::getArgumentAliasingToReturnedPointer(ImmutableCallSite CS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool llvm::isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(
|
bool llvm::isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(
|
||||||
ImmutableCallSite CS) {
|
ImmutableCallSite CS) {
|
||||||
return CS.getIntrinsicID() == Intrinsic::launder_invariant_group;
|
return CS.getIntrinsicID() == Intrinsic::launder_invariant_group ||
|
||||||
|
CS.getIntrinsicID() == Intrinsic::strip_invariant_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \p PN defines a loop-variant pointer to an object. Check if the
|
/// \p PN defines a loop-variant pointer to an object. Check if the
|
||||||
|
@ -3454,13 +3455,15 @@ Value *llvm::GetUnderlyingObject(Value *V, const DataLayout &DL,
|
||||||
return V;
|
return V;
|
||||||
} else {
|
} else {
|
||||||
if (auto CS = CallSite(V)) {
|
if (auto CS = CallSite(V)) {
|
||||||
// Note: getArgumentAliasingToReturnedPointer keeps it in sync with
|
// CaptureTracking can know about special capturing properties of some
|
||||||
// CaptureTracking, which is needed for correctness. This is because
|
// intrinsics like launder.invariant.group, that can't be expressed with
|
||||||
// some intrinsics like launder.invariant.group returns pointers that
|
// the attributes, but have properties like returning aliasing pointer.
|
||||||
// are aliasing it's argument, which is known to CaptureTracking.
|
// Because some analysis may assume that nocaptured pointer is not
|
||||||
// If AliasAnalysis does not use the same information, it could assume
|
// returned from some special intrinsic (because function would have to
|
||||||
// that pointer returned from launder does not alias it's argument
|
// be marked with returns attribute), it is crucial to use this function
|
||||||
// because launder could not return it if the pointer was not captured.
|
// because it should be in sync with CaptureTracking. Not using it may
|
||||||
|
// cause weird miscompilations where 2 aliasing pointers are assumed to
|
||||||
|
// noalias.
|
||||||
if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) {
|
if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) {
|
||||||
V = RP;
|
V = RP;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1702,6 +1702,7 @@ bool CodeGenPrepare::optimizeCallInst(CallInst *CI, bool &ModifiedDT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
II->replaceAllUsesWith(II->getArgOperand(0));
|
II->replaceAllUsesWith(II->getArgOperand(0));
|
||||||
II->eraseFromParent();
|
II->eraseFromParent();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1437,6 +1437,7 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
case Intrinsic::expect: {
|
case Intrinsic::expect: {
|
||||||
unsigned ResultReg = getRegForValue(II->getArgOperand(0));
|
unsigned ResultReg = getRegForValue(II->getArgOperand(0));
|
||||||
if (!ResultReg)
|
if (!ResultReg)
|
||||||
|
|
|
@ -5768,6 +5768,7 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||||
case Intrinsic::annotation:
|
case Intrinsic::annotation:
|
||||||
case Intrinsic::ptr_annotation:
|
case Intrinsic::ptr_annotation:
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
// 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;
|
||||||
|
|
|
@ -521,7 +521,8 @@ static const Value *stripPointerCastsAndOffsets(const Value *V) {
|
||||||
// but it can't be marked with returned attribute, that's why it needs
|
// but it can't be marked with returned attribute, that's why it needs
|
||||||
// special case.
|
// special case.
|
||||||
if (StripKind == PSK_ZeroIndicesAndAliasesAndInvariantGroups &&
|
if (StripKind == PSK_ZeroIndicesAndAliasesAndInvariantGroups &&
|
||||||
CS.getIntrinsicID() == Intrinsic::launder_invariant_group) {
|
(CS.getIntrinsicID() == Intrinsic::launder_invariant_group ||
|
||||||
|
CS.getIntrinsicID() == Intrinsic::strip_invariant_group)) {
|
||||||
V = CS.getArgOperand(0);
|
V = CS.getArgOperand(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -457,6 +457,7 @@ static bool isCallPromotable(CallInst *CI) {
|
||||||
case Intrinsic::invariant_start:
|
case Intrinsic::invariant_start:
|
||||||
case Intrinsic::invariant_end:
|
case Intrinsic::invariant_end:
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
case Intrinsic::objectsize:
|
case Intrinsic::objectsize:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
@ -882,6 +883,7 @@ bool AMDGPUPromoteAlloca::handleAlloca(AllocaInst &I, bool SufficientLDS) {
|
||||||
case Intrinsic::invariant_start:
|
case Intrinsic::invariant_start:
|
||||||
case Intrinsic::invariant_end:
|
case Intrinsic::invariant_end:
|
||||||
case Intrinsic::launder_invariant_group:
|
case Intrinsic::launder_invariant_group:
|
||||||
|
case Intrinsic::strip_invariant_group:
|
||||||
Intr->eraseFromParent();
|
Intr->eraseFromParent();
|
||||||
// FIXME: I think the invariant marker should still theoretically apply,
|
// FIXME: I think the invariant marker should still theoretically apply,
|
||||||
// but the intrinsics need to be changed to accept pointers with any
|
// but the intrinsics need to be changed to accept pointers with any
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; RUN: opt -S -instsimplify -instcombine < %s | FileCheck %s
|
; RUN: opt -S -instsimplify -instcombine < %s | FileCheck %s
|
||||||
|
|
||||||
; CHECK-LABEL: define void @checkNonnull()
|
; CHECK-LABEL: define void @checkNonnullLaunder()
|
||||||
define void @checkNonnull() {
|
define void @checkNonnullLaunder() {
|
||||||
; CHECK: %p = call i8* @llvm.launder.invariant.group.p0i8(i8* nonnull %0)
|
; CHECK: %p = call i8* @llvm.launder.invariant.group.p0i8(i8* nonnull %0)
|
||||||
; CHECK: %p2 = call i8* @llvm.launder.invariant.group.p0i8(i8* nonnull %p)
|
; CHECK: %p2 = call i8* @llvm.launder.invariant.group.p0i8(i8* nonnull %p)
|
||||||
; CHECK: call void @use(i8* nonnull %p2)
|
; CHECK: call void @use(i8* nonnull %p2)
|
||||||
|
@ -15,5 +15,22 @@ entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define void @checkNonnullStrip()
|
||||||
|
define void @checkNonnullStrip() {
|
||||||
|
; CHECK: %p = call i8* @llvm.strip.invariant.group.p0i8(i8* nonnull %0)
|
||||||
|
; CHECK: %p2 = call i8* @llvm.strip.invariant.group.p0i8(i8* nonnull %p)
|
||||||
|
; CHECK: call void @use(i8* nonnull %p2)
|
||||||
|
entry:
|
||||||
|
%0 = alloca i8, align 8
|
||||||
|
|
||||||
|
%p = call i8* @llvm.strip.invariant.group.p0i8(i8* %0)
|
||||||
|
%p2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
|
||||||
|
call void @use(i8* %p2)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
|
|
||||||
declare void @use(i8*)
|
declare void @use(i8*)
|
||||||
|
|
|
@ -41,11 +41,19 @@ define double @test_cos(float %F) {
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group(i8*)
|
declare i8* @llvm.launder.invariant.group(i8*)
|
||||||
|
|
||||||
define i8* @barrier(i8* %p) {
|
define i8* @launder(i8* %p) {
|
||||||
%q = call i8* @llvm.launder.invariant.group(i8* %p)
|
%q = call i8* @llvm.launder.invariant.group(i8* %p)
|
||||||
ret i8* %q
|
ret i8* %q
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i8* @llvm.strip.invariant.group(i8*)
|
||||||
|
|
||||||
|
define i8* @strip(i8* %p) {
|
||||||
|
%q = call i8* @llvm.strip.invariant.group(i8* %p)
|
||||||
|
ret i8* %q
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
; sideeffect
|
; sideeffect
|
||||||
|
|
||||||
declare void @llvm.sideeffect()
|
declare void @llvm.sideeffect()
|
||||||
|
|
|
@ -77,8 +77,14 @@ define i8 @unoptimizable2() {
|
||||||
define void @dontProveEquality(i8* %a) {
|
define void @dontProveEquality(i8* %a) {
|
||||||
%b = call i8* @llvm.launder.invariant.group.p0i8(i8* %a)
|
%b = call i8* @llvm.launder.invariant.group.p0i8(i8* %a)
|
||||||
%r = icmp eq i8* %b, %a
|
%r = icmp eq i8* %b, %a
|
||||||
;CHECK: call void @useBool(i1 %r)
|
; CHECK: call void @useBool(i1 %r)
|
||||||
call void @useBool(i1 %r)
|
call void @useBool(i1 %r)
|
||||||
|
|
||||||
|
%b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
%r2 = icmp eq i8* %b2, %a
|
||||||
|
; CHECK: call void @useBool(i1 %r2)
|
||||||
|
call void @useBool(i1 %r2)
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,5 +96,9 @@ declare void @clobber(i8*)
|
||||||
; CHECK-NEXT: declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
; CHECK-NEXT: declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
|
||||||
!0 = !{}
|
; CHECK: Function Attrs: nounwind readnone speculatable{{$}}
|
||||||
|
; CHECK-NEXT: declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
|
|
||||||
|
|
||||||
|
!0 = !{}
|
|
@ -7,8 +7,8 @@ define void @foo() {
|
||||||
enter:
|
enter:
|
||||||
; CHECK-NOT: !invariant.group
|
; CHECK-NOT: !invariant.group
|
||||||
; CHECK-NOT: @llvm.launder.invariant.group.p0i8(
|
; CHECK-NOT: @llvm.launder.invariant.group.p0i8(
|
||||||
; CHECK: %val = load i8, i8* @tmp, !tbaa
|
; CHECK: %val = load i8, i8* @tmp{{$}}
|
||||||
%val = load i8, i8* @tmp, !invariant.group !0, !tbaa !{!1, !1, i64 0}
|
%val = load i8, i8* @tmp, !invariant.group !0
|
||||||
%ptr = call i8* @llvm.launder.invariant.group.p0i8(i8* @tmp)
|
%ptr = call i8* @llvm.launder.invariant.group.p0i8(i8* @tmp)
|
||||||
|
|
||||||
; CHECK: store i8 42, i8* @tmp{{$}}
|
; CHECK: store i8 42, i8* @tmp{{$}}
|
||||||
|
@ -18,7 +18,23 @@ enter:
|
||||||
}
|
}
|
||||||
; CHECK-LABEL: }
|
; CHECK-LABEL: }
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
; CHECK-LABEL: define void @foo2() {
|
||||||
|
define void @foo2() {
|
||||||
|
enter:
|
||||||
|
; CHECK-NOT: !invariant.group
|
||||||
|
; CHECK-NOT: @llvm.strip.invariant.group.p0i8(
|
||||||
|
; CHECK: %val = load i8, i8* @tmp{{$}}
|
||||||
|
%val = load i8, i8* @tmp, !invariant.group !0
|
||||||
|
%ptr = call i8* @llvm.strip.invariant.group.p0i8(i8* @tmp)
|
||||||
|
|
||||||
|
; CHECK: store i8 42, i8* @tmp{{$}}
|
||||||
|
store i8 42, i8* %ptr, !invariant.group !0
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; CHECK-LABEL: }
|
||||||
|
|
||||||
|
|
||||||
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
!0 = !{}
|
!0 = !{}
|
||||||
!1 = !{!"x", !0}
|
|
||||||
|
|
|
@ -27,4 +27,39 @@ define void @skip2Barriers(i8* %ptr) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: void @skip3Barriers(i8* %ptr)
|
||||||
|
define void @skip3Barriers(i8* %ptr) {
|
||||||
|
; CHECK-NOT: store i8 42
|
||||||
|
store i8 42, i8* %ptr
|
||||||
|
; CHECK: %ptr2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr)
|
||||||
|
%ptr2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr)
|
||||||
|
; CHECK-NOT: store i8 43
|
||||||
|
store i8 43, i8* %ptr2
|
||||||
|
%ptr3 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr2)
|
||||||
|
%ptr4 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr3)
|
||||||
|
|
||||||
|
; CHECK: store i8 44
|
||||||
|
store i8 44, i8* %ptr4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: void @skip4Barriers(i8* %ptr)
|
||||||
|
define void @skip4Barriers(i8* %ptr) {
|
||||||
|
; CHECK-NOT: store i8 42
|
||||||
|
store i8 42, i8* %ptr
|
||||||
|
; CHECK: %ptr2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr)
|
||||||
|
%ptr2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr)
|
||||||
|
; CHECK-NOT: store i8 43
|
||||||
|
store i8 43, i8* %ptr2
|
||||||
|
%ptr3 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr2)
|
||||||
|
%ptr4 = call i8* @llvm.strip.invariant.group.p0i8(i8* %ptr3)
|
||||||
|
%ptr5 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr3)
|
||||||
|
|
||||||
|
; CHECK: store i8 44
|
||||||
|
store i8 44, i8* %ptr5
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
|
@ -237,4 +237,21 @@ define void @captureLaunder(i8* %p) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK: @nocaptureStrip(i8* nocapture %p)
|
||||||
|
define void @nocaptureStrip(i8* %p) {
|
||||||
|
entry:
|
||||||
|
%b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
|
||||||
|
store i8 42, i8* %b
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
@g3 = global i8* null
|
||||||
|
; CHECK: define void @captureStrip(i8* %p)
|
||||||
|
define void @captureStrip(i8* %p) {
|
||||||
|
%b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
|
||||||
|
store i8* %b, i8** @g3
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
|
|
|
@ -51,6 +51,18 @@ entry:
|
||||||
ret i8 %b
|
ret i8 %b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i1 @proveEqualityForStrip(
|
||||||
|
define i1 @proveEqualityForStrip(i8* %a) {
|
||||||
|
; FIXME: The first call could be also removed by GVN. Right now
|
||||||
|
; DCE removes it. The second call is CSE'd with the first one.
|
||||||
|
; CHECK: %b1 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
%b1 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
; CHECK-NOT: llvm.strip.invariant.group
|
||||||
|
%b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
%r = icmp eq i8* %b1, %b2
|
||||||
|
; CHECK: ret i1 true
|
||||||
|
ret i1 %r
|
||||||
|
}
|
||||||
; CHECK-LABEL: define i8 @unoptimizable1() {
|
; CHECK-LABEL: define i8 @unoptimizable1() {
|
||||||
define i8 @unoptimizable1() {
|
define i8 @unoptimizable1() {
|
||||||
entry:
|
entry:
|
||||||
|
@ -437,10 +449,10 @@ declare void @_ZN1AC1Ev(%struct.A*)
|
||||||
declare void @fooBit(i1*, i1)
|
declare void @fooBit(i1*, i1)
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
; Function Attrs: nounwind
|
|
||||||
declare void @llvm.assume(i1 %cmp.vtables) #0
|
|
||||||
|
declare void @llvm.assume(i1 %cmp.vtables)
|
||||||
|
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
|
||||||
!0 = !{}
|
!0 = !{}
|
|
@ -27,15 +27,15 @@ enter:
|
||||||
define void @_optimizable() {
|
define void @_optimizable() {
|
||||||
enter:
|
enter:
|
||||||
%valptr = alloca i32
|
%valptr = alloca i32
|
||||||
|
|
||||||
%val = call i32 @TheAnswerToLifeTheUniverseAndEverything()
|
%val = call i32 @TheAnswerToLifeTheUniverseAndEverything()
|
||||||
store i32 %val, i32* @tmp
|
store i32 %val, i32* @tmp
|
||||||
store i32 %val, i32* %valptr
|
store i32 %val, i32* %valptr
|
||||||
|
|
||||||
%0 = bitcast i32* %valptr to i8*
|
%0 = bitcast i32* %valptr to i8*
|
||||||
%barr = call i8* @llvm.launder.invariant.group(i8* %0)
|
%barr = call i8* @llvm.launder.invariant.group(i8* %0)
|
||||||
%1 = bitcast i8* %barr to i32*
|
%1 = bitcast i8* %barr to i32*
|
||||||
|
|
||||||
%val2 = load i32, i32* %1
|
%val2 = load i32, i32* %1
|
||||||
store i32 %val2, i32* @tmp2
|
store i32 %val2, i32* @tmp2
|
||||||
ret void
|
ret void
|
||||||
|
@ -43,30 +43,30 @@ enter:
|
||||||
|
|
||||||
; We can't step through launder.invariant.group here, because that would change
|
; We can't step through launder.invariant.group here, because that would change
|
||||||
; this load in @usage_of_globals()
|
; this load in @usage_of_globals()
|
||||||
; val = load i32, i32* %ptrVal, !invariant.group !0
|
; val = load i32, i32* %ptrVal, !invariant.group !0
|
||||||
; into
|
; into
|
||||||
; %val = load i32, i32* @tmp3, !invariant.group !0
|
; %val = load i32, i32* @tmp3, !invariant.group !0
|
||||||
; and then we could assume that %val and %val2 to be the same, which coud be
|
; and then we could assume that %val and %val2 to be the same, which coud be
|
||||||
; false, because @changeTmp3ValAndCallBarrierInside() may change the value
|
; false, because @changeTmp3ValAndCallBarrierInside() may change the value
|
||||||
; of @tmp3.
|
; of @tmp3.
|
||||||
define void @_not_optimizable() {
|
define void @_not_optimizable() {
|
||||||
enter:
|
enter:
|
||||||
store i32 13, i32* @tmp3, !invariant.group !0
|
store i32 13, i32* @tmp3, !invariant.group !0
|
||||||
|
|
||||||
%0 = bitcast i32* @tmp3 to i8*
|
%0 = bitcast i32* @tmp3 to i8*
|
||||||
%barr = call i8* @llvm.launder.invariant.group(i8* %0)
|
%barr = call i8* @llvm.launder.invariant.group(i8* %0)
|
||||||
%1 = bitcast i8* %barr to i32*
|
%1 = bitcast i8* %barr to i32*
|
||||||
|
|
||||||
store i32* %1, i32** @ptrToTmp3
|
store i32* %1, i32** @ptrToTmp3
|
||||||
store i32 42, i32* %1, !invariant.group !0
|
store i32 42, i32* %1, !invariant.group !0
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
define void @usage_of_globals() {
|
define void @usage_of_globals() {
|
||||||
entry:
|
entry:
|
||||||
%ptrVal = load i32*, i32** @ptrToTmp3
|
%ptrVal = load i32*, i32** @ptrToTmp3
|
||||||
%val = load i32, i32* %ptrVal, !invariant.group !0
|
%val = load i32, i32* %ptrVal, !invariant.group !0
|
||||||
|
|
||||||
call void @changeTmp3ValAndCallBarrierInside()
|
call void @changeTmp3ValAndCallBarrierInside()
|
||||||
%val2 = load i32, i32* @tmp3, !invariant.group !0
|
%val2 = load i32, i32* @tmp3, !invariant.group !0
|
||||||
ret void;
|
ret void;
|
|
@ -1,5 +1,6 @@
|
||||||
; RUN: opt -instcombine -S < %s | FileCheck %s
|
; RUN: opt -instcombine -S < %s | FileCheck %s
|
||||||
|
|
||||||
|
|
||||||
; CHECK-LABEL: define i8* @simplifyNullLaunder()
|
; CHECK-LABEL: define i8* @simplifyNullLaunder()
|
||||||
define i8* @simplifyNullLaunder() {
|
define i8* @simplifyNullLaunder() {
|
||||||
; CHECK-NEXT: ret i8* null
|
; CHECK-NEXT: ret i8* null
|
||||||
|
@ -29,6 +30,39 @@ define i8 addrspace(42)* @simplifyUndefLaunder2() {
|
||||||
ret i8 addrspace(42)* %b2
|
ret i8 addrspace(42)* %b2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
declare i8* @llvm.launder.invariant.group.p0i8(i8*)
|
||||||
declare i8 addrspace(42)* @llvm.launder.invariant.group.p42i8(i8 addrspace(42)*)
|
declare i8 addrspace(42)* @llvm.launder.invariant.group.p42i8(i8 addrspace(42)*)
|
||||||
|
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i8* @simplifyNullStrip()
|
||||||
|
define i8* @simplifyNullStrip() {
|
||||||
|
; CHECK-NEXT: ret i8* null
|
||||||
|
%b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* null)
|
||||||
|
ret i8* %b2
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i8 addrspace(42)* @dontsimplifyNullStripForDifferentAddrspace()
|
||||||
|
define i8 addrspace(42)* @dontsimplifyNullStripForDifferentAddrspace() {
|
||||||
|
; CHECK: %b2 = call i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)* null)
|
||||||
|
; CHECK: ret i8 addrspace(42)* %b2
|
||||||
|
%b2 = call i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)* null)
|
||||||
|
ret i8 addrspace(42)* %b2
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i8* @simplifyUndefStrip()
|
||||||
|
define i8* @simplifyUndefStrip() {
|
||||||
|
; CHECK-NEXT: ret i8* undef
|
||||||
|
%b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* undef)
|
||||||
|
ret i8* %b2
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i8 addrspace(42)* @simplifyUndefStrip2()
|
||||||
|
define i8 addrspace(42)* @simplifyUndefStrip2() {
|
||||||
|
; CHECK-NEXT: ret i8 addrspace(42)* undef
|
||||||
|
%b2 = call i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)* undef)
|
||||||
|
ret i8 addrspace(42)* %b2
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i8* @llvm.strip.invariant.group.p0i8(i8*)
|
||||||
|
declare i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)*)
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,19 @@ entry:
|
||||||
ret i8 %b
|
ret i8 %b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: define i1 @proveEqualityForStrip(
|
||||||
|
define i1 @proveEqualityForStrip(i8* %a) {
|
||||||
|
; FIXME: The first call could be also removed by GVN. Right now
|
||||||
|
; DCE removes it. The second call is CSE'd with the first one.
|
||||||
|
; CHECK: %b1 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
%b1 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
; CHECK-NOT: llvm.strip.invariant.group
|
||||||
|
%b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a)
|
||||||
|
%r = icmp eq i8* %b1, %b2
|
||||||
|
; CHECK: ret i1 true
|
||||||
|
ret i1 %r
|
||||||
|
}
|
||||||
|
|
||||||
; CHECK-LABEL: define i8 @unoptimizable1() {
|
; CHECK-LABEL: define i8 @unoptimizable1() {
|
||||||
define i8 @unoptimizable1() {
|
define i8 @unoptimizable1() {
|
||||||
entry:
|
entry:
|
||||||
|
|
Loading…
Reference in New Issue