Skip to content

[MLIR][LLVM] Improve inline asm importer #139989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

bcardosolopes
Copy link
Member

Add support for importing more information into InlineAsmOp: elementtype, side effects, align stack, asm dialect and operand attrs.

Add support for importing more information into InlineAsmOp: elementtype, side
effects, align stack, asm dialect and operand attrs.
@bcardosolopes bcardosolopes marked this pull request as ready for review May 15, 2025 01:40
@bcardosolopes bcardosolopes requested review from gysit and Dinistro May 15, 2025 01:40
@llvmbot
Copy link
Member

llvmbot commented May 15, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-llvm

Author: Bruno Cardoso Lopes (bcardosolopes)

Changes

Add support for importing more information into InlineAsmOp: elementtype, side effects, align stack, asm dialect and operand attrs.


Full diff: https://github.com/llvm/llvm-project/pull/139989.diff

5 Files Affected:

  • (modified) mlir/include/mlir/Target/LLVMIR/ModuleImport.h (+3)
  • (modified) mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp (+2)
  • (modified) mlir/lib/Target/LLVMIR/ModuleImport.cpp (+42-3)
  • (modified) mlir/test/Target/LLVMIR/Import/instructions.ll (+43-2)
  • (modified) mlir/test/Target/LLVMIR/llvmir.mlir (+23-1)
diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
index 568dc00b3bb97..564e10c43dee8 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
@@ -330,6 +330,9 @@ class ModuleImport {
   /// Converts a single debug intrinsic.
   LogicalResult processDebugIntrinsic(llvm::DbgVariableIntrinsic *dbgIntr,
                                       DominanceInfo &domInfo);
+  /// Converts LLMV IR asm inline call operand's attributes into an array of
+  /// MLIR attributes to be utilized in `llvm.inline_asm`.
+  ArrayAttr convertAsmInlineOperandAttrs(const llvm::CallBase &llvmCall);
   /// Converts an LLVM intrinsic to an MLIR LLVM dialect operation if an MLIR
   /// counterpart exists. Otherwise, returns failure.
   LogicalResult convertIntrinsic(llvm::CallInst *inst);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 9470b54c9f3aa..d9605b19aaaa5 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -515,6 +515,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
         if (!attr)
           continue;
         DictionaryAttr dAttr = cast<DictionaryAttr>(attr);
+        if (dAttr.empty())
+          continue;
         TypeAttr tAttr =
             cast<TypeAttr>(dAttr.get(InlineAsmOp::getElementTypeAttrName()));
         llvm::AttrBuilder b(moduleTranslation.getLLVMContext());
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 7d7d0bb02a3d7..220be8ff79794 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -2073,6 +2073,42 @@ LogicalResult ModuleImport::convertIntrinsic(llvm::CallInst *inst) {
   return emitError(loc) << "unhandled intrinsic: " << diag(*inst);
 }
 
+ArrayAttr
+ModuleImport::convertAsmInlineOperandAttrs(const llvm::CallBase &llvmCall) {
+  const llvm::InlineAsm *ia =
+      cast<llvm::InlineAsm>(llvmCall.getCalledOperand());
+  unsigned argIdx = 0;
+  SmallVector<mlir::Attribute> opAttrs;
+  bool hasIndirect = false;
+
+  for (const llvm::InlineAsm::ConstraintInfo &ci : ia->ParseConstraints()) {
+    if (ci.Type == llvm::InlineAsm::isLabel)
+      continue;
+
+    // Only deal with constraints that correspond to call arguments.
+    if (!ci.hasArg())
+      continue;
+
+    if (ci.isIndirect) {
+      if (llvm::Type *paramEltType = llvmCall.getParamElementType(argIdx)) {
+        SmallVector<mlir::NamedAttribute> attrs;
+        attrs.push_back(builder.getNamedAttr(
+            mlir::LLVM::InlineAsmOp::getElementTypeAttrName(),
+            mlir::TypeAttr::get(convertType(paramEltType))));
+        opAttrs.push_back(builder.getDictionaryAttr(attrs));
+        hasIndirect = true;
+      }
+    } else {
+      opAttrs.push_back(builder.getDictionaryAttr({}));
+    }
+    argIdx++;
+  }
+
+  // Avoid emitting an array where all entries are empty dictionaries.
+  return hasIndirect ? ArrayAttr::get(mlirModule->getContext(), opAttrs)
+                     : nullptr;
+}
+
 LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
   // Convert all instructions that do not provide an MLIR builder.
   Location loc = translateLoc(inst->getDebugLoc());
@@ -2159,14 +2195,17 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
         Type resultTy = convertType(callInst->getType());
         if (!resultTy)
           return failure();
+        ArrayAttr operandAttrs = convertAsmInlineOperandAttrs(*callInst);
         return builder
             .create<InlineAsmOp>(
                 loc, resultTy, *operands,
                 builder.getStringAttr(asmI->getAsmString()),
                 builder.getStringAttr(asmI->getConstraintString()),
-                /*has_side_effects=*/true,
-                /*is_align_stack=*/false, /*asm_dialect=*/nullptr,
-                /*operand_attrs=*/nullptr)
+                asmI->hasSideEffects(), asmI->isAlignStack(),
+                AsmDialectAttr::get(
+                    mlirModule.getContext(),
+                    convertAsmDialectFromLLVM(asmI->getDialect())),
+                operandAttrs)
             .getOperation();
       }
       bool isIncompatibleCall;
diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll
index 9dacd35c26833..68ef47c3f42f1 100644
--- a/mlir/test/Target/LLVMIR/Import/instructions.ll
+++ b/mlir/test/Target/LLVMIR/Import/instructions.ll
@@ -542,14 +542,55 @@ define void @indirect_vararg_call(ptr addrspace(42) %fn) {
 ; CHECK-LABEL: @inlineasm
 ; CHECK-SAME:  %[[ARG1:[a-zA-Z0-9]+]]
 define i32 @inlineasm(i32 %arg1) {
-  ; CHECK:  %[[RES:.+]] = llvm.inline_asm has_side_effects "bswap $0", "=r,r" %[[ARG1]] : (i32) -> i32
-  %1 = call i32 asm "bswap $0", "=r,r"(i32 %arg1)
+  ; CHECK:  %[[RES:.+]] = llvm.inline_asm has_side_effects is_align_stack asm_dialect = intel "bswap $0", "=r,r" %[[ARG1]] : (i32) -> i32
+  %1 = call i32 asm sideeffect alignstack inteldialect "bswap $0", "=r,r"(i32 %arg1)
   ; CHECK: return %[[RES]]
   ret i32 %1
 }
 
 ; // -----
 
+; CHECK-LABEL: @inlineasm2
+define void @inlineasm2() {
+  %p = alloca ptr, align 8
+  ; CHECK: {{.*}} = llvm.alloca %0 x !llvm.ptr {alignment = 8 : i64} : (i32) -> !llvm.ptr
+  ; CHECK-NEXT: llvm.inline_asm has_side_effects asm_dialect = att operand_attrs = [{elementtype = !llvm.ptr}] "", "*m,~{memory}" {{.*}} : (!llvm.ptr) -> !llvm.void
+  call void asm sideeffect "", "*m,~{memory}"(ptr elementtype(ptr) %p)
+  ret void
+}
+
+; // -----
+
+; CHECK: llvm.func @inlineasm3
+; CHECK-SAME:(%[[A0:.*]]: !llvm.ptr, %[[A1:.*]]: i64, %[[A2:.*]]: !llvm.ptr, %[[A3:.*]]: i64) {
+define void @inlineasm3(
+    ptr %ptr0,
+    i64 %b,
+    ptr %ptr1,
+    i64 %c
+  ) {
+  ; CHECK: llvm.inline_asm asm_dialect = att operand_attrs =
+  ; CHECK-SAME: [{elementtype = !llvm.array<16 x i64>}, {},
+  ; CHECK-SAME: {elementtype = !llvm.array<16 x i64>}, {}, {}, {},
+  ; CHECK-SAME: {elementtype = !llvm.array<16 x i64>}]
+  ; CHECK-SAME: "ldr x4, [$2], #8   \0A\09ldr x5, [$1]       \0A\09mul x6, x4, $4     \0A\09",
+  ; CHECK-SAME: "=r,=r,=r,=*m,r,*m,0,1,2,*m,~{x4},~{x5},~{x6},~{x7},~{cc}"
+  ; CHECK-SAME: %[[A0]], %[[A1]], %[[A2]], %[[A3]], %[[A0]], %[[A2]], %[[A0]] :
+  ; CHECK-SAME: (!llvm.ptr, i64, !llvm.ptr, i64, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> !llvm.struct<(i64, ptr, ptr)>
+  %r = call { i64, ptr, ptr } asm "ldr x4, [$2], #8   \0A\09ldr x5, [$1]       \0A\09mul x6, x4, $4     \0A\09",
+  "=r,=r,=r,=*m,r,*m,0,1,2,*m,~{x4},~{x5},~{x6},~{x7},~{cc}"(
+    ptr elementtype([16 x i64]) %ptr0,
+    i64 %b,
+    ptr elementtype([16 x i64]) %ptr1,
+    i64 %c,
+    ptr %ptr0,
+    ptr %ptr1,
+    ptr elementtype([16 x i64]) %ptr0)
+  ret void
+}
+
+; // -----
+
 ; CHECK-LABEL: @gep_static_idx
 ; CHECK-SAME:  %[[PTR:[a-zA-Z0-9]+]]
 define void @gep_static_idx(ptr %ptr) {
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 4ef68fa83a70d..3c8de1cf63b94 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2056,7 +2056,7 @@ module attributes {} {}
 // -----
 
 // CHECK-LABEL: @useInlineAsm
-llvm.func @useInlineAsm(%arg0: i32) {
+llvm.func @useInlineAsm(%arg0: i32, %arg1 : !llvm.ptr) {
   // Constraints string is checked at LLVM InlineAsm instruction construction time.
   // So we can't just use "bar" everywhere, number of in/out arguments has to match.
 
@@ -2081,6 +2081,28 @@ llvm.func @useInlineAsm(%arg0: i32) {
   // CHECK-NEXT:  call { i8, i8 } asm "foo", "=r,=r,r"(i32 {{.*}})
   %5 = llvm.inline_asm "foo", "=r,=r,r" %arg0 : (i32) -> !llvm.struct<(i8, i8)>
 
+  // CHECK-NEXT:  call void asm sideeffect "", "*m,~{memory}"(ptr elementtype(ptr) %1)
+  %6 = llvm.inline_asm has_side_effects operand_attrs = [{elementtype = !llvm.ptr}] "", "*m,~{memory}" %arg1 : (!llvm.ptr) -> !llvm.void
+
+  llvm.return
+}
+
+// -----
+
+// CHECK: @useInlineAsm2(ptr %[[A0:.*]], i64 %[[A1:.*]], ptr %[[A2:.*]], i64 %[[A3:.*]]) {
+llvm.func @useInlineAsm2(%arg0: !llvm.ptr, %arg1: i64, %arg2: !llvm.ptr, %arg3: i64) {
+  // CHECK:  call { i64, ptr, ptr } asm sideeffect
+  // CHECK-SAME: "ldr x4, [$2], #8   \0A\09ldr x5, [$1]       \0A\09mul x6, x4, $4     \0A\09",
+  // CHECK-SAME: "=r,=r,=r,=*m,r,*m,0,1,2,*m,~{x4},~{x5},~{x6},~{x7},~{cc}"
+  // CHECK-SAME:(ptr elementtype([16 x i64]) %[[A0]], i64 %[[A1]], ptr elementtype([16 x i64]) %[[A2]],
+  // CHECK-SAME: i64 %[[A3]], ptr %[[A0]], ptr %[[A2]], ptr elementtype([16 x i64]) %[[A0]])
+  %0 = llvm.inline_asm has_side_effects operand_attrs = [
+    {elementtype = !llvm.array<16 x i64>}, {}, {elementtype = !llvm.array<16 x i64>},
+    {}, {}, {}, {elementtype = !llvm.array<16 x i64>}]
+    "ldr x4, [$2], #8   \0A\09ldr x5, [$1]       \0A\09mul x6, x4, $4     \0A\09",
+    "=r,=r,=r,=*m,r,*m,0,1,2,*m,~{x4},~{x5},~{x6},~{x7},~{cc}"
+    %arg0, %arg1, %arg2, %arg3, %arg0, %arg2, %arg0 :
+      (!llvm.ptr, i64, !llvm.ptr, i64, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> !llvm.struct<(i64, ptr, ptr)>
   llvm.return
 }
 

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

LGTM modulo nit comments

@bcardosolopes bcardosolopes merged commit 8d3a707 into llvm:main May 16, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants