-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[mlir][LLVMIR] Add IFuncOp to LLVM dialect #147697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1285,6 +1285,10 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof", | |
/// Return the llvm.mlir.alias operation that defined the value referenced | ||
/// here. | ||
AliasOp getAlias(SymbolTableCollection &symbolTable); | ||
|
||
/// Return the llvm.mlir.ifunc operation that defined the value referenced | ||
/// here. | ||
IFuncOp getIFunc(SymbolTableCollection &symbolTable); | ||
}]; | ||
|
||
let assemblyFormat = "$global_name attr-dict `:` qualified(type($res))"; | ||
|
@@ -1601,6 +1605,42 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias", | |
let hasRegionVerifier = 1; | ||
} | ||
|
||
def LLVM_IFuncOp : LLVM_Op<"mlir.ifunc", | ||
[IsolatedFromAbove, Symbol, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { | ||
let arguments = (ins | ||
SymbolNameAttr:$sym_name, | ||
TypeAttr:$i_func_type, | ||
FlatSymbolRefAttr:$resolver, | ||
TypeAttr:$resolver_type, | ||
UnitAttr:$dso_local, | ||
DefaultValuedAttr<ConfinedAttr<I32Attr, [IntNonNegative]>, "0">:$address_space, | ||
DefaultValuedAttr<Linkage, "mlir::LLVM::Linkage::External">:$linkage, | ||
DefaultValuedAttr<UnnamedAddr, "mlir::LLVM::UnnamedAddr::None">:$unnamed_addr, | ||
DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_ | ||
); | ||
let summary = "LLVM dialect ifunc"; | ||
let description = [{ | ||
`llvm.mlir.ifunc` is a top level operation that defines a global ifunc. | ||
It defines a new symbol and takes a symbol refering to a resolver function. | ||
IFuncs can be called as regular functions. The function type is the same | ||
as the IFuncType. The symbol is resolved at runtime by calling a resolver | ||
function. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add an IR example similar to the one alias op has in its description? This can be quite nice to get a better intuition what the op looks like together with the text? |
||
}]; | ||
|
||
let builders = [ | ||
OpBuilder<(ins "StringRef":$name, "Type":$i_func_type, | ||
"StringRef":$resolver, "Type":$resolver_type, | ||
"Linkage":$linkage, "LLVM::Visibility":$visibility)> | ||
]; | ||
|
||
let assemblyFormat = [{ | ||
(custom<LLVMLinkage>($linkage)^)? ($visibility_^)? ($unnamed_addr^)? | ||
$sym_name `:` $i_func_type `,` $resolver_type $resolver attr-dict | ||
}]; | ||
let hasVerifier = 1; | ||
} | ||
|
||
|
||
def LLVM_DSOLocalEquivalentOp : LLVM_Op<"dso_local_equivalent", | ||
[Pure, ConstantLike, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { | ||
let arguments = (ins FlatSymbolRefAttr:$function_name); | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -71,6 +71,9 @@ class ModuleImport { | |||||
/// Converts all aliases of the LLVM module to MLIR variables. | ||||||
LogicalResult convertAliases(); | ||||||
|
||||||
/// Converts all ifuncs of the LLVM module to MLIR variables. | ||||||
LogicalResult convertIFuncs(); | ||||||
|
||||||
/// Converts the data layout of the LLVM module to an MLIR data layout | ||||||
/// specification. | ||||||
LogicalResult convertDataLayout(); | ||||||
|
@@ -320,6 +323,8 @@ class ModuleImport { | |||||
/// Converts an LLVM global alias variable into an MLIR LLVM dialect alias | ||||||
/// operation if a conversion exists. Otherwise, returns failure. | ||||||
LogicalResult convertAlias(llvm::GlobalAlias *alias); | ||||||
// Converts an LLVM global ifunc into an MLIR LLVM diaeclt ifunc operation | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
LogicalResult convertIFunc(llvm::GlobalIFunc *ifunc); | ||||||
/// Returns personality of `func` as a FlatSymbolRefAttr. | ||||||
FlatSymbolRefAttr getPersonalityAsAttr(llvm::Function *func); | ||||||
/// Imports `bb` into `block`, which must be initially empty. | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -223,6 +223,12 @@ class ModuleTranslation { | |||||||
return aliasesMapping.lookup(op); | ||||||||
} | ||||||||
|
||||||||
/// Finds an LLVM IR global value that corresponds to the given MLIR operation | ||||||||
/// defining an IFunc. | ||||||||
llvm::GlobalValue *lookupIFunc(Operation *op) { | ||||||||
return ifuncMapping.lookup(op); | ||||||||
} | ||||||||
|
||||||||
/// Returns the OpenMP IR builder associated with the LLVM IR module being | ||||||||
/// constructed. | ||||||||
llvm::OpenMPIRBuilder *getOpenMPBuilder(); | ||||||||
|
@@ -308,6 +314,7 @@ class ModuleTranslation { | |||||||
bool recordInsertions = false); | ||||||||
LogicalResult convertFunctionSignatures(); | ||||||||
LogicalResult convertFunctions(); | ||||||||
LogicalResult convertIFuncs(); | ||||||||
LogicalResult convertComdats(); | ||||||||
|
||||||||
LogicalResult convertUnresolvedBlockAddress(); | ||||||||
|
@@ -369,6 +376,8 @@ class ModuleTranslation { | |||||||
/// aliases. | ||||||||
DenseMap<Operation *, llvm::GlobalValue *> aliasesMapping; | ||||||||
|
||||||||
DenseMap<Operation *, llvm::GlobalValue *> ifuncMapping; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: just for symmetry :) |
||||||||
|
||||||||
/// A stateful object used to translate types. | ||||||||
TypeToLLVMIRTranslator typeTranslator; | ||||||||
|
||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -139,6 +139,17 @@ static RetTy parseOptionalLLVMKeyword(OpAsmParser &parser, | |||||||||||
return static_cast<RetTy>(index); | ||||||||||||
} | ||||||||||||
|
||||||||||||
static void printLLVMLinkage(OpAsmPrinter &p, Operation *, LinkageAttr val) { | ||||||||||||
p << stringifyLinkage(val.getLinkage()); | ||||||||||||
} | ||||||||||||
|
||||||||||||
static OptionalParseResult parseLLVMLinkage(OpAsmParser &p, LinkageAttr &val) { | ||||||||||||
val = LinkageAttr::get( | ||||||||||||
p.getContext(), | ||||||||||||
parseOptionalLLVMKeyword<LLVM::Linkage>(p, LLVM::Linkage::External)); | ||||||||||||
return success(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
// Operand bundle helpers. | ||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
|
@@ -1175,14 +1186,17 @@ LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) { | |||||||||||
return emitOpError() | ||||||||||||
<< "'" << calleeName.getValue() | ||||||||||||
<< "' does not reference a symbol in the current scope"; | ||||||||||||
auto fn = dyn_cast<LLVMFuncOp>(callee); | ||||||||||||
if (!fn) | ||||||||||||
return emitOpError() << "'" << calleeName.getValue() | ||||||||||||
<< "' does not reference a valid LLVM function"; | ||||||||||||
|
||||||||||||
if (failed(verifyCallOpDebugInfo(*this, fn))) | ||||||||||||
return failure(); | ||||||||||||
fnType = fn.getFunctionType(); | ||||||||||||
if (auto fn = dyn_cast<LLVMFuncOp>(callee)) { | ||||||||||||
if (failed(verifyCallOpDebugInfo(*this, fn))) | ||||||||||||
return failure(); | ||||||||||||
fnType = fn.getFunctionType(); | ||||||||||||
} else if (auto ifunc = dyn_cast<IFuncOp>(callee)) { | ||||||||||||
fnType = ifunc.getIFuncType(); | ||||||||||||
} else { | ||||||||||||
return emitOpError() | ||||||||||||
<< "'" << calleeName.getValue() | ||||||||||||
<< "' does not reference a valid LLVM function or IFunc"; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
LLVMFunctionType funcType = llvm::dyn_cast<LLVMFunctionType>(fnType); | ||||||||||||
|
@@ -2038,14 +2052,6 @@ LogicalResult ReturnOp::verify() { | |||||||||||
// LLVM::AddressOfOp. | ||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
|
||||||||||||
static Operation *parentLLVMModule(Operation *op) { | ||||||||||||
Operation *module = op->getParentOp(); | ||||||||||||
while (module && !satisfiesLLVMModule(module)) | ||||||||||||
module = module->getParentOp(); | ||||||||||||
assert(module && "unexpected operation outside of a module"); | ||||||||||||
return module; | ||||||||||||
} | ||||||||||||
|
||||||||||||
GlobalOp AddressOfOp::getGlobal(SymbolTableCollection &symbolTable) { | ||||||||||||
return dyn_cast_or_null<GlobalOp>( | ||||||||||||
symbolTable.lookupSymbolIn(parentLLVMModule(*this), getGlobalNameAttr())); | ||||||||||||
|
@@ -2061,6 +2067,11 @@ AliasOp AddressOfOp::getAlias(SymbolTableCollection &symbolTable) { | |||||||||||
symbolTable.lookupSymbolIn(parentLLVMModule(*this), getGlobalNameAttr())); | ||||||||||||
} | ||||||||||||
|
||||||||||||
IFuncOp AddressOfOp::getIFunc(SymbolTableCollection &symbolTable) { | ||||||||||||
return dyn_cast_or_null<IFuncOp>( | ||||||||||||
symbolTable.lookupSymbolIn(parentLLVMModule(*this), getGlobalNameAttr())); | ||||||||||||
} | ||||||||||||
|
||||||||||||
LogicalResult | ||||||||||||
AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) { | ||||||||||||
Operation *symbol = | ||||||||||||
|
@@ -2069,10 +2080,11 @@ AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) { | |||||||||||
auto global = dyn_cast_or_null<GlobalOp>(symbol); | ||||||||||||
auto function = dyn_cast_or_null<LLVMFuncOp>(symbol); | ||||||||||||
auto alias = dyn_cast_or_null<AliasOp>(symbol); | ||||||||||||
auto ifunc = dyn_cast_or_null<IFuncOp>(symbol); | ||||||||||||
|
||||||||||||
if (!global && !function && !alias) | ||||||||||||
if (!global && !function && !alias && !ifunc) | ||||||||||||
return emitOpError("must reference a global defined by 'llvm.mlir.global', " | ||||||||||||
"'llvm.mlir.alias' or 'llvm.func'"); | ||||||||||||
"'llvm.mlir.alias' or 'llvm.func' or 'llvm.mlir.ifunc'"); | ||||||||||||
|
||||||||||||
LLVMPointerType type = getType(); | ||||||||||||
if ((global && global.getAddrSpace() != type.getAddressSpace()) || | ||||||||||||
|
@@ -2682,6 +2694,56 @@ unsigned AliasOp::getAddrSpace() { | |||||||||||
return ptrTy.getAddressSpace(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
// IFuncOp | ||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
|
||||||||||||
void IFuncOp::build(OpBuilder &builder, OperationState &result, StringRef name, | ||||||||||||
Type iFuncType, StringRef resolverName, Type resolverType, | ||||||||||||
Linkage linkage, LLVM::Visibility visibility) { | ||||||||||||
return build(builder, result, name, iFuncType, resolverName, resolverType, | ||||||||||||
/* dso_local */ false, /* addr_space */ 0, linkage, | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: style |
||||||||||||
UnnamedAddr::None, visibility); | ||||||||||||
} | ||||||||||||
LogicalResult IFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) { | ||||||||||||
Comment on lines
+2707
to
+2708
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit missing newline |
||||||||||||
return success(); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes me believe the PR needs some more verifier tests! |
||||||||||||
Operation *symbol = | ||||||||||||
symbolTable.lookupSymbolIn(parentLLVMModule(*this), getResolverAttr()); | ||||||||||||
auto resolver = dyn_cast<LLVMFuncOp>(symbol); | ||||||||||||
if (!resolver) | ||||||||||||
return emitOpError("IFunc must have a Function resolver"); | ||||||||||||
|
||||||||||||
// Copying logic from llvm/lib/IR/Verifier.cpp | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
ultra nit: I suggest to avoid the term copying since it reminds of copy pasta :) |
||||||||||||
Linkage linkage = resolver.getLinkage(); | ||||||||||||
if (resolver.isExternal() || linkage == Linkage::AvailableExternally) | ||||||||||||
return emitOpError("IFunc resolver must be a definition"); | ||||||||||||
if (!isa<LLVMPointerType>(resolver.getFunctionType().getReturnType())) | ||||||||||||
return emitOpError("IFunc resolver must return a pointer"); | ||||||||||||
auto resolverPtr = dyn_cast<LLVMPointerType>(getResolverType()); | ||||||||||||
if (!resolverPtr || resolverPtr.getAddressSpace() != getAddressSpace()) | ||||||||||||
return emitOpError("IFunc resolver has incorrect type"); | ||||||||||||
return success(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
LogicalResult IFuncOp::verify() { | ||||||||||||
switch (getLinkage()) { | ||||||||||||
case Linkage::External: | ||||||||||||
case Linkage::Internal: | ||||||||||||
case Linkage::Private: | ||||||||||||
case Linkage::Weak: | ||||||||||||
case Linkage::WeakODR: | ||||||||||||
case Linkage::Linkonce: | ||||||||||||
case Linkage::LinkonceODR: | ||||||||||||
break; | ||||||||||||
default: | ||||||||||||
return emitOpError() << "'" << stringifyLinkage(getLinkage()) | ||||||||||||
<< "' linkage not supported in ifuncs, available " | ||||||||||||
"options: private, internal, linkonce, weak, " | ||||||||||||
"linkonce_odr, weak_odr, or external linkage"; | ||||||||||||
} | ||||||||||||
return success(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
// ShuffleVectorOp | ||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
|
@@ -4329,3 +4391,11 @@ bool mlir::LLVM::satisfiesLLVMModule(Operation *op) { | |||||||||||
return op->hasTrait<OpTrait::SymbolTable>() && | ||||||||||||
op->hasTrait<OpTrait::IsIsolatedFromAbove>(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
Operation *mlir::LLVM::parentLLVMModule(Operation *op) { | ||||||||||||
Operation *module = op->getParentOp(); | ||||||||||||
while (module && !satisfiesLLVMModule(module)) | ||||||||||||
module = module->getParentOp(); | ||||||||||||
assert(module && "unexpected operation outside of a module"); | ||||||||||||
return module; | ||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -422,9 +422,18 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, | |||||
ArrayRef<llvm::Value *> operandsRef(operands); | ||||||
llvm::CallInst *call; | ||||||
if (auto attr = callOp.getCalleeAttr()) { | ||||||
call = | ||||||
builder.CreateCall(moduleTranslation.lookupFunction(attr.getValue()), | ||||||
operandsRef, opBundles); | ||||||
if (llvm::Function *function = | ||||||
moduleTranslation.lookupFunction(attr.getValue())) { | ||||||
call = builder.CreateCall(function, operandsRef, opBundles); | ||||||
} else { | ||||||
Operation *module = parentLLVMModule(&opInst); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: Let's use module op to avoid the red color. I wonder if it would make sense if LLVM::CallOp would have a getFunction / getIFunc helper similar to the addressOf operation. But I suspect this would likely be the only use here? |
||||||
Operation *ifuncOp = | ||||||
moduleTranslation.symbolTable().lookupSymbolIn(module, attr); | ||||||
llvm::GlobalValue *ifunc = moduleTranslation.lookupIFunc(ifuncOp); | ||||||
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>( | ||||||
moduleTranslation.convertType(callOp.getCalleeFunctionType())); | ||||||
call = builder.CreateCall(calleeType, ifunc, operandsRef, opBundles); | ||||||
} | ||||||
} else { | ||||||
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>( | ||||||
moduleTranslation.convertType(callOp.getCalleeFunctionType())); | ||||||
|
@@ -648,18 +657,21 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, | |||||
LLVM::LLVMFuncOp function = | ||||||
addressOfOp.getFunction(moduleTranslation.symbolTable()); | ||||||
LLVM::AliasOp alias = addressOfOp.getAlias(moduleTranslation.symbolTable()); | ||||||
LLVM::IFuncOp ifunc = addressOfOp.getIFunc(moduleTranslation.symbolTable()); | ||||||
|
||||||
// The verifier should not have allowed this. | ||||||
assert((global || function || alias) && | ||||||
"referencing an undefined global, function, or alias"); | ||||||
assert((global || function || alias || ifunc) && | ||||||
"referencing an undefined global, function, alias, or ifunc"); | ||||||
|
||||||
llvm::Value *llvmValue = nullptr; | ||||||
if (global) | ||||||
llvmValue = moduleTranslation.lookupGlobal(global); | ||||||
else if (alias) | ||||||
llvmValue = moduleTranslation.lookupAlias(alias); | ||||||
else | ||||||
else if (function) | ||||||
llvmValue = moduleTranslation.lookupFunction(function.getName()); | ||||||
else | ||||||
llvmValue = moduleTranslation.lookupIFunc(ifunc); | ||||||
|
||||||
moduleTranslation.mapValue(addressOfOp.getResult(), llvmValue); | ||||||
return success(); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For functions and globals we put the linkage always before dso_local. Should we do this here as well?
Also was there a specific reason to use a DefaultValuedAttr for the linkage. AliasOp and GlobalOp always print the value which seems reasonable for such a relevant attribute (Or did you want to follow FuncOp?).