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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mlir/include/mlir/Target/LLVMIR/ModuleImport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
43 changes: 40 additions & 3 deletions mlir/lib/Target/LLVMIR/ModuleImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,40 @@ LogicalResult ModuleImport::convertIntrinsic(llvm::CallInst *inst) {
return emitError(loc) << "unhandled intrinsic: " << diag(*inst);
}

ArrayAttr
ModuleImport::convertAsmInlineOperandAttrs(const llvm::CallBase &llvmCall) {
const auto *ia = cast<llvm::InlineAsm>(llvmCall.getCalledOperand());
unsigned argIdx = 0;
SmallVector<mlir::Attribute> opAttrs;
bool hasIndirect = false;

for (const llvm::InlineAsm::ConstraintInfo &ci : ia->ParseConstraints()) {
// Only deal with constraints that correspond to call arguments.
if (ci.Type == llvm::InlineAsm::isLabel || !ci.hasArg())
continue;

// Only increment `argIdx` in terms of constraints containing arguments,
// which are guaranteed to happen in the same order of the call arguments.
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());
Expand Down Expand Up @@ -2159,14 +2193,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;
Expand Down
45 changes: 43 additions & 2 deletions mlir/test/Target/LLVMIR/Import/instructions.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
24 changes: 23 additions & 1 deletion mlir/test/Target/LLVMIR/llvmir.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
}

Expand Down