[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
Sat Mar 28 02:59:56 PDT 2026
https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/189154
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
>From c2f371bb2964ae549324c5e752d9fc6d63476efb 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 | 38 +++++++++++++++----
mlir/test/Dialect/LLVMIR/alias.mlir | 21 ++++++++++
mlir/test/Target/LLVMIR/alias.mlir | 23 +++++++++++
4 files changed, 80 insertions(+), 9 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..7900c3ee4f4a2 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -449,12 +449,16 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
call = builder.CreateCall(function, operandsRef, opBundles);
} else {
Operation *moduleOp = parentLLVMModule(&opInst);
- Operation *ifuncOp =
+ Operation *calleeOp =
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);
+ llvm::GlobalValue *calleeGV;
+ if (isa<LLVM::AliasOp>(calleeOp))
+ calleeGV = moduleTranslation.lookupAlias(calleeOp);
+ else
+ calleeGV = moduleTranslation.lookupIFunc(calleeOp);
+ call = builder.CreateCall(calleeType, calleeGV, operandsRef, opBundles);
}
} else {
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
@@ -639,11 +643,29 @@ 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 {
+ Operation *moduleOp = parentLLVMModule(&opInst);
+ Operation *calleeOp =
+ moduleTranslation.symbolTable().lookupSymbolIn(moduleOp, attr);
+ llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
+ moduleTranslation.convertType(invOp.getCalleeFunctionType()));
+ llvm::GlobalValue *calleeGV;
+ if (isa<LLVM::AliasOp>(calleeOp))
+ calleeGV = moduleTranslation.lookupAlias(calleeOp);
+ else
+ calleeGV = moduleTranslation.lookupIFunc(calleeOp);
+ 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..c4e67305c2038 100644
--- a/mlir/test/Target/LLVMIR/alias.mlir
+++ b/mlir/test/Target/LLVMIR/alias.mlir
@@ -90,3 +90,26 @@ 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()
More information about the Mlir-commits
mailing list