[Mlir-commits] [mlir] [MLIR][LLVMIR] Import unregistered intrinsics via llvm.intrinsic_call (PR #128626)

Bruno Cardoso Lopes llvmlistbot at llvm.org
Mon Feb 24 21:00:32 PST 2025


https://github.com/bcardosolopes created https://github.com/llvm/llvm-project/pull/128626

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 adding new operations to dozen of different targets (cc @xlauko @smeenai).

Implemented:
- Cover intrinsics across all current supported LLVM targets and generic ones.
- Use existing LLVM tablegen files to populate intrinsic matching.
- Matches with lower priority against dialect supported intrinsic conversion.

Open question: how to add the proper include path for tablegen to find Intrinsics.td?

>From 3896bb916e925427f997906591cb813bdde202f8 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Thu, 20 Feb 2025 16:24:56 -0800
Subject: [PATCH] [MLIR][LLVMIR] Import unregistered intrinsics via
 llvm.intrinsic_call

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
adding new operations to dozen of different targets (cc @xlauko @smeenai).

Implemented:
- Cover intrinsics across all current supported LLVM targets and generic ones.
- Use existing LLVM tablegen files to populate intrinsic matching.
- Matches with lower priority against dialect supported intrinsic conversion.

Open question: how to add the proper include path for tablegen to find
Intrinsics.td?
---
 .../mlir/Dialect/LLVMIR/CMakeLists.txt        |  26 ++++
 .../mlir/Target/LLVMIR/LLVMImportInterface.h  |  24 +++-
 mlir/lib/Dialect/LLVMIR/CMakeLists.txt        |   3 +
 .../LLVMIR/LLVMIRToLLVMTranslation.cpp        | 125 ++++++++++++++++++
 .../Target/LLVMIR/Import/import-failure.ll    |  12 --
 .../LLVMIR/Import/intrinsic-unregistered.ll   |  68 ++++++++++
 .../tools/mlir-tblgen/LLVMIRConversionGen.cpp |  57 ++++++++
 7 files changed, 302 insertions(+), 13 deletions(-)
 create mode 100644 mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll

diff --git a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
index 759de745440c2..cefd1bbe4b027 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
+++ b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
@@ -81,3 +81,29 @@ mlir_tablegen(VCIXConversions.inc -gen-llvmir-conversions)
 mlir_tablegen(VCIXOpsAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=vcix)
 mlir_tablegen(VCIXOpsAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect=vcix)
 add_public_tablegen_target(MLIRVCIXConversionsIncGen)
+
+# FIXME: Should emit extra file with intrinsics_gen if MLIR is enabled? Or maybe find
+# a better way to get the path for Intrinsics.td?
+set(LLVM_TARGET_DEFINITIONS ../../../../../llvm/include/llvm/IR/Intrinsics.td)
+
+mlir_tablegen(LLVMUnregisteredLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics)
+mlir_tablegen(LLVMUnregisteredAArch64LLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=aarch64)
+mlir_tablegen(LLVMUnregisteredAMDGPULLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=amdgcn)
+mlir_tablegen(LLVMUnregisteredARMLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=arm)
+mlir_tablegen(LLVMUnregisteredBPFLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=bpf)
+mlir_tablegen(LLVMUnregisteredDirectXLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=dx)
+mlir_tablegen(LLVMUnregisteredHexagonLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=hexagon)
+mlir_tablegen(LLVMUnregisteredLoongArchLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=loongarch)
+mlir_tablegen(LLVMUnregisteredMipsLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=mips)
+mlir_tablegen(LLVMUnregisteredNVPTXLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=nvvm)
+mlir_tablegen(LLVMUnregisteredPowerPCLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=ppc)
+mlir_tablegen(LLVMUnregisteredR600LLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=r600)
+mlir_tablegen(LLVMUnregisteredRISCVLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=riscv)
+mlir_tablegen(LLVMUnregisteredSPIRVLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=spv)
+mlir_tablegen(LLVMUnregisteredS390LLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=s390)
+mlir_tablegen(LLVMUnregisteredWebAssemblyLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=wasm)
+mlir_tablegen(LLVMUnregisteredX86LLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=x86)
+mlir_tablegen(LLVMUnregisteredXCoreLLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=xcore)
+mlir_tablegen(LLVMUnregisteredVELLVMIRIntrinsics.inc -gen-unregistered-llvmir-intrinsics -intrinsic-prefix=ve)
+
+add_public_tablegen_target(MLIRLLVMIntrinsicUnregisteredIncGen)
\ No newline at end of file
diff --git a/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h b/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
index cc5a77ed35d2b..d122021e8586d 100644
--- a/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
+++ b/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h
@@ -74,6 +74,10 @@ class LLVMImportDialectInterface
   /// returns the list of supported intrinsic identifiers.
   virtual ArrayRef<unsigned> getSupportedIntrinsics() const { return {}; }
 
+  /// Returns the list of LLVM IR intrinsic identifiers that are unsupported
+  /// but dialects might have a generic way to represent them.
+  virtual ArrayRef<unsigned> getUnregisteredIntrinsics() const { return {}; }
+
   /// Hook for derived dialect interfaces to publish the supported instructions.
   /// As every LLVM IR instruction has a unique integer identifier, the function
   /// returns the list of supported instruction identifiers. These identifiers
@@ -139,6 +143,9 @@ class LLVMImportInterface
       // Add a mapping for all supported intrinsic identifiers.
       for (unsigned id : iface.getSupportedIntrinsics())
         intrinsicToDialect[id] = iface.getDialect();
+      // Add a mapping for all unregistered intrinsic identifiers.
+      for (unsigned id : iface.getUnregisteredIntrinsics())
+        unregisteredIntrinscToDialect[id] = iface.getDialect();
       // Add a mapping for all supported instruction identifiers.
       for (unsigned id : iface.getSupportedInstructions())
         instructionToDialect[id] = &iface;
@@ -155,7 +162,19 @@ 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());
+    llvm::Intrinsic::ID intrinId = inst->getIntrinsicID();
+    if (intrinId == llvm::Intrinsic::not_intrinsic)
+      return failure();
+
+    // First lookup intrinsic across different dialects for known
+    // supported converstions, 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)
+      dialect = unregisteredIntrinscToDialect.lookup(intrinId);
+
     if (!dialect)
       return failure();
 
@@ -227,6 +246,9 @@ class LLVMImportInterface
   DenseMap<unsigned, Dialect *> intrinsicToDialect;
   DenseMap<unsigned, const LLVMImportDialectInterface *> instructionToDialect;
   DenseMap<unsigned, SmallVector<Dialect *, 1>> metadataToDialect;
+
+  /// Unregistered generic and target independent intrinsics.
+  DenseMap<unsigned, Dialect *> unregisteredIntrinscToDialect;
 };
 
 } // namespace mlir
diff --git a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
index c9a3b97294562..030aed9cdb06a 100644
--- a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
+++ b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
@@ -17,6 +17,9 @@ add_mlir_dialect_library(MLIRLLVMDialect
   MLIRLLVMTypesIncGen
   MLIRLLVMIntrinsicOpsIncGen
   MLIRLLVMInterfacesIncGen
+  MLIRLLVMConversionsIncGen
+  MLIRLLVMIntrinsicConversionsIncGen
+  MLIRLLVMIntrinsicUnregisteredIncGen
   MLIROpenMPOpsIncGen
   intrinsics_gen
 
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index 4fd043c7c93e6..88ea2ff58434d 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -24,6 +24,24 @@
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IntrinsicsAArch64.h"
+#include "llvm/IR/IntrinsicsAMDGPU.h"
+#include "llvm/IR/IntrinsicsARM.h"
+#include "llvm/IR/IntrinsicsBPF.h"
+#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/IR/IntrinsicsHexagon.h"
+#include "llvm/IR/IntrinsicsLoongArch.h"
+#include "llvm/IR/IntrinsicsMips.h"
+#include "llvm/IR/IntrinsicsNVPTX.h"
+#include "llvm/IR/IntrinsicsPowerPC.h"
+#include "llvm/IR/IntrinsicsR600.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
+#include "llvm/IR/IntrinsicsS390.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/IR/IntrinsicsVE.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
+#include "llvm/IR/IntrinsicsX86.h"
+#include "llvm/IR/IntrinsicsXCore.h"
 #include "llvm/Support/ModRef.h"
 
 using namespace mlir;
@@ -56,6 +74,105 @@ static ArrayRef<unsigned> getSupportedIntrinsicsImpl() {
   return convertibleIntrinsics;
 }
 
+/// Returns true if the LLVM IR intrinsic is convertible to llvm.intrinsic_call
+/// Returns false otherwise.
+static bool isConvertibleUnregisteredIntrinsic(llvm::Intrinsic::ID id) {
+  static const DenseSet<unsigned> convertibleTargetIntrinsics = {
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredAArch64LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredAMDGPULLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredARMLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredBPFLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredDirectXLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredHexagonLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredLoongArchLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredMipsLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredNVPTXLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredPowerPCLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredR600LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredRISCVLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredS390LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredSPIRVLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredVELLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredWebAssemblyLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredX86LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredXCoreLLVMIRIntrinsics.inc"
+  };
+  return convertibleTargetIntrinsics.contains(id);
+}
+
+/// Returns the list of LLVM IR intrinsic identifiers that are not registered
+/// by any dialect but can be convertible to llvm.intrinsic_call operation.
+static ArrayRef<unsigned> getUnregisteredIntrinsicsImpl() {
+  static const SmallVector<unsigned> convertibleTargetIntrinsics = {
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredAArch64LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredAMDGPULLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredARMLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredBPFLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredDirectXLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredHexagonLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredLoongArchLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredMipsLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredNVPTXLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredPowerPCLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredR600LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredRISCVLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredS390LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredSPIRVLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredVELLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredWebAssemblyLLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredX86LLVMIRIntrinsics.inc"
+#include "mlir/Dialect/LLVMIR/LLVMUnregisteredXCoreLLVMIRIntrinsics.inc"
+  };
+  return convertibleTargetIntrinsics;
+}
+
+/// Converts the LLVM intrinsic to a generic LLVM intrinsic call using
+/// llvm.intrinsic_call. Returns failure otherwise.
+static LogicalResult
+convertUnregisteredIntrinsicImpl(OpBuilder &odsBuilder, llvm::CallInst *inst,
+                                 LLVM::ModuleImport &moduleImport) {
+  llvm::Intrinsic::ID intrinsicID = inst->getIntrinsicID();
+  StringRef intrinName = inst->getCalledFunction()->getName();
+
+  // Sanity check the intrinsic ID.
+  assert(isConvertibleUnregisteredIntrinsic(intrinsicID));
+  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();
+
+  mlir::Type results = moduleImport.convertType(inst->getType());
+  auto op = odsBuilder.create<::mlir::LLVM::CallIntrinsicOp>(
+      moduleImport.translateLoc(inst->getDebugLoc()), results,
+      StringAttr::get(odsBuilder.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();
+}
+
 /// Converts the LLVM intrinsic to an MLIR LLVM dialect operation if a
 /// conversion exits. Returns failure otherwise.
 static LogicalResult convertIntrinsicImpl(OpBuilder &odsBuilder,
@@ -75,6 +192,8 @@ static LogicalResult convertIntrinsicImpl(OpBuilder &odsBuilder,
       llvmOpBundles.push_back(inst->getOperandBundleAt(i));
 
 #include "mlir/Dialect/LLVMIR/LLVMIntrinsicFromLLVMIRConversions.inc"
+  } else if (isConvertibleUnregisteredIntrinsic(intrinsicID)) {
+    return convertUnregisteredIntrinsicImpl(odsBuilder, inst, moduleImport);
   }
 
   return failure();
@@ -422,6 +541,12 @@ class LLVMDialectLLVMIRImportInterface : public LLVMImportDialectInterface {
     return getSupportedIntrinsicsImpl();
   }
 
+  /// Returns the list of LLVM IR intrinsic identifiers that are unsupported
+  /// by existing dialects by are convertible to generic llvm.call_intrinsic.
+  ArrayRef<unsigned> getUnregisteredIntrinsics() const final {
+    return getUnregisteredIntrinsicsImpl();
+  }
+
   /// Returns the list of LLVM IR metadata kinds that are convertible to MLIR
   /// LLVM dialect attributes.
   ArrayRef<unsigned>
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..0f30ce438dbaa
--- /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
+}
\ No newline at end of file
diff --git a/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp b/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp
index 9e19f479d673a..c78872fa2de39 100644
--- a/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp
+++ b/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp
@@ -19,6 +19,7 @@
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
@@ -564,6 +565,46 @@ static bool emitConvertibleIntrinsics(const RecordKeeper &records,
   return false;
 }
 
+static void emitOneUnregisteredIntrinsic(const Record &record, raw_ostream &os,
+                                         StringRef targetName) {
+  StringRef targetPrefix = record.getValueAsString("TargetPrefix");
+
+  // Not interested in target specific intrinsics in the generic namespace.
+  if (targetName.empty() && !targetPrefix.empty())
+    return;
+
+  // Skip unknown targets.
+  if (!targetName.empty() && targetPrefix != targetName)
+    return;
+
+  StringRef defName = record.getName();
+  ArrayRef<SMLoc> defLoc = record.getLoc();
+
+  // Sanity check the input.
+  if (!defName.starts_with("int_"))
+    PrintFatalError(defLoc,
+                    "Intrinsic '" + defName + "' does not start with 'int_'!");
+
+  StringRef enumName = defName.substr(4);
+
+  os << "llvm::Intrinsic::";
+  if (!targetName.empty())
+    os << StringRef(targetName).upper() << "Intrinsics::";
+  os << enumName << ",\n";
+}
+
+// Emit the list of LLVM IR intrinsics enums, both target and generic. Those
+// are used for LLVMImporter's convenience when looking at intrinsics while
+// being up-to-date with new additions to LLVM.
+static bool emitUnregisteredIntrinsics(const RecordKeeper &records,
+                                       raw_ostream &os, StringRef targetName) {
+
+  for (const Record *def : records.getAllDerivedDefinitions("Intrinsic"))
+    emitOneUnregisteredIntrinsic(*def, os, targetName);
+
+  return false;
+}
+
 static mlir::GenRegistration
     genLLVMIRConversions("gen-llvmir-conversions",
                          "Generate LLVM IR conversions", emitBuilders);
@@ -590,3 +631,19 @@ static mlir::GenRegistration genConvertibleLLVMIRIntrinsics(
     "gen-convertible-llvmir-intrinsics",
     "Generate list of convertible LLVM IR intrinsics",
     emitConvertibleIntrinsics);
+
+static llvm::cl::OptionCategory
+    genUnregIntrinsicCat("Options for -gen-unregistered-llvmir-intrinsics");
+static llvm::cl::opt<std::string> unregIntrinsicPrefix(
+    "intrinsic-prefix",
+    cl::desc("Specify target to generate intrinsic information"),
+    cl::value_desc("target prefix"), cl::cat(genUnregIntrinsicCat));
+
+static mlir::GenRegistration genUnregisteredLLVMIRIntrinsics(
+    "gen-unregistered-llvmir-intrinsics",
+    "Generate enum list of target specific or generic intrinsics according to "
+    "enums defined in LLVM by other tablegen backends",
+    [](const RecordKeeper &records, raw_ostream &os) {
+      return emitUnregisteredIntrinsics(records, os,
+                                        unregIntrinsicPrefix.getValue());
+    });
\ No newline at end of file



More information about the Mlir-commits mailing list