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<LLVM_EnumAttrCase> 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<LLVM_Type>:$operands,
+ StrAttr:$asm_string,
+ StrAttr:$constraints,
+ UnitAttr:$has_side_effects,
+ UnitAttr:$is_align_stack,
+ OptionalAttr<
+ DefaultValuedAttr<AsmATTOrIntel, "AsmDialect::AD_ATT">>:$asm_dialect);
+
+ let results = (outs Optional<LLVM_Type>:$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
--- /dev/null
+import platform
+
+if platform.machine() != 'x86_64':
+ config.unsupported = True
+
+# No JIT on win32.
+if sys.platform == 'win32':
+ config.unsupported = True
--- /dev/null
+// 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
+ }
+}
#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"
#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"
return success(result->getType()->isVoidTy());
}
+ if (auto inlineAsmOp = dyn_cast<LLVM::InlineAsmOp>(opInst)) {
+ // TODO: refactor function type creation which usually occurs in std-LLVM
+ // conversion.
+ SmallVector<LLVM::LLVMType, 8> operandTypes;
+ operandTypes.reserve(inlineAsmOp.operands().size());
+ for (auto t : inlineAsmOp.operands().getTypes())
+ operandTypes.push_back(t.cast<LLVM::LLVMType>());
+
+ LLVM::LLVMType resultType;
+ if (inlineAsmOp.getNumResults() == 0) {
+ resultType = LLVM::LLVMType::getVoidTy(mlirModule->getContext());
+ } else {
+ assert(inlineAsmOp.getNumResults() == 1);
+ resultType = inlineAsmOp.getResultTypes()[0].cast<LLVM::LLVMType>();
+ }
+ auto ft = LLVM::LLVMType::getFunctionTy(resultType, operandTypes,
+ /*isVarArg=*/false);
+ llvm::InlineAsm *inlineAsmInst =
+ inlineAsmOp.asm_dialect().hasValue()
+ ? llvm::InlineAsm::get(
+ static_cast<llvm::FunctionType *>(convertType(ft)),
+ inlineAsmOp.asm_string(), inlineAsmOp.constraints(),
+ inlineAsmOp.has_side_effects(), inlineAsmOp.is_align_stack(),
+ convertAsmDialectToLLVM(*inlineAsmOp.asm_dialect()))
+ : llvm::InlineAsm::get(
+ static_cast<llvm::FunctionType *>(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<LLVM::InvokeOp>(opInst)) {
auto operands = lookupValues(opInst.getOperands());
ArrayRef<llvm::Value *> operandsRef(operands);
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
+}
// 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
+}
+
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
target_link_libraries(mlir-cpu-runner PRIVATE
${dialect_libs}
+ LLVMAsmParser
+ LLVMX86AsmParser
MLIRAnalysis
MLIREDSC
MLIRExecutionEngine
llvm::InitLLVM y(argc, argv);
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
+ llvm::InitializeNativeTargetAsmParser();
mlir::initializeLLVMPasses();
return mlir::JitRunnerMain(argc, argv);