Skip to content

Commit fe04f91

Browse files
committed
[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.
1 parent 29e3c2e commit fe04f91

File tree

5 files changed

+81
-15
lines changed

5 files changed

+81
-15
lines changed

llvm/include/llvm/IR/Intrinsics.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ namespace Intrinsic {
104104
LLVM_ABI Function *getOrInsertDeclaration(Module *M, ID id,
105105
ArrayRef<Type *> Tys = {});
106106

107+
/// Look up the Function declaration of the intrinsic \p IID in the Module
108+
/// \p M. If it does not exist, add a declaration and return it. Otherwise,
109+
/// return the existing declaration.
110+
///
111+
/// This overload automatically resolves overloaded intrinsics based on the
112+
/// provided return type and argument types. For non-overloaded intrinsics,
113+
/// the return type and argument types are ignored.
114+
///
115+
/// \param M - The module to get or insert the intrinsic declaration.
116+
/// \param IID - The intrinsic ID.
117+
/// \param RetTy - The return type of the intrinsic.
118+
/// \param ArgTys - The argument types of the intrinsic.
119+
LLVM_ABI Function *getOrInsertDeclaration(Module *M, ID IID, Type *RetTy,
120+
ArrayRef<Type *> ArgTys);
121+
107122
/// Look up the Function declaration of the intrinsic \p id in the Module
108123
/// \p M and return it if it exists. Otherwise, return nullptr. This version
109124
/// supports non-overloaded intrinsics.

llvm/lib/IR/Intrinsics.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -720,14 +720,14 @@ Intrinsic::ID Intrinsic::lookupIntrinsicID(StringRef Name) {
720720
#include "llvm/IR/IntrinsicImpl.inc"
721721
#undef GET_INTRINSIC_ATTRIBUTES
722722

723-
Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
724-
ArrayRef<Type *> Tys) {
725-
// There can never be multiple globals with the same name of different types,
726-
// because intrinsics must be a specific type.
727-
auto *FT = getType(M->getContext(), id, Tys);
723+
static Function *getOrInsertIntrinsicDeclarationImpl(Module *M,
724+
Intrinsic::ID id,
725+
ArrayRef<Type *> Tys,
726+
FunctionType *FT) {
728727
Function *F = cast<Function>(
729-
M->getOrInsertFunction(
730-
Tys.empty() ? getName(id) : getName(id, Tys, M, FT), FT)
728+
M->getOrInsertFunction(Tys.empty() ? Intrinsic::getName(id)
729+
: Intrinsic::getName(id, Tys, M, FT),
730+
FT)
731731
.getCallee());
732732
if (F->getFunctionType() == FT)
733733
return F;
@@ -739,11 +739,43 @@ Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
739739
// invalid declaration will get upgraded later.
740740
F->setName(F->getName() + ".invalid");
741741
return cast<Function>(
742-
M->getOrInsertFunction(
743-
Tys.empty() ? getName(id) : getName(id, Tys, M, FT), FT)
742+
M->getOrInsertFunction(Tys.empty() ? Intrinsic::getName(id)
743+
: Intrinsic::getName(id, Tys, M, FT),
744+
FT)
744745
.getCallee());
745746
}
746747

748+
Function *Intrinsic::getOrInsertDeclaration(Module *M, ID id,
749+
ArrayRef<Type *> Tys) {
750+
// There can never be multiple globals with the same name of different types,
751+
// because intrinsics must be a specific type.
752+
FunctionType *FT = getType(M->getContext(), id, Tys);
753+
return getOrInsertIntrinsicDeclarationImpl(M, id, Tys, FT);
754+
}
755+
756+
Function *Intrinsic::getOrInsertDeclaration(Module *M, ID IID, Type *RetTy,
757+
ArrayRef<Type *> ArgTys) {
758+
// If the intrinsic is not overloaded, use the non-overloaded version.
759+
if (!Intrinsic::isOverloaded(IID))
760+
return getOrInsertDeclaration(M, IID);
761+
762+
// Get the intrinsic signature metadata.
763+
SmallVector<Intrinsic::IITDescriptor, 8> Table;
764+
getIntrinsicInfoTableEntries(IID, Table);
765+
ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
766+
767+
FunctionType *FTy = FunctionType::get(RetTy, ArgTys, /*isVarArg=*/false);
768+
769+
// Automatically determine the overloaded types.
770+
SmallVector<Type *, 4> OverloadTys;
771+
[[maybe_unused]] Intrinsic::MatchIntrinsicTypesResult Res =
772+
matchIntrinsicSignature(FTy, TableRef, OverloadTys);
773+
assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
774+
"intrinsic signature mismatch");
775+
776+
return getOrInsertIntrinsicDeclarationImpl(M, IID, OverloadTys, FTy);
777+
}
778+
747779
Function *Intrinsic::getDeclarationIfExists(const Module *M, ID id) {
748780
return M->getFunction(getName(id));
749781
}

mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3176,12 +3176,7 @@ def NVVM_PrefetchOp : NVVM_Op<"prefetch",
31763176
let llvmBuilder = [{
31773177
auto [id, args] = NVVM::PrefetchOp::getIntrinsicIDAndArgs(op,
31783178
moduleTranslation, builder);
3179-
3180-
if(op.getTensormap())
3181-
// Overloaded intrinsic
3182-
createIntrinsicCall(builder, id, args, {args[0]->getType()});
3183-
else
3184-
createIntrinsicCall(builder, id, args);
3179+
createIntrinsicCall(builder, id, builder.getVoidTy(), args);
31853180
}];
31863181
}
31873182

mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,15 @@ llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &builder,
512512
ArrayRef<llvm::Value *> args = {},
513513
ArrayRef<llvm::Type *> tys = {});
514514

515+
/// Creates a call to an LLVM IR intrinsic function with the given return type
516+
/// and arguments. If the intrinsic is overloaded, the function signature will
517+
/// be automatically resolved based on the provided return type and argument
518+
/// types.
519+
llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &builder,
520+
llvm::Intrinsic::ID intrinsic,
521+
llvm::Type *retTy,
522+
ArrayRef<llvm::Value *> args);
523+
515524
/// Creates a call to a LLVM IR intrinsic defined by LLVM_IntrOpBase. This
516525
/// resolves the overloads, and maps mixed MLIR value and attribute arguments to
517526
/// LLVM values.

mlir/lib/Target/LLVMIR/ModuleTranslation.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,21 @@ llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
898898
return builder.CreateCall(fn, args);
899899
}
900900

901+
llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
902+
llvm::IRBuilderBase &builder, llvm::Intrinsic::ID intrinsic,
903+
llvm::Type *retTy, ArrayRef<llvm::Value *> args) {
904+
llvm::Module *module = builder.GetInsertBlock()->getModule();
905+
906+
SmallVector<llvm::Type *> argTys;
907+
argTys.reserve(args.size());
908+
for (llvm::Value *arg : args)
909+
argTys.push_back(arg->getType());
910+
911+
llvm::Function *fn =
912+
llvm::Intrinsic::getOrInsertDeclaration(module, intrinsic, retTy, argTys);
913+
return builder.CreateCall(fn, args);
914+
}
915+
901916
llvm::CallInst *mlir::LLVM::detail::createIntrinsicCall(
902917
llvm::IRBuilderBase &builder, ModuleTranslation &moduleTranslation,
903918
Operation *intrOp, llvm::Intrinsic::ID intrinsic, unsigned numResults,

0 commit comments

Comments
 (0)