diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -190,6 +190,7 @@ kw_convergent, kw_dereferenceable, kw_dereferenceable_or_null, + kw_elementtype, kw_inaccessiblememonly, kw_inaccessiblemem_or_argmemonly, kw_inlinehint, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -670,6 +670,7 @@ 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/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -346,6 +346,7 @@ 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/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -86,6 +86,9 @@ 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/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -643,6 +643,7 @@ KEYWORD(convergent); KEYWORD(dereferenceable); KEYWORD(dereferenceable_or_null); + KEYWORD(elementtype); KEYWORD(inaccessiblememonly); KEYWORD(inaccessiblemem_or_argmemonly); KEYWORD(inlinehint); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1385,6 +1385,8 @@ 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/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -630,6 +630,8 @@ 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/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -708,6 +708,10 @@ 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 @@ .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/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1813,6 +1813,11 @@ 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 @@ 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 @@ 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/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -902,6 +902,7 @@ case Attribute::Convergent: case Attribute::Dereferenceable: case Attribute::DereferenceableOrNull: + case Attribute::ElementType: case Attribute::InAlloca: case Attribute::InReg: case Attribute::InaccessibleMemOnly: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -465,6 +465,13 @@ 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/llvm/test/Verifier/elementtype.ll b/llvm/test/Verifier/elementtype.ll new file mode 100644 --- /dev/null +++ b/llvm/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/llvm/test/Verifier/opaque-ptr.ll b/llvm/test/Verifier/opaque-ptr.ll --- a/llvm/test/Verifier/opaque-ptr.ll +++ b/llvm/test/Verifier/opaque-ptr.ll @@ -53,11 +53,13 @@ ; 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 <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)