Skip to content

Commit 9185896

Browse files
committed
[mlir][llvm] Add atomic support to the StoreOp.
This revision adds atomic support to the StoreOp. It chooses to print the atomic keywords together with the syncscope and ordering arguments. The revision also implements verifiers to ensure the constraints that apply to atomic store operations are checked. Depends on D144112 Reviewed By: Dinistro Differential Revision: https://reviews.llvm.org/D144200
1 parent a28b252 commit 9185896

File tree

6 files changed

+145
-30
lines changed

6 files changed

+145
-30
lines changed

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -430,16 +430,47 @@ def LLVM_StoreOp : LLVM_Op<"store">, MemoryOpBase {
430430
OptionalAttr<SymbolRefArrayAttr>:$alias_scopes,
431431
OptionalAttr<SymbolRefArrayAttr>:$noalias_scopes,
432432
OptionalAttr<SymbolRefArrayAttr>:$tbaa,
433-
OptionalAttr<I64Attr>:$alignment, UnitAttr:$volatile_,
434-
UnitAttr:$nontemporal);
433+
OptionalAttr<I64Attr>:$alignment,
434+
UnitAttr:$volatile_,
435+
UnitAttr:$nontemporal,
436+
DefaultValuedAttr<AtomicOrdering,
437+
"AtomicOrdering::not_atomic">:$ordering,
438+
OptionalAttr<StrAttr>:$syncscope);
435439
string llvmInstName = "Store";
440+
let description = [{
441+
The `store` operation is used to write to memory. A store may be marked as
442+
atomic, volatile, and/or nontemporal, and takes a number of optional
443+
attributes that specify aliasing information.
444+
445+
An atomic store only supports a limited set of pointer, integer, and
446+
floating point types, and requires an explicit alignment.
447+
448+
Examples:
449+
```mlir
450+
// A volatile store of a float variable.
451+
llvm.store volatile %val, %ptr : f32, !llvm.ptr
452+
453+
// A nontemporal store of a float variable.
454+
llvm.store %val, %ptr {nontemporal} : f32, !llvm.ptr
455+
456+
// An atomic store of an integer variable.
457+
llvm.store %val, %ptr atomic monotonic {alignment = 8 : i64}
458+
: i64, !llvm.ptr
459+
```
460+
461+
See the following link for more details:
462+
https://llvm.org/docs/LangRef.html#store-instruction
463+
}];
436464
let assemblyFormat = [{
437-
(`volatile` $volatile_^)? $value `,` $addr attr-dict `:`
438-
custom<StoreType>(type($value), type($addr))
465+
(`volatile` $volatile_^)? $value `,` $addr
466+
(`atomic` (`syncscope` `(` $syncscope^ `)`)? $ordering^)?
467+
attr-dict `:` custom<StoreType>(type($value), type($addr))
439468
}];
440469
string llvmBuilder = [{
441470
auto *inst = builder.CreateStore($value, $addr, $volatile_);
442-
}] # setAlignmentCode
471+
}] # setOrderingCode
472+
# setSyncScopeCode
473+
# setAlignmentCode
443474
# setNonTemporalMetadataCode
444475
# setAccessGroupsMetadataCode
445476
# setAliasScopeMetadataCode
@@ -449,12 +480,16 @@ def LLVM_StoreOp : LLVM_Op<"store">, MemoryOpBase {
449480
unsigned alignment = storeInst->getAlign().value();
450481
$_op = $_builder.create<LLVM::StoreOp>($_location, $value, $addr,
451482
alignment, storeInst->isVolatile(),
452-
storeInst->hasMetadata(llvm::LLVMContext::MD_nontemporal));
483+
storeInst->hasMetadata(llvm::LLVMContext::MD_nontemporal),
484+
convertAtomicOrderingFromLLVM(storeInst->getOrdering()),
485+
getLLVMSyncScope(storeInst));
453486
}];
454487
let builders = [
455488
OpBuilder<(ins "Value":$value, "Value":$addr,
456489
CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isVolatile,
457-
CArg<"bool", "false">:$isNonTemporal)>
490+
CArg<"bool", "false">:$isNonTemporal,
491+
CArg<"AtomicOrdering", "AtomicOrdering::not_atomic">:$ordering,
492+
CArg<"StringRef", "StringRef()">:$syncscope)>
458493
];
459494
let hasVerifier = 1;
460495
}

mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -826,21 +826,35 @@ static bool isTypeCompatibleWithAtomicOp(Type type, bool isPointerTypeAllowed) {
826826
*bitWidth == 64;
827827
}
828828

829-
LogicalResult LoadOp::verify() {
830-
if (getOrdering() != AtomicOrdering::not_atomic) {
831-
if (!isTypeCompatibleWithAtomicOp(getResult().getType(),
829+
/// Verifies the attributes and the type of atomic memory access operations.
830+
template <typename OpTy>
831+
LogicalResult verifyAtomicMemOp(OpTy memOp, Type valueType,
832+
ArrayRef<AtomicOrdering> unsupportedOrderings) {
833+
if (memOp.getOrdering() != AtomicOrdering::not_atomic) {
834+
if (!isTypeCompatibleWithAtomicOp(valueType,
832835
/*isPointerTypeAllowed=*/true))
833-
return emitOpError("unsupported type ")
834-
<< getResult().getType() << " for atomic access";
835-
if (getOrdering() == AtomicOrdering::release ||
836-
getOrdering() == AtomicOrdering::acq_rel)
837-
return emitOpError("unsupported ordering '")
838-
<< stringifyAtomicOrdering(getOrdering()) << "'";
839-
if (!getAlignment())
840-
return emitOpError("expected alignment for atomic access");
841-
} else if (getSyncscope()) {
842-
return emitOpError("expected syncscope to be null for non-atomic access");
836+
return memOp.emitOpError("unsupported type ")
837+
<< valueType << " for atomic access";
838+
if (llvm::is_contained(unsupportedOrderings, memOp.getOrdering()))
839+
return memOp.emitOpError("unsupported ordering '")
840+
<< stringifyAtomicOrdering(memOp.getOrdering()) << "'";
841+
if (!memOp.getAlignment())
842+
return memOp.emitOpError("expected alignment for atomic access");
843+
return success();
843844
}
845+
if (memOp.getSyncscope())
846+
return memOp.emitOpError(
847+
"expected syncscope to be null for non-atomic access");
848+
return success();
849+
}
850+
851+
LogicalResult LoadOp::verify() {
852+
Type valueType = getResult().getType();
853+
if (failed(verifyAtomicMemOp(
854+
*this, valueType,
855+
{AtomicOrdering::release, AtomicOrdering::acq_rel})))
856+
return failure();
857+
844858
return verifyMemOpMetadata(*this);
845859
}
846860

@@ -914,15 +928,25 @@ static void printLoadType(OpAsmPrinter &printer, Operation *op, Type type,
914928
// StoreOp
915929
//===----------------------------------------------------------------------===//
916930

917-
LogicalResult StoreOp::verify() { return verifyMemOpMetadata(*this); }
931+
LogicalResult StoreOp::verify() {
932+
Type valueType = getValue().getType();
933+
if (failed(verifyAtomicMemOp(
934+
*this, valueType,
935+
{AtomicOrdering::acquire, AtomicOrdering::acq_rel})))
936+
return failure();
937+
938+
return verifyMemOpMetadata(*this);
939+
}
918940

919941
void StoreOp::build(OpBuilder &builder, OperationState &state, Value value,
920942
Value addr, unsigned alignment, bool isVolatile,
921-
bool isNonTemporal) {
943+
bool isNonTemporal, AtomicOrdering ordering,
944+
StringRef syncscope) {
922945
build(builder, state, value, addr, /*access_groups=*/nullptr,
923946
/*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr, /*tbaa=*/nullptr,
924947
alignment ? builder.getI64IntegerAttr(alignment) : nullptr, isVolatile,
925-
isNonTemporal);
948+
isNonTemporal, ordering,
949+
syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
926950
}
927951

928952
/// Parses the StoreOp type either using the typed or opaque pointer format.

mlir/test/Dialect/LLVMIR/invalid.mlir

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,41 @@ func.func @store_malformed_elem_type(%foo: !llvm.ptr, %bar: f32) {
209209

210210
// -----
211211

212+
func.func @store_syncscope(%val : f32, %ptr : !llvm.ptr) {
213+
// expected-error@below {{expected syncscope to be null for non-atomic access}}
214+
"llvm.store"(%val, %ptr) {syncscope = "singlethread"} : (f32, !llvm.ptr) -> ()
215+
}
216+
217+
// -----
218+
219+
func.func @store_unsupported_ordering(%val : f32, %ptr : !llvm.ptr) {
220+
// expected-error@below {{unsupported ordering 'acquire'}}
221+
llvm.store %val, %ptr atomic acquire {alignment = 4 : i64} : f32, !llvm.ptr
222+
}
223+
224+
// -----
225+
226+
func.func @store_unsupported_type(%val : f80, %ptr : !llvm.ptr) {
227+
// expected-error@below {{unsupported type 'f80' for atomic access}}
228+
llvm.store %val, %ptr atomic monotonic {alignment = 16 : i64} : f80, !llvm.ptr
229+
}
230+
231+
// -----
232+
233+
func.func @store_unsupported_type(%val : i1, %ptr : !llvm.ptr) {
234+
// expected-error@below {{unsupported type 'i1' for atomic access}}
235+
llvm.store %val, %ptr atomic monotonic {alignment = 16 : i64} : i1, !llvm.ptr
236+
}
237+
238+
// -----
239+
240+
func.func @store_unaligned_atomic(%val : f32, %ptr : !llvm.ptr) {
241+
// expected-error@below {{expected alignment for atomic access}}
242+
llvm.store %val, %ptr atomic monotonic : f32, !llvm.ptr
243+
}
244+
245+
// -----
246+
212247
func.func @invalid_call() {
213248
// expected-error@+1 {{'llvm.call' op must have either a `callee` attribute or at least an operand}}
214249
"llvm.call"() : () -> ()

mlir/test/Dialect/LLVMIR/roundtrip.mlir

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,15 @@ func.func @atomic_load(%ptr : !llvm.ptr) {
348348
llvm.return
349349
}
350350

351+
// CHECK-LABEL: @atomic_store
352+
func.func @atomic_store(%val : f32, %ptr : !llvm.ptr) {
353+
// CHECK: llvm.store %{{.*}}, %{{.*}} atomic monotonic {alignment = 4 : i64} : f32, !llvm.ptr
354+
llvm.store %val, %ptr atomic monotonic {alignment = 4 : i64} : f32, !llvm.ptr
355+
// CHECK: llvm.store volatile %{{.*}}, %{{.*}} atomic syncscope("singlethread") monotonic {alignment = 16 : i64} : f32, !llvm.ptr
356+
llvm.store volatile %val, %ptr atomic syncscope("singlethread") monotonic {alignment = 16 : i64} : f32, !llvm.ptr
357+
llvm.return
358+
}
359+
351360
// CHECK-LABEL: @atomicrmw
352361
func.func @atomicrmw(%ptr : !llvm.ptr, %val : f32) {
353362
// CHECK: llvm.atomicrmw fadd %{{.*}}, %{{.*}} monotonic : !llvm.ptr, f32

mlir/test/Target/LLVMIR/Import/instructions.ll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,13 +368,18 @@ define void @load_store(ptr %ptr) {
368368

369369
; // -----
370370

371-
; CHECK-LABEL: @atomic_load
371+
; CHECK-LABEL: @atomic_load_store
372372
; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]]
373-
define void @atomic_load(ptr %ptr) {
373+
define void @atomic_load_store(ptr %ptr) {
374374
; CHECK: %[[V1:[0-9]+]] = llvm.load %[[PTR]] atomic acquire {alignment = 8 : i64} : !llvm.ptr -> f64
375375
; CHECK: %[[V2:[0-9]+]] = llvm.load volatile %[[PTR]] atomic syncscope("singlethreaded") acquire {alignment = 16 : i64} : !llvm.ptr -> f64
376376
%1 = load atomic double, ptr %ptr acquire, align 8
377377
%2 = load atomic volatile double, ptr %ptr syncscope("singlethreaded") acquire, align 16
378+
379+
; CHECK: llvm.store %[[V1]], %[[PTR]] atomic release {alignment = 8 : i64} : f64, !llvm.ptr
380+
; CHECK: llvm.store volatile %[[V2]], %[[PTR]] atomic syncscope("singlethreaded") release {alignment = 16 : i64} : f64, !llvm.ptr
381+
store atomic double %1, ptr %ptr release, align 8
382+
store atomic volatile double %2, ptr %ptr syncscope("singlethreaded") release, align 16
378383
ret void
379384
}
380385

mlir/test/Target/LLVMIR/llvmir.mlir

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,13 +1780,20 @@ llvm.func @nontemporal_store_and_load() {
17801780

17811781
// -----
17821782

1783-
llvm.func @atomic_load(%ptr : !llvm.ptr) {
1783+
llvm.func @atomic_store_and_load(%ptr : !llvm.ptr) {
17841784
// CHECK: load atomic
1785-
// CHECK-SAME: monotonic, align 4
1786-
%1 = llvm.load %ptr atomic monotonic {alignment = 4 : i64} : !llvm.ptr -> f32
1785+
// CHECK-SAME: acquire, align 4
1786+
%1 = llvm.load %ptr atomic acquire {alignment = 4 : i64} : !llvm.ptr -> f32
17871787
// CHECK: load atomic
1788-
// CHECK-SAME: syncscope("singlethread") monotonic, align 4
1789-
%2 = llvm.load %ptr atomic syncscope("singlethread") monotonic {alignment = 4 : i64} : !llvm.ptr -> f32
1788+
// CHECK-SAME: syncscope("singlethread") acquire, align 4
1789+
%2 = llvm.load %ptr atomic syncscope("singlethread") acquire {alignment = 4 : i64} : !llvm.ptr -> f32
1790+
1791+
// CHECK: store atomic
1792+
// CHECK-SAME: release, align 4
1793+
llvm.store %1, %ptr atomic release {alignment = 4 : i64} : f32, !llvm.ptr
1794+
// CHECK: store atomic
1795+
// CHECK-SAME: syncscope("singlethread") release, align 4
1796+
llvm.store %2, %ptr atomic syncscope("singlethread") release {alignment = 4 : i64} : f32, !llvm.ptr
17901797
llvm.return
17911798
}
17921799

0 commit comments

Comments
 (0)