[Mlir-commits] [llvm] [mlir] [MLIR][LLVM][Intrinsics] Add new MLIR and LLVM APIs to automatically resolve overload types (PR #168188)
Rajat Bajpai
llvmlistbot at llvm.org
Tue Nov 18 02:25:39 PST 2025
https://github.com/rajatbajpai updated https://github.com/llvm/llvm-project/pull/168188
>From fe04f9109f469eba1b2ac0520553bc086ba65144 Mon Sep 17 00:00:00 2001
From: rbajpai <rbajpai at nvidia.com>
Date: Sun, 9 Nov 2025 16:35:52 +0530
Subject: [PATCH 1/2] [MLIR][LLVM][Intrinsics] Add new MLIR and LLVM APIs to
automatically resolve overload types
Currently, the `getOrInsertDeclaration` API requires callers to explicitly
provide overload types for overloaded intrinsics, placing a significant burden
on callers who must determine whether overload types are needed. This typically
results in conditional logic at each call site to check if the intrinsic is
overloaded and manually match the intrinsic signature.
This patch introduces a new `getOrInsertDeclaration` overload that automatically
deduces overload types from the provided return type and argument types. The new
API uses `Intrinsic::matchIntrinsicSignature` internally to resolve overloaded
types, eliminating the need for callers to perform manual overload detection.
This patch introduces two levels of convenience APIs to eliminate manual
overload detection:
1. A new `getOrInsertDeclaration` overload that automatically deduces overload
types from return and argument types
2. A new `createIntrinsicCall` overload at MLIR level that leverages the above to provide a
simple, type-based intrinsic call interface
Key changes:
- Added `Intrinsic::getOrInsertDeclaration(Module*, ID, Type *RetTy,
ArrayRef<Type*> ArgTys)` overload that automatically resolves overloaded
intrinsics by internally using `matchIntrinsicSignature`. For non-overloaded
intrinsics, it delegates to the existing API.
- Refactored `Intrinsics.cpp` to extract a common helper function
`getOrInsertIntrinsicDeclarationImpl` to reduce code duplication between
the two `getOrInsertDeclaration` overloads.
- Added `createIntrinsicCall(IRBuilderBase&, Intrinsic::ID, Type *retTy,
ArrayRef<Value*> args)` overload in ModuleTranslation that provides a
simple interface by delegating overload resolution to the new LLVM API.
- Simplified NVVM_PrefetchOp implementation by removing all conditional logic
for handling overloaded intrinsics.
---
llvm/include/llvm/IR/Intrinsics.h | 15 ++++++
llvm/lib/IR/Intrinsics.cpp | 50 +++++++++++++++----
mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td | 7 +--
.../mlir/Target/LLVMIR/ModuleTranslation.h | 9 ++++
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 15 ++++++
5 files changed, 81 insertions(+), 15 deletions(-)
diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index 9577d0141f168..ffcc4677830ca 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -104,6 +104,21 @@ namespace Intrinsic {
LLVM_ABI Function *getOrInsertDeclaration(Module *M, ID id,
ArrayRef<Type *> Tys = {});
+ /// Look up the Function declaration of the intrinsic \p IID in the Module
+ /// \p M. If it does not exist, add a declaration and return it. Otherwise,
+ /// return the existing declaration.
+ ///
+ /// This overload automatically resolves overloaded intrinsics based on the
+ /// provided return type and argument types. For non-overloaded intrinsics,
+ /// the return type and argument types are ignored.
+ ///
+ /// \param M - The module to get or insert the intrinsic declaration.
+ /// \param IID - The intrinsic ID.
+ /// \param RetTy - The return type of the intrinsic.
+ /// \param ArgTys - The argument types of the intrinsic.
+ LLVM_ABI Function *getOrInsertDeclaration(Module *M, ID IID, Type *RetTy,
+ ArrayRef<Type *> ArgTys);
+
/// Look up the Function declaration of the intrinsic \p id in the Module
/// \p M and return it if it exists. Otherwise, return nullptr. This version
/// supports non-overloaded intrinsics.
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 526800e217399..61c83be82a917 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -720,14 +720,14 @@ Intrinsic::ID Intrinsic::lookupIntrinsicID(StringRef Name) {
#include "llvm/IR/IntrinsicImpl.inc"
#undef GET_INTRINSIC_ATTRIBUTES
-Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
- ArrayRef<Type *> Tys) {
- // There can never be multiple globals with the same name of different types,
- // because intrinsics must be a specific type.
- auto *FT = getType(M->getContext(), id, Tys);
+static Function *getOrInsertIntrinsicDeclarationImpl(Module *M,
+ Intrinsic::ID id,
+ ArrayRef<Type *> Tys,
+ FunctionType *FT) {
Function *F = cast<Function>(
- M->getOrInsertFunction(
- Tys.empty() ? getName(id) : getName(id, Tys, M, FT), FT)
+ M->getOrInsertFunction(Tys.empty() ? Intrinsic::getName(id)
+ : Intrinsic::getName(id, Tys, M, FT),
+ FT)
.getCallee());
if (F->getFunctionType() == FT)
return F;
@@ -739,11 +739,43 @@ Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
// invalid declaration will get upgraded later.
F->setName(F->getName() + ".invalid");
return cast<Function>(
- M->getOrInsertFunction(
- Tys.empty() ? getName(id) : getName(id, Tys, M, FT), FT)
+ M->getOrInsertFunction(Tys.empty() ? Intrinsic::getName(id)
+ : Intrinsic::getName(id, Tys, M, FT),
+ FT)
.getCallee());
}
+Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
+ ArrayRef<Type *> Tys) {
+ // There can never be multiple globals with the same name of different types,
+ // because intrinsics must be a specific type.
+ FunctionType *FT = getType(M->getContext(), id, Tys);
+ return getOrInsertIntrinsicDeclarationImpl(M, id, Tys, FT);
+}
+
+Function *Intrinsic::getOrInsertDeclaration(Module *M, ID IID, Type *RetTy,
+ ArrayRef<Type *> ArgTys) {
+ // If the intrinsic is not overloaded, use the non-overloaded version.
+ if (!Intrinsic::isOverloaded(IID))
+ return getOrInsertDeclaration(M, IID);
+
+ // Get the intrinsic signature metadata.
+ SmallVector<Intrinsic::IITDescriptor, 8> Table;
+ getIntrinsicInfoTableEntries(IID, Table);
+ ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
+
+ FunctionType *FTy = FunctionType::get(RetTy, ArgTys, /*isVarArg=*/false);
+
+ // Automatically determine the overloaded types.
+ SmallVector<Type *, 4> OverloadTys;
+ [[maybe_unused]] Intrinsic::MatchIntrinsicTypesResult Res =
+ matchIntrinsicSignature(FTy, TableRef, OverloadTys);
+ assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
+ "intrinsic signature mismatch");
+
+ return getOrInsertIntrinsicDeclarationImpl(M, IID, OverloadTys, FTy);
+}
+
Function *Intrinsic::getDeclarationIfExists(const Module *M, ID id) {
return M->getFunction(getName(id));
}
diff --git a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
index 995ade5c9b033..5747021be189c 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
@@ -3176,12 +3176,7 @@ def NVVM_PrefetchOp : NVVM_Op<"prefetch",
let llvmBuilder = [{
auto [id, args] = NVVM::PrefetchOp::getIntrinsicIDAndArgs(op,
moduleTranslation, builder);
-
- if(op.getTensormap())
- // Overloaded intrinsic
- createIntrinsicCall(builder, id, args, {args[0]->getType()});
- else
- createIntrinsicCall(builder, id, args);
+ createIntrinsicCall(builder, id, builder.getVoidTy(), args);
}];
}
diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
index eb7dfa7637e52..039ac8e2e1911 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
@@ -512,6 +512,15 @@ llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &builder,
ArrayRef<llvm::Value *> args = {},
ArrayRef<llvm::Type *> tys = {});
+/// Creates a call to an LLVM IR intrinsic function with the given return type
+/// and arguments. If the intrinsic is overloaded, the function signature will
+/// be automatically resolved based on the provided return type and argument
+/// types.
+llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &builder,
+ llvm::Intrinsic::ID intrinsic,
+ llvm::Type *retTy,
+ ArrayRef<llvm::Value *> args);
+
/// Creates a call to a LLVM IR intrinsic defined by LLVM_IntrOpBase. This
/// resolves the overloads, and maps mixed MLIR value and attribute arguments to
/// LLVM values.
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 64e3c5f085bb3..e48e841dae18a 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -898,6 +898,21 @@ llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
return builder.CreateCall(fn, args);
}
+llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
+ llvm::IRBuilderBase &builder, llvm::Intrinsic::ID intrinsic,
+ llvm::Type *retTy, ArrayRef<llvm::Value *> args) {
+ llvm::Module *module = builder.GetInsertBlock()->getModule();
+
+ SmallVector<llvm::Type *> argTys;
+ argTys.reserve(args.size());
+ for (llvm::Value *arg : args)
+ argTys.push_back(arg->getType());
+
+ llvm::Function *fn =
+ llvm::Intrinsic::getOrInsertDeclaration(module, intrinsic, retTy, argTys);
+ return builder.CreateCall(fn, args);
+}
+
llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
llvm::IRBuilderBase &builder, ModuleTranslation &moduleTranslation,
Operation *intrOp, llvm::Intrinsic::ID intrinsic, unsigned numResults,
>From ed8c9c9f0c4dce1ac64840b1ded0dde611fbd923 Mon Sep 17 00:00:00 2001
From: rbajpai <rbajpai at nvidia.com>
Date: Tue, 18 Nov 2025 15:33:28 +0530
Subject: [PATCH 2/2] Addressed review comments
Simplify IRBuilder::CreateIntrinsic by delegating to the newer
Intrinsic::getOrInsertDeclaration API, which now supports vararg
intrinsics like experimental_gc_statepoint.
Changes:
- Remove duplicated intrinsic signature matching logic from
IRBuilder::CreateIntrinsic and delegate to getOrInsertDeclaration
- Extend Intrinsic::getOrInsertDeclaration to handle vararg intrinsics
by recreating the FunctionType with isVarArg=true when needed
- Add comprehensive unit tests covering non-overloaded, overloaded
(scalar/vector), and vararg intrinsics for both direct API usage
and IRBuilder
---
llvm/lib/IR/IRBuilder.cpp | 16 +--
llvm/lib/IR/Intrinsics.cpp | 8 +-
llvm/unittests/IR/IntrinsicsTest.cpp | 171 ++++++++++++++++++++++++++-
3 files changed, 177 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index 95edb2e8e56d8..8e1707ac98a51 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -858,24 +858,12 @@ CallInst *IRBuilderBase::CreateIntrinsic(Type *RetTy, Intrinsic::ID ID,
const Twine &Name) {
Module *M = BB->getModule();
- SmallVector<Intrinsic::IITDescriptor> Table;
- Intrinsic::getIntrinsicInfoTableEntries(ID, Table);
- ArrayRef<Intrinsic::IITDescriptor> TableRef(Table);
-
SmallVector<Type *> ArgTys;
ArgTys.reserve(Args.size());
for (auto &I : Args)
ArgTys.push_back(I->getType());
- FunctionType *FTy = FunctionType::get(RetTy, ArgTys, false);
- SmallVector<Type *> OverloadTys;
- Intrinsic::MatchIntrinsicTypesResult Res =
- matchIntrinsicSignature(FTy, TableRef, OverloadTys);
- (void)Res;
- assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
- "Wrong types for intrinsic!");
- // TODO: Handle varargs intrinsics.
-
- Function *Fn = Intrinsic::getOrInsertDeclaration(M, ID, OverloadTys);
+
+ Function *Fn = Intrinsic::getOrInsertDeclaration(M, ID, RetTy, ArgTys);
return createCallHelper(Fn, Args, Name, FMFSource);
}
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 61c83be82a917..68ec076b637bf 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -770,9 +770,15 @@ Function *Intrinsic::getOrInsertDeclaration(Module *M, ID IID, Type *RetTy,
SmallVector<Type *, 4> OverloadTys;
[[maybe_unused]] Intrinsic::MatchIntrinsicTypesResult Res =
matchIntrinsicSignature(FTy, TableRef, OverloadTys);
- assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
+ assert(Res == Intrinsic::MatchIntrinsicTypes_Match &&
"intrinsic signature mismatch");
+ // If intrinsic requires vararg, recreate the FunctionType accordingly.
+ if (!matchIntrinsicVarArg(/*isVarArg=*/true, TableRef))
+ FTy = FunctionType::get(RetTy, ArgTys, /*isVarArg=*/true);
+
+ assert(TableRef.empty() && "Unprocessed descriptors remain");
+
return getOrInsertIntrinsicDeclarationImpl(M, IID, OverloadTys, FTy);
}
diff --git a/llvm/unittests/IR/IntrinsicsTest.cpp b/llvm/unittests/IR/IntrinsicsTest.cpp
index cfd99ed542162..630808e437cab 100644
--- a/llvm/unittests/IR/IntrinsicsTest.cpp
+++ b/llvm/unittests/IR/IntrinsicsTest.cpp
@@ -30,14 +30,12 @@
using namespace llvm;
namespace {
-
class IntrinsicsTest : public ::testing::Test {
+protected:
LLVMContext Context;
std::unique_ptr<Module> M;
BasicBlock *BB = nullptr;
- void TearDown() override { M.reset(); }
-
void SetUp() override {
M = std::make_unique<Module>("Test", Context);
auto F = M->getOrInsertFunction(
@@ -46,6 +44,8 @@ class IntrinsicsTest : public ::testing::Test {
EXPECT_NE(BB, nullptr);
}
+ void TearDown() override { M.reset(); }
+
public:
Instruction *makeIntrinsic(Intrinsic::ID ID) const {
IRBuilder<> Builder(BB);
@@ -197,4 +197,169 @@ TEST(IntrinsicAttributes, TestGetFnAttributesBug) {
AttributeSet AS = getFnAttributes(Context, experimental_guard);
EXPECT_FALSE(AS.hasAttributes());
}
+
+// Test non-overloaded intrinsic
+TEST_F(IntrinsicsTest, NonOverloadedIntrinsic) {
+ Type *RetTy = Type::getVoidTy(Context);
+ SmallVector<Type *, 1> ArgTys;
+ ArgTys.push_back(Type::getInt1Ty(Context));
+
+ Function *F = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::assume,
+ RetTy, ArgTys);
+
+ ASSERT_NE(F, nullptr);
+ EXPECT_EQ(F->getIntrinsicID(), Intrinsic::assume);
+ EXPECT_EQ(F->getReturnType(), RetTy);
+ EXPECT_EQ(F->arg_size(), 1u);
+ EXPECT_FALSE(F->isVarArg());
+ EXPECT_EQ(F->getName(), "llvm.assume");
+}
+
+// Test overloaded intrinsic with automatic type resolution (scalar)
+TEST_F(IntrinsicsTest, OverloadedIntrinsicScalar) {
+ Type *RetTy = Type::getInt32Ty(Context);
+ SmallVector<Type *, 2> ArgTys;
+ ArgTys.push_back(Type::getInt32Ty(Context));
+ ArgTys.push_back(Type::getInt32Ty(Context));
+
+ Function *F = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::umax,
+ RetTy, ArgTys);
+
+ ASSERT_NE(F, nullptr);
+ EXPECT_EQ(F->getIntrinsicID(), Intrinsic::umax);
+ EXPECT_EQ(F->getReturnType(), RetTy);
+ EXPECT_EQ(F->arg_size(), 2u);
+ EXPECT_FALSE(F->isVarArg());
+ EXPECT_EQ(F->getName(), "llvm.umax.i32");
+}
+
+// Test overloaded intrinsic with automatic type resolution (vector)
+TEST_F(IntrinsicsTest, OverloadedIntrinsicVector) {
+ Type *RetTy = FixedVectorType::get(Type::getInt32Ty(Context), 4);
+ SmallVector<Type *, 2> ArgTys;
+ ArgTys.push_back(RetTy);
+ ArgTys.push_back(RetTy);
+
+ Function *F = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::umax,
+ RetTy, ArgTys);
+
+ ASSERT_NE(F, nullptr);
+ EXPECT_EQ(F->getIntrinsicID(), Intrinsic::umax);
+ EXPECT_EQ(F->getReturnType(), RetTy);
+ EXPECT_EQ(F->arg_size(), 2u);
+ EXPECT_FALSE(F->isVarArg());
+ EXPECT_EQ(F->getName(), "llvm.umax.v4i32");
+}
+
+// Test vararg intrinsic: experimental_gc_statepoint
+TEST_F(IntrinsicsTest, VarArgIntrinsicStatepoint) {
+ Type *RetTy = Type::getTokenTy(Context);
+ SmallVector<Type *, 5> ArgTys;
+ ArgTys.push_back(Type::getInt64Ty(Context)); // ID
+ ArgTys.push_back(Type::getInt32Ty(Context)); // NumPatchBytes
+ ArgTys.push_back(PointerType::get(Context, 0)); // Target
+ ArgTys.push_back(Type::getInt32Ty(Context)); // NumCallArgs
+ ArgTys.push_back(Type::getInt32Ty(Context)); // Flags
+
+ Function *F = Intrinsic::getOrInsertDeclaration(
+ M.get(), Intrinsic::experimental_gc_statepoint, RetTy, ArgTys);
+
+ ASSERT_NE(F, nullptr);
+ EXPECT_EQ(F->getIntrinsicID(), Intrinsic::experimental_gc_statepoint);
+ EXPECT_EQ(F->getReturnType(), RetTy);
+ EXPECT_EQ(F->arg_size(), 5u);
+ EXPECT_TRUE(F->isVarArg()) << "experimental_gc_statepoint must be vararg";
+ EXPECT_EQ(F->getName(), "llvm.experimental.gc.statepoint.p0");
+}
+
+// Test different overloads create different declarations
+TEST_F(IntrinsicsTest, DifferentOverloads) {
+ // i32 version
+ Type *RetTy32 = Type::getInt32Ty(Context);
+ SmallVector<Type *, 2> ArgTys32;
+ ArgTys32.push_back(Type::getInt32Ty(Context));
+ ArgTys32.push_back(Type::getInt32Ty(Context));
+
+ Function *F32 = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::umax,
+ RetTy32, ArgTys32);
+
+ // i64 version
+ Type *RetTy64 = Type::getInt64Ty(Context);
+ SmallVector<Type *, 2> ArgTys64;
+ ArgTys64.push_back(Type::getInt64Ty(Context));
+ ArgTys64.push_back(Type::getInt64Ty(Context));
+
+ Function *F64 = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::umax,
+ RetTy64, ArgTys64);
+
+ EXPECT_NE(F32, F64) << "Different overloads should be different functions";
+ EXPECT_EQ(F32->getName(), "llvm.umax.i32");
+ EXPECT_EQ(F64->getName(), "llvm.umax.i64");
+}
+
+// Test IRBuilder::CreateIntrinsic with overloaded scalar type
+TEST_F(IntrinsicsTest, IRBuilderCreateIntrinsicScalar) {
+ IRBuilder<> Builder(BB);
+
+ Type *RetTy = Type::getInt32Ty(Context);
+ SmallVector<Value *, 2> Args;
+ Args.push_back(ConstantInt::get(Type::getInt32Ty(Context), 10));
+ Args.push_back(ConstantInt::get(Type::getInt32Ty(Context), 20));
+
+ CallInst *CI = Builder.CreateIntrinsic(RetTy, Intrinsic::umax, Args);
+
+ ASSERT_NE(CI, nullptr);
+ EXPECT_EQ(CI->getIntrinsicID(), Intrinsic::umax);
+ EXPECT_EQ(CI->getType(), RetTy);
+ EXPECT_EQ(CI->arg_size(), 2u);
+ EXPECT_FALSE(CI->getCalledFunction()->isVarArg());
+}
+
+// Test IRBuilder::CreateIntrinsic with vector intrinsic
+TEST_F(IntrinsicsTest, IRBuilderCreateIntrinsicVector) {
+ IRBuilder<> Builder(BB);
+
+ Type *RetTy = FixedVectorType::get(Type::getInt32Ty(Context), 4);
+ SmallVector<Value *, 2> Args;
+ Args.push_back(Constant::getNullValue(RetTy));
+ Args.push_back(Constant::getNullValue(RetTy));
+
+ CallInst *CI = Builder.CreateIntrinsic(RetTy, Intrinsic::umax, Args);
+
+ ASSERT_NE(CI, nullptr);
+ EXPECT_EQ(CI->getIntrinsicID(), Intrinsic::umax);
+ EXPECT_EQ(CI->getType(), RetTy);
+ EXPECT_EQ(CI->arg_size(), 2u);
+ EXPECT_FALSE(CI->getCalledFunction()->isVarArg());
+}
+
+// Test IRBuilder::CreateIntrinsic with vararg intrinsic
+TEST_F(IntrinsicsTest, IRBuilderCreateIntrinsicVarArg) {
+ IRBuilder<> Builder(BB);
+
+ // Create a dummy function to call through statepoint
+ FunctionType *DummyFnTy = FunctionType::get(Type::getVoidTy(Context), false);
+ Function *DummyFn = Function::Create(DummyFnTy, GlobalValue::ExternalLinkage,
+ "dummy", M.get());
+
+ Type *RetTy = Type::getTokenTy(Context);
+ SmallVector<Value *, 5> Args;
+ Args.push_back(ConstantInt::get(Type::getInt64Ty(Context), 0)); // ID
+ Args.push_back(
+ ConstantInt::get(Type::getInt32Ty(Context), 0)); // NumPatchBytes
+ Args.push_back(DummyFn); // Target
+ Args.push_back(ConstantInt::get(Type::getInt32Ty(Context), 0)); // NumCallArgs
+ Args.push_back(ConstantInt::get(Type::getInt32Ty(Context), 0)); // Flags
+
+ CallInst *CI = Builder.CreateIntrinsic(
+ RetTy, Intrinsic::experimental_gc_statepoint, Args);
+
+ ASSERT_NE(CI, nullptr);
+ EXPECT_EQ(CI->getIntrinsicID(), Intrinsic::experimental_gc_statepoint);
+ EXPECT_EQ(CI->getType(), RetTy);
+ EXPECT_EQ(CI->arg_size(), 5u);
+ EXPECT_TRUE(CI->getCalledFunction()->isVarArg())
+ << "experimental_gc_statepoint must be vararg";
+}
+
} // end namespace
More information about the Mlir-commits
mailing list