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,212 @@ TEST(IntrinsicAttributes, TestGetFnAttributesBug) {
197197 AttributeSet AS = getFnAttributes (Context, experimental_guard);
198198 EXPECT_FALSE (AS.hasAttributes ());
199199}
200+
201+ // Tests non-overloaded intrinsic declaration.
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+ // Tests overloaded intrinsic with automatic type resolution for scalar types.
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+ // Tests overloaded intrinsic with automatic type resolution for vector types.
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+ // Tests overloaded intrinsic with automatic type resolution for addrspace.
255+ TEST_F (IntrinsicsTest, OverloadedIntrinsicAddressSpace) {
256+ Type *RetTy = Type::getVoidTy (Context);
257+ SmallVector<Type *, 4 > ArgTys;
258+ ArgTys.push_back (PointerType::get (Context, 1 )); // ptr addrspace(1)
259+ ArgTys.push_back (Type::getInt32Ty (Context)); // rw
260+ ArgTys.push_back (Type::getInt32Ty (Context)); // locality
261+ ArgTys.push_back (Type::getInt32Ty (Context)); // cache type
262+
263+ Function *F = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::prefetch,
264+ RetTy, ArgTys);
265+
266+ ASSERT_NE (F, nullptr );
267+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::prefetch);
268+ EXPECT_EQ (F->getReturnType (), RetTy);
269+ EXPECT_EQ (F->arg_size (), 4u );
270+ EXPECT_FALSE (F->isVarArg ());
271+ EXPECT_EQ (F->getName (), " llvm.prefetch.p1" );
272+ }
273+
274+ // Tests vararg intrinsic declaration.
275+ TEST_F (IntrinsicsTest, VarArgIntrinsicStatepoint) {
276+ Type *RetTy = Type::getTokenTy (Context);
277+ SmallVector<Type *, 5 > ArgTys;
278+ ArgTys.push_back (Type::getInt64Ty (Context)); // ID
279+ ArgTys.push_back (Type::getInt32Ty (Context)); // NumPatchBytes
280+ ArgTys.push_back (PointerType::get (Context, 0 )); // Target
281+ ArgTys.push_back (Type::getInt32Ty (Context)); // NumCallArgs
282+ ArgTys.push_back (Type::getInt32Ty (Context)); // Flags
283+
284+ Function *F = Intrinsic::getOrInsertDeclaration (
285+ M.get (), Intrinsic::experimental_gc_statepoint, RetTy, ArgTys);
286+
287+ ASSERT_NE (F, nullptr );
288+ EXPECT_EQ (F->getIntrinsicID (), Intrinsic::experimental_gc_statepoint);
289+ EXPECT_EQ (F->getReturnType (), RetTy);
290+ EXPECT_EQ (F->arg_size (), 5u );
291+ EXPECT_TRUE (F->isVarArg ()) << " experimental_gc_statepoint must be vararg" ;
292+ EXPECT_EQ (F->getName (), " llvm.experimental.gc.statepoint.p0" );
293+ }
294+
295+ // Tests that different overloads create different declarations.
296+ TEST_F (IntrinsicsTest, DifferentOverloads) {
297+ // i32 version
298+ Type *RetTy32 = Type::getInt32Ty (Context);
299+ SmallVector<Type *, 2 > ArgTys32;
300+ ArgTys32.push_back (Type::getInt32Ty (Context));
301+ ArgTys32.push_back (Type::getInt32Ty (Context));
302+
303+ Function *Func32 = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
304+ RetTy32, ArgTys32);
305+
306+ // i64 version
307+ Type *RetTy64 = Type::getInt64Ty (Context);
308+ SmallVector<Type *, 2 > ArgTys64;
309+ ArgTys64.push_back (Type::getInt64Ty (Context));
310+ ArgTys64.push_back (Type::getInt64Ty (Context));
311+
312+ Function *Func64 = Intrinsic::getOrInsertDeclaration (M.get (), Intrinsic::umax,
313+ RetTy64, ArgTys64);
314+
315+ EXPECT_NE (Func32, Func64)
316+ << " Different overloads should be different functions" ;
317+ EXPECT_EQ (Func32->getName (), " llvm.umax.i32" );
318+ EXPECT_EQ (Func64->getName (), " llvm.umax.i64" );
319+ }
320+
321+ // Tests IRBuilder::CreateIntrinsic with overloaded scalar type.
322+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicScalar) {
323+ IRBuilder<> Builder (BB);
324+
325+ Type *RetTy = Type::getInt32Ty (Context);
326+ SmallVector<Value *, 2 > Args;
327+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 10 ));
328+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 20 ));
329+
330+ CallInst *CI = Builder.CreateIntrinsic (RetTy, Intrinsic::umax, Args);
331+
332+ ASSERT_NE (CI, nullptr );
333+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::umax);
334+ EXPECT_EQ (CI->getType (), RetTy);
335+ EXPECT_EQ (CI->arg_size (), 2u );
336+ EXPECT_FALSE (CI->getCalledFunction ()->isVarArg ());
337+ }
338+
339+ // Tests IRBuilder::CreateIntrinsic with overloaded vector type.
340+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicVector) {
341+ IRBuilder<> Builder (BB);
342+
343+ Type *RetTy = FixedVectorType::get (Type::getInt32Ty (Context), 4 );
344+ SmallVector<Value *, 2 > Args;
345+ Args.push_back (Constant::getNullValue (RetTy));
346+ Args.push_back (Constant::getNullValue (RetTy));
347+
348+ CallInst *CI = Builder.CreateIntrinsic (RetTy, Intrinsic::umax, Args);
349+
350+ ASSERT_NE (CI, nullptr );
351+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::umax);
352+ EXPECT_EQ (CI->getType (), RetTy);
353+ EXPECT_EQ (CI->arg_size (), 2u );
354+ EXPECT_FALSE (CI->getCalledFunction ()->isVarArg ());
355+ }
356+
357+ // Tests IRBuilder::CreateIntrinsic with overloaded address space.
358+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicAddressSpace) {
359+ IRBuilder<> Builder (BB);
360+
361+ Type *RetTy = Type::getVoidTy (Context);
362+ SmallVector<Value *, 4 > Args;
363+ Args.push_back (Constant::getNullValue (
364+ PointerType::get (Context, 1 ))); // ptr addrspace(1) null
365+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 0 )); // rw
366+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 3 )); // locality
367+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 1 )); // cache type
368+
369+ CallInst *CI = Builder.CreateIntrinsic (RetTy, Intrinsic::prefetch, Args);
370+
371+ ASSERT_NE (CI, nullptr );
372+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::prefetch);
373+ EXPECT_EQ (CI->getType (), RetTy);
374+ EXPECT_EQ (CI->arg_size (), 4u );
375+ EXPECT_FALSE (CI->getCalledFunction ()->isVarArg ());
376+ EXPECT_EQ (CI->getCalledFunction ()->getName (), " llvm.prefetch.p1" );
377+ }
378+
379+ // Tests IRBuilder::CreateIntrinsic with vararg intrinsic.
380+ TEST_F (IntrinsicsTest, IRBuilderCreateIntrinsicVarArg) {
381+ IRBuilder<> Builder (BB);
382+
383+ // Create a dummy function to call through statepoint
384+ FunctionType *DummyFnTy = FunctionType::get (Type::getVoidTy (Context), false );
385+ Function *DummyFn = Function::Create (DummyFnTy, GlobalValue::ExternalLinkage,
386+ " dummy" , M.get ());
387+
388+ Type *RetTy = Type::getTokenTy (Context);
389+ SmallVector<Value *, 5 > Args;
390+ Args.push_back (ConstantInt::get (Type::getInt64Ty (Context), 0 )); // ID
391+ Args.push_back (
392+ ConstantInt::get (Type::getInt32Ty (Context), 0 )); // NumPatchBytes
393+ Args.push_back (DummyFn); // Target
394+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 0 )); // NumCallArgs
395+ Args.push_back (ConstantInt::get (Type::getInt32Ty (Context), 0 )); // Flags
396+
397+ CallInst *CI = Builder.CreateIntrinsic (
398+ RetTy, Intrinsic::experimental_gc_statepoint, Args);
399+
400+ ASSERT_NE (CI, nullptr );
401+ EXPECT_EQ (CI->getIntrinsicID (), Intrinsic::experimental_gc_statepoint);
402+ EXPECT_EQ (CI->getType (), RetTy);
403+ EXPECT_EQ (CI->arg_size (), 5u );
404+ EXPECT_TRUE (CI->getCalledFunction ()->isVarArg ())
405+ << " experimental_gc_statepoint must be vararg" ;
406+ }
407+
200408} // end namespace
0 commit comments