Index: llvm/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/include/llvm/IR/IRBuilder.h +++ llvm/include/llvm/IR/IRBuilder.h @@ -810,6 +810,9 @@ MetadataAsValue::get(Context, ScopeTag)); } + /// Create a llvm.experimental.ptr.provenance intrinsic call. + Instruction *CreatePtrProvenance(Value *PtrValue, Value *PtrProvenance); + /// Create a call to the experimental.gc.statepoint intrinsic to /// start a new statepoint sequence. CallInst *CreateGCStatepointCall(uint64_t ID, uint32_t NumPatchBytes, Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -590,6 +590,22 @@ : DefaultAttrsIntrinsic<[], [llvm_metadata_ty], [IntrInaccessibleMemOnly]>; // blocks LICM and some more +// 'llvm.experimental.ptr.provenance' intrinsic: Associates a (different) +// ptr_provenance path to the pointer value. This serves as a guard for pointers +// that escape through function arguments, through memory or are returned. +// For Load/Store instructions, there is a separate ptr_provenance operand for +// tracking the provenance of the pointer operand. +// +// Purpose of the different arguments: +// - arg0: p.val: the incoming pointer value. +// - arg1: p.prov: the ptr_provenance, associated with this pointer +// computation. +// - returns: arg0 (hidden for most optimization passes) +def int_experimental_ptr_provenance + : DefaultAttrsIntrinsic<[llvm_anyptr_ty], + [LLVMMatchType<0>, llvm_anyptr_ty], + [IntrNoMem, IntrSpeculatable]>; // NOTE: Returned<0> must not be used here + // 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], []>; Index: llvm/lib/IR/IRBuilder.cpp =================================================================== --- llvm/lib/IR/IRBuilder.cpp +++ llvm/lib/IR/IRBuilder.cpp @@ -576,6 +576,17 @@ return CreateCall(FnIntrinsic, {Scope}); } +Instruction *IRBuilderBase::CreatePtrProvenance(Value *PtrValue, + Value *PtrProvenance) { + assert(PtrValue->getType() == PtrProvenance->getType() && + "pointer and provenance must have the same type"); + Module *M = BB->getModule(); + auto *FnIntrinsic = + Intrinsic::getDeclaration(M, Intrinsic::experimental_ptr_provenance, + {PtrValue->getType(), PtrValue->getType()}); + return createCallHelper(FnIntrinsic, {PtrValue, PtrProvenance}); +} + /// Create a call to a Masked Load intrinsic. /// \p Ty - vector type to load /// \p Ptr - base pointer for the load Index: llvm/unittests/IR/IRBuilderTest.cpp =================================================================== --- llvm/unittests/IR/IRBuilderTest.cpp +++ llvm/unittests/IR/IRBuilderTest.cpp @@ -479,6 +479,18 @@ EXPECT_EQ(S->getPtrProvenance(), GV); } +TEST_F(IRBuilderTest, PtrProvenanceIntrinsics) { + IRBuilder<> Builder(BB); + auto *A = Builder.CreateAlloca(GV->getValueType()); + + auto *PP = Builder.CreatePtrProvenance(GV, A); + IntrinsicInst *II_PP = dyn_cast(PP); + ASSERT_TRUE(II_PP != nullptr); + EXPECT_EQ(II_PP->getIntrinsicID(), Intrinsic::experimental_ptr_provenance); + EXPECT_EQ(PP->getOperand(0), GV); + EXPECT_EQ(PP->getOperand(1), A); +} + TEST_F(IRBuilderTest, Lifetime) { IRBuilder<> Builder(BB); AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt8Ty());