[mlir][LLVMIR] Add IFuncOp to LLVM dialect (#147697)
Add IFunc to LLVM dialect and add support for lifting/exporting LLVMIR IFunc.
This commit is contained in:
parent
b84f72a7f5
commit
46c059f925
@ -223,6 +223,9 @@ Value createGlobalString(Location loc, OpBuilder &builder, StringRef name,
|
||||
/// function confirms that the Operation has the desired properties.
|
||||
bool satisfiesLLVMModule(Operation *op);
|
||||
|
||||
/// Lookup parent Module satisfying LLVM conditions on the Module Operation.
|
||||
Operation *parentLLVMModule(Operation *op);
|
||||
|
||||
/// Convert an array of integer attributes to a vector of integers that can be
|
||||
/// used as indices in LLVM operations.
|
||||
template <typename IntT = int64_t>
|
||||
|
@ -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,67 @@ 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,
|
||||
Linkage:$linkage,
|
||||
UnitAttr:$dso_local,
|
||||
DefaultValuedAttr<ConfinedAttr<I32Attr, [IntNonNegative]>, "0">:$address_space,
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
```mlir
|
||||
// IFuncs resolve a symbol at runtime using a resovler function.
|
||||
llvm.mlir.ifunc external @foo: !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
|
||||
llvm.func @foo_1(i64) -> f32
|
||||
llvm.func @foo_2(i64) -> f32
|
||||
|
||||
llvm.func @resolve_foo() -> !llvm.ptr attributes {
|
||||
%0 = llvm.mlir.addressof @foo_2 : !llvm.ptr
|
||||
%1 = llvm.mlir.addressof @foo_1 : !llvm.ptr
|
||||
|
||||
// ... Logic selecting from foo_{1, 2}
|
||||
|
||||
// Return function pointer to the selected function
|
||||
llvm.return %7 : !llvm.ptr
|
||||
}
|
||||
|
||||
llvm.func @use_foo() {
|
||||
// IFuncs are called as regular functions
|
||||
%res = llvm.call @foo(%value) : i64 -> f32
|
||||
}
|
||||
```
|
||||
}];
|
||||
|
||||
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);
|
||||
|
@ -71,6 +71,9 @@ public:
|
||||
/// 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 @@ private:
|
||||
/// 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 dialect ifunc operation.
|
||||
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.
|
||||
|
@ -260,6 +260,12 @@ public:
|
||||
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();
|
||||
@ -345,6 +351,7 @@ private:
|
||||
bool recordInsertions = false);
|
||||
LogicalResult convertFunctionSignatures();
|
||||
LogicalResult convertFunctions();
|
||||
LogicalResult convertIFuncs();
|
||||
LogicalResult convertComdats();
|
||||
|
||||
LogicalResult convertUnresolvedBlockAddress();
|
||||
@ -406,6 +413,10 @@ private:
|
||||
/// aliases.
|
||||
DenseMap<Operation *, llvm::GlobalValue *> aliasesMapping;
|
||||
|
||||
/// Mappings between llvm.mlir.ifunc definitions and corresponding global
|
||||
/// ifuncs.
|
||||
DenseMap<Operation *, llvm::GlobalValue *> ifuncMapping;
|
||||
|
||||
/// A stateful object used to translate types.
|
||||
TypeToLLVMIRTranslator typeTranslator;
|
||||
|
||||
|
@ -130,6 +130,17 @@ static RetTy parseOptionalLLVMKeyword(OpAsmParser &parser,
|
||||
return static_cast<RetTy>(index);
|
||||
}
|
||||
|
||||
static void printLLVMLinkage(OpAsmPrinter &p, Operation *, LinkageAttr val) {
|
||||
p << stringifyLinkage(val.getLinkage());
|
||||
}
|
||||
|
||||
static ParseResult parseLLVMLinkage(OpAsmParser &p, LinkageAttr &val) {
|
||||
val = LinkageAttr::get(
|
||||
p.getContext(),
|
||||
parseOptionalLLVMKeyword<LLVM::Linkage>(p, LLVM::Linkage::External));
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Operand bundle helpers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1166,14 +1177,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);
|
||||
@ -2029,14 +2043,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()));
|
||||
@ -2052,6 +2058,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 =
|
||||
@ -2060,10 +2071,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()) ||
|
||||
@ -2673,6 +2685,69 @@ 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,
|
||||
linkage, /*dso_local=*/false, /*address_space=*/0,
|
||||
UnnamedAddr::None, visibility);
|
||||
}
|
||||
|
||||
LogicalResult IFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
||||
Operation *symbol =
|
||||
symbolTable.lookupSymbolIn(parentLLVMModule(*this), getResolverAttr());
|
||||
// This matches LLVM IR verification logic, see llvm/lib/IR/Verifier.cpp
|
||||
auto resolver = dyn_cast<LLVMFuncOp>(symbol);
|
||||
auto alias = dyn_cast<AliasOp>(symbol);
|
||||
while (alias) {
|
||||
Block &initBlock = alias.getInitializerBlock();
|
||||
auto returnOp = cast<ReturnOp>(initBlock.getTerminator());
|
||||
auto addrOp = dyn_cast<AddressOfOp>(returnOp.getArg().getDefiningOp());
|
||||
// FIXME: This is a best effort solution. The AliasOp body might be more
|
||||
// complex and in that case we bail out with success. To completely match
|
||||
// the LLVM IR logic it would be necessary to implement proper alias and
|
||||
// cast stripping.
|
||||
if (!addrOp)
|
||||
return success();
|
||||
resolver = addrOp.getFunction(symbolTable);
|
||||
alias = addrOp.getAlias(symbolTable);
|
||||
}
|
||||
if (!resolver)
|
||||
return emitOpError("must have a function resolver");
|
||||
Linkage linkage = resolver.getLinkage();
|
||||
if (resolver.isExternal() || linkage == Linkage::AvailableExternally)
|
||||
return emitOpError("resolver must be a definition");
|
||||
if (!isa<LLVMPointerType>(resolver.getFunctionType().getReturnType()))
|
||||
return emitOpError("resolver must return a pointer");
|
||||
auto resolverPtr = dyn_cast<LLVMPointerType>(getResolverType());
|
||||
if (!resolverPtr || resolverPtr.getAddressSpace() != getAddressSpace())
|
||||
return emitOpError("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
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -4320,3 +4395,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;
|
||||
}
|
||||
|
@ -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 *moduleOp = parentLLVMModule(&opInst);
|
||||
Operation *ifuncOp =
|
||||
moduleTranslation.symbolTable().lookupSymbolIn(moduleOp, 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();
|
||||
|
@ -1031,6 +1031,16 @@ LogicalResult ModuleImport::convertAliases() {
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleImport::convertIFuncs() {
|
||||
for (llvm::GlobalIFunc &ifunc : llvmModule->ifuncs()) {
|
||||
if (failed(convertIFunc(&ifunc))) {
|
||||
return emitError(UnknownLoc::get(context))
|
||||
<< "unhandled global ifunc: " << diag(ifunc);
|
||||
}
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleImport::convertDataLayout() {
|
||||
Location loc = mlirModule.getLoc();
|
||||
DataLayoutImporter dataLayoutImporter(context, llvmModule->getDataLayout());
|
||||
@ -1369,6 +1379,21 @@ LogicalResult ModuleImport::convertAlias(llvm::GlobalAlias *alias) {
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleImport::convertIFunc(llvm::GlobalIFunc *ifunc) {
|
||||
OpBuilder::InsertionGuard guard = setGlobalInsertionPoint();
|
||||
|
||||
Type type = convertType(ifunc->getValueType());
|
||||
llvm::Constant *resolver = ifunc->getResolver();
|
||||
Type resolverType = convertType(resolver->getType());
|
||||
builder.create<IFuncOp>(mlirModule.getLoc(), ifunc->getName(), type,
|
||||
resolver->getName(), resolverType,
|
||||
convertLinkageFromLLVM(ifunc->getLinkage()),
|
||||
ifunc->isDSOLocal(), ifunc->getAddressSpace(),
|
||||
convertUnnamedAddrFromLLVM(ifunc->getUnnamedAddr()),
|
||||
convertVisibilityFromLLVM(ifunc->getVisibility()));
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) {
|
||||
// Insert the global after the last one or at the start of the module.
|
||||
OpBuilder::InsertionGuard guard = setGlobalInsertionPoint();
|
||||
@ -1973,8 +1998,9 @@ ModuleImport::convertCallOperands(llvm::CallBase *callInst,
|
||||
// treated as indirect calls to constant operands that need to be converted.
|
||||
// Skip the callee operand if it's inline assembly, as it's handled separately
|
||||
// in InlineAsmOp.
|
||||
if (!isa<llvm::Function>(callInst->getCalledOperand()) && !isInlineAsm) {
|
||||
FailureOr<Value> called = convertValue(callInst->getCalledOperand());
|
||||
llvm::Value *calleeOperand = callInst->getCalledOperand();
|
||||
if (!isa<llvm::Function, llvm::GlobalIFunc>(calleeOperand) && !isInlineAsm) {
|
||||
FailureOr<Value> called = convertValue(calleeOperand);
|
||||
if (failed(called))
|
||||
return failure();
|
||||
operands.push_back(*called);
|
||||
@ -2035,12 +2061,20 @@ ModuleImport::convertFunctionType(llvm::CallBase *callInst,
|
||||
if (failed(callType))
|
||||
return failure();
|
||||
auto *callee = dyn_cast<llvm::Function>(calledOperand);
|
||||
|
||||
llvm::FunctionType *origCalleeType = nullptr;
|
||||
if (callee) {
|
||||
origCalleeType = callee->getFunctionType();
|
||||
} else if (auto *ifunc = dyn_cast<llvm::GlobalIFunc>(calledOperand)) {
|
||||
origCalleeType = cast<llvm::FunctionType>(ifunc->getValueType());
|
||||
}
|
||||
|
||||
// For indirect calls, return the type of the call itself.
|
||||
if (!callee)
|
||||
if (!origCalleeType)
|
||||
return callType;
|
||||
|
||||
FailureOr<LLVMFunctionType> calleeType =
|
||||
castOrFailure(convertType(callee->getFunctionType()));
|
||||
castOrFailure(convertType(origCalleeType));
|
||||
if (failed(calleeType))
|
||||
return failure();
|
||||
|
||||
@ -2059,8 +2093,8 @@ ModuleImport::convertFunctionType(llvm::CallBase *callInst,
|
||||
|
||||
FlatSymbolRefAttr ModuleImport::convertCalleeName(llvm::CallBase *callInst) {
|
||||
llvm::Value *calledOperand = callInst->getCalledOperand();
|
||||
if (auto *callee = dyn_cast<llvm::Function>(calledOperand))
|
||||
return SymbolRefAttr::get(context, callee->getName());
|
||||
if (isa<llvm::Function, llvm::GlobalIFunc>(calledOperand))
|
||||
return SymbolRefAttr::get(context, calledOperand->getName());
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -3162,6 +3196,8 @@ OwningOpRef<ModuleOp> mlir::translateLLVMIRToModule(
|
||||
return {};
|
||||
if (failed(moduleImport.convertAliases()))
|
||||
return {};
|
||||
if (failed(moduleImport.convertIFuncs()))
|
||||
return {};
|
||||
moduleImport.convertTargetTriple();
|
||||
return module;
|
||||
}
|
||||
|
@ -791,6 +791,8 @@ void ModuleTranslation::forgetMapping(Region ®ion) {
|
||||
globalsMapping.erase(&op);
|
||||
if (isa<LLVM::AliasOp>(op))
|
||||
aliasesMapping.erase(&op);
|
||||
if (isa<LLVM::IFuncOp>(op))
|
||||
ifuncMapping.erase(&op);
|
||||
if (isa<LLVM::CallOp>(op))
|
||||
callMapping.erase(&op);
|
||||
llvm::append_range(
|
||||
@ -1868,6 +1870,33 @@ LogicalResult ModuleTranslation::convertFunctions() {
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleTranslation::convertIFuncs() {
|
||||
for (auto op : getModuleBody(mlirModule).getOps<IFuncOp>()) {
|
||||
llvm::Type *type = convertType(op.getIFuncType());
|
||||
llvm::GlobalValue::LinkageTypes linkage =
|
||||
convertLinkageToLLVM(op.getLinkage());
|
||||
llvm::Constant *resolver;
|
||||
if (auto *resolverFn = lookupFunction(op.getResolver())) {
|
||||
resolver = cast<llvm::Constant>(resolverFn);
|
||||
} else {
|
||||
Operation *aliasOp = symbolTable().lookupSymbolIn(parentLLVMModule(op),
|
||||
op.getResolverAttr());
|
||||
resolver = cast<llvm::Constant>(lookupAlias(aliasOp));
|
||||
}
|
||||
|
||||
auto *ifunc =
|
||||
llvm::GlobalIFunc::create(type, op.getAddressSpace(), linkage,
|
||||
op.getSymName(), resolver, llvmModule.get());
|
||||
addRuntimePreemptionSpecifier(op.getDsoLocal(), ifunc);
|
||||
ifunc->setUnnamedAddr(convertUnnamedAddrToLLVM(op.getUnnamedAddr()));
|
||||
ifunc->setVisibility(convertVisibilityToLLVM(op.getVisibility_()));
|
||||
|
||||
ifuncMapping.try_emplace(op, ifunc);
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleTranslation::convertComdats() {
|
||||
for (auto comdatOp : getModuleBody(mlirModule).getOps<ComdatOp>()) {
|
||||
for (auto selectorOp : comdatOp.getOps<ComdatSelectorOp>()) {
|
||||
@ -2284,6 +2313,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
|
||||
return nullptr;
|
||||
if (failed(translator.convertGlobalsAndAliases()))
|
||||
return nullptr;
|
||||
if (failed(translator.convertIFuncs()))
|
||||
return nullptr;
|
||||
if (failed(translator.createTBAAMetadata()))
|
||||
return nullptr;
|
||||
if (failed(translator.createIdentMetadata()))
|
||||
@ -2296,7 +2327,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
|
||||
// Convert other top-level operations if possible.
|
||||
for (Operation &o : getModuleBody(module).getOperations()) {
|
||||
if (!isa<LLVM::LLVMFuncOp, LLVM::AliasOp, LLVM::GlobalOp,
|
||||
LLVM::GlobalCtorsOp, LLVM::GlobalDtorsOp, LLVM::ComdatOp>(&o) &&
|
||||
LLVM::GlobalCtorsOp, LLVM::GlobalDtorsOp, LLVM::ComdatOp,
|
||||
LLVM::IFuncOp>(&o) &&
|
||||
!o.hasTrait<OpTrait::IsTerminator>() &&
|
||||
failed(translator.convertOperation(o, llvmBuilder))) {
|
||||
return nullptr;
|
||||
|
40
mlir/test/Dialect/LLVMIR/ifunc.mlir
Normal file
40
mlir/test/Dialect/LLVMIR/ifunc.mlir
Normal file
@ -0,0 +1,40 @@
|
||||
// RUN: mlir-opt %s -split-input-file --verify-roundtrip | FileCheck %s
|
||||
|
||||
// CHECK: llvm.mlir.ifunc external @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
llvm.mlir.ifunc @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: llvm.mlir.ifunc linkonce_odr hidden @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.mlir.ifunc linkonce_odr hidden @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: llvm.mlir.ifunc private @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.mlir.ifunc private @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: llvm.mlir.ifunc weak @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
llvm.mlir.ifunc weak @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
@ -1931,3 +1931,30 @@ llvm.func @invalid_xevm_matrix_3(%a: !llvm.ptr<1>, %base_width_a: i32, %base_hei
|
||||
llvm.return %loaded_a : vector<8xi16>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.func external @resolve_foo() -> !llvm.ptr attributes {dso_local}
|
||||
// expected-error@+1 {{'llvm.mlir.ifunc' op resolver must be a definition}}
|
||||
llvm.mlir.ifunc external @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @resolve_foo {dso_local}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.mlir.global external @resolve_foo() : !llvm.ptr
|
||||
// expected-error@+1 {{'llvm.mlir.ifunc' op must have a function resolver}}
|
||||
llvm.mlir.ifunc external @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @resolve_foo {dso_local}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.func external @resolve_foo() -> !llvm.ptr
|
||||
// expected-error@+1 {{'llvm.mlir.ifunc' op 'common' linkage not supported in ifuncs}}
|
||||
llvm.mlir.ifunc common @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @resolve_foo {dso_local}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.mlir.global external @resolve_foo() : !llvm.ptr
|
||||
llvm.mlir.alias external @alias_resolver : !llvm.ptr {
|
||||
%0 = llvm.mlir.addressof @resolve_foo : !llvm.ptr
|
||||
llvm.return %0 : !llvm.ptr
|
||||
}
|
||||
// expected-error@+1 {{'llvm.mlir.ifunc' op must have a function resolver}}
|
||||
llvm.mlir.ifunc external @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @alias_resolver {dso_local}
|
||||
|
63
mlir/test/Target/LLVMIR/Import/ifunc.ll
Normal file
63
mlir/test/Target/LLVMIR/Import/ifunc.ll
Normal file
@ -0,0 +1,63 @@
|
||||
; RUN: mlir-translate --import-llvm %s --split-input-file | FileCheck %s
|
||||
|
||||
; CHECK: llvm.mlir.ifunc external @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @resolve_foo {dso_local}
|
||||
@foo = dso_local ifunc void (ptr, i32), ptr @resolve_foo
|
||||
|
||||
define dso_local void @call_foo(ptr noundef %0, i32 noundef %1) {
|
||||
; CHECK: llvm.call @foo
|
||||
call void @foo(ptr noundef %0, i32 noundef %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
define dso_local ptr @foo_fptr() {
|
||||
; CHECK: [[FPTR:%[0-9]+]] = llvm.mlir.addressof @foo
|
||||
; CHECK: llvm.return [[FPTR]]
|
||||
ret ptr @foo
|
||||
}
|
||||
|
||||
define internal ptr @resolve_foo() {
|
||||
ret ptr @foo_1
|
||||
}
|
||||
|
||||
declare void @foo_1(ptr noundef, i32 noundef)
|
||||
|
||||
; // -----
|
||||
|
||||
define ptr @resolver() {
|
||||
ret ptr inttoptr (i64 333 to ptr)
|
||||
}
|
||||
|
||||
@resolver_alias = alias ptr (), ptr @resolver
|
||||
@resolver_alias_alias = alias ptr (), ptr @resolver_alias
|
||||
|
||||
; CHECK-DAG: llvm.mlir.ifunc external @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver_alias
|
||||
@ifunc = ifunc float (i64), ptr @resolver_alias
|
||||
; CHECK-DAG: llvm.mlir.ifunc external @ifunc2 : !llvm.func<f32 (i64)>, !llvm.ptr @resolver_alias_alias
|
||||
@ifunc2 = ifunc float (i64), ptr @resolver_alias_alias
|
||||
|
||||
; // -----
|
||||
|
||||
define ptr @resolver() {
|
||||
ret ptr inttoptr (i64 333 to ptr)
|
||||
}
|
||||
|
||||
; CHECK: llvm.mlir.ifunc linkonce_odr hidden @ifunc
|
||||
@ifunc = linkonce_odr hidden ifunc float (i64), ptr @resolver
|
||||
|
||||
; // -----
|
||||
|
||||
define ptr @resolver() {
|
||||
ret ptr inttoptr (i64 333 to ptr)
|
||||
}
|
||||
|
||||
; CHECK: llvm.mlir.ifunc private @ifunc {{.*}} {dso_local}
|
||||
@ifunc = private dso_local ifunc float (i64), ptr @resolver
|
||||
|
||||
; // -----
|
||||
|
||||
define ptr @resolver() {
|
||||
ret ptr inttoptr (i64 333 to ptr)
|
||||
}
|
||||
|
||||
; CHECK: llvm.mlir.ifunc weak @ifunc
|
||||
@ifunc = weak ifunc float (i64), ptr @resolver
|
70
mlir/test/Target/LLVMIR/ifunc.mlir
Normal file
70
mlir/test/Target/LLVMIR/ifunc.mlir
Normal file
@ -0,0 +1,70 @@
|
||||
// RUN: mlir-translate -mlir-to-llvmir %s --split-input-file | FileCheck %s
|
||||
|
||||
// CHECK: @foo = dso_local ifunc void (ptr, i32), ptr @resolve_foo
|
||||
llvm.mlir.ifunc external @foo : !llvm.func<void (ptr, i32)>, !llvm.ptr @resolve_foo {dso_local}
|
||||
llvm.func @call_foo(%arg0: !llvm.ptr {llvm.noundef}, %arg1: i32 {llvm.noundef}) attributes {dso_local} {
|
||||
// CHECK: call void @foo
|
||||
llvm.call @foo(%arg0, %arg1) : (!llvm.ptr {llvm.noundef}, i32 {llvm.noundef}) -> ()
|
||||
llvm.return
|
||||
}
|
||||
llvm.func @foo_fptr() -> !llvm.ptr attributes {dso_local} {
|
||||
%1 = llvm.mlir.addressof @foo : !llvm.ptr
|
||||
// CHECK: ret ptr @foo
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
llvm.func internal @resolve_foo() -> !llvm.ptr attributes {dso_local} {
|
||||
%0 = llvm.mlir.addressof @foo_1 : !llvm.ptr
|
||||
llvm.return %0 : !llvm.ptr
|
||||
}
|
||||
llvm.func @foo_1(!llvm.ptr {llvm.noundef}, i32 {llvm.noundef})
|
||||
|
||||
// -----
|
||||
|
||||
llvm.mlir.alias external @resolver_alias : !llvm.func<ptr ()> {
|
||||
%0 = llvm.mlir.addressof @resolver : !llvm.ptr
|
||||
llvm.return %0 : !llvm.ptr
|
||||
}
|
||||
llvm.mlir.alias external @resolver_alias_alias : !llvm.func<ptr ()> {
|
||||
%0 = llvm.mlir.addressof @resolver_alias : !llvm.ptr
|
||||
llvm.return %0 : !llvm.ptr
|
||||
}
|
||||
|
||||
// CHECK-DAG: @ifunc = ifunc float (i64), ptr @resolver_alias
|
||||
// CHECK-DAG: @ifunc2 = ifunc float (i64), ptr @resolver_alias_alias
|
||||
llvm.mlir.ifunc external @ifunc2 : !llvm.func<f32 (i64)>, !llvm.ptr @resolver_alias_alias
|
||||
llvm.mlir.ifunc external @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver_alias
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: @ifunc = linkonce_odr hidden ifunc
|
||||
llvm.mlir.ifunc linkonce_odr hidden @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: @ifunc = private ifunc
|
||||
llvm.mlir.ifunc private @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver {dso_local}
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK: @ifunc = weak ifunc
|
||||
llvm.mlir.ifunc weak @ifunc : !llvm.func<f32 (i64)>, !llvm.ptr @resolver
|
||||
llvm.func @resolver() -> !llvm.ptr {
|
||||
%0 = llvm.mlir.constant(333 : i64) : i64
|
||||
%1 = llvm.inttoptr %0 : i64 to !llvm.ptr
|
||||
llvm.return %1 : !llvm.ptr
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user