Skip to content

[Intrinsics] Add support for range attributes #135642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion llvm/include/llvm/IR/Intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ namespace Intrinsic {
ID lookupIntrinsicID(StringRef Name);

/// Return the attributes for an intrinsic.
AttributeList getAttributes(LLVMContext &C, ID id);
AttributeList getAttributes(LLVMContext &C, ID id, FunctionType *FT);

/// Return the function attributes for an intrinsic.
AttributeSet getFnAttributes(LLVMContext &C, ID id);
Expand Down
12 changes: 10 additions & 2 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ class ReadNone<ArgIndex idx> : IntrinsicProperty {
int ArgNo = idx.Value;
}

// The return value or argument is in the range [lower, upper),
// where lower and upper are interpreted as signed integers.
class Range<AttrIndex idx, int lower, int upper> : IntrinsicProperty {
int ArgNo = idx.Value;
int Lower = lower;
int Upper = upper;
}

def IntrNoReturn : IntrinsicProperty;

// Applied by default.
Expand Down Expand Up @@ -1620,10 +1628,10 @@ def int_umin : DefaultAttrsIntrinsic<
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
def int_scmp : DefaultAttrsIntrinsic<
[llvm_anyint_ty], [llvm_anyint_ty, LLVMMatchType<1>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Range<RetIndex, -1, 2>]>;
def int_ucmp : DefaultAttrsIntrinsic<
[llvm_anyint_ty], [llvm_anyint_ty, LLVMMatchType<1>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Range<RetIndex, -1, 2>]>;

//===------------------------- Memory Use Markers -------------------------===//
//
Expand Down
9 changes: 7 additions & 2 deletions llvm/lib/IR/AutoUpgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1519,8 +1519,13 @@ bool llvm::UpgradeIntrinsicFunction(Function *F, Function *&NewFn,
// Upgrade intrinsic attributes. This does not change the function.
if (NewFn)
F = NewFn;
if (Intrinsic::ID id = F->getIntrinsicID())
F->setAttributes(Intrinsic::getAttributes(F->getContext(), id));
if (Intrinsic::ID id = F->getIntrinsicID()) {
// Only do this if the intrinsic signature is valid.
SmallVector<Type *> OverloadTys;
if (Intrinsic::getIntrinsicSignature(id, F->getFunctionType(), OverloadTys))
F->setAttributes(
Intrinsic::getAttributes(F->getContext(), id, F->getFunctionType()));
}
return Upgraded;
}

Expand Down
11 changes: 9 additions & 2 deletions llvm/lib/IR/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,15 @@ Function::Function(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace,
// Ensure intrinsics have the right parameter attributes.
// Note, the IntID field will have been set in Value::setName if this function
// name is a valid intrinsic ID.
if (IntID)
setAttributes(Intrinsic::getAttributes(getContext(), IntID));
if (IntID) {
// Don't set the attributes if the intrinsic signature is invalid. This
// case will either be auto-upgraded or fail verification.
SmallVector<Type *> OverloadTys;
if (!Intrinsic::getIntrinsicSignature(IntID, Ty, OverloadTys))
return;

setAttributes(Intrinsic::getAttributes(getContext(), IntID, Ty));
}
}

Function::~Function() {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/IR/Intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringTable.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicsAArch64.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/AMDGPU/SIISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16506,7 +16506,8 @@ Align SITargetLowering::computeKnownAlignForTargetInstr(
// site specifies a lower alignment?
Intrinsic::ID IID = GI->getIntrinsicID();
LLVMContext &Ctx = VT.getMachineFunction().getFunction().getContext();
AttributeList Attrs = Intrinsic::getAttributes(Ctx, IID);
AttributeList Attrs =
Intrinsic::getAttributes(Ctx, IID, Intrinsic::getType(Ctx, IID));
if (MaybeAlign RetAlign = Attrs.getRetAlignment())
return *RetAlign;
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2903,7 +2903,7 @@ static void stripNonValidAttributesFromPrototype(Function &F) {
// assumes that the attributes defined in Intrinsic.td are conservatively
// correct for both physical and abstract model.
if (Intrinsic::ID id = F.getIntrinsicID()) {
F.setAttributes(Intrinsic::getAttributes(Ctx, id));
F.setAttributes(Intrinsic::getAttributes(Ctx, id, F.getFunctionType()));
return;
}

Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Assembler/aarch64-intrinsics-attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ declare i64 @llvm.aarch64.ldxr.p0(ptr)
; CHECK: declare i32 @llvm.aarch64.stxp(i64, i64, ptr) [[NOFREE_NOUNWIND_WILLRETURN]]
declare i32 @llvm.aarch64.stxp(i64, i64, ptr)

; CHECK: declare i32 @llvm.aarch64.dsb(i32) [[NOFREE_NOUNWIND_WILLRETURN]]
declare i32 @llvm.aarch64.dsb(i32)
; CHECK: declare void @llvm.aarch64.dsb(i32) [[NOFREE_NOUNWIND_WILLRETURN]]
declare void @llvm.aarch64.dsb(i32)

; CHECK: declare i64 @llvm.aarch64.neon.sqdmulls.scalar(i32, i32) [[NO_CALLBACK_NOFREE_NOSYNC_NOUNWIND_READNONE_WILLRETURN:#[0-9]+]]
declare i64 @llvm.aarch64.neon.sqdmulls.scalar(i32, i32)
Expand Down
12 changes: 5 additions & 7 deletions llvm/test/Assembler/autoupgrade-invalid-mem-intrinsics.ll
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
; RUN: llvm-as < %s | llvm-dis | FileCheck %s

; Check that remangling code doesn't fail on an intrinsic with wrong signature
; TODO: This should probably produce an error.

; CHECK: Attribute after last parameter!
; CHECK-NEXT: ptr @llvm.memset.i64
; CHECK: declare void @llvm.memset.i64
declare void @llvm.memset.i64(ptr nocapture, i8, i64) nounwind

; CHECK: Attribute after last parameter!
; CHECK-NEXT: ptr @llvm.memcpy.i64
; CHECK: declare void @llvm.memcpy.i64
declare void @llvm.memcpy.i64(ptr nocapture, i8, i64) nounwind

; CHECK: Attribute after last parameter!
; CHECK-NEXT: ptr @llvm.memmove.i64
; CHECK: declare void @llvm.memmove.i64
declare void @llvm.memmove.i64(ptr nocapture, i8, i64) nounwind
11 changes: 7 additions & 4 deletions llvm/test/TableGen/intrinsic-attrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ def int_random_gen : Intrinsic<[llvm_i32_ty], [], [IntrNoMem, IntrHasSideEffec

def int_deref_ptr_ret : Intrinsic<[llvm_ptr_ty], [], [Dereferenceable<RetIndex, 16>]>;

// CHECK: static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID) {
// CHECK-NEXT: switch (ID) {
// CHECK: static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,
// CHECK-NEXT: Type *ArgType) {
// CHECK-NEXT: unsigned BitWidth = ArgType->getScalarSizeInBits();
// CHECK-NEXT: switch (ID) {
// CHECK-NEXT: default: llvm_unreachable("Invalid attribute set number");
// CHECK-NEXT: case 0:
// CHECK-NEXT: return AttributeSet::get(C, {
Expand All @@ -26,13 +28,14 @@ def int_deref_ptr_ret : Intrinsic<[llvm_ptr_ty], [], [Dereferenceable<RetIndex,
// CHECK: 0 << 8 | 0, // llvm.deref.ptr.ret
// CHECK: 1 << 8 | 1, // llvm.random.gen

// CHECK: getAttributes(LLVMContext &C, ID id)
// CHECK: getAttributes(LLVMContext &C, ID id,
// CHECK-NEXT: FunctionType *FT) {
// CHECK: case 1:
// CHECK-NEXT: return AttributeList::get(C, {
// CHECK-NEXT: {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, FnAttrID)}
// CHECK-NEXT: });
// CHECK-NEXT: case 0:
// CHECK-NEXT: return AttributeList::get(C, {
// CHECK-NEXT: {0, getIntrinsicArgAttributeSet(C, 0)},
// CHECK-NEXT: {0, getIntrinsicArgAttributeSet(C, 0, FT->getContainedType(0))},
// CHECK-NEXT: {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, FnAttrID)}
// CHECK-NEXT: });
12 changes: 6 additions & 6 deletions llvm/test/Transforms/CorrelatedValuePropagation/uscmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,9 @@ define i8 @ucmp_switch(i32 %x, i32 %y) {
; CHECK-LABEL: @ucmp_switch(
; CHECK-NEXT: [[CMP:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT: switch i8 [[CMP]], label [[DEFAULT_UNREACHABLE:%.*]] [
; CHECK-NEXT: i8 -2, label [[BB_NEG2:%.*]]
; CHECK-NEXT: i8 1, label [[BB_1:%.*]]
; CHECK-NEXT: i8 -1, label [[BB_NEG1:%.*]]
; CHECK-NEXT: i8 0, label [[BB_0:%.*]]
; CHECK-NEXT: i8 1, label [[BB_1:%.*]]
; CHECK-NEXT: i8 2, label [[BB_2:%.*]]
; CHECK-NEXT: ]
; CHECK: bb.neg2:
; CHECK-NEXT: ret i8 -2
Expand All @@ -277,6 +275,8 @@ define i8 @ucmp_switch(i32 %x, i32 %y) {
; CHECK-NEXT: ret i8 1
; CHECK: bb.2:
; CHECK-NEXT: ret i8 2
; CHECK: default.unreachable:
; CHECK-NEXT: unreachable
; CHECK: default:
; CHECK-NEXT: ret i8 123
;
Expand Down Expand Up @@ -312,11 +312,9 @@ define i8 @scmp_switch(i32 %x, i32 %y) {
; CHECK-LABEL: @scmp_switch(
; CHECK-NEXT: [[CMP:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT: switch i8 [[CMP]], label [[DEFAULT_UNREACHABLE:%.*]] [
; CHECK-NEXT: i8 -2, label [[BB_NEG2:%.*]]
; CHECK-NEXT: i8 1, label [[BB_1:%.*]]
; CHECK-NEXT: i8 -1, label [[BB_NEG1:%.*]]
; CHECK-NEXT: i8 0, label [[BB_0:%.*]]
; CHECK-NEXT: i8 1, label [[BB_1:%.*]]
; CHECK-NEXT: i8 2, label [[BB_2:%.*]]
; CHECK-NEXT: ]
; CHECK: bb.neg2:
; CHECK-NEXT: ret i8 -2
Expand All @@ -328,6 +326,8 @@ define i8 @scmp_switch(i32 %x, i32 %y) {
; CHECK-NEXT: ret i8 1
; CHECK: bb.2:
; CHECK-NEXT: ret i8 2
; CHECK: default.unreachable:
; CHECK-NEXT: unreachable
; CHECK: default:
; CHECK-NEXT: ret i8 123
;
Expand Down
13 changes: 9 additions & 4 deletions llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,11 @@ void CodeGenIntrinsic::setProperty(const Record *R) {
unsigned ArgNo = R->getValueAsInt("ArgNo");
uint64_t Bytes = R->getValueAsInt("Bytes");
addArgAttribute(ArgNo, Dereferenceable, Bytes);
} else if (R->isSubClassOf("Range")) {
unsigned ArgNo = R->getValueAsInt("ArgNo");
int64_t Lower = R->getValueAsInt("Lower");
int64_t Upper = R->getValueAsInt("Upper");
addArgAttribute(ArgNo, Range, Lower, Upper);
} else
llvm_unreachable("Unknown property!");
}
Expand All @@ -455,14 +460,14 @@ bool CodeGenIntrinsic::isParamImmArg(unsigned ParamIdx) const {
++ParamIdx;
if (ParamIdx >= ArgumentAttributes.size())
return false;
ArgAttribute Val{ImmArg, 0};
ArgAttribute Val{ImmArg, 0, 0};
return std::binary_search(ArgumentAttributes[ParamIdx].begin(),
ArgumentAttributes[ParamIdx].end(), Val);
}

void CodeGenIntrinsic::addArgAttribute(unsigned Idx, ArgAttrKind AK,
uint64_t V) {
void CodeGenIntrinsic::addArgAttribute(unsigned Idx, ArgAttrKind AK, uint64_t V,
uint64_t V2) {
if (Idx >= ArgumentAttributes.size())
ArgumentAttributes.resize(Idx + 1);
ArgumentAttributes[Idx].emplace_back(AK, V);
ArgumentAttributes[Idx].emplace_back(AK, V, V2);
}
13 changes: 9 additions & 4 deletions llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,29 @@ struct CodeGenIntrinsic {
ReadNone,
ImmArg,
Alignment,
Dereferenceable
Dereferenceable,
Range,
};

struct ArgAttribute {
ArgAttrKind Kind;
uint64_t Value;
uint64_t Value2;

ArgAttribute(ArgAttrKind K, uint64_t V) : Kind(K), Value(V) {}
ArgAttribute(ArgAttrKind K, uint64_t V, uint64_t V2)
: Kind(K), Value(V), Value2(V2) {}

bool operator<(const ArgAttribute &Other) const {
return std::tie(Kind, Value) < std::tie(Other.Kind, Other.Value);
return std::tie(Kind, Value, Value2) <
std::tie(Other.Kind, Other.Value, Other.Value2);
}
};

/// Vector of attributes for each argument.
SmallVector<SmallVector<ArgAttribute, 0>> ArgumentAttributes;

void addArgAttribute(unsigned Idx, ArgAttrKind AK, uint64_t V = 0);
void addArgAttribute(unsigned Idx, ArgAttrKind AK, uint64_t V = 0,
uint64_t V2 = 0);

bool hasProperty(enum SDNP Prop) const { return Properties & (1 << Prop); }

Expand Down
25 changes: 21 additions & 4 deletions llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
return "Alignment";
case CodeGenIntrinsic::Dereferenceable:
return "Dereferenceable";
case CodeGenIntrinsic::Range:
return "Range";
}
llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");
}
Expand All @@ -502,7 +504,9 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,
raw_ostream &OS) {
OS << R"(// Add parameter attributes that are not common to all intrinsics.
#ifdef GET_INTRINSIC_ATTRIBUTES
static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID) {
static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,
Type *ArgType) {
unsigned BitWidth = ArgType->getScalarSizeInBits();
switch (ID) {
default: llvm_unreachable("Invalid attribute set number");)";
// Compute unique argument attribute sets.
Expand Down Expand Up @@ -535,6 +539,17 @@ static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID) {
Attr.Kind == CodeGenIntrinsic::Dereferenceable)
OS << formatv(" Attribute::get(C, Attribute::{}, {}),\n",
AttrName, Attr.Value);
else if (Attr.Kind == CodeGenIntrinsic::Range)
// This allows implicitTrunc because the range may only fit the
// type based on rules implemented in the IR verifier. E.g. the
// [-1, 1] range for ucmp/scmp intrinsics requires a minimum i2 type.
// Give the verifier a chance to diagnose this instead of asserting
// here.
OS << formatv(" Attribute::get(C, Attribute::{}, "
"ConstantRange(APInt(BitWidth, {}, /*isSigned=*/true, "
"/*implicitTrunc=*/true), APInt(BitWidth, {}, "
"/*isSigned=*/true, /*implicitTrunc=*/true))),\n",
AttrName, (int64_t)Attr.Value, (int64_t)Attr.Value2);
else
OS << formatv(" Attribute::get(C, Attribute::{}),\n", AttrName);
}
Expand Down Expand Up @@ -635,7 +650,8 @@ static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";
OS << R"(
};

AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) {)";
AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,
FunctionType *FT) {)";

OS << formatv(R"(
if (id == 0)
Expand Down Expand Up @@ -669,8 +685,9 @@ AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) {)";

unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second;
OS << LS
<< formatv(" {{{}, getIntrinsicArgAttributeSet(C, {})}", AttrIdx,
ArgAttrID);
<< formatv(" {{{}, getIntrinsicArgAttributeSet(C, {}, "
"FT->getContainedType({}))}",
AttrIdx, ArgAttrID, AttrIdx);
}

if (hasFnAttributes(Int)) {
Expand Down
Loading