Introduce llvm.noalias.decl intrinsic

The ``llvm.experimental.noalias.scope.decl`` intrinsic identifies where a noalias
scope is declared. When the intrinsic is duplicated, a decision must
also be made about the scope: depending on the reason of the duplication,
the scope might need to be duplicated as well.

Reviewed By: nikic, jdoerfert

Differential Revision: https://reviews.llvm.org/D93039
This commit is contained in:
Jeroen Dobbelaere 2021-01-16 09:14:18 +01:00
parent 8456c3a789
commit 668827b648
10 changed files with 257 additions and 1 deletions

View File

@ -19603,6 +19603,82 @@ Semantics:
This function returns the same values as the libm ``trunc`` functions
would and handles error conditions in the same way.
.. _int_experimental_noalias_scope_decl:
'``llvm.experimental.noalias.scope.decl``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax:
"""""""
::
declare void @llvm.experimental.noalias.scope.decl(metadata !id.scope.list)
Overview:
"""""""""
The ``llvm.experimental.noalias.scope.decl`` intrinsic identifies where a
noalias scope is declared. When the intrinsic is duplicated, a decision must
also be made about the scope: depending on the reason of the duplication,
the scope might need to be duplicated as well.
Arguments:
""""""""""
The ``!id.scope.list`` argument is metadata that is a list of ``noalias``
metadata references. The format is identical to that required for ``noalias``
metadata. This list must have exactly one element.
Semantics:
""""""""""
The ``llvm.experimental.noalias.scope.decl`` intrinsic identifies where a
noalias scope is declared. When the intrinsic is duplicated, a decision must
also be made about the scope: depending on the reason of the duplication,
the scope might need to be duplicated as well.
For example, when the intrinsic is used inside a loop body, and that loop is
unrolled, the associated noalias scope must also be duplicated. Otherwise, the
noalias property it signifies would spill across loop iterations, whereas it
was only valid within a single iteration.
.. code-block:: llvm
; This examples shows two possible positions for noalias.decl and how they impact the semantics:
; If it is outside the loop (Version 1), then %a and %b are noalias across *all* iterations.
; If it is inside the loop (Version 2), then %a and %b are noalias only within *one* iteration.
declare void @decl_in_loop(i8* %a.base, i8* %b.base) {
entry:
; call void @llvm.experimental.noalias.scope.decl(metadata !2) ; Version 1: noalias decl outside loop
br label %loop
loop:
%a = phi i8* [ %a.base, %entry ], [ %a.inc, %loop ]
%b = phi i8* [ %b.base, %entry ], [ %b.inc, %loop ]
; call void @llvm.experimental.noalias.scope.decl(metadata !2) ; Version 2: noalias decl inside loop
%val = load i8, i8* %a, !alias.scope !2
store i8 %val, i8* %b, !noalias !2
%a.inc = getelementptr inbounds i8, i8* %a, i64 1
%b.inc = getelementptr inbounds i8, i8* %b, i64 1
%cond = call i1 @cond()
br i1 %cond, label %loop, label %exit
exit:
ret void
}
!0 = !{!0} ; domain
!1 = !{!1, !0} ; scope
!2 = !{!1} ; scope list
Multiple calls to `@llvm.experimental.noalias.scope.decl` for the same scope
are possible, but one should never dominate another. Violations are pointed out
by the verifier as they indicate a problem in either a transformation pass or
the input.
Floating Point Environment Manipulation intrinsics
--------------------------------------------------

View File

@ -858,6 +858,13 @@ public:
CallInst *CreateAssumption(Value *Cond,
ArrayRef<OperandBundleDef> OpBundles = llvm::None);
/// Create a llvm.experimental.noalias.scope.decl intrinsic call.
Instruction *CreateNoAliasScopeDeclaration(Value *Scope);
Instruction *CreateNoAliasScopeDeclaration(MDNode *ScopeTag) {
return CreateNoAliasScopeDeclaration(
MetadataAsValue::get(Context, ScopeTag));
}
/// Create a call to the experimental.gc.statepoint intrinsic to
/// start a new statepoint sequence.
CallInst *CreateGCStatepointCall(uint64_t ID, uint32_t NumPatchBytes,

View File

@ -34,6 +34,9 @@ class AttributeList;
/// function known by LLVM. The enum values are returned by
/// Function::getIntrinsicID().
namespace Intrinsic {
// Abstraction for the arguments of the noalias intrinsics
static const int NoAliasScopeDeclScopeArg = 0;
// Intrinsic ID type. This is an opaque typedef to facilitate splitting up
// the enum into target-specific enums.
typedef unsigned ID;

View File

@ -539,6 +539,16 @@ def int_readcyclecounter : DefaultAttrsIntrinsic<[llvm_i64_ty]>;
def int_assume : DefaultAttrsIntrinsic<[], [llvm_i1_ty], [IntrWillReturn,
NoUndef<ArgIndex<0>>]>;
// 'llvm.experimental.noalias.scope.decl' intrinsic: Inserted at the location of
// noalias scope declaration. Makes it possible to identify that a noalias scope
// is only valid inside the body of a loop.
//
// Purpose of the different arguments:
// - arg0: id.scope: metadata representing the scope declaration.
def int_experimental_noalias_scope_decl
: DefaultAttrsIntrinsic<[], [llvm_metadata_ty],
[IntrInaccessibleMemOnly]>; // blocks LICM and some more
// Stack Protector Intrinsic - The stackprotector intrinsic writes the stack
// guard to the correct place on the stack frame.
def int_stackprotector : DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;

View File

@ -329,6 +329,7 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) {
break;
case Intrinsic::assume:
case Intrinsic::experimental_noalias_scope_decl:
case Intrinsic::var_annotation:
break; // Strip out these intrinsics

View File

@ -1252,6 +1252,8 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
case Intrinsic::sideeffect:
// Neither does the assume intrinsic; it's also OK not to codegen its operand.
case Intrinsic::assume:
// Neither does the llvm.experimental.noalias.scope.decl intrinsic
case Intrinsic::experimental_noalias_scope_decl:
return true;
case Intrinsic::dbg_declare: {
const DbgDeclareInst *DI = cast<DbgDeclareInst>(II);

View File

@ -6466,10 +6466,13 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
// Drop the intrinsic, but forward the value
setValue(&I, getValue(I.getOperand(0)));
return;
case Intrinsic::assume:
case Intrinsic::experimental_noalias_scope_decl:
case Intrinsic::var_annotation:
case Intrinsic::sideeffect:
// Discard annotate attributes, assumptions, and artificial side-effects.
// Discard annotate attributes, noalias scope declarations, assumptions, and
// artificial side-effects.
return;
case Intrinsic::codeview_annotation: {

View File

@ -452,6 +452,13 @@ IRBuilderBase::CreateAssumption(Value *Cond,
return createCallHelper(FnAssume, Ops, this, "", nullptr, OpBundles);
}
Instruction *IRBuilderBase::CreateNoAliasScopeDeclaration(Value *Scope) {
Module *M = BB->getModule();
auto *FnIntrinsic = Intrinsic::getDeclaration(
M, Intrinsic::experimental_noalias_scope_decl, {});
return createCallHelper(FnIntrinsic, {Scope}, this);
}
/// Create a call to a Masked Load intrinsic.
/// \p Ptr - base pointer for the load
/// \p Alignment - alignment of the source location

View File

@ -115,6 +115,11 @@
using namespace llvm;
static cl::opt<bool> VerifyNoAliasScopeDomination(
"verify-noalias-scope-decl-dom", cl::Hidden, cl::init(false),
cl::desc("Ensure that llvm.experimental.noalias.scope.decl for identical "
"scopes are not dominating"));
namespace llvm {
struct VerifierSupport {
@ -313,6 +318,8 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
TBAAVerifier TBAAVerifyHelper;
SmallVector<IntrinsicInst *, 4> NoAliasScopeDecls;
void checkAtomicMemAccessSize(Type *Ty, const Instruction *I);
public:
@ -360,6 +367,8 @@ public:
LandingPadResultTy = nullptr;
SawFrameEscape = false;
SiblingFuncletInfo.clear();
verifyNoAliasScopeDecl();
NoAliasScopeDecls.clear();
return !Broken;
}
@ -536,6 +545,9 @@ private:
/// Verify all-or-nothing property of DIFile source attribute within a CU.
void verifySourceDebugInfo(const DICompileUnit &U, const DIFile &F);
/// Verify the llvm.experimental.noalias.scope.decl declarations
void verifyNoAliasScopeDecl();
};
} // end anonymous namespace
@ -5163,6 +5175,10 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
&Call);
break;
}
case Intrinsic::experimental_noalias_scope_decl: {
NoAliasScopeDecls.push_back(cast<IntrinsicInst>(&Call));
break;
}
};
}
@ -5513,6 +5529,76 @@ void Verifier::verifySourceDebugInfo(const DICompileUnit &U, const DIFile &F) {
"inconsistent use of embedded source");
}
void Verifier::verifyNoAliasScopeDecl() {
if (NoAliasScopeDecls.empty())
return;
// only a single scope must be declared at a time.
for (auto *II : NoAliasScopeDecls) {
assert(II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl &&
"Not a llvm.experimental.noalias.scope.decl ?");
const auto *ScopeListMV = dyn_cast<MetadataAsValue>(
II->getOperand(Intrinsic::NoAliasScopeDeclScopeArg));
Assert(ScopeListMV != nullptr,
"llvm.experimental.noalias.scope.decl must have a MetadataAsValue "
"argument",
II);
const auto *ScopeListMD = dyn_cast<MDNode>(ScopeListMV->getMetadata());
Assert(ScopeListMD != nullptr, "!id.scope.list must point to an MDNode",
II);
Assert(ScopeListMD->getNumOperands() == 1,
"!id.scope.list must point to a list with a single scope", II);
}
// Only check the domination rule when requested. Once all passes have been
// adapted this option can go away.
if (!VerifyNoAliasScopeDomination)
return;
// Now sort the intrinsics based on the scope MDNode so that declarations of
// the same scopes are next to each other.
auto GetScope = [](IntrinsicInst *II) {
const auto *ScopeListMV = cast<MetadataAsValue>(
II->getOperand(Intrinsic::NoAliasScopeDeclScopeArg));
return &cast<MDNode>(ScopeListMV->getMetadata())->getOperand(0);
};
// We are sorting on MDNode pointers here. For valid input IR this is ok.
// TODO: Sort on Metadata ID to avoid non-deterministic error messages.
auto Compare = [GetScope](IntrinsicInst *Lhs, IntrinsicInst *Rhs) {
return GetScope(Lhs) < GetScope(Rhs);
};
llvm::sort(NoAliasScopeDecls, Compare);
// Go over the intrinsics and check that for the same scope, they are not
// dominating each other.
auto ItCurrent = NoAliasScopeDecls.begin();
while (ItCurrent != NoAliasScopeDecls.end()) {
auto CurScope = GetScope(*ItCurrent);
auto ItNext = ItCurrent;
do {
++ItNext;
} while (ItNext != NoAliasScopeDecls.end() &&
GetScope(*ItNext) == CurScope);
// [ItCurrent, ItNext[ represents the declarations for the same scope.
// Ensure they are not dominating each other
for (auto *I : llvm::make_range(ItCurrent, ItNext)) {
for (auto *J : llvm::make_range(ItCurrent, ItNext)) {
if (I != J) {
Assert(!DT.dominates(I, J),
"llvm.experimental.noalias.scope.decl dominates another one "
"with the same scope",
I);
}
}
}
ItCurrent = ItNext;
}
}
//===----------------------------------------------------------------------===//
// Implement the public interfaces to this file...
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,61 @@
; RUN: not llvm-as -disable-output --verify-noalias-scope-decl-dom < %s 2>&1 | FileCheck %s
define void @test_single_scope01() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
ret void
}
define void @test_single_scope02() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !5)
ret void
}
; CHECK: !id.scope.list must point to a list with a single scope
; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata !5)
define void @test_single_scope03() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !"test")
ret void
}
; CHECK-NEXT: !id.scope.list must point to an MDNode
; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata !"test")
define void @test_dom01() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
tail call void @llvm.experimental.noalias.scope.decl(metadata !8)
ret void
}
define void @test_dom02() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
tail call void @llvm.experimental.noalias.scope.decl(metadata !6)
ret void
}
; CHECK-NEXT: llvm.experimental.noalias.scope.decl dominates another one with the same scope
; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
define void @test_dom03() nounwind ssp {
tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
ret void
}
; CHECK-NEXT: llvm.experimental.noalias.scope.decl dominates another one with the same scope
; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
; CHECK-NOT: llvm.experimental.noalias.scope.decl
; Function Attrs: inaccessiblememonly nounwind
declare void @llvm.experimental.noalias.scope.decl(metadata) #1
attributes #1 = { inaccessiblememonly nounwind }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang"}
!2 = !{!3}
!3 = distinct !{!3, !4, !"test: pA"}
!4 = distinct !{!4, !"test"}
!5 = !{!3, !3}
!6 = !{!3}
!7 = distinct !{!7, !4, !"test: pB"}
!8 = !{!7}