[Mlir-commits] [mlir] [MLIR][LLVMIR] Allow llvm.call and llvm.invoke to use llvm.mlir.alias as callee (PR #189154)
Mehdi Amini
llvmlistbot at llvm.org
Sun Mar 29 04:30:42 PDT 2026
https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/189154
>From 9528bfd69be8418ed2f6f578f67d9dd00bae5fb6 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Fri, 27 Mar 2026 13:29:11 -0700
Subject: [PATCH] [MLIR][LLVMIR] Allow llvm.call and llvm.invoke to use
llvm.mlir.alias as callee
Previously, the verifier for `llvm.call` and `llvm.invoke` would reject
calls where the callee was an `llvm.mlir.alias`, reporting that the
symbol does not reference a valid LLVM function or IFunc. Similarly, the
MLIR-to-LLVM-IR translation had no handling for aliases as callees.
This patch extends both the verifier and the translation to accept
`llvm.mlir.alias` as a valid callee for `llvm.call` and `llvm.invoke`,
mirroring the existing support for `llvm.mlir.ifunc`. The function type
for alias calls is derived from the call operands and result types, and
the translation emits a call through the alias global value.
Fixes #147057
Assisted-by: Claude Code
---
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 7 ++-
.../LLVMIR/LLVMToLLVMIRTranslation.cpp | 50 +++++++++++++-----
mlir/test/Dialect/LLVMIR/alias.mlir | 21 ++++++++
mlir/test/Target/LLVMIR/alias.mlir | 52 +++++++++++++++++++
4 files changed, 117 insertions(+), 13 deletions(-)
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index ce51884368b69..88b4857b52957 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -1255,10 +1255,15 @@ LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
fnType = fn.getFunctionType();
} else if (auto ifunc = dyn_cast<IFuncOp>(callee)) {
fnType = ifunc.getIFuncType();
+ } else if (isa<AliasOp>(callee)) {
+ // Aliases can alias functions, so calling through an alias is valid.
+ // The function type is determined by the call's operands and result
+ // types.
+ fnType = getCalleeFunctionType();
} else {
return emitOpError()
<< "'" << calleeName.getValue()
- << "' does not reference a valid LLVM function or IFunc";
+ << "' does not reference a valid LLVM function, IFunc, or alias";
}
}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 8c3680033b2b9..9b376e3f9e1b7 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -410,6 +410,26 @@ static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder,
}
}
+/// Looks up the GlobalValue and FunctionType for a callee symbol that is not a
+/// regular LLVM function (i.e. an alias or ifunc). Returns the lowered
+/// GlobalValue and FunctionType derived from \p calleeFuncType.
+static std::pair<llvm::GlobalValue *, llvm::FunctionType *>
+lookupNonFunctionSymbolCallee(FlatSymbolRefAttr attr, mlir::Type calleeFuncType,
+ Operation &opInst,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ Operation *moduleOp = parentLLVMModule(&opInst);
+ Operation *calleeOp =
+ moduleTranslation.symbolTable().lookupSymbolIn(moduleOp, attr);
+ llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
+ moduleTranslation.convertType(calleeFuncType));
+ llvm::GlobalValue *calleeGV;
+ if (isa<LLVM::AliasOp>(calleeOp))
+ calleeGV = moduleTranslation.lookupAlias(calleeOp);
+ else
+ calleeGV = moduleTranslation.lookupIFunc(calleeOp);
+ return {calleeGV, calleeType};
+}
+
static llvm::DILocalScope *
getLocalScopeFromLoc(llvm::IRBuilderBase &builder, Location loc,
LLVM::ModuleTranslation &moduleTranslation) {
@@ -448,13 +468,9 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
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);
+ auto [calleeGV, calleeType] = lookupNonFunctionSymbolCallee(
+ attr, callOp.getCalleeFunctionType(), opInst, moduleTranslation);
+ call = builder.CreateCall(calleeType, calleeGV, operandsRef, opBundles);
}
} else {
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
@@ -639,11 +655,21 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
ArrayRef<llvm::Value *> operandsRef(operands);
llvm::InvokeInst *result;
if (auto attr = opInst.getAttrOfType<FlatSymbolRefAttr>("callee")) {
- result = builder.CreateInvoke(
- moduleTranslation.lookupFunction(attr.getValue()),
- moduleTranslation.lookupBlock(invOp.getSuccessor(0)),
- moduleTranslation.lookupBlock(invOp.getSuccessor(1)), operandsRef,
- opBundles);
+ if (llvm::Function *function =
+ moduleTranslation.lookupFunction(attr.getValue())) {
+ result = builder.CreateInvoke(
+ function, moduleTranslation.lookupBlock(invOp.getSuccessor(0)),
+ moduleTranslation.lookupBlock(invOp.getSuccessor(1)), operandsRef,
+ opBundles);
+ } else {
+ auto [calleeGV, calleeType] = lookupNonFunctionSymbolCallee(
+ attr, invOp.getCalleeFunctionType(), opInst, moduleTranslation);
+ result = builder.CreateInvoke(
+ calleeType, calleeGV,
+ moduleTranslation.lookupBlock(invOp.getSuccessor(0)),
+ moduleTranslation.lookupBlock(invOp.getSuccessor(1)), operandsRef,
+ opBundles);
+ }
} else {
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
moduleTranslation.convertType(invOp.getCalleeFunctionType()));
diff --git a/mlir/test/Dialect/LLVMIR/alias.mlir b/mlir/test/Dialect/LLVMIR/alias.mlir
index 7ce54f35a5557..5f578a382a668 100644
--- a/mlir/test/Dialect/LLVMIR/alias.mlir
+++ b/mlir/test/Dialect/LLVMIR/alias.mlir
@@ -139,3 +139,24 @@ llvm.mlir.alias private thread_local unnamed_addr @a30 {dso_local} : i32 {
// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr
// CHECK: llvm.return %0 : !llvm.ptr
// CHECK: }
+
+// -----
+
+// Test that llvm.call and llvm.invoke can use an alias as the callee.
+
+llvm.func @aliased_func() {
+ llvm.return
+}
+
+llvm.mlir.alias external @func_alias {addr_space = 0 : i32} : !llvm.ptr {
+ %0 = llvm.mlir.addressof @aliased_func : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+llvm.func @caller() {
+ llvm.call @func_alias() : () -> ()
+ llvm.return
+}
+
+// CHECK-LABEL: llvm.func @caller()
+// CHECK: llvm.call @func_alias() : () -> ()
diff --git a/mlir/test/Target/LLVMIR/alias.mlir b/mlir/test/Target/LLVMIR/alias.mlir
index 56832a4900746..2ba9a2145eb32 100644
--- a/mlir/test/Target/LLVMIR/alias.mlir
+++ b/mlir/test/Target/LLVMIR/alias.mlir
@@ -90,3 +90,55 @@ llvm.mlir.global internal constant @g3() {dso_local} : !llvm.ptr {
// CHECK: @g3 = internal constant ptr @a2
// CHECK: @a1 = private alias i32, ptr @g1
// CHECK: @a2 = private alias ptr, ptr @a1
+
+// -----
+
+// Test that llvm.call and llvm.invoke can use an alias as the callee, and that
+// the translation emits a call through the alias.
+
+llvm.func @aliased_func() {
+ llvm.return
+}
+
+llvm.mlir.alias external @func_alias {addr_space = 0 : i32} : !llvm.ptr {
+ %0 = llvm.mlir.addressof @aliased_func : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+llvm.func @caller() {
+ llvm.call @func_alias() : () -> ()
+ llvm.return
+}
+
+// CHECK: @func_alias = alias ptr, ptr @aliased_func
+// CHECK-LABEL: define void @caller()
+// CHECK: call void @func_alias()
+
+// -----
+
+// Test that llvm.invoke can use an alias as the callee, and that the
+// translation emits an invoke through the alias.
+
+llvm.func @__gxx_personality_v0(...) -> i32
+
+llvm.func @aliased_func2() {
+ llvm.return
+}
+
+llvm.mlir.alias external @func_alias2 {addr_space = 0 : i32} : !llvm.ptr {
+ %0 = llvm.mlir.addressof @aliased_func2 : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+llvm.func @invoke_caller() attributes {personality = @__gxx_personality_v0} {
+ llvm.invoke @func_alias2() to ^bb1 unwind ^bb2 : () -> ()
+^bb1:
+ llvm.return
+^bb2:
+ %0 = llvm.landingpad cleanup : !llvm.struct<(ptr, i32)>
+ llvm.resume %0 : !llvm.struct<(ptr, i32)>
+}
+
+// CHECK: @func_alias2 = alias ptr, ptr @aliased_func2
+// CHECK-LABEL: define void @invoke_caller()
+// CHECK: invoke void @func_alias2()
More information about the Mlir-commits
mailing list