[Mlir-commits] [mlir] Reapply [MLIR][LLVMIR] Import unregistered intrinsics via llvm.intrin… (PR #129174)

Bruno Cardoso Lopes llvmlistbot at llvm.org
Fri Feb 28 18:21:21 PST 2025


https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/129174

>From 88e2f2cf43db83e21e449a1e96cbd3b888cab709 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bcardosolopes at users.noreply.github.com>
Date: Wed, 26 Feb 2025 14:33:55 -0800
Subject: [PATCH 1/2] Reapply [MLIR][LLVMIR] Import unregistered intrinsics via
 llvm.intrinsic_call

Original introduced in https://github.com/llvm/llvm-project/pull/128626,
reverted in https://github.com/llvm/llvm-project/pull/128973

Reproduced the issue on a shared lib build locally on Linux, moved content
arround to statisfy both static and shared lib builds.

Currently, the llvm importer can only cover intrinsics that have a first
class representation in an MLIR dialect (arm-neon, etc). This PR
introduces a fallback mechanism that allow "unregistered" intrinsics to
be imported by using the generic `llvm.intrinsic_call` operation. This
is useful in several ways:

1. Allows round-trip the LLVM dialect output lowered from other dialects
(example: ClangIR)
2. Enables MLIR-linking tools to operate on imported LLVM IR without
requiring to add new operations to dozen of different targets (cc
@xlauko @smeenai).

If multiple dialects implement this interface hook, the last one to
register is the one converting all unregistered intrinsics.
---
 .../mlir/Target/LLVMIR/LLVMImportInterface.h  | 18 ++++-
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       | 40 +++++++++++
 .../Target/LLVMIR/Import/import-failure.ll    | 12 ----
 .../LLVMIR/Import/intrinsic-unregistered.ll   | 68 +++++++++++++++++++
 4 files changed, 124 insertions(+), 14 deletions(-)
 create mode 100644 mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll

diff --git a/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h b/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
index cc5a77ed35d2b..686969f891f20 100644
--- a/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
+++ b/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
@@ -155,9 +155,18 @@ class LLVMImportInterface
   LogicalResult convertIntrinsic(OpBuilder &builder, llvm::CallInst *inst,
                                  LLVM::ModuleImport &moduleImport) const {
     // Lookup the dialect interface for the given intrinsic.
-    Dialect *dialect = intrinsicToDialect.lookup(inst->getIntrinsicID());
+    // Verify the intrinsic identifier maps to an actual intrinsic.
+    llvm::Intrinsic::ID intrinId = inst->getIntrinsicID();
+    assert(intrinId != llvm::Intrinsic::not_intrinsic);
+
+    // First lookup the intrinsic across different dialects for known
+    // supported conversions, examples include arm-neon, nvm-sve, etc.
+    Dialect *dialect = intrinsicToDialect.lookup(intrinId);
+
+    // No specialized (supported) intrinsics, attempt to generate a generic
+    // version via llvm.call_intrinsic (if available).
     if (!dialect)
-      return failure();
+      return convertUnregisteredIntrinsic(builder, inst, moduleImport);
 
     // Dispatch the conversion to the dialect interface.
     const LLVMImportDialectInterface *iface = getInterfaceFor(dialect);
@@ -224,6 +233,11 @@ class LLVMImportInterface
   }
 
 private:
+  /// Generate llvm.call_intrinsic when no supporting dialect available.
+  static LogicalResult
+  convertUnregisteredIntrinsic(OpBuilder &builder, llvm::CallInst *inst,
+                               LLVM::ModuleImport &moduleImport);
+
   DenseMap<unsigned, Dialect *> intrinsicToDialect;
   DenseMap<unsigned, const LLVMImportDialectInterface *> instructionToDialect;
   DenseMap<unsigned, SmallVector<Dialect *, 1>> metadataToDialect;
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 8445e609c2244..4affe7f3a4902 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1642,6 +1642,46 @@ FlatSymbolRefAttr ModuleImport::convertCalleeName(llvm::CallBase *callInst) {
   return {};
 }
 
+LogicalResult mlir::LLVMImportInterface::convertUnregisteredIntrinsic(
+    OpBuilder &builder, llvm::CallInst *inst,
+    LLVM::ModuleImport &moduleImport) {
+  StringRef intrinName = inst->getCalledFunction()->getName();
+
+  SmallVector<llvm::Value *> args(inst->args());
+  ArrayRef<llvm::Value *> llvmOperands(args);
+
+  SmallVector<llvm::OperandBundleUse> llvmOpBundles;
+  llvmOpBundles.reserve(inst->getNumOperandBundles());
+  for (unsigned i = 0; i < inst->getNumOperandBundles(); ++i)
+    llvmOpBundles.push_back(inst->getOperandBundleAt(i));
+
+  SmallVector<Value> mlirOperands;
+  SmallVector<NamedAttribute> mlirAttrs;
+  if (failed(moduleImport.convertIntrinsicArguments(
+          llvmOperands, llvmOpBundles, false, {}, {}, mlirOperands, mlirAttrs)))
+    return failure();
+
+  Type results = moduleImport.convertType(inst->getType());
+  auto op = builder.create<::mlir::LLVM::CallIntrinsicOp>(
+      moduleImport.translateLoc(inst->getDebugLoc()), results,
+      StringAttr::get(builder.getContext(), intrinName),
+      ValueRange{mlirOperands}, FastmathFlagsAttr{});
+
+  moduleImport.setFastmathFlagsAttr(inst, op);
+
+  // Update importer tracking of results.
+  unsigned numRes = op.getNumResults();
+  if (numRes == 1)
+    moduleImport.mapValue(inst) = op.getResult(0);
+  else if (numRes == 0)
+    moduleImport.mapNoResultOp(inst);
+  else
+    return op.emitError(
+        "expected at most one result from target intrinsic call");
+
+  return success();
+}
+
 LogicalResult ModuleImport::convertIntrinsic(llvm::CallInst *inst) {
   if (succeeded(iface.convertIntrinsic(builder, inst, *this)))
     return success();
diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index d929a59284762..fc4ccddb756d5 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -38,18 +38,6 @@ bb1:
 
 ; // -----
 
-declare void @llvm.gcroot(ptr %arg1, ptr %arg2)
-
-; CHECK:      <unknown>
-; CHECK-SAME: error: unhandled intrinsic: call void @llvm.gcroot(ptr %arg1, ptr null)
-define void @unhandled_intrinsic() gc "example" {
-  %arg1 = alloca ptr
-  call void @llvm.gcroot(ptr %arg1, ptr null)
-  ret void
-}
-
-; // -----
-
 ; Check that debug intrinsics with an unsupported argument are dropped.
 
 declare void @llvm.dbg.value(metadata, metadata, metadata)
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
new file mode 100644
index 0000000000000..554be8f797b75
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
@@ -0,0 +1,68 @@
+; RUN: mlir-translate -import-llvm %s -split-input-file | FileCheck %s
+
+declare i64 @llvm.aarch64.ldxr.p0(ptr)
+
+define dso_local void @t0(ptr %a) {
+  %x = call i64 @llvm.aarch64.ldxr.p0(ptr elementtype(i8) %a)
+  ret void
+}
+
+; CHECK-LABEL: llvm.func @llvm.aarch64.ldxr.p0(!llvm.ptr)
+; CHECK-LABEL: llvm.func @t0
+; CHECK:   llvm.call_intrinsic "llvm.aarch64.ldxr.p0"({{.*}}) : (!llvm.ptr) -> i64
+; CHECK:   llvm.return
+; CHECK: }
+
+; // -----
+
+declare <8 x i8> @llvm.aarch64.neon.uabd.v8i8(<8 x i8>, <8 x i8>)
+
+define dso_local <8 x i8> @t1(<8 x i8> %lhs, <8 x i8> %rhs) {
+  %r = call <8 x i8> @llvm.aarch64.neon.uabd.v8i8(<8 x i8> %lhs, <8 x i8> %rhs)
+  ret <8 x i8> %r
+}
+
+; CHECK: llvm.func @t1(%[[A0:.*]]: vector<8xi8>, %[[A1:.*]]: vector<8xi8>) -> vector<8xi8> {{.*}} {
+; CHECK:   %[[R:.*]] = llvm.call_intrinsic "llvm.aarch64.neon.uabd.v8i8"(%[[A0]], %[[A1]]) : (vector<8xi8>, vector<8xi8>) -> vector<8xi8>
+; CHECK:   llvm.return %[[R]] : vector<8xi8>
+; CHECK: }
+
+; // -----
+
+declare void @llvm.aarch64.neon.st2.v8i8.p0(<8 x i8>, <8 x i8>, ptr)
+
+define dso_local void @t2(<8 x i8> %lhs, <8 x i8> %rhs, ptr %a) {
+  call void @llvm.aarch64.neon.st2.v8i8.p0(<8 x i8> %lhs, <8 x i8> %rhs, ptr %a)
+  ret void
+}
+
+; CHECK: llvm.func @t2(%[[A0:.*]]: vector<8xi8>, %[[A1:.*]]: vector<8xi8>, %[[A2:.*]]: !llvm.ptr) {{.*}} {
+; CHECK:   llvm.call_intrinsic "llvm.aarch64.neon.st2.v8i8.p0"(%[[A0]], %[[A1]], %[[A2]]) : (vector<8xi8>, vector<8xi8>, !llvm.ptr) -> !llvm.void
+; CHECK:   llvm.return
+; CHECK: }
+
+; // -----
+
+declare void @llvm.gcroot(ptr %arg1, ptr %arg2)
+define void @gctest() gc "example" {
+  %arg1 = alloca ptr
+  call void @llvm.gcroot(ptr %arg1, ptr null)
+  ret void
+}
+
+; CHECK-LABEL: @gctest
+; CHECK: llvm.call_intrinsic "llvm.gcroot"({{.*}}, {{.*}}) : (!llvm.ptr, !llvm.ptr) -> !llvm.void
+
+; // -----
+
+; Test we get the supported version, not the unregistered one.
+
+declare i32 @llvm.lround.i32.f32(float)
+
+; CHECK-LABEL: llvm.func @lround_test
+define void @lround_test(float %0, double %1) {
+  ; CHECK-NOT: llvm.call_intrinsic "llvm.lround
+  ; CHECK: llvm.intr.lround(%{{.*}}) : (f32) -> i32
+  %3 = call i32 @llvm.lround.i32.f32(float %0)
+  ret void
+}

>From 2abcbbccfdfade645e875d0722cf7aab9dd7601a Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 28 Feb 2025 18:15:50 -0800
Subject: [PATCH 2/2] Add a new file to cover implementation

---
 mlir/lib/Target/LLVMIR/CMakeLists.txt         |  2 +
 .../lib/Target/LLVMIR/LLVMImportInterface.cpp | 51 +++++++++++++++++++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       | 40 ---------------
 3 files changed, 53 insertions(+), 40 deletions(-)
 create mode 100644 mlir/lib/Target/LLVMIR/LLVMImportInterface.cpp

diff --git a/mlir/lib/Target/LLVMIR/CMakeLists.txt b/mlir/lib/Target/LLVMIR/CMakeLists.txt
index ccb4cfcb7ae40..f59f1d51093ee 100644
--- a/mlir/lib/Target/LLVMIR/CMakeLists.txt
+++ b/mlir/lib/Target/LLVMIR/CMakeLists.txt
@@ -8,6 +8,7 @@ set(LLVM_OPTIONAL_SOURCES
   DebugImporter.cpp
   LoopAnnotationImporter.cpp
   LoopAnnotationTranslation.cpp
+  LLVMImportInterface.cpp
   ModuleTranslation.cpp
   ModuleImport.cpp
   TypeToLLVM.cpp
@@ -68,6 +69,7 @@ add_mlir_translation_library(MLIRTargetLLVMIRImport
   DebugImporter.cpp
   LoopAnnotationImporter.cpp
   ModuleImport.cpp
+  LLVMImportInterface.cpp
   TypeFromLLVM.cpp
 
   ADDITIONAL_HEADER_DIRS
diff --git a/mlir/lib/Target/LLVMIR/LLVMImportInterface.cpp b/mlir/lib/Target/LLVMIR/LLVMImportInterface.cpp
new file mode 100644
index 0000000000000..7eb1f02eed523
--- /dev/null
+++ b/mlir/lib/Target/LLVMIR/LLVMImportInterface.cpp
@@ -0,0 +1,51 @@
+//===------------------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements methods from LLVMImportInterface.
+//
+//===----------------------------------------------------------------------===//
+
+LogicalResult mlir::LLVMImportInterface::convertUnregisteredIntrinsic(
+    OpBuilder &builder, llvm::CallInst *inst,
+    LLVM::ModuleImport &moduleImport) {
+  StringRef intrinName = inst->getCalledFunction()->getName();
+
+  SmallVector<llvm::Value *> args(inst->args());
+  ArrayRef<llvm::Value *> llvmOperands(args);
+
+  SmallVector<llvm::OperandBundleUse> llvmOpBundles;
+  llvmOpBundles.reserve(inst->getNumOperandBundles());
+  for (unsigned i = 0; i < inst->getNumOperandBundles(); ++i)
+    llvmOpBundles.push_back(inst->getOperandBundleAt(i));
+
+  SmallVector<Value> mlirOperands;
+  SmallVector<NamedAttribute> mlirAttrs;
+  if (failed(moduleImport.convertIntrinsicArguments(
+          llvmOperands, llvmOpBundles, false, {}, {}, mlirOperands, mlirAttrs)))
+    return failure();
+
+  Type results = moduleImport.convertType(inst->getType());
+  auto op = builder.create<::mlir::LLVM::CallIntrinsicOp>(
+      moduleImport.translateLoc(inst->getDebugLoc()), results,
+      StringAttr::get(builder.getContext(), intrinName),
+      ValueRange{mlirOperands}, FastmathFlagsAttr{});
+
+  moduleImport.setFastmathFlagsAttr(inst, op);
+
+  // Update importer tracking of results.
+  unsigned numRes = op.getNumResults();
+  if (numRes == 1)
+    moduleImport.mapValue(inst) = op.getResult(0);
+  else if (numRes == 0)
+    moduleImport.mapNoResultOp(inst);
+  else
+    return op.emitError(
+        "expected at most one result from target intrinsic call");
+
+  return success();
+}
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 4affe7f3a4902..8445e609c2244 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1642,46 +1642,6 @@ FlatSymbolRefAttr ModuleImport::convertCalleeName(llvm::CallBase *callInst) {
   return {};
 }
 
-LogicalResult mlir::LLVMImportInterface::convertUnregisteredIntrinsic(
-    OpBuilder &builder, llvm::CallInst *inst,
-    LLVM::ModuleImport &moduleImport) {
-  StringRef intrinName = inst->getCalledFunction()->getName();
-
-  SmallVector<llvm::Value *> args(inst->args());
-  ArrayRef<llvm::Value *> llvmOperands(args);
-
-  SmallVector<llvm::OperandBundleUse> llvmOpBundles;
-  llvmOpBundles.reserve(inst->getNumOperandBundles());
-  for (unsigned i = 0; i < inst->getNumOperandBundles(); ++i)
-    llvmOpBundles.push_back(inst->getOperandBundleAt(i));
-
-  SmallVector<Value> mlirOperands;
-  SmallVector<NamedAttribute> mlirAttrs;
-  if (failed(moduleImport.convertIntrinsicArguments(
-          llvmOperands, llvmOpBundles, false, {}, {}, mlirOperands, mlirAttrs)))
-    return failure();
-
-  Type results = moduleImport.convertType(inst->getType());
-  auto op = builder.create<::mlir::LLVM::CallIntrinsicOp>(
-      moduleImport.translateLoc(inst->getDebugLoc()), results,
-      StringAttr::get(builder.getContext(), intrinName),
-      ValueRange{mlirOperands}, FastmathFlagsAttr{});
-
-  moduleImport.setFastmathFlagsAttr(inst, op);
-
-  // Update importer tracking of results.
-  unsigned numRes = op.getNumResults();
-  if (numRes == 1)
-    moduleImport.mapValue(inst) = op.getResult(0);
-  else if (numRes == 0)
-    moduleImport.mapNoResultOp(inst);
-  else
-    return op.emitError(
-        "expected at most one result from target intrinsic call");
-
-  return success();
-}
-
 LogicalResult ModuleImport::convertIntrinsic(llvm::CallInst *inst) {
   if (succeeded(iface.convertIntrinsic(builder, inst, *this)))
     return success();



More information about the Mlir-commits mailing list