The OpenACC standard specifies an `atomic` construct in section 2.12 (of 3.3 spec), used to ensure that a specific location is accessed or updated atomically. Four different clauses are allowed: `read`, `write`, `update`, or `capture`. If no clause appears, it is as if `update` is used. The OpenMP specification defines the same clauses for `omp atomic`. The types of expression and the clauses in the OpenACC spec match the OpenMP spec exactly. The main difference is that the OpenMP specification is a superset - it includes clauses for `hint` and `memory order`. It also allows conditional expression statements. But otherwise, the expression definition matches. Thus, for OpenACC, we refactor and reuse the OpenMP implementation as follows: * The atomic operations are duplicated in OpenACC dialect. This is preferable so that each language's semantics are precisely represented even if specs have divergence. * However, since semantics overlap, a common interface between the atomic operations is being added. The semantics for the interfaces are not generic enough to be used outside of OpenACC and OpenMP, and thus new folders were added to hold common pieces of the two dialects. * The atomic interfaces define common accessors (such as getting `x` or `v`) which match the OpenMP and OpenACC specs. It also adds common verifiers intended to be called by each dialect's operation verifier. * The OpenMP write operation was updated to use `x` and `expr` to be consistent with its other operations (that use naming based on spec). The frontend lowering necessary to generate the dialect can also be reused. This will be done in a follow up change.
730 lines
19 KiB
MLIR
730 lines
19 KiB
MLIR
// RUN: mlir-opt -split-input-file -verify-diagnostics %s
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop gang {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop worker {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop vector {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop gang worker {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop gang vector {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop worker vector {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{gang, worker or vector cannot appear with the seq attr}}
|
|
acc.loop gang worker vector {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
} attributes {seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expected non-empty body.}}
|
|
acc.loop {
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{only one of "auto", "independent", "seq" can be present at the same time}}
|
|
acc.loop {
|
|
acc.yield
|
|
} attributes {auto_, seq}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{at least one operand or the default attribute must appear on the data operation}}
|
|
acc.data {
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry/exit operation or acc.getdeviceptr as defining op}}
|
|
acc.data dataOperands(%value : memref<10xf32>) {
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{at least one value must be present in dataOperands}}
|
|
acc.update
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.update_device varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{wait_devnum cannot appear without waitOperands}}
|
|
acc.update wait_devnum(%cst: index) dataOperands(%0: memref<f32>)
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.update_device varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{async attribute cannot appear with asyncOperand}}
|
|
acc.update async(%cst: index) dataOperands(%0 : memref<f32>) attributes {async}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.update_device varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{wait attribute cannot appear with waitOperands}}
|
|
acc.update wait(%cst: index) dataOperands(%0: memref<f32>) attributes {wait}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
// expected-error@+1 {{wait_devnum cannot appear without waitOperands}}
|
|
acc.wait wait_devnum(%cst: index)
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
// expected-error@+1 {{async attribute cannot appear with asyncOperand}}
|
|
acc.wait async(%cst: index) attributes {async}
|
|
|
|
// -----
|
|
|
|
acc.parallel {
|
|
// expected-error@+1 {{'acc.init' op cannot be nested in a compute operation}}
|
|
acc.init
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
acc.loop {
|
|
// expected-error@+1 {{'acc.init' op cannot be nested in a compute operation}}
|
|
acc.init
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
acc.parallel {
|
|
// expected-error@+1 {{'acc.shutdown' op cannot be nested in a compute operation}}
|
|
acc.shutdown
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
acc.loop {
|
|
// expected-error@+1 {{'acc.shutdown' op cannot be nested in a compute operation}}
|
|
acc.shutdown
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
acc.loop {
|
|
"test.openacc_dummy_op"() ({
|
|
// expected-error@+1 {{'acc.shutdown' op cannot be nested in a compute operation}}
|
|
acc.shutdown
|
|
}) : () -> ()
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{at least one operand must be present in dataOperands on the exit data operation}}
|
|
acc.exit_data attributes {async}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.getdeviceptr varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{async attribute cannot appear with asyncOperand}}
|
|
acc.exit_data async(%cst: index) dataOperands(%0 : memref<f32>) attributes {async}
|
|
acc.delete accPtr(%0 : memref<f32>)
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.getdeviceptr varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{wait_devnum cannot appear without waitOperands}}
|
|
acc.exit_data wait_devnum(%cst: index) dataOperands(%0 : memref<f32>)
|
|
acc.delete accPtr(%0 : memref<f32>)
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{at least one operand must be present in dataOperands on the enter data operation}}
|
|
acc.enter_data attributes {async}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.create varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{async attribute cannot appear with asyncOperand}}
|
|
acc.enter_data async(%cst: index) dataOperands(%0 : memref<f32>) attributes {async}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.create varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{wait attribute cannot appear with waitOperands}}
|
|
acc.enter_data wait(%cst: index) dataOperands(%0 : memref<f32>) attributes {wait}
|
|
|
|
// -----
|
|
|
|
%cst = arith.constant 1 : index
|
|
%value = memref.alloc() : memref<f32>
|
|
%0 = acc.create varPtr(%value : memref<f32>) -> memref<f32>
|
|
// expected-error@+1 {{wait_devnum cannot appear without waitOperands}}
|
|
acc.enter_data wait_devnum(%cst: index) dataOperands(%0 : memref<f32>)
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry operation as defining op}}
|
|
acc.enter_data dataOperands(%value : memref<10xf32>)
|
|
|
|
// -----
|
|
|
|
%0 = arith.constant 1.0 : f32
|
|
// expected-error@+1 {{operand #0 must be integer or index, but got 'f32'}}
|
|
%1 = acc.bounds lowerbound(%0 : f32)
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry/exit operation or acc.getdeviceptr as defining op}}
|
|
acc.update dataOperands(%value : memref<10xf32>)
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry/exit operation or acc.getdeviceptr as defining op}}
|
|
acc.parallel dataOperands(%value : memref<10xf32>) {
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry/exit operation or acc.getdeviceptr as defining op}}
|
|
acc.serial dataOperands(%value : memref<10xf32>) {
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
%value = memref.alloc() : memref<10xf32>
|
|
// expected-error@+1 {{expect data entry/exit operation or acc.getdeviceptr as defining op}}
|
|
acc.kernels dataOperands(%value : memref<10xf32>) {
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects non-empty init region}}
|
|
acc.private.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects init region first argument of the privatization type}}
|
|
acc.private.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<f32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects init region to yield a value of the privatization type}}
|
|
acc.private.recipe @privatization_i32 : !llvm.ptr<f32> init {
|
|
^bb0(%arg0 : !llvm.ptr<f32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects destroy region first argument of the privatization type}}
|
|
acc.private.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<i32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} destroy {
|
|
^bb0(%arg0 : f32):
|
|
"test.openacc_dummy_op"(%arg0) : (f32) -> ()
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects non-empty init region}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
} copy {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects init region first argument of the privatization type}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<f32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} copy {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects init region to yield a value of the privatization type}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<f32> init {
|
|
^bb0(%arg0 : !llvm.ptr<f32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} copy {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects non-empty copy region}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<i32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} copy {
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects copy region with two arguments of the privatization type}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<i32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} copy {
|
|
^bb0(%arg0 : f32):
|
|
"test.openacc_dummy_op"(%arg0) : (f32) -> ()
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects copy region with two arguments of the privatization type}}
|
|
acc.firstprivate.recipe @privatization_i32 : !llvm.ptr<i32> init {
|
|
^bb0(%arg0 : !llvm.ptr<i32>):
|
|
%c1 = arith.constant 1 : i32
|
|
%c0 = arith.constant 0 : i32
|
|
%0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr<i32>
|
|
llvm.store %c0, %0 : !llvm.ptr<i32>
|
|
acc.yield %0 : !llvm.ptr<i32>
|
|
} copy {
|
|
^bb0(%arg0 : f32, %arg1 : i32):
|
|
"test.openacc_dummy_op"(%arg0) : (f32) -> ()
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects destroy region first argument of the privatization type}}
|
|
acc.firstprivate.recipe @privatization_i32 : i32 init {
|
|
^bb0(%arg0 : i32):
|
|
%0 = arith.constant 1 : i32
|
|
acc.yield %0 : i32
|
|
} copy {
|
|
^bb0(%arg0 : i32, %arg1 : !llvm.ptr<i32>):
|
|
llvm.store %arg0, %arg1 : !llvm.ptr<i32>
|
|
acc.yield
|
|
} destroy {
|
|
^bb0(%arg0 : f32):
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expected ')'}}
|
|
acc.loop gang(static=%i64Value: i64, num=%i64Value: i64 {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects non-empty init region}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
} combiner {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects init region first argument of the reduction type}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
^bb0(%0: i32):
|
|
%1 = arith.constant 0 : i64
|
|
acc.yield %1 : i64
|
|
} combiner {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects non-empty combiner region}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
^bb0(%0: i64):
|
|
%1 = arith.constant 0 : i64
|
|
acc.yield %1 : i64
|
|
} combiner {}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects combiner region with the first two arguments of the reduction type}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
^bb0(%0: i64):
|
|
%1 = arith.constant 0 : i64
|
|
acc.yield %1 : i64
|
|
} combiner {
|
|
^bb0(%0: i32):
|
|
acc.yield %0 : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects combiner region with the first two arguments of the reduction type}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
^bb0(%0: i64):
|
|
%1 = arith.constant 0 : i64
|
|
acc.yield %1 : i64
|
|
} combiner {
|
|
^bb0(%0: i64):
|
|
acc.yield %0 : i64
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expects combiner region to yield a value of the reduction type}}
|
|
acc.reduction.recipe @reduction_i64 : i64 reduction_operator<add> init {
|
|
^bb0(%0: i64):
|
|
%1 = arith.constant 0 : i64
|
|
acc.yield %1 : i64
|
|
} combiner {
|
|
^bb0(%0: i64, %1: i64):
|
|
%2 = arith.constant 0 : i32
|
|
acc.yield %2 : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{new value expected after comma}}
|
|
acc.loop gang(static=%i64Value: i64, ) {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @fct1(%0 : !llvm.ptr<i32>) -> () {
|
|
// expected-error@+1 {{expected symbol reference @privatization_i32 to point to a private declaration}}
|
|
acc.serial private(@privatization_i32 -> %0 : !llvm.ptr<i32>) {
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{expect at least one of num, dim or static values}}
|
|
acc.loop gang() {
|
|
"test.openacc_dummy_op"() : () -> ()
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
%i64value = arith.constant 1 : i64
|
|
// expected-error@+1 {{num_gangs expects a maximum of 3 values}}
|
|
acc.parallel num_gangs(%i64value, %i64value, %i64value, %i64value : i64, i64, i64, i64) {
|
|
}
|
|
|
|
// -----
|
|
|
|
%i64value = arith.constant 1 : i64
|
|
acc.parallel {
|
|
// expected-error@+1 {{'acc.set' op cannot be nested in a compute operation}}
|
|
acc.set device_type(%i64value : i64)
|
|
acc.yield
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-error@+1 {{'acc.set' op at least one default_async, device_num, or device_type operand must appear}}
|
|
acc.set
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_write(%addr : memref<memref<i32>>, %val : i32) {
|
|
// expected-error @below {{address must dereference to value type}}
|
|
acc.atomic.write %addr = %val : memref<memref<i32>>, i32
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: f32) {
|
|
// expected-error @below {{the type of the operand must be a pointer type whose element type is the same as that of the region argument}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: f32):
|
|
%newval = llvm.fadd %xval, %expr : f32
|
|
acc.yield %newval : f32
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: i32) {
|
|
// expected-error @+2 {{op expects regions to end with 'acc.yield', found 'acc.terminator'}}
|
|
// expected-note @below {{in custom textual format, the absence of terminator implies 'acc.yield'}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: i32) {
|
|
// expected-error @below {{invalid kind of type specified}}
|
|
acc.atomic.update %x : i32 {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: i32) {
|
|
// expected-error @below {{only updated value must be returned}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval, %expr : i32, i32
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: i32, %y: f32) {
|
|
// expected-error @below {{input and yielded value must have the same type}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %y: f32
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_update(%x: memref<i32>, %expr: i32) {
|
|
// expected-error @below {{the region must accept exactly one argument}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32, %tmp: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
// expected-error @below {{expected three operations in atomic.capture region}}
|
|
acc.atomic.capture {
|
|
acc.atomic.read %v = %x : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.read %v = %x : memref<i32>, i32
|
|
acc.atomic.read %v = %x : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.write %x = %expr : memref<i32>, i32
|
|
acc.atomic.write %x = %expr : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.write %x = %expr : memref<i32>, i32
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.atomic.write %x = %expr : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{invalid sequence of operations in the capture region}}
|
|
acc.atomic.write %x = %expr : memref<i32>, i32
|
|
acc.atomic.read %v = %x : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{updated variable in atomic.update must be captured in second operation}}
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.atomic.read %v = %y : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{captured variable in atomic.read must be updated in second operation}}
|
|
acc.atomic.read %v = %y : memref<i32>, i32
|
|
acc.atomic.update %x : memref<i32> {
|
|
^bb0(%xval: i32):
|
|
%newval = llvm.add %xval, %expr : i32
|
|
acc.yield %newval : i32
|
|
}
|
|
acc.terminator
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @acc_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
|
|
acc.atomic.capture {
|
|
// expected-error @below {{captured variable in atomic.read must be updated in second operation}}
|
|
acc.atomic.read %v = %x : memref<i32>, i32
|
|
acc.atomic.write %y = %expr : memref<i32>, i32
|
|
acc.terminator
|
|
}
|
|
}
|