3030using namespace llvm ;
3131
3232namespace {
33-
3433class IntrinsicsTest : public ::testing::Test {
34+ protected:
3535 LLVMContext Context;
3636 std::unique_ptr<Module> M;
3737 BasicBlock *BB = nullptr ;
3838
39- void TearDown () override { M.reset (); }
40-
4139 void SetUp () override {
4240 M = std::make_unique<Module>(" Test" , Context);
4341 auto F = M->getOrInsertFunction (
@@ -46,6 +44,8 @@ class IntrinsicsTest : public ::testing::Test {
4644 EXPECT_NE (BB, nullptr );
4745 }
4846
47+ void TearDown () override { M.reset (); }
48+
4949public:
5050 Instruction *makeIntrinsic (Intrinsic::ID ID) const {
5151 IRBuilder<> Builder (BB);
@@ -197,4 +197,169 @@ TEST(IntrinsicAttributes, TestGetFnAttributesBug) {
197197 AttributeSet AS = getFnAttributes (Context, experimental_guard);
198198 EXPECT_FALSE (AS.hasAttributes ());
199199}
200+
201+ // Test non-overloaded intrinsic
202+ TEST_F (IntrinsicsTest, NonOverloadedIntrinsic) {
203+ Type *RetTy = Type::getVoidTy (Context);
204+ SmallVector<Type *, 1 > ArgTys;
205+ ArgTys.push_back (Type::getInt1Ty (Context));
206+
207+ Function *F = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::assume,
208+ RetTy, ArgTys);
209+
210+ ASSERT_NE (F, nullptr );
211+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::assume);
212+ EXPECT_EQ (F->getReturnType (), RetTy);
213+ EXPECT_EQ (F->arg_size (), 1u );
214+ EXPECT_FALSE (F->isVarArg ());
215+ EXPECT_EQ (F->getName (), " llvm.assume" );
216+ }
217+
218+ // Test overloaded intrinsic with automatic type resolution (scalar)
219+ TEST_F (IntrinsicsTest, OverloadedIntrinsicScalar) {
220+ Type *RetTy = Type::getInt32Ty (Context);
221+ SmallVector<Type *, 2 > ArgTys;
222+ ArgTys.push_back (Type::getInt32Ty (Context));
223+ ArgTys.push_back (Type::getInt32Ty (Context));
224+
225+ Function *F = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
226+ RetTy, ArgTys);
227+
228+ ASSERT_NE (F, nullptr );
229+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::umax);
230+ EXPECT_EQ (F->getReturnType (), RetTy);
231+ EXPECT_EQ (F->arg_size (), 2u );
232+ EXPECT_FALSE (F->isVarArg ());
233+ EXPECT_EQ (F->getName (), " llvm.umax.i32" );
234+ }
235+
236+ // Test overloaded intrinsic with automatic type resolution (vector)
237+ TEST_F (IntrinsicsTest, OverloadedIntrinsicVector) {
238+ Type *RetTy = FixedVectorType::get (Type::getInt32Ty (Context), 4 );
239+ SmallVector<Type *, 2 > ArgTys;
240+ ArgTys.push_back (RetTy);
241+ ArgTys.push_back (RetTy);
242+
243+ Function *F = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
244+ RetTy, ArgTys);
245+
246+ ASSERT_NE (F, nullptr );
247+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::umax);
248+ EXPECT_EQ (F->getReturnType (), RetTy);
249+ EXPECT_EQ (F->arg_size (), 2u );
250+ EXPECT_FALSE (F->isVarArg ());
251+ EXPECT_EQ (F->getName (), " llvm.umax.v4i32" );
252+ }
253+
254+ // Test vararg intrinsic: experimental_gc_statepoint
255+ TEST_F (IntrinsicsTest, VarArgIntrinsicStatepoint) {
256+ Type *RetTy = Type::getTokenTy (Context);
257+ SmallVector<Type *, 5 > ArgTys;
258+ ArgTys.push_back (Type::getInt64Ty (Context)); // ID
259+ ArgTys.push_back (Type::getInt32Ty (Context)); // NumPatchBytes
260+ ArgTys.push_back (PointerType::get (Context, 0 )); // Target
261+ ArgTys.push_back (Type::getInt32Ty (Context)); // NumCallArgs
262+ ArgTys.push_back (Type::getInt32Ty (Context)); // Flags
263+
264+ Function *F = Intrinsic::getOrInsertDeclaration (
265+ M.get (), Intrinsic::experimental_gc_statepoint, RetTy, ArgTys);
266+
267+ ASSERT_NE (F, nullptr );
268+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::experimental_gc_statepoint);
269+ EXPECT_EQ (F->getReturnType (), RetTy);
270+ EXPECT_EQ (F->arg_size (), 5u );
271+ EXPECT_TRUE (F->isVarArg ()) << " experimental_gc_statepoint must be vararg" ;
272+ EXPECT_EQ (F->getName (), " llvm.experimental.gc.statepoint.p0" );
273+ }
274+
275+ // Test different overloads create different declarations
276+ TEST_F (IntrinsicsTest, DifferentOverloads) {
277+ // i32 version
278+ Type *RetTy32 = Type::getInt32Ty (Context);
279+ SmallVector<Type *, 2 > ArgTys32;
280+ ArgTys32.push_back (Type::getInt32Ty (Context));
281+ ArgTys32.push_back (Type::getInt32Ty (Context));
282+
283+ Function *F32 = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
284+ RetTy32, ArgTys32);
285+
286+ // i64 version
287+ Type *RetTy64 = Type::getInt64Ty (Context);
288+ SmallVector<Type *, 2 > ArgTys64;
289+ ArgTys64.push_back (Type::getInt64Ty (Context));
290+ ArgTys64.push_back (Type::getInt64Ty (Context));
291+
292+ Function *F64 = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
293+ RetTy64, ArgTys64);
294+
295+ EXPECT_NE (F32, F64) << " Different overloads should be different functions" ;
296+ EXPECT_EQ (F32->getName (), " llvm.umax.i32" );
297+ EXPECT_EQ (F64->getName (), " llvm.umax.i64" );
298+ }
299+
300+ // Test IRBuilder::CreateIntrinsic with overloaded scalar type
301+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicScalar) {
302+ IRBuilder<> Builder (BB);
303+
304+ Type *RetTy = Type::getInt32Ty (Context);
305+ SmallVector<Value *, 2 > Args;
306+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 10 ));
307+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 20 ));
308+
309+ CallInst *CI = Builder.CreateIntrinsic (RetTy, Intrinsic::umax, Args);
310+
311+ ASSERT_NE (CI, nullptr );
312+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::umax);
313+ EXPECT_EQ (CI->getType (), RetTy);
314+ EXPECT_EQ (CI->arg_size (), 2u );
315+ EXPECT_FALSE (CI->getCalledFunction ()->isVarArg ());
316+ }
317+
318+ // Test IRBuilder::CreateIntrinsic with vector intrinsic
319+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicVector) {
320+ IRBuilder<> Builder (BB);
321+
322+ Type *RetTy = FixedVectorType::get (Type::getInt32Ty (Context), 4 );
323+ SmallVector<Value *, 2 > Args;
324+ Args.push_back (Constant::getNullValue (RetTy));
325+ Args.push_back (Constant::getNullValue (RetTy));
326+
327+ CallInst *CI = Builder.CreateIntrinsic (RetTy, Intrinsic::umax, Args);
328+
329+ ASSERT_NE (CI, nullptr );
330+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::umax);
331+ EXPECT_EQ (CI->getType (), RetTy);
332+ EXPECT_EQ (CI->arg_size (), 2u );
333+ EXPECT_FALSE (CI->getCalledFunction ()->isVarArg ());
334+ }
335+
336+ // Test IRBuilder::CreateIntrinsic with vararg intrinsic
337+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicVarArg) {
338+ IRBuilder<> Builder (BB);
339+
340+ // Create a dummy function to call through statepoint
341+ FunctionType *DummyFnTy = FunctionType::get (Type::getVoidTy (Context), false );
342+ Function *DummyFn = Function::Create (DummyFnTy, GlobalValue::ExternalLinkage,
343+ " dummy" , M.get ());
344+
345+ Type *RetTy = Type::getTokenTy (Context);
346+ SmallVector<Value *, 5 > Args;
347+ Args.push_back (ConstantInt::get (Type::getInt64Ty (Context), 0 )); // ID
348+ Args.push_back (
349+ ConstantInt::get (Type::getInt32Ty (Context), 0 )); // NumPatchBytes
350+ Args.push_back (DummyFn); // Target
351+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 0 )); // NumCallArgs
352+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 0 )); // Flags
353+
354+ CallInst *CI = Builder.CreateIntrinsic (
355+ RetTy, Intrinsic::experimental_gc_statepoint, Args);
356+
357+ ASSERT_NE (CI, nullptr );
358+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::experimental_gc_statepoint);
359+ EXPECT_EQ (CI->getType (), RetTy);
360+ EXPECT_EQ (CI->arg_size (), 5u );
361+ EXPECT_TRUE (CI->getCalledFunction ()->isVarArg ())
362+ << " experimental_gc_statepoint must be vararg" ;
363+ }
364+
200365} // end namespace
0 commit comments