diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -877,6 +877,23 @@ Instruction *FMFSource = nullptr, const Twine &Name = ""); + /// Create a call to intrinsic \p ID with return type \p RetTy and arguments + /// \p Args. Type overloads are deduced automatically. + /// + /// If \p FMFSource is provided, copy fast-math-flags from that instruction to + /// the intrinsic. + /// + /// Var-args functions are not supported. + /// + /// \note This method is provided specifically as a convenience to external + /// users of LLVM, allowing them to create intrinsics whose overload + /// signature is changing (e.g., a previously fixed type is replaced by + /// an overloadable type). + CallInst *CreateIntrinsicByType(Intrinsic::ID ID, Type *RetTy, + ArrayRef Args, + Instruction *FMFSource = nullptr, + const Twine &Name = ""); + /// Create call to the minnum intrinsic. CallInst *CreateMinNum(Value *LHS, Value *RHS, const Twine &Name = "") { return CreateBinaryIntrinsic(Intrinsic::minnum, LHS, RHS, nullptr, Name); diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h --- a/llvm/include/llvm/IR/Intrinsics.h +++ b/llvm/include/llvm/IR/Intrinsics.h @@ -83,6 +83,14 @@ /// the intrinsic. Function *getDeclaration(Module *M, ID id, ArrayRef Tys = None); + /// Find or create and insert an LLVM function declaration for an intrinsic + /// and return it. + /// + /// Overloaded types are deduced from \p RetTy and \p ArgTys. + Function *getDeclarationByType(Module *M, ID id, Type *RetTy, + ArrayRef ArgTys, + bool IsVarArg = false); + /// Looks up Name in NameTable via binary search. NameTable must be sorted /// and all entries must start with "llvm.". If NameTable contains an exact /// match for Name or a prefix of Name followed by a dot, its index in @@ -204,6 +212,7 @@ MatchIntrinsicTypes_Match = 0, MatchIntrinsicTypes_NoMatchRet = 1, MatchIntrinsicTypes_NoMatchArg = 2, + MatchIntrinsicTypes_NoMatchVarArg = 3, }; /// Match the specified function type with the type constraints specified by @@ -214,20 +223,23 @@ /// otherwise. MatchIntrinsicTypesResult matchIntrinsicSignature(FunctionType *FTy, ArrayRef &Infos, - SmallVectorImpl &ArgTys); + SmallVectorImpl &OverloadTys); - /// Verify if the intrinsic has variable arguments. This method is intended to - /// be called after all the fixed arguments have been matched first. - /// - /// This method returns true on error. - bool matchIntrinsicVarArg(bool isVarArg, ArrayRef &Infos); + MatchIntrinsicTypesResult + matchIntrinsicSignature(Type *RetTy, ArrayRef ArgTys, bool IsVarArg, + ArrayRef &Infos, + SmallVectorImpl &OverloadTys); /// Gets the type arguments of an intrinsic call by matching type contraints /// specified by the .td file. The overloaded types are pushed into the - /// AgTys vector. + /// OverloadedTys vector. /// /// Returns false if the given function is not a valid intrinsic call. - bool getIntrinsicSignature(Function *F, SmallVectorImpl &ArgTys); + bool getIntrinsicSignature(Function *F, SmallVectorImpl &OverloadTys); + + bool getIntrinsicSignature(ID Id, Type *RetTy, ArrayRef ArgTys, + bool IsVarArg, + SmallVectorImpl &OverloadTys); // Checks if the intrinsic name matches with its signature and if not // returns the declaration with the same signature and remangled name. diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -1207,6 +1207,19 @@ .getCallee()); } +Function *Intrinsic::getDeclarationByType(Module *M, ID id, Type *RetTy, + ArrayRef ArgTys, + bool IsVarArg) { + SmallVector OverloadTys; + if (!getIntrinsicSignature(id, RetTy, ArgTys, IsVarArg, OverloadTys)) + return nullptr; + + return cast( + M->getOrInsertFunction(getName(id, OverloadTys), + FunctionType::get(RetTy, ArgTys, IsVarArg)) + .getCallee()); +} + // This defines the "Intrinsic::getIntrinsicForGCCBuiltin()" method. #define GET_LLVM_INTRINSIC_FOR_GCC_BUILTIN #include "llvm/IR/IntrinsicImpl.inc" @@ -1443,68 +1456,70 @@ Intrinsic::MatchIntrinsicTypesResult Intrinsic::matchIntrinsicSignature(FunctionType *FTy, ArrayRef &Infos, - SmallVectorImpl &ArgTys) { + SmallVectorImpl &OverloadTys) { + return matchIntrinsicSignature(FTy->getReturnType(), FTy->params(), + FTy->isVarArg(), Infos, OverloadTys); +} + +Intrinsic::MatchIntrinsicTypesResult +Intrinsic::matchIntrinsicSignature(Type *RetTy, ArrayRef ArgTys, + bool IsVarArg, + ArrayRef &Infos, + SmallVectorImpl &OverloadTys) { SmallVector DeferredChecks; - if (matchIntrinsicType(FTy->getReturnType(), Infos, ArgTys, DeferredChecks, - false)) + if (matchIntrinsicType(RetTy, Infos, OverloadTys, DeferredChecks, false)) return MatchIntrinsicTypes_NoMatchRet; unsigned NumDeferredReturnChecks = DeferredChecks.size(); - for (auto Ty : FTy->params()) - if (matchIntrinsicType(Ty, Infos, ArgTys, DeferredChecks, false)) + for (auto Ty : ArgTys) + if (matchIntrinsicType(Ty, Infos, OverloadTys, DeferredChecks, false)) return MatchIntrinsicTypes_NoMatchArg; for (unsigned I = 0, E = DeferredChecks.size(); I != E; ++I) { DeferredIntrinsicMatchPair &Check = DeferredChecks[I]; - if (matchIntrinsicType(Check.first, Check.second, ArgTys, DeferredChecks, - true)) + if (matchIntrinsicType(Check.first, Check.second, OverloadTys, + DeferredChecks, true)) return I < NumDeferredReturnChecks ? MatchIntrinsicTypes_NoMatchRet : MatchIntrinsicTypes_NoMatchArg; } - return MatchIntrinsicTypes_Match; -} - -bool -Intrinsic::matchIntrinsicVarArg(bool isVarArg, - ArrayRef &Infos) { - // If there are no descriptors left, then it can't be a vararg. - if (Infos.empty()) - return isVarArg; - - // There should be only one descriptor remaining at this point. - if (Infos.size() != 1) - return true; + bool InfoVarArg = false; + if (!Infos.empty()) { + if (Infos.size() >= 2 || Infos.front().Kind != IITDescriptor::VarArg) + return MatchIntrinsicTypes_NoMatchArg; + Infos = Infos.slice(1); + InfoVarArg = true; + } - // Check and verify the descriptor. - IITDescriptor D = Infos.front(); - Infos = Infos.slice(1); - if (D.Kind == IITDescriptor::VarArg) - return !isVarArg; + if (IsVarArg != InfoVarArg) + return MatchIntrinsicTypes_NoMatchVarArg; - return true; + return MatchIntrinsicTypes_Match; } bool Intrinsic::getIntrinsicSignature(Function *F, - SmallVectorImpl &ArgTys) { + SmallVectorImpl &OverloadTys) { Intrinsic::ID ID = F->getIntrinsicID(); if (!ID) return false; + FunctionType *FnType = F->getFunctionType(); + return getIntrinsicSignature(ID, FnType->getReturnType(), FnType->params(), + FnType->isVarArg(), OverloadTys); +} + +bool Intrinsic::getIntrinsicSignature(Intrinsic::ID Id, Type *RetTy, + ArrayRef ArgTys, bool IsVarArg, + SmallVectorImpl &OverloadTys) { + SmallVector Table; - getIntrinsicInfoTableEntries(ID, Table); + getIntrinsicInfoTableEntries(Id, Table); ArrayRef TableRef = Table; - if (Intrinsic::matchIntrinsicSignature(F->getFunctionType(), TableRef, - ArgTys) != - Intrinsic::MatchIntrinsicTypesResult::MatchIntrinsicTypes_Match) { - return false; - } - if (Intrinsic::matchIntrinsicVarArg(F->getFunctionType()->isVarArg(), - TableRef)) - return false; - return true; + return Intrinsic::matchIntrinsicSignature(RetTy, ArgTys, IsVarArg, TableRef, + OverloadTys) == + Intrinsic::MatchIntrinsicTypesResult::MatchIntrinsicTypes_Match; } Optional Intrinsic::remangleIntrinsicFunction(Function *F) { diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -790,6 +790,18 @@ return createCallHelper(Fn, Args, this, Name, FMFSource); } +CallInst *IRBuilderBase::CreateIntrinsicByType(Intrinsic::ID ID, Type *RetTy, + ArrayRef Args, + Instruction *FMFSource, + const Twine &Name) { + Module *M = BB->getModule(); + SmallVector ArgTys; + for (Value *Arg : Args) + ArgTys.push_back(Arg->getType()); + Function *Fn = Intrinsic::getDeclarationByType(M, ID, RetTy, ArgTys); + return createCallHelper(Fn, Args, this, Name, FMFSource); +} + CallInst *IRBuilderBase::CreateConstrainedFPBinOp( Intrinsic::ID ID, Value *L, Value *R, Instruction *FMFSource, const Twine &Name, MDNode *FPMathTag, 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 @@ -4443,10 +4443,10 @@ // Verify if the intrinsic call matches the vararg property. if (IsVarArg) - Assert(!Intrinsic::matchIntrinsicVarArg(IsVarArg, TableRef), + Assert(Res != Intrinsic::MatchIntrinsicTypes_NoMatchVarArg, "Intrinsic was not defined with variable arguments!", IF); else - Assert(!Intrinsic::matchIntrinsicVarArg(IsVarArg, TableRef), + Assert(Res != Intrinsic::MatchIntrinsicTypes_NoMatchVarArg, "Callsite was not defined with variable arguments!", IF); // All descriptors should be absorbed by now. diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -122,6 +122,12 @@ EXPECT_TRUE(II->hasNoInfs()); EXPECT_FALSE(II->hasNoNaNs()); + Call = Builder.CreateIntrinsicByType(Intrinsic::fma, V->getType(), {V, V, V}); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fma); + EXPECT_TRUE(II->hasNoInfs()); + EXPECT_FALSE(II->hasNoNaNs()); + Call = Builder.CreateUnaryIntrinsic(Intrinsic::roundeven, V); II = cast(Call); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::roundeven);