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 @@ -845,4 +845,18 @@ }]; } +def LLVM_FenceOp : LLVM_ZeroResultOp<"fence", []>, + Arguments<(ins OptionalAttr:$syncscope, + AtomicOrdering:$ordering)> { + let llvmBuilder = [{ + llvm::LLVMContext &llvmContext = builder.getContext(); + if($syncscope.hasValue()) + builder.CreateFence(getLLVMAtomicOrdering($ordering), llvmContext.getOrInsertSyncScopeID($syncscope.getValue())); + else + builder.CreateFence(getLLVMAtomicOrdering($ordering)); + }]; + let parser = [{ return parseFenceOp(parser, result); }]; + let printer = [{ printFenceOp(p, *this); }]; + let verifier = "return ::verify(*this);"; +} #endif // LLVMIR_OPS diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -1469,6 +1469,41 @@ return success(); } +//===----------------------------------------------------------------------===// +// Printer, parser and verifier for LLVM::FenceOp. +//===----------------------------------------------------------------------===// + +// ::= `llvm.fence` (`syncscope(`keyword`)`)? +// keyword attribute-dict? +static ParseResult parseFenceOp(OpAsmParser &parser, OperationState &result) { + StringAttr sScope; + if (!failed(parser.parseOptionalKeyword("syncscope"))) { + if (parser.parseLParen() || + parser.parseAttribute(sScope, "syncscope", result.attributes) || + parser.parseRParen()) + return failure(); + } + if (parseAtomicOrdering(parser, result, "ordering") || + parser.parseOptionalAttrDict(result.attributes)) + return failure(); + return success(); +} + +static void printFenceOp(OpAsmPrinter &p, FenceOp &op) { + p << op.getOperationName() << ' '; + if (op.getAttr("syncscope") && + !op.getAttr("syncscope").dyn_cast().getValue().equals("")) + p << "syncscope(" << op.getAttr("syncscope") << ") "; + p << stringifyAtomicOrdering(op.ordering()); +} + +static LogicalResult verify(FenceOp &op) { + StringRef ord = stringifyAtomicOrdering(op.ordering()); + if (ord.equals("unordered") || ord.equals("monotonic")) + return op.emitOpError("fence can be given only acquire, release, acq_rel, " + "and seq_cst orderings"); + return success(); +} //===----------------------------------------------------------------------===// // Printer, parser and verifier for LLVM::AtomicCmpXchgOp. //===----------------------------------------------------------------------===// 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 @@ -485,8 +485,7 @@ INST(Or, Or), INST(Xor, XOr), INST(Alloca, Alloca), INST(Load, Load), INST(Store, Store), // Getelementptr is handled specially. - INST(Ret, Return), - // FIXME: fence + INST(Ret, Return), INST(Fence, Fence), // FIXME: atomiccmpxchg // FIXME: atomicrmw INST(Trunc, Trunc), INST(ZExt, ZExt), INST(SExt, SExt), @@ -539,6 +538,26 @@ llvm_unreachable("incorrect comparison predicate"); } +static AtomicOrdering getLLVMAtomicOrdering(llvm::AtomicOrdering ordering) { + switch (ordering) { + case llvm::AtomicOrdering::NotAtomic: + return LLVM::AtomicOrdering::not_atomic; + case llvm::AtomicOrdering::Unordered: + return LLVM::AtomicOrdering::unordered; + case llvm::AtomicOrdering::Monotonic: + return LLVM::AtomicOrdering::monotonic; + case llvm::AtomicOrdering::Acquire: + return LLVM::AtomicOrdering::acquire; + case llvm::AtomicOrdering::Release: + return LLVM::AtomicOrdering::release; + case llvm::AtomicOrdering::AcquireRelease: + return LLVM::AtomicOrdering::acq_rel; + case llvm::AtomicOrdering::SequentiallyConsistent: + return LLVM::AtomicOrdering::seq_cst; + } + llvm_unreachable("incorrect atomic ordering"); +} + // `br` branches to `target`. Return the branch arguments to `br`, in the // same order of the PHIs in `target`. LogicalResult @@ -703,6 +722,23 @@ ops); return success(); } + case llvm::Instruction::Fence: { + StringAttr syncscope; + SmallVector SSNs; + llvm::LLVMContext &llvmContext = dialect->getLLVMContext(); + llvm::FenceInst *fence = cast(inst); + llvmContext.getSyncScopeNames(SSNs); + int fenceSyncScopeID = fence->getSyncScopeID(); + for (unsigned i = 0; i < SSNs.size(); i++) { + if (fenceSyncScopeID == llvmContext.getOrInsertSyncScopeID(SSNs[i])) { + syncscope = b.getStringAttr(SSNs[i]); + break; + } + } + b.create(loc, syncscope, + getLLVMAtomicOrdering(fence->getOrdering())); + return success(); + } case llvm::Instruction::Invoke: { llvm::InvokeInst *ii = cast(inst); 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 @@ -292,4 +292,13 @@ // CHECK: = llvm.freeze %[[x]] : !llvm.i8 %2 = llvm.freeze %1 : !llvm.i8 return -} \ No newline at end of file +} + +// CHECK-LABEL: @useFenceInst +func @useFenceInst() { + // CHECK: syncscope("agent") seq_cst + llvm.fence syncscope("agent") seq_cst + // CHECK: release + llvm.fence release + return +} diff --git a/mlir/test/Target/import.ll b/mlir/test/Target/import.ll --- a/mlir/test/Target/import.ll +++ b/mlir/test/Target/import.ll @@ -307,4 +307,15 @@ %3 = freeze i8 %2 %poison = add nsw i1 0, undef ret i32 0 +} + +;CHECK-LABEL: @useFenceInst +define i32 @useFenceInst() { + ;CHECK: llvm.fence syncscope("agent") seq_cst + fence syncscope("agent") seq_cst + ;CHECK: llvm.fence release + fence release + ;CHECK: llvm.fence syncscope("singlethread") seq_cst + fence syncscope("singlethread") seq_cst + ret i32 0 } \ No newline at end of file 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 @@ -1180,4 +1180,13 @@ // CHECK: freeze i32 undef %2 = llvm.freeze %1 : !llvm.i32 llvm.return +} + +// CHECK-LABEL: @callFenceInst +llvm.func @callFenceInst() { + // CHECK: fence syncscope("agent") release + llvm.fence syncscope("agent") release + // CHECK: fence release + llvm.fence release + llvm.return } \ No newline at end of file