From d58a8fbeab779548508098f52250d223038e806c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 7 Jul 2021 22:29:43 +0200 Subject: [PATCH] [IR] Add elementtype attribute This implements the elementtype attribute specified in D105407. It just adds the attribute and the specified verifier rules, but doesn't yet make use of it anywhere. Differential Revision: https://reviews.llvm.org/D106008 --- include/llvm/AsmParser/LLToken.h | 1 + include/llvm/Bitcode/LLVMBitCodes.h | 1 + include/llvm/IR/Attributes.h | 1 + include/llvm/IR/Attributes.td | 3 +++ lib/AsmParser/LLLexer.cpp | 1 + lib/Bitcode/Reader/BitcodeReader.cpp | 2 ++ lib/Bitcode/Writer/BitcodeWriter.cpp | 2 ++ lib/IR/Attributes.cpp | 7 ++++++- lib/IR/Verifier.cpp | 10 +++++++++ lib/Transforms/Utils/CodeExtractor.cpp | 1 + test/Bitcode/attributes.ll | 7 +++++++ test/Verifier/elementtype.ll | 28 ++++++++++++++++++++++++++ test/Verifier/opaque-ptr.ll | 3 +++ 13 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test/Verifier/elementtype.ll diff --git a/include/llvm/AsmParser/LLToken.h b/include/llvm/AsmParser/LLToken.h index 7aed41e241a..50cad74d697 100644 --- a/include/llvm/AsmParser/LLToken.h +++ b/include/llvm/AsmParser/LLToken.h @@ -190,6 +190,7 @@ enum Kind { kw_convergent, kw_dereferenceable, kw_dereferenceable_or_null, + kw_elementtype, kw_inaccessiblememonly, kw_inaccessiblemem_or_argmemonly, kw_inlinehint, diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index da591f4183b..28870afb2fc 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -670,6 +670,7 @@ enum AttributeKindCodes { ATTR_KIND_VSCALE_RANGE = 74, ATTR_KIND_SWIFT_ASYNC = 75, ATTR_KIND_NO_SANITIZE_COVERAGE = 76, + ATTR_KIND_ELEMENTTYPE = 77, }; enum ComdatSelectionKindCodes { diff --git a/include/llvm/IR/Attributes.h b/include/llvm/IR/Attributes.h index b375a783974..96c8054e7fa 100644 --- a/include/llvm/IR/Attributes.h +++ b/include/llvm/IR/Attributes.h @@ -346,6 +346,7 @@ public: Type *getByRefType() const; Type *getPreallocatedType() const; Type *getInAllocaType() const; + Type *getElementType() const; std::pair> getAllocSizeArgs() const; std::pair getVScaleRangeArgs() const; std::string getAsString(bool InAttrGrp = false) const; diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td index 327953d3718..99b474161df 100644 --- a/include/llvm/IR/Attributes.td +++ b/include/llvm/IR/Attributes.td @@ -86,6 +86,9 @@ def Dereferenceable : IntAttr<"dereferenceable", [ParamAttr, RetAttr]>; def DereferenceableOrNull : IntAttr<"dereferenceable_or_null", [ParamAttr, RetAttr]>; +/// Provide pointer element type to intrinsic. +def ElementType : TypeAttr<"elementtype", [ParamAttr]>; + /// Function may only access memory that is inaccessible from IR. def InaccessibleMemOnly : EnumAttr<"inaccessiblememonly", [FnAttr]>; diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 5f68db84367..b062fb81fc8 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -643,6 +643,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(convergent); KEYWORD(dereferenceable); KEYWORD(dereferenceable_or_null); + KEYWORD(elementtype); KEYWORD(inaccessiblememonly); KEYWORD(inaccessiblemem_or_argmemonly); KEYWORD(inlinehint); diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index c9c1cef3c87..07db3c05eb4 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1385,6 +1385,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::Cold; case bitc::ATTR_KIND_CONVERGENT: return Attribute::Convergent; + case bitc::ATTR_KIND_ELEMENTTYPE: + return Attribute::ElementType; case bitc::ATTR_KIND_INACCESSIBLEMEM_ONLY: return Attribute::InaccessibleMemOnly; case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY: diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index bdb973e8e42..da3158bcdfa 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -630,6 +630,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_COLD; case Attribute::Hot: return bitc::ATTR_KIND_HOT; + case Attribute::ElementType: + return bitc::ATTR_KIND_ELEMENTTYPE; case Attribute::InaccessibleMemOnly: return bitc::ATTR_KIND_INACCESSIBLEMEM_ONLY; case Attribute::InaccessibleMemOrArgMemOnly: diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index 368fc87dc80..4ef61c78de6 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -708,6 +708,10 @@ Type *AttributeSet::getInAllocaType() const { return SetNode ? SetNode->getAttributeType(Attribute::InAlloca) : nullptr; } +Type *AttributeSet::getElementType() const { + return SetNode ? SetNode->getAttributeType(Attribute::ElementType) : nullptr; +} + std::pair> AttributeSet::getAllocSizeArgs() const { return SetNode ? SetNode->getAllocSizeArgs() : std::pair>(0, 0); @@ -1909,7 +1913,8 @@ AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) { .addInAllocaAttr(Ty) .addByValAttr(Ty) .addStructRetAttr(Ty) - .addByRefAttr(Ty); + .addByRefAttr(Ty) + .addTypeAttr(Attribute::ElementType, Ty); // Some attributes can apply to all "values" but there are no `void` values. if (Ty->isVoidTy()) diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 442857ecb68..71e778c8ec7 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -1813,6 +1813,11 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, Assert(Attrs.getInAllocaType() == PTy->getElementType(), "Attribute 'inalloca' type does not match parameter!", V); } + + if (Attrs.hasAttribute(Attribute::ElementType)) { + Assert(Attrs.getElementType() == PTy->getElementType(), + "Attribute 'elementtype' type does not match parameter!", V); + } } } } @@ -1874,6 +1879,8 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, if (!IsIntrinsic) { Assert(!ArgAttrs.hasAttribute(Attribute::ImmArg), "immarg attribute only applies to intrinsics",V); + Assert(!ArgAttrs.hasAttribute(Attribute::ElementType), + "Attribute 'elementtype' can only be applied to intrinsics.", V); } verifyParameterAttrs(ArgAttrs, Ty, V); @@ -2331,6 +2338,9 @@ void Verifier::visitFunction(const Function &F) { Assert(!Attrs.hasFnAttribute(Attribute::Builtin), "Attribute 'builtin' can only be applied to a callsite.", &F); + Assert(!Attrs.hasAttrSomewhere(Attribute::ElementType), + "Attribute 'elementtype' can only be applied to a callsite.", &F); + // Check that this function meets the restrictions on this calling convention. // Sometimes varargs is used for perfectly forwarding thunks, so some of these // restrictions can be lifted. diff --git a/lib/Transforms/Utils/CodeExtractor.cpp b/lib/Transforms/Utils/CodeExtractor.cpp index 3d631651fa7..9edc52b5355 100644 --- a/lib/Transforms/Utils/CodeExtractor.cpp +++ b/lib/Transforms/Utils/CodeExtractor.cpp @@ -902,6 +902,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::Convergent: case Attribute::Dereferenceable: case Attribute::DereferenceableOrNull: + case Attribute::ElementType: case Attribute::InAlloca: case Attribute::InReg: case Attribute::InaccessibleMemOnly: diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index 57bea084ac8..6ba8ce2854d 100644 --- a/test/Bitcode/attributes.ll +++ b/test/Bitcode/attributes.ll @@ -465,6 +465,13 @@ define void @f78() noprofile ret void; } +declare void @llvm.some.intrinsic(i32*) +define void @f79() { +; CHECK: call void @llvm.some.intrinsic(i32* elementtype(i32) null) + call void @llvm.some.intrinsic(i32* elementtype(i32) null) + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } diff --git a/test/Verifier/elementtype.ll b/test/Verifier/elementtype.ll new file mode 100644 index 00000000000..ff957469d70 --- /dev/null +++ b/test/Verifier/elementtype.ll @@ -0,0 +1,28 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @some_function(i32*) + +; CHECK: Attribute 'elementtype(i32)' applied to incompatible type! +define void @type_mismatch1() { + call i32* @llvm.preserve.array.access.index.p0i32.p0i32(i32* null, i32 elementtype(i32) 0, i32 0) + ret void +} + +; CHECK: Attribute 'elementtype' type does not match parameter! +define void @type_mismatch2() { + call i32* @llvm.preserve.array.access.index.p0i32.p0i32(i32* elementtype(i64) null, i32 0, i32 0) + ret void +} + +; CHECK: Attribute 'elementtype' can only be applied to intrinsics. +define void @not_intrinsic() { + call void @some_function(i32* elementtype(i32) null) + ret void +} + +; CHECK: Attribute 'elementtype' can only be applied to a callsite. +define void @llvm.not_call(i32* elementtype(i32)) { + ret void +} + +declare i32* @llvm.preserve.array.access.index.p0i32.p0i32(i32*, i32, i32) diff --git a/test/Verifier/opaque-ptr.ll b/test/Verifier/opaque-ptr.ll index 4d824ef49f7..1f29000db56 100644 --- a/test/Verifier/opaque-ptr.ll +++ b/test/Verifier/opaque-ptr.ll @@ -53,11 +53,13 @@ define void @intrinsic_calls(ptr %a) { ; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.masked.load.v2i32.p0(ptr [[A:%.*]], i32 4, <2 x i1> zeroinitializer, <2 x i32> zeroinitializer) ; CHECK-NEXT: call void @llvm.masked.store.v2i32.p0(<2 x i32> zeroinitializer, ptr [[A]], i32 4, <2 x i1> zeroinitializer) ; CHECK-NEXT: [[TMP2:%.*]] = call <2 x i64> @llvm.masked.gather.v2i64.v2p0(<2 x ptr> zeroinitializer, i32 4, <2 x i1> zeroinitializer, <2 x i64> zeroinitializer) +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype(i32) null, i32 0, i32 0) ; CHECK-NEXT: ret void ; call <2 x i32> @llvm.masked.load.v2i32.p0(ptr %a, i32 4, <2 x i1> zeroinitializer, <2 x i32> zeroinitializer) call void @llvm.masked.store.v2i32.p0(<2 x i32> zeroinitializer, ptr %a, i32 4, <2 x i1> zeroinitializer) call <2 x i64> @llvm.masked.gather.v2i64.v2p0(<2 x ptr> zeroinitializer, i32 4, <2 x i1> zeroinitializer, <2 x i64> zeroinitializer) + call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype(i32) null, i32 0, i32 0) ret void } @@ -69,3 +71,4 @@ declare void @llvm.lifetime.end.p0(i64, ptr nocapture) declare <2 x i32> @llvm.masked.load.v2i32.p0(ptr, i32, <2 x i1>, <2 x i32>) declare void @llvm.masked.store.v2i32.p0(<2 x i32>, ptr, i32, <2 x i1>) declare <2 x i64> @llvm.masked.gather.v2i64.v2p0(<2 x ptr>, i32, <2 x i1>, <2 x i64>) +declare ptr @llvm.preserve.array.access.index.p0.p0(ptr, i32, i32)