blob: 4cca5eba03f239578ac19fb530feb9a8b4e8e39f [file]
!===----------------------------------------------------------------------===!
! This directory can be used to add Integration tests involving multiple
! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
! contain executable tests. We should only add tests here sparingly and only
! if there is no other way to test. Repeat this message in each test that is
! added to this directory and sub-directories.
!===----------------------------------------------------------------------===!
!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s --check-prefix=HLFIR
!RUN: %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s --check-prefix=FIR
!RUN: %if x86-registered-target %{ %flang_fc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s --check-prefix=LLVM %}
!RUN: %if aarch64-registered-target %{ %flang_fc1 -triple aarch64-unknown-linux-gnu -emit-llvm -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s --check-prefix=LLVM %}
! OpenMP atomic compare-capture tracked through every stage of the compiler
! (HLFIR, FIR/omp dialect, LLVM IR) for every operand type: integer, real,
! logical and complex under ==, plus integer and real under min/max (<, >).
! Equality lowers to a cmpxchg; min/max lowers to an atomicrmw.
! ===========================================================================
! integer == (postfix): cmpxchg + select(new value) + store into v
! HLFIR-LABEL: func.func @_QPcc_integer(
! HLFIR: %[[X:.*]]:2 = hlfir.declare %{{.*}}Ex"
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.compare %[[X]]#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: }
! HLFIR: omp.atomic.read %{{.*}} = %[[X]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_integer(
! FIR: omp.atomic.capture memory_order(relaxed) {
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR: arith.cmpi eq
! FIR: }
! FIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! FIR: }
! LLVM-LABEL: define void @cc_integer_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %[[D:.*]] monotonic monotonic
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
! LLVM: %[[OK:.*]] = extractvalue { i32, i1 } %[[RES]], 1
! LLVM: %[[NEW:.*]] = select i1 %[[OK]], i32 %[[D]], i32 %[[OLD]]
! LLVM: store i32 %[[NEW]], ptr
subroutine cc_integer(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture
if (x == e) x = d
v = x
!$omp end atomic
end
! ===========================================================================
! real == (postfix): equality on floats uses the HandleFPNegZero cmpxchg path.
! HLFIR-LABEL: func.func @_QPcc_real(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<f32> {
! HLFIR: arith.cmpf oeq
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_real(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<f32> {
! FIR: arith.cmpf oeq
! FIR: }
! LLVM-LABEL: define void @cc_real_(
! LLVM: cmpxchg ptr
! LLVM: store float %{{.*}}, ptr
subroutine cc_real(x, e, d, v)
real :: x, e, d, v
!$omp atomic compare capture
if (x == e) x = d
v = x
!$omp end atomic
end
! ===========================================================================
! logical == (postfix): the logical atom is converted to i32, so the omp op
! operates on !fir.ref<i32> and lowers to an i32 cmpxchg.
! HLFIR-LABEL: func.func @_QPcc_logical(
! HLFIR: fir.convert %{{.*}} : (!fir.logical<4>) -> i32
! HLFIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_logical(
! FIR: fir.convert %{{.*}} : (!fir.logical<4>) -> i32
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! LLVM-LABEL: define void @cc_logical_(
! LLVM: cmpxchg ptr %{{.*}}, i32
! LLVM: store i32 %{{.*}}, ptr
subroutine cc_logical(x, e, d, v)
logical :: x, e, d, v
!$omp atomic compare capture
if (x .eqv. e) x = d
v = x
!$omp end atomic
end
! ===========================================================================
! complex == (postfix): fir.cmpc region; lowers to an i64 (bitcast) cmpxchg
! and the captured new value is a { float, float } struct.
! HLFIR-LABEL: func.func @_QPcc_complex(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<complex<f32>> {
! HLFIR: fir.cmpc "oeq"
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_complex(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<complex<f32>> {
! FIR: fir.cmpc "oeq"
! FIR: }
! LLVM-LABEL: define void @cc_complex_(
! LLVM: %[[REEQ:.*]] = fcmp oeq float %[[REX:.*]], %[[REE:.*]]
! LLVM: %[[IMEQ:.*]] = fcmp oeq float %[[IMX:.*]], %[[IME:.*]]
! LLVM: cmpxchg ptr %{{.*}}, i64
! LLVM: store { float, float } %{{.*}}, ptr
subroutine cc_complex(x, e, d, v)
complex :: x, e, d, v
!$omp atomic compare capture
if (x == e) x = d
v = x
!$omp end atomic
end
! ===========================================================================
! integer min (x > e, postfix): atomicrmw min, v gets the new value smin(old,e).
! HLFIR-LABEL: func.func @_QPcc_integer_min(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi sgt
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_integer_min(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR: arith.cmpi sgt
! FIR: }
! LLVM-LABEL: define void @cc_integer_min_(
! LLVM: %[[OLD:.*]] = atomicrmw min ptr %{{.*}}, i32 %[[E:.*]] monotonic
! LLVM: %[[NEW:.*]] = call i32 @llvm.smin.i32(i32 %[[OLD]], i32 %[[E]])
! LLVM: store i32 %[[NEW]], ptr
subroutine cc_integer_min(x, e, v)
integer :: x, e, v
!$omp atomic compare capture
if (x > e) x = e
v = x
!$omp end atomic
end
! ===========================================================================
! integer max (x < e, postfix): atomicrmw max, v gets the new value smax(old,e).
! HLFIR-LABEL: func.func @_QPcc_integer_max(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi slt
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_integer_max(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR: arith.cmpi slt
! FIR: }
! LLVM-LABEL: define void @cc_integer_max_(
! LLVM: %[[OLD:.*]] = atomicrmw max ptr %{{.*}}, i32 %[[E:.*]] monotonic
! LLVM: %[[NEW:.*]] = call i32 @llvm.smax.i32(i32 %[[OLD]], i32 %[[E]])
! LLVM: store i32 %[[NEW]], ptr
subroutine cc_integer_max(x, e, v)
integer :: x, e, v
!$omp atomic compare capture
if (x < e) x = e
v = x
!$omp end atomic
end
! ===========================================================================
! real min (x > e, postfix): atomicrmw fmin, v gets minnum(old, e).
! HLFIR-LABEL: func.func @_QPcc_real_min(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<f32> {
! HLFIR: arith.cmpf ogt
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_real_min(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<f32> {
! FIR: arith.cmpf ogt
! FIR: }
! LLVM-LABEL: define void @cc_real_min_(
! LLVM: %[[OLD:.*]] = atomicrmw fmin ptr %{{.*}}, float %[[E:.*]] monotonic
! LLVM: %[[NEW:.*]] = call float @llvm.minnum.f32(float %[[OLD]], float %[[E]])
! LLVM: store float %[[NEW]], ptr
subroutine cc_real_min(x, e, v)
real :: x, e, v
!$omp atomic compare capture
if (x > e) x = e
v = x
!$omp end atomic
end
! ===========================================================================
! real max (x < e, postfix): atomicrmw fmax, v gets maxnum(old, e).
! HLFIR-LABEL: func.func @_QPcc_real_max(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<f32> {
! HLFIR: arith.cmpf olt
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_real_max(
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<f32> {
! FIR: arith.cmpf olt
! FIR: }
! LLVM-LABEL: define void @cc_real_max_(
! LLVM: %[[OLD:.*]] = atomicrmw fmax ptr %{{.*}}, float %[[E:.*]] monotonic
! LLVM: %[[NEW:.*]] = call float @llvm.maxnum.f32(float %[[OLD]], float %[[E]])
! LLVM: store float %[[NEW]], ptr
subroutine cc_real_max(x, e, v)
real :: x, e, v
!$omp atomic compare capture
if (x < e) x = e
v = x
!$omp end atomic
end
! The remaining cases exercise the different *capture-statement* forms allowed
! for atomic compare-capture (all with an integer == atom).
! ===========================================================================
! Prefix form {v = x; cond-update-stmt}: the read precedes the update, so v
! captures the OLD value of x - the read is emitted first and v is a direct
! store of the extracted old value (no select).
! HLFIR-LABEL: func.func @_QPcc_prefix(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.read %{{.*}} = %[[X:.*]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
! HLFIR: omp.atomic.compare %[[X]]#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: }
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_prefix(
! FIR: omp.atomic.capture memory_order(relaxed) {
! FIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR: }
! LLVM-LABEL: define void @cc_prefix_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %{{.*}} monotonic monotonic
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
! LLVM: store i32 %[[OLD]], ptr
subroutine cc_prefix(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture
v = x
if (x == e) x = d
!$omp end atomic
end
! ===========================================================================
! Fail-only form {if(x == e){x = d;} else {v = x;}}: v is captured only when
! the compare fails; the capture carries the fail_only attribute and lowers to
! a conditional store of the old value.
! HLFIR-LABEL: func.func @_QPcc_failonly(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: }
! HLFIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! HLFIR: } fail_only
! FIR-LABEL: func.func @_QPcc_failonly(
! FIR: omp.atomic.capture memory_order(relaxed) {
! FIR: } fail_only
! LLVM-LABEL: define void @cc_failonly_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %{{.*}} monotonic monotonic
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
! LLVM: %[[OK:.*]] = extractvalue { i32, i1 } %[[RES]], 1
! LLVM: br i1 %[[OK]], label %[[EXIT:.*]], label %[[CONT:.*]]
! LLVM: [[CONT]]:
! LLVM: store i32 %[[OLD]], ptr
subroutine cc_failonly(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture
if (x == e) then
x = d
else
v = x
end if
!$omp end atomic
end
! ===========================================================================
! Reversed comparison operand order {if(e == x){x = d;} ; v = x}: the front
! end normalises 'e == x' to the same compare-capture as 'x == e', so this
! accepts the alternate spelling and lowers identically (cmpxchg + select).
! HLFIR-LABEL: func.func @_QPcc_eq_reversed(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_eq_reversed(
! LLVM-LABEL: define void @cc_eq_reversed_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %[[D:.*]] monotonic monotonic
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
! LLVM: %[[OK:.*]] = extractvalue { i32, i1 } %[[RES]], 1
! LLVM: %[[NEW:.*]] = select i1 %[[OK]], i32 %[[D]], i32 %[[OLD]]
! LLVM: store i32 %[[NEW]], ptr
subroutine cc_eq_reversed(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture
if (e == x) x = d
v = x
!$omp end atomic
end
! ===========================================================================
! The prefix and fail-only forms for the remaining operand types (real,
! logical, complex), to confirm each type honours every capture form.
! real, prefix: v gets the old value of x (float).
! HLFIR-LABEL: func.func @_QPcc_real_prefix(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.read %{{.*}} = %[[X:.*]]#0 : !fir.ref<f32>, !fir.ref<f32>, f32
! HLFIR: omp.atomic.compare %[[X]]#0 : !fir.ref<f32> {
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_real_prefix(
! FIR: omp.atomic.read %{{.*}} : !fir.ref<f32>, !fir.ref<f32>, f32
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<f32> {
! LLVM-LABEL: define void @cc_real_prefix_(
! LLVM: cmpxchg ptr
! LLVM: store float %{{.*}}, ptr
subroutine cc_real_prefix(x, e, d, v)
real :: x, e, d, v
!$omp atomic compare capture
v = x
if (x == e) x = d
!$omp end atomic
end
! real, fail-only: conditional store of the old value (float).
! HLFIR-LABEL: func.func @_QPcc_real_failonly(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<f32> {
! HLFIR: } fail_only
! FIR-LABEL: func.func @_QPcc_real_failonly(
! FIR: } fail_only
! LLVM-LABEL: define void @cc_real_failonly_(
! LLVM: cmpxchg ptr
! LLVM: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
! LLVM: store float %{{.*}}, ptr
subroutine cc_real_failonly(x, e, d, v)
real :: x, e, d, v
!$omp atomic compare capture
if (x == e) then
x = d
else
v = x
end if
!$omp end atomic
end
! logical, prefix: v gets the old value (via the i32 atom).
! HLFIR-LABEL: func.func @_QPcc_logical_prefix(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! HLFIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR-LABEL: func.func @_QPcc_logical_prefix(
! FIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! LLVM-LABEL: define void @cc_logical_prefix_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
subroutine cc_logical_prefix(x, e, d, v)
logical :: x, e, d, v
!$omp atomic compare capture
v = x
if (x .eqv. e) x = d
!$omp end atomic
end
! logical, fail-only: conditional store (via the i32 atom).
! HLFIR-LABEL: func.func @_QPcc_logical_failonly(
! HLFIR: } fail_only
! FIR-LABEL: func.func @_QPcc_logical_failonly(
! FIR: } fail_only
! LLVM-LABEL: define void @cc_logical_failonly_(
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i32
! LLVM: %[[OK:.*]] = extractvalue { i32, i1 } %[[RES]], 1
! LLVM: br i1 %[[OK]], label %{{.*}}, label %{{.*}}
subroutine cc_logical_failonly(x, e, d, v)
logical :: x, e, d, v
!$omp atomic compare capture
if (x .eqv. e) then
x = d
else
v = x
end if
!$omp end atomic
end
! complex, prefix: v gets the old complex value (struct), no select.
! HLFIR-LABEL: func.func @_QPcc_complex_prefix(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.read %{{.*}} = %[[X:.*]]#0 : !fir.ref<complex<f32>>, !fir.ref<complex<f32>>, complex<f32>
! HLFIR: omp.atomic.compare %[[X]]#0 : !fir.ref<complex<f32>> {
! FIR-LABEL: func.func @_QPcc_complex_prefix(
! FIR: omp.atomic.read %{{.*}} : !fir.ref<complex<f32>>, !fir.ref<complex<f32>>, complex<f32>
! LLVM-LABEL: define void @cc_complex_prefix_(
! LLVM: %[[REEQ:.*]] = fcmp oeq float %[[REX:.*]], %[[REE:.*]]
! LLVM: %[[IMEQ:.*]] = fcmp oeq float %[[IMX:.*]], %[[IME:.*]]
! LLVM: %[[RES:.*]] = cmpxchg ptr %{{.*}}, i64
! LLVM: store { float, float } %{{.*}}, ptr
subroutine cc_complex_prefix(x, e, d, v)
complex :: x, e, d, v
!$omp atomic compare capture
v = x
if (x == e) x = d
!$omp end atomic
end
! complex, fail-only: conditional store of the old complex value.
! HLFIR-LABEL: func.func @_QPcc_complex_failonly(
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<complex<f32>> {
! HLFIR: } fail_only
! FIR-LABEL: func.func @_QPcc_complex_failonly(
! FIR: } fail_only
! LLVM-LABEL: define void @cc_complex_failonly_(
! LLVM: %[[REEQ:.*]] = fcmp oeq float %[[REX:.*]], %[[REE:.*]]
! LLVM: %[[IMEQ:.*]] = fcmp oeq float %[[IMX:.*]], %[[IME:.*]]
! LLVM: cmpxchg ptr %{{.*}}, i64
! LLVM: %[[OK:.*]] = phi i1
! LLVM: %[[FAILED:.*]] = xor i1 %[[OK]], true
! LLVM: br i1 %[[FAILED]], label %{{.*}}, label %{{.*}}
! LLVM: store { float, float } %{{.*}}, ptr
subroutine cc_complex_failonly(x, e, d, v)
complex :: x, e, d, v
!$omp atomic compare capture
if (x == e) then
x = d
else
v = x
end if
!$omp end atomic
end
! ===========================================================================
! weak clause (postfix): cmpxchg gets the 'weak' modifier.
! HLFIR-LABEL: func.func @_QPcc_weak(
! HLFIR: omp.atomic.capture memory_order(relaxed) {
! HLFIR: omp.atomic.compare %{{.*}}#0 : !fir.ref<i32> {
! HLFIR: arith.cmpi eq
! HLFIR: } weak
! HLFIR: omp.atomic.read %{{.*}} = %{{.*}}#0 : !fir.ref<i32>, !fir.ref<i32>, i32
! HLFIR: }
! FIR-LABEL: func.func @_QPcc_weak(
! FIR: omp.atomic.capture memory_order(relaxed) {
! FIR: omp.atomic.compare %{{.*}} : !fir.ref<i32> {
! FIR: arith.cmpi eq
! FIR: } weak
! FIR: omp.atomic.read %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, i32
! FIR: }
! LLVM-LABEL: define void @cc_weak_(
! LLVM: %[[RES:.*]] = cmpxchg weak ptr %{{.*}}, i32 %{{.*}}, i32 %[[D:.*]] monotonic monotonic
! LLVM: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
! LLVM: %[[OK:.*]] = extractvalue { i32, i1 } %[[RES]], 1
! LLVM: %[[NEW:.*]] = select i1 %[[OK]], i32 %[[D]], i32 %[[OLD]]
! LLVM: store i32 %[[NEW]], ptr
subroutine cc_weak(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture weak
if (x == e) x = d
v = x
!$omp end atomic
end