-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[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
[MLIR][LLVM] Improve inline asm importer #139989
Conversation
Add support for importing more information into InlineAsmOp: elementtype, side effects, align stack, asm dialect and operand attrs.
@llvm/pr-subscribers-mlir @llvm/pr-subscribers-mlir-llvm Author: Bruno Cardoso Lopes (bcardosolopes) ChangesAdd 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:
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
}
|
There was a problem hiding this 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
Add support for importing more information into InlineAsmOp: elementtype, side effects, align stack, asm dialect and operand attrs.