diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1191,4 +1191,48 @@ let printer = [{ printFenceOp(p, *this); }]; let verifier = "return ::verify(*this);"; } + +def AsmATT : LLVM_EnumAttrCase< + /*string cppSym=*/"AD_ATT", /*string irSym=*/"att", + /*string llvmSym=*/"AD_ATT", /*int val=*/0>; +def AsmIntel : LLVM_EnumAttrCase< + /*string cppSym=*/"AD_Intel", /*string irSym=*/"intel", + /*string llvmSym=*/"AD_Intel", /*int val=*/1>; +def AsmATTOrIntel : LLVM_EnumAttr< + /*string name=*/"AsmDialect", + /*string llvmName=*/"::llvm::InlineAsm::AsmDialect", + /*string description=*/"ATT (0) or Intel (1) asm dialect", + /*list cases=*/[AsmATT, AsmIntel]> { + let cppNamespace = "::mlir::LLVM"; +} + +def LLVM_InlineAsmOp : LLVM_Op<"inline_asm", []> { + let description = [{ + The InlineAsmOp mirrors the underlying LLVM semantics with a notable + exception: the embedded `asm_string` is not allowed to define or reference + any symbol or any global variable: only the operands of the op may be read, + written, or referenced. + Attempting to define or reference any symbol or any global behavior is + considered undefined behavior at this time. + }]; + let arguments = ( + ins Variadic:$operands, + StrAttr:$asm_string, + StrAttr:$constraints, + UnitAttr:$has_side_effects, + UnitAttr:$is_align_stack, + OptionalAttr< + DefaultValuedAttr>:$asm_dialect); + + let results = (outs Optional:$res); + + let assemblyFormat = [{ + (`has_side_effects` $has_side_effects^)? + (`is_align_stack` $is_align_stack^)? + (`asm_dialect` `=` $asm_dialect^)? + attr-dict + $asm_string `,` $constraints + operands `:` functional-type(operands, results) + }]; +} #endif // LLVMIR_OPS diff --git a/mlir/integration_test/Dialect/LLVMIR/CPU/X86/lit.local.cfg b/mlir/integration_test/Dialect/LLVMIR/CPU/X86/lit.local.cfg new file mode 100644 --- /dev/null +++ b/mlir/integration_test/Dialect/LLVMIR/CPU/X86/lit.local.cfg @@ -0,0 +1,8 @@ +import platform + +if platform.machine() != 'x86_64': + config.unsupported = True + +# No JIT on win32. +if sys.platform == 'win32': + config.unsupported = True diff --git a/mlir/integration_test/Dialect/LLVMIR/CPU/X86/test-inline-asm.mlir b/mlir/integration_test/Dialect/LLVMIR/CPU/X86/test-inline-asm.mlir new file mode 100644 --- /dev/null +++ b/mlir/integration_test/Dialect/LLVMIR/CPU/X86/test-inline-asm.mlir @@ -0,0 +1,16 @@ +// RUN: mlir-cpu-runner %s -e entry -entry-point-result=void \ +// RUN: -shared-libs=%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \ +// RUN: FileCheck %s + +module { + llvm.func @printI64(!llvm.i64) + llvm.func @entry() { + %c2 = llvm.mlir.constant(-42: i64) :!llvm.i64 + %val = llvm.inline_asm "xor $0, $0", "=r,r" %c2 : + (!llvm.i64) -> !llvm.i64 + + // CHECK: 0 + llvm.call @printI64(%val) : (!llvm.i64) -> () + llvm.return + } +} diff --git a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp --- a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Type.h" #include "llvm/IRReader/IRReader.h" diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" @@ -559,6 +560,41 @@ return success(result->getType()->isVoidTy()); } + if (auto inlineAsmOp = dyn_cast(opInst)) { + // TODO: refactor function type creation which usually occurs in std-LLVM + // conversion. + SmallVector operandTypes; + operandTypes.reserve(inlineAsmOp.operands().size()); + for (auto t : inlineAsmOp.operands().getTypes()) + operandTypes.push_back(t.cast()); + + LLVM::LLVMType resultType; + if (inlineAsmOp.getNumResults() == 0) { + resultType = LLVM::LLVMType::getVoidTy(mlirModule->getContext()); + } else { + assert(inlineAsmOp.getNumResults() == 1); + resultType = inlineAsmOp.getResultTypes()[0].cast(); + } + auto ft = LLVM::LLVMType::getFunctionTy(resultType, operandTypes, + /*isVarArg=*/false); + llvm::InlineAsm *inlineAsmInst = + inlineAsmOp.asm_dialect().hasValue() + ? llvm::InlineAsm::get( + static_cast(convertType(ft)), + inlineAsmOp.asm_string(), inlineAsmOp.constraints(), + inlineAsmOp.has_side_effects(), inlineAsmOp.is_align_stack(), + convertAsmDialectToLLVM(*inlineAsmOp.asm_dialect())) + : llvm::InlineAsm::get( + static_cast(convertType(ft)), + inlineAsmOp.asm_string(), inlineAsmOp.constraints(), + inlineAsmOp.has_side_effects(), inlineAsmOp.is_align_stack()); + llvm::Value *result = + builder.CreateCall(inlineAsmInst, lookupValues(inlineAsmOp.operands())); + if (opInst.getNumResults() != 0) + valueMapping[opInst.getResult(0)] = result; + return success(); + } + if (auto invOp = dyn_cast(opInst)) { auto operands = lookupValues(opInst.getOperands()); ArrayRef operandsRef(operands); diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -333,3 +333,23 @@ llvm.fence release return } + +// CHECK-LABEL: @useInlineAsm +llvm.func @useInlineAsm(%arg0: !llvm.i32) { + // CHECK: llvm.inline_asm {{.*}} (!llvm.i32) -> !llvm.i8 + %0 = llvm.inline_asm "bswap $0", "=r,r" %arg0 : (!llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: llvm.inline_asm {{.*}} (!llvm.i32, !llvm.i32) -> !llvm.i8 + %1 = llvm.inline_asm "foo", "bar" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: llvm.inline_asm has_side_effects {{.*}} (!llvm.i32, !llvm.i32) -> !llvm.i8 + %2 = llvm.inline_asm has_side_effects "foo", "bar" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: llvm.inline_asm is_align_stack {{.*}} (!llvm.i32, !llvm.i32) -> !llvm.i8 + %3 = llvm.inline_asm is_align_stack "foo", "bar" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: llvm.inline_asm "foo", "=r,=r,r" {{.*}} : (!llvm.i32) -> !llvm.struct<(i8, i8)> + %5 = llvm.inline_asm "foo", "=r,=r,r" %arg0 : (!llvm.i32) -> !llvm.struct<(i8, i8)> + + llvm.return +} diff --git a/mlir/test/Target/llvmir.mlir b/mlir/test/Target/llvmir.mlir --- a/mlir/test/Target/llvmir.mlir +++ b/mlir/test/Target/llvmir.mlir @@ -1326,3 +1326,35 @@ // CHECK-NOT: "CodeView", i32 1 module attributes {} {} + +// ----- + +// CHECK-LABEL: @useInlineAsm +llvm.func @useInlineAsm(%arg0: !llvm.i32) { + // 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. + + // CHECK-NEXT: call void asm "foo", "r"(i32 {{.*}}), !dbg !7 + llvm.inline_asm "foo", "r" %arg0 : (!llvm.i32) -> () + + // CHECK-NEXT: call i8 asm "foo", "=r,r"(i32 {{.*}}), !dbg !9 + %0 = llvm.inline_asm "foo", "=r,r" %arg0 : (!llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: call i8 asm "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !10 + %1 = llvm.inline_asm "foo", "=r,r,r" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: call i8 asm sideeffect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !11 + %2 = llvm.inline_asm has_side_effects "foo", "=r,r,r" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: call i8 asm alignstack "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !12 + %3 = llvm.inline_asm is_align_stack "foo", "=r,r,r" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: call i8 asm inteldialect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !13 + %4 = llvm.inline_asm asm_dialect = 1 "foo", "=r,r,r" %arg0, %arg0 : (!llvm.i32, !llvm.i32) -> !llvm.i8 + + // CHECK-NEXT: call { i8, i8 } asm "foo", "=r,=r,r"(i32 {{.*}}), !dbg !14 + %5 = llvm.inline_asm "foo", "=r,=r,r" %arg0 : (!llvm.i32) -> !llvm.struct<(i8, i8)> + + llvm.return +} + diff --git a/mlir/tools/mlir-cpu-runner/CMakeLists.txt b/mlir/tools/mlir-cpu-runner/CMakeLists.txt --- a/mlir/tools/mlir-cpu-runner/CMakeLists.txt +++ b/mlir/tools/mlir-cpu-runner/CMakeLists.txt @@ -11,6 +11,8 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) target_link_libraries(mlir-cpu-runner PRIVATE ${dialect_libs} + LLVMAsmParser + LLVMX86AsmParser MLIRAnalysis MLIREDSC MLIRExecutionEngine diff --git a/mlir/tools/mlir-cpu-runner/mlir-cpu-runner.cpp b/mlir/tools/mlir-cpu-runner/mlir-cpu-runner.cpp --- a/mlir/tools/mlir-cpu-runner/mlir-cpu-runner.cpp +++ b/mlir/tools/mlir-cpu-runner/mlir-cpu-runner.cpp @@ -22,6 +22,7 @@ llvm::InitLLVM y(argc, argv); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); mlir::initializeLLVMPasses(); return mlir::JitRunnerMain(argc, argv);