Skip to content

Commit

Permalink
Merged main:668827b64856 into amd-gfx:0510a18a75d0
Browse files Browse the repository at this point in the history
Local branch amd-gfx 0510a18 Merged main:a61e42efbb73 into amd-gfx:c40e345e152a
Remote branch main 668827b Introduce llvm.noalias.decl intrinsic
  • Loading branch information
Sw authored and Sw committed Jan 16, 2021
2 parents 0510a18 + 668827b commit 2f169bb
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 10 deletions.
76 changes: 76 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/Config/llvm-config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

/* Indicate that this is LLVM compiled from the amd-gfx branch. */
#define LLVM_HAVE_BRANCH_AMD_GFX
#define LLVM_MAIN_REVISION 377189
#define LLVM_MAIN_REVISION 377191

/* Define if LLVM_ENABLE_DUMP is enabled */
#cmakedefine LLVM_ENABLE_DUMP
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/IR/IRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,13 @@ class IRBuilderBase {
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,
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -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], []>;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/IntrinsicLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/IR/IRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 86 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -313,6 +318,8 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {

TBAAVerifier TBAAVerifyHelper;

SmallVector<IntrinsicInst *, 4> NoAliasScopeDecls;

void checkAtomicMemAccessSize(Type *Ty, const Instruction *I);

public:
Expand Down Expand Up @@ -360,6 +367,8 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
LandingPadResultTy = nullptr;
SawFrameEscape = false;
SiblingFuncletInfo.clear();
verifyNoAliasScopeDecl();
NoAliasScopeDecls.clear();

return !Broken;
}
Expand Down Expand Up @@ -536,6 +545,9 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {

/// 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
Expand Down Expand Up @@ -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;
}
};
}

Expand Down Expand Up @@ -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...
//===----------------------------------------------------------------------===//
Expand Down
25 changes: 17 additions & 8 deletions llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/IntrinsicsAArch64.h"
Expand Down Expand Up @@ -177,8 +178,10 @@ class AArch64InstructionSelector : public InstructionSelector {
MachineIRBuilder &MIRBuilder) const;

/// Emit a floating point comparison between \p LHS and \p RHS.
/// \p Pred if given is the intended predicate to use.
MachineInstr *emitFPCompare(Register LHS, Register RHS,
MachineIRBuilder &MIRBuilder) const;
MachineIRBuilder &MIRBuilder,
Optional<CmpInst::Predicate> = None) const;

MachineInstr *emitInstr(unsigned Opcode,
std::initializer_list<llvm::DstOp> DstOps,
Expand Down Expand Up @@ -1483,11 +1486,11 @@ bool AArch64InstructionSelector::selectCompareBranchFedByFCmp(
assert(I.getOpcode() == TargetOpcode::G_BRCOND);
// Unfortunately, the mapping of LLVM FP CC's onto AArch64 CC's isn't
// totally clean. Some of them require two branches to implement.
emitFPCompare(FCmp.getOperand(2).getReg(), FCmp.getOperand(3).getReg(), MIB);
auto Pred = (CmpInst::Predicate)FCmp.getOperand(1).getPredicate();
emitFPCompare(FCmp.getOperand(2).getReg(), FCmp.getOperand(3).getReg(), MIB,
Pred);
AArch64CC::CondCode CC1, CC2;
changeFCMPPredToAArch64CC(
static_cast<CmpInst::Predicate>(FCmp.getOperand(1).getPredicate()), CC1,
CC2);
changeFCMPPredToAArch64CC(static_cast<CmpInst::Predicate>(Pred), CC1, CC2);
MachineBasicBlock *DestMBB = I.getOperand(1).getMBB();
MIB.buildInstr(AArch64::Bcc, {}, {}).addImm(CC1).addMBB(DestMBB);
if (CC2 != AArch64CC::AL)
Expand Down Expand Up @@ -3090,7 +3093,7 @@ bool AArch64InstructionSelector::select(MachineInstr &I) {
CmpInst::Predicate Pred =
static_cast<CmpInst::Predicate>(I.getOperand(1).getPredicate());
if (!emitFPCompare(I.getOperand(2).getReg(), I.getOperand(3).getReg(),
MIRBuilder) ||
MIRBuilder, Pred) ||
!emitCSetForFCmp(I.getOperand(0).getReg(), Pred, MIRBuilder))
return false;
I.eraseFromParent();
Expand Down Expand Up @@ -4211,7 +4214,8 @@ MachineInstr *AArch64InstructionSelector::emitCSetForFCmp(

MachineInstr *
AArch64InstructionSelector::emitFPCompare(Register LHS, Register RHS,
MachineIRBuilder &MIRBuilder) const {
MachineIRBuilder &MIRBuilder,
Optional<CmpInst::Predicate> Pred) const {
MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
LLT Ty = MRI.getType(LHS);
if (Ty.isVector())
Expand All @@ -4224,7 +4228,12 @@ AArch64InstructionSelector::emitFPCompare(Register LHS, Register RHS,
// to explicitly materialize a constant.
const ConstantFP *FPImm = getConstantFPVRegVal(RHS, MRI);
bool ShouldUseImm = FPImm && (FPImm->isZero() && !FPImm->isNegative());
if (!ShouldUseImm) {

auto IsEqualityPred = [](CmpInst::Predicate P) {
return P == CmpInst::FCMP_OEQ || P == CmpInst::FCMP_ONE ||
P == CmpInst::FCMP_UEQ || P == CmpInst::FCMP_UNE;
};
if (!ShouldUseImm && Pred && IsEqualityPred(*Pred)) {
// Try commutating the operands.
const ConstantFP *LHSImm = getConstantFPVRegVal(LHS, MRI);
if (LHSImm && (LHSImm->isZero() && !LHSImm->isNegative())) {
Expand Down
Loading

0 comments on commit 2f169bb

Please sign in to comment.