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 @@ -849,4 +849,16 @@ }]; } +def LLVM_FenceOp : LLVM_ZeroResultOp<"fence", []>, + Arguments<(ins AtomicOrdering:$ordering, + StrAttr:$syncscope)> { + let llvmBuilder = [{ + llvm::LLVMContext &llvmContext = builder.getContext(); + builder.CreateFence(getLLVMAtomicOrdering($ordering), + llvmContext.getOrInsertSyncScopeID($syncscope)); + }]; + 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 @@ -1532,6 +1532,47 @@ return success(); } +//===----------------------------------------------------------------------===// +// Printer, parser and verifier for LLVM::FenceOp. +//===----------------------------------------------------------------------===// + +// ::= `llvm.fence` (`syncscope(`strAttr`)`)? keyword +// attribute-dict? +static ParseResult parseFenceOp(OpAsmParser &parser, OperationState &result) { + StringAttr sScope; + StringRef syncscopeKeyword = "syncscope"; + if (!failed(parser.parseOptionalKeyword(syncscopeKeyword))) { + if (parser.parseLParen() || + parser.parseAttribute(sScope, syncscopeKeyword, result.attributes) || + parser.parseRParen()) + return failure(); + } else { + result.addAttribute(syncscopeKeyword, + parser.getBuilder().getStringAttr("")); + } + if (parseAtomicOrdering(parser, result, "ordering") || + parser.parseOptionalAttrDict(result.attributes)) + return failure(); + return success(); +} + +static void printFenceOp(OpAsmPrinter &p, FenceOp &op) { + StringRef syncscopeKeyword = "syncscope"; + p << op.getOperationName() << ' '; + if (!op.getAttr(syncscopeKeyword).cast().getValue().empty()) + p << "syncscope(" << op.getAttr(syncscopeKeyword) << ") "; + p << stringifyAtomicOrdering(op.ordering()); +} + +static LogicalResult verify(FenceOp &op) { + if (op.ordering() == AtomicOrdering::not_atomic || + op.ordering() == AtomicOrdering::unordered || + op.ordering() == AtomicOrdering::monotonic) + return op.emitOpError("can be given only acquire, release, acq_rel, " + "and seq_cst orderings"); + return success(); +} + //===----------------------------------------------------------------------===// // LLVMDialect initialization, type parsing, and registration. //===----------------------------------------------------------------------===// 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 @@ -735,6 +754,23 @@ v = op->getResult(0); return success(); } + case llvm::Instruction::Fence: { + StringRef 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 = SSNs[i]; + break; + } + } + b.create(loc, getLLVMAtomicOrdering(fence->getOrdering()), + syncscope); + return success(); + } case llvm::Instruction::GetElementPtr: { // FIXME: Support inbounds GEPs. llvm::GetElementPtrInst *gep = cast(inst); diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -561,3 +561,10 @@ %2 = llvm.landingpad : !llvm<"{ i8*, i32 }"> llvm.return %0 : !llvm.i32 } + +// ----- + +func @invalid_ordering_in_fence() { + // expected-error @+1 {{can be given only acquire, release, acq_rel, and seq_cst orderings}} + llvm.fence syncscope("agent") monotonic +} 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,15 @@ // CHECK: = llvm.freeze %[[x]] : !llvm.i8 %2 = llvm.freeze %1 : !llvm.i8 return +} + +// CHECK-LABEL: @useFenceInst +func @useFenceInst() { + // CHECK: syncscope("agent") seq_cst + llvm.fence syncscope("agent") seq_cst + // CHECK: seq_cst + llvm.fence syncscope("") seq_cst + // CHECK: release + llvm.fence release + return } \ No newline at end of file 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 seq_cst + fence syncscope("") 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,15 @@ // 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 + // CHECK: fence release + llvm.fence syncscope("") release + llvm.return } \ No newline at end of file