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

Introducing llvm.invariant.group.barrier intrinsic

For more info for what reason it was invented, goto:
http://lists.llvm.org/pipermail/cfe-dev/2015-July/044227.html

invariant.group.barrier:
http://reviews.llvm.org/D12310
docs:
http://reviews.llvm.org/D11399
CodeGenPrepare:
http://reviews.llvm.org/D12875

llvm-svn: 247711
This commit is contained in:
Piotr Padlewski 2015-09-15 18:32:14 +00:00
parent 4a4ca5700a
commit 7016a01b0d
6 changed files with 230 additions and 5 deletions

View File

@ -4421,6 +4421,50 @@ the loop identifier metadata node directly:
The ``llvm.bitsets`` global metadata is used to implement The ``llvm.bitsets`` global metadata is used to implement
:doc:`bitsets <BitSets>`. :doc:`bitsets <BitSets>`.
'``invariant.group``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``invariant.group`` metadata may be attached to ``load``/``store`` instructions.
The existence of the ``invariant.group`` metadata on the instruction tells
the optimizer that every ``load`` and ``store`` to the same pointer operand
within the same invariant group can be assumed to load or store the same
value (but see the ``llvm.invariant.group.barrier`` intrinsic which affects
when two pointers are considered the same).
Examples:
.. code-block:: llvm
@unknownPtr = external global i8
...
%ptr = alloca i8
store i8 42, i8* %ptr, !invariant.group !0
call void @foo(i8* %ptr)
%a = load i8, i8* %ptr, !invariant.group !0 ; Can assume that value under %ptr didn't change
call void @foo(i8* %ptr)
%b = load i8, i8* %ptr, !invariant.group !1 ; Can't assume anything, because group changed
%newPtr = call i8* @getPointer(i8* %ptr)
%c = load i8, i8* %newPtr, !invariant.group !0 ; Can't assume anything, because we only have information about %ptr
%unknownValue = load i8, i8* @unknownPtr
store i8 %unknownValue, i8* %ptr, !invariant.group !0 ; Can assume that %unknownValue == 42
call void @foo(i8* %ptr)
%newPtr2 = call i8* @llvm.invariant.group.barrier(i8* %ptr)
%d = load i8, i8* %newPtr2, !invariant.group !0 ; Can't step through invariant.group.barrier to get value of %ptr
...
declare void @foo(i8*)
declare i8* @getPointer(i8*)
declare i8* @llvm.invariant.group.barrier(i8*)
!0 = !{!"magic ptr"}
!1 = !{!"other ptr"}
Module Flags Metadata Module Flags Metadata
===================== =====================
@ -6768,8 +6812,8 @@ Syntax:
:: ::
<result> = load [volatile] <ty>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<index>][, !invariant.load !<index>][, !nonnull !<index>][, !dereferenceable !<index>][, !dereferenceable_or_null !<index>] <result> = load [volatile] <ty>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<index>][, !invariant.load !<index>][, !invariant.group !<index>][, !nonnull !<index>][, !dereferenceable !<index>][, !dereferenceable_or_null !<index>]
<result> = load atomic [volatile] <ty>* <pointer> [singlethread] <ordering>, align <alignment> <result> = load atomic [volatile] <ty>* <pointer> [singlethread] <ordering>, align <alignment> [, !invariant.group !<index>]
!<index> = !{ i32 1 } !<index> = !{ i32 1 }
Overview: Overview:
@ -6825,6 +6869,9 @@ Being invariant does not imply that a location is dereferenceable,
but it does imply that once the location is known dereferenceable but it does imply that once the location is known dereferenceable
its value is henceforth unchanging. its value is henceforth unchanging.
The optional ``!invariant.group`` metadata must reference a single metadata name
``<index>`` corresponding to a metadata node. See ``invariant.group`` metadata.
The optional ``!nonnull`` metadata must reference a single The optional ``!nonnull`` metadata must reference a single
metadata name ``<index>`` corresponding to a metadata node with no metadata name ``<index>`` corresponding to a metadata node with no
entries. The existence of the ``!nonnull`` metadata on the entries. The existence of the ``!nonnull`` metadata on the
@ -6882,8 +6929,8 @@ Syntax:
:: ::
store [volatile] <ty> <value>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<index>] ; yields void store [volatile] <ty> <value>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<index>][, !invariant.group !<index>] ; yields void
store atomic [volatile] <ty> <value>, <ty>* <pointer> [singlethread] <ordering>, align <alignment> ; yields void store atomic [volatile] <ty> <value>, <ty>* <pointer> [singlethread] <ordering>, align <alignment> [, !invariant.group !<index>] ; yields void
Overview: Overview:
""""""""" """""""""
@ -6930,6 +6977,9 @@ be reused in the cache. The code generator may select special
instructions to save cache bandwidth, such as the MOVNT instruction on instructions to save cache bandwidth, such as the MOVNT instruction on
x86. x86.
The optional ``!invariant.group`` metadata must reference a
single metadata name ``<index>``. See ``invariant.group`` metadata.
Semantics: Semantics:
"""""""""" """"""""""
@ -11465,6 +11515,36 @@ Semantics:
This intrinsic indicates that the memory is mutable again. This intrinsic indicates that the memory is mutable again.
'``llvm.invariant.group.barrier``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax:
"""""""
::
declare i8* @llvm.invariant.group.barrier(i8* <ptr>)
Overview:
"""""""""
The '``llvm.invariant.group.barrier``' 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.
Arguments:
""""""""""
The ``llvm.invariant.group.barrier`` takes only one argument, which is
the pointer to the memory for which the ``invariant.group`` no longer holds.
Semantics:
""""""""""
Returns another pointer that aliases its argument but which is considered different
for the purposes of ``load``/``store`` ``invariant.group`` metadata.
General Intrinsics General Intrinsics
------------------ ------------------

View File

@ -24,6 +24,7 @@
#include "llvm/IR/Function.h" #include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h" #include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Operator.h" #include "llvm/IR/Operator.h"
#include "llvm/IR/ValueHandle.h" #include "llvm/IR/ValueHandle.h"
@ -1634,6 +1635,32 @@ public:
Name); Name);
} }
/// \brief Create an invariant.group.barrier intrinsic call, that stops
/// optimizer to propagate equality using invariant.group metadata.
/// If Ptr type is different from i8*, it's casted to i8* before call
/// and casted back to Ptr type after call.
Value *CreateInvariantGroupBarrier(Value *Ptr) {
Module *M = BB->getParent()->getParent();
Function *FnInvariantGroupBarrier = Intrinsic::getDeclaration(M,
Intrinsic::invariant_group_barrier);
Type *ArgumentAndReturnType = FnInvariantGroupBarrier->getReturnType();
assert(ArgumentAndReturnType ==
FnInvariantGroupBarrier->getFunctionType()->getParamType(0) &&
"InvariantGroupBarrier should take and return the same type");
Type *PtrType = Ptr->getType();
bool PtrTypeConversionNeeded = PtrType != ArgumentAndReturnType;
if (PtrTypeConversionNeeded)
Ptr = CreateBitCast(Ptr, ArgumentAndReturnType);
CallInst *Fn = CreateCall(FnInvariantGroupBarrier, {Ptr});
if (PtrTypeConversionNeeded)
return CreateBitCast(Fn, PtrType);
return Fn;
}
/// \brief Return a vector value that contains \arg V broadcasted to \p /// \brief 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 = "") {

View File

@ -530,6 +530,10 @@ def int_invariant_end : Intrinsic<[],
llvm_ptr_ty], llvm_ptr_ty],
[IntrReadWriteArgMem, NoCapture<2>]>; [IntrReadWriteArgMem, NoCapture<2>]>;
def int_invariant_group_barrier : Intrinsic<[llvm_ptr_ty],
[llvm_ptr_ty],
[IntrNoMem]>;
//===------------------------ Stackmap Intrinsics -------------------------===// //===------------------------ Stackmap Intrinsics -------------------------===//
// //
def int_experimental_stackmap : Intrinsic<[], def int_experimental_stackmap : Intrinsic<[],

View File

@ -187,6 +187,7 @@ class TypePromotionTransaction;
unsigned CreatedInstCost); unsigned CreatedInstCost);
bool splitBranchCondition(Function &F); bool splitBranchCondition(Function &F);
bool simplifyOffsetableRelocate(Instruction &I); bool simplifyOffsetableRelocate(Instruction &I);
void stripInvariantGroupMetadata(Instruction &I);
}; };
} }
@ -1411,6 +1412,10 @@ bool CodeGenPrepare::OptimizeCallInst(CallInst *CI, bool& ModifiedDT) {
InsertedInsts.insert(ExtVal); InsertedInsts.insert(ExtVal);
return true; return true;
} }
case Intrinsic::invariant_group_barrier:
II->replaceAllUsesWith(II->getArgOperand(0));
II->eraseFromParent();
return true;
} }
if (TLI) { if (TLI) {
@ -4421,6 +4426,7 @@ bool CodeGenPrepare::OptimizeInst(Instruction *I, bool& ModifiedDT) {
return OptimizeCmpExpression(CI); return OptimizeCmpExpression(CI);
if (LoadInst *LI = dyn_cast<LoadInst>(I)) { if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
stripInvariantGroupMetadata(*LI);
if (TLI) { if (TLI) {
unsigned AS = LI->getPointerAddressSpace(); unsigned AS = LI->getPointerAddressSpace();
return OptimizeMemoryInst(I, I->getOperand(0), LI->getType(), AS); return OptimizeMemoryInst(I, I->getOperand(0), LI->getType(), AS);
@ -4429,6 +4435,7 @@ bool CodeGenPrepare::OptimizeInst(Instruction *I, bool& ModifiedDT) {
} }
if (StoreInst *SI = dyn_cast<StoreInst>(I)) { if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
stripInvariantGroupMetadata(*SI);
if (TLI) { if (TLI) {
unsigned AS = SI->getPointerAddressSpace(); unsigned AS = SI->getPointerAddressSpace();
return OptimizeMemoryInst(I, SI->getOperand(1), return OptimizeMemoryInst(I, SI->getOperand(1),
@ -4827,3 +4834,8 @@ bool CodeGenPrepare::splitBranchCondition(Function &F) {
} }
return MadeChange; return MadeChange;
} }
void CodeGenPrepare::stripInvariantGroupMetadata(Instruction &I) {
if (auto *InvariantMD = I.getMetadata("invariant.group"))
I.dropUnknownNonDebugMetadata(InvariantMD->getMetadataID());
}

View File

@ -0,0 +1,23 @@
; RUN: opt -codegenprepare -S < %s | FileCheck %s
@tmp = global i8 0
; CHECK-LABEL: define void @foo() {
define void @foo() {
enter:
; CHECK-NOT: !invariant.group
; CHECK-NOT: @llvm.invariant.group.barrier(
; CHECK: %val = load i8, i8* @tmp
%val = load i8, i8* @tmp, !invariant.group !0
%ptr = call i8* @llvm.invariant.group.barrier(i8* @tmp)
; CHECK: store i8 42, i8* @tmp
store i8 42, i8* %ptr, !invariant.group !0
ret void
}
; CHECK-LABEL: }
declare i8* @llvm.invariant.group.barrier(i8*)
!0 = !{!"something"}

View File

@ -0,0 +1,79 @@
; RUN: opt -S -globalopt < %s | FileCheck %s
; This test is hint, what could globalOpt optimize and what it can't
; FIXME: @tmp and @tmp2 can be safely set to 42
; CHECK: @tmp = global i32 0
; CHECK: @tmp2 = global i32 0
; CHECK: @tmp3 = global i32 0
@tmp = global i32 0
@tmp2 = global i32 0
@tmp3 = global i32 0
@ptrToTmp3 = global i32* null
@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_a }]
define i32 @TheAnswerToLifeTheUniverseAndEverything() {
ret i32 42
}
define void @_GLOBAL__I_a() {
enter:
call void @_optimizable()
call void @_not_optimizable()
ret void
}
define void @_optimizable() {
enter:
%valptr = alloca i32
%val = call i32 @TheAnswerToLifeTheUniverseAndEverything()
store i32 %val, i32* @tmp
store i32 %val, i32* %valptr
%0 = bitcast i32* %valptr to i8*
%barr = call i8* @llvm.invariant.group.barrier(i8* %0)
%1 = bitcast i8* %barr to i32*
%val2 = load i32, i32* %1
store i32 %val2, i32* @tmp2
ret void
}
; We can't step through invariant.group.barrier here, because that would change
; this load in @usage_of_globals()
; val = load i32, i32* %ptrVal, !invariant.group !0
; into
; %val = load i32, i32* @tmp3, !invariant.group !0
; and then we could assume that %val and %val2 to be the same, which coud be
; false, because @changeTmp3ValAndCallBarrierInside() may change the value
; of @tmp3.
define void @_not_optimizable() {
enter:
store i32 13, i32* @tmp3, !invariant.group !0
%0 = bitcast i32* @tmp3 to i8*
%barr = call i8* @llvm.invariant.group.barrier(i8* %0)
%1 = bitcast i8* %barr to i32*
store i32* %1, i32** @ptrToTmp3
store i32 42, i32* %1, !invariant.group !0
ret void
}
define void @usage_of_globals() {
entry:
%ptrVal = load i32*, i32** @ptrToTmp3
%val = load i32, i32* %ptrVal, !invariant.group !0
call void @changeTmp3ValAndCallBarrierInside()
%val2 = load i32, i32* @tmp3, !invariant.group !0
ret void;
}
declare void @changeTmp3ValAndCallBarrierInside()
declare i8* @llvm.invariant.group.barrier(i8*)
!0 = !{!"something"}