[Mlir-commits] [mlir] 4151f5d - [MLIR][LLVMIR] Allow llvm.call and llvm.invoke to use llvm.mlir.alias as callee (#189154)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Mar 29 04:41:02 PDT 2026


Author: Mehdi Amini
Date: 2026-03-29T13:40:58+02:00
New Revision: 4151f5d36f26492079dca23f2a5ac13480098fb1

URL: https://github.com/llvm/llvm-project/commit/4151f5d36f26492079dca23f2a5ac13480098fb1
DIFF: https://github.com/llvm/llvm-project/commit/4151f5d36f26492079dca23f2a5ac13480098fb1.diff

LOG: [MLIR][LLVMIR] Allow llvm.call and llvm.invoke to use llvm.mlir.alias as callee (#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

Added: 
    

Modified: 
    mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
    mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
    mlir/test/Dialect/LLVMIR/alias.mlir
    mlir/test/Target/LLVMIR/alias.mlir

Removed: 
    


################################################################################
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