blob: 21bdbc5aaa8e35610e1058683c0bc9d218ae4869 [file] [edit]
// RUN: mlir-opt %s -acc-to-llvm -split-input-file | FileCheck %s
// CHECK-LABEL: llvm.func @convert_read
// CHECK: %[[ZERO:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[RMW:.*]] = llvm.atomicrmw _or %{{.*}}, %[[ZERO]] monotonic : !llvm.ptr, i32
// CHECK: llvm.store %[[RMW]], %{{.*}} : i32, !llvm.ptr
module {
func.func @convert_read(%v: memref<i32>, %x: memref<i32>) {
acc.atomic.read %v = %x : memref<i32>, memref<i32>, i32
return
}
}
// -----
// CHECK-LABEL: llvm.func @convert_write
// CHECK: llvm.atomicrmw xchg %{{.*}}, %{{.*}} monotonic : !llvm.ptr, i32
module {
func.func @convert_write(%x: memref<i32>, %val: i32) {
acc.atomic.write %x = %val : memref<i32>, i32
return
}
}
// -----
// Normal subtraction: update argument is the LHS, so atomicrmw sub is valid.
// CHECK-LABEL: llvm.func @convert_atomic_sub_lhs
// CHECK: llvm.atomicrmw sub %{{.*}}, %{{.*}} monotonic : !llvm.ptr, i32
// CHECK-NOT: llvm.cmpxchg
module {
func.func @convert_atomic_sub_lhs(%x: memref<i32>, %val: i32) {
acc.atomic.update %x : memref<i32> {
^bb0(%arg: i32):
%0 = arith.subi %arg, %val : i32
acc.yield %0 : i32
}
return
}
}
// -----
// Reversed subtraction: update argument is the RHS. atomicrmw sub would compute
// `*ptr - val`, which is not equivalent, so fall back to cmpxchg.
// CHECK-LABEL: llvm.func @convert_atomic_sub_rhs
// CHECK-NOT: llvm.atomicrmw sub
// CHECK: %[[LOAD:.*]] = llvm.load %{{.*}} : !llvm.ptr -> i32
// CHECK: llvm.br ^bb1(%[[LOAD]] : i32)
// CHECK: ^bb1(%[[LOOP_ARG:.*]]: i32):
// CHECK: %[[SUB:.*]] = llvm.sub %{{.*}}, %[[LOOP_ARG]]
// CHECK: %[[CMPXCHG:.*]] = llvm.cmpxchg %{{.*}}, %[[LOOP_ARG]], %[[SUB]] acq_rel monotonic : !llvm.ptr, i32
// CHECK: %[[NEW_LOADED:.*]] = llvm.extractvalue %[[CMPXCHG]][0]
// CHECK: %[[OK:.*]] = llvm.extractvalue %[[CMPXCHG]][1]
// CHECK: llvm.cond_br %[[OK]], ^bb2, ^bb1(%[[NEW_LOADED]] : i32)
module {
func.func @convert_atomic_sub_rhs(%x: memref<i32>, %val: i32) {
acc.atomic.update %x : memref<i32> {
^bb0(%arg: i32):
%0 = arith.subi %val, %arg : i32
acc.yield %0 : i32
}
return
}
}
// -----
// Test atomic update with max via cmpi/select (no direct atomicrmw mapping).
// CHECK-LABEL: llvm.func @convert_atomic_max
// CHECK: llvm.cmpxchg %{{.*}}, %{{.*}}, %{{.*}} acq_rel monotonic : !llvm.ptr, i32
module {
func.func @convert_atomic_max(%x: memref<i32>, %val: i32) {
acc.atomic.update %x : memref<i32> {
^bb0(%arg: i32):
%0 = arith.cmpi sgt, %arg, %val : i32
%1 = arith.select %0, %arg, %val : i32
acc.yield %1 : i32
}
return
}
}
// -----
// CHECK-LABEL: llvm.func @convert_capture_rw
// CHECK: %[[XPTR:.*]] = llvm.getelementptr
// CHECK: %[[LOAD:.*]] = llvm.load %[[XPTR]] : !llvm.ptr -> i32
// CHECK: llvm.br ^bb1(%[[LOAD]] : i32)
// CHECK: ^bb1(%[[LOOP_ARG:.*]]: i32):
// CHECK: %[[MUL:.*]] = llvm.mul %[[LOOP_ARG]], %[[LOOP_ARG]] : i32
// CHECK: %{{.*}} = llvm.cmpxchg %[[XPTR]], %[[LOOP_ARG]], %[[MUL]] acq_rel monotonic : !llvm.ptr, i32
// CHECK: llvm.cond_br %{{.*}}, ^bb2, ^bb1(%{{.*}} : i32)
module {
func.func @convert_capture_rw(%v: memref<i32>, %x: memref<i32>) {
%0 = memref.load %v[] : memref<i32>
%1 = arith.muli %0, %0 : i32
acc.atomic.capture {
acc.atomic.read %v = %x : memref<i32>, memref<i32>, i32
acc.atomic.write %x = %1 : memref<i32>, i32
}
return
}
}
// -----
// CHECK-LABEL: llvm.func @convert_capture_ru
// CHECK: %[[XPTR:.*]] = llvm.getelementptr
// CHECK: %[[LOAD:.*]] = llvm.load %[[XPTR]] : !llvm.ptr -> i32
// CHECK: llvm.br ^bb1(%[[LOAD]] : i32)
// CHECK: ^bb1(%[[LOOP_ARG:.*]]: i32):
// CHECK: %[[MUL:.*]] = llvm.mul %[[LOOP_ARG]], %[[LOOP_ARG]] : i32
// CHECK: %{{.*}} = llvm.cmpxchg %[[XPTR]], %[[LOOP_ARG]], %[[MUL]] acq_rel monotonic : !llvm.ptr, i32
// CHECK: llvm.cond_br %{{.*}}, ^bb2, ^bb1(%{{.*}} : i32)
module {
func.func @convert_capture_ru(%v: memref<i32>, %x: memref<i32>) {
%0 = memref.load %v[] : memref<i32>
acc.atomic.capture {
acc.atomic.read %v = %x : memref<i32>, memref<i32>, i32
acc.atomic.update %x : memref<i32> {
^bb0(%arg: i32):
%1 = arith.muli %0, %arg : i32
acc.yield %1 : i32
}
}
return
}
}
// -----
// CHECK-LABEL: llvm.func @convert_capture_ur
// CHECK: llvm.cmpxchg %{{.*}}, %{{.*}}, %{{.*}} acq_rel monotonic : !llvm.ptr, i32
// CHECK: llvm.store %{{.*}}, %{{.*}} : i32, !llvm.ptr
module {
func.func @convert_capture_ur(%v: memref<i32>, %x: memref<i32>, %val: i32) {
acc.atomic.capture {
acc.atomic.update %x : memref<i32> {
^bb0(%arg: i32):
%0 = arith.addi %arg, %val : i32
acc.yield %0 : i32
}
acc.atomic.read %v = %x : memref<i32>, memref<i32>, i32
}
return
}
}
// -----
// Test per-component atomicrmw for double complex (complex<f64>) atomic update.
// CHECK-LABEL: llvm.func @double_complex_atomic_add
// CHECK: %[[GEP0:.*]] = llvm.getelementptr %{{.*}}[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(f64, f64)>
// CHECK: llvm.atomicrmw fadd %[[GEP0]], %{{.*}} monotonic : !llvm.ptr, f64
// CHECK: %[[GEP1:.*]] = llvm.getelementptr %{{.*}}[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(f64, f64)>
// CHECK: llvm.atomicrmw fadd %[[GEP1]], %{{.*}} monotonic : !llvm.ptr, f64
module {
func.func @double_complex_atomic_add(%x: memref<complex<f64>>, %val: complex<f64>) {
%re_val = complex.re %val : complex<f64>
%im_val = complex.im %val : complex<f64>
acc.atomic.update %x : memref<complex<f64>> {
^bb0(%arg: complex<f64>):
%re = complex.re %arg : complex<f64>
%im = complex.im %arg : complex<f64>
%new_re = arith.addf %re, %re_val : f64
%new_im = arith.addf %im, %im_val : f64
%result = complex.create %new_re, %new_im : complex<f64>
acc.yield %result : complex<f64>
}
return
}
}
// -----
// Two binops on the real lane only must not take the dual-atomicrmw path.
// CHECK-LABEL: llvm.func @double_complex_duplicate_re_lane
// CHECK-NOT: llvm.atomicrmw
// CHECK: llvm.cmpxchg
module {
func.func @double_complex_duplicate_re_lane(%x: memref<complex<f64>>, %val: complex<f64>) {
%re_val = complex.re %val : complex<f64>
%im_val = complex.im %val : complex<f64>
acc.atomic.update %x : memref<complex<f64>> {
^bb0(%arg: complex<f64>):
%re = complex.re %arg : complex<f64>
%im = complex.im %arg : complex<f64>
%a = arith.addf %re, %re_val : f64
%b = arith.addf %re, %im_val : f64
%result = complex.create %a, %b : complex<f64>
acc.yield %result : complex<f64>
}
return
}
}