299 lines
9.2 KiB
LLVM
299 lines
9.2 KiB
LLVM
; RUN: opt -instcombine -S -o - %s | FileCheck %s
|
|
; Check that we can replace `atomicrmw <op> LHS, 0` with `load atomic LHS`.
|
|
; This is possible when:
|
|
; - <op> LHS, 0 == LHS
|
|
; - the ordering of atomicrmw is compatible with a load (i.e., no release semantic)
|
|
|
|
; CHECK-LABEL: atomic_add_zero
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_add_zero(i32* %addr) {
|
|
%res = atomicrmw add i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_or_zero
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_or_zero(i32* %addr) {
|
|
%res = atomicrmw add i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_sub_zero
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_sub_zero(i32* %addr) {
|
|
%res = atomicrmw sub i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_and_allones
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_and_allones(i32* %addr) {
|
|
%res = atomicrmw and i32* %addr, i32 -1 monotonic
|
|
ret i32 %res
|
|
}
|
|
; CHECK-LABEL: atomic_umin_uint_max
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_umin_uint_max(i32* %addr) {
|
|
%res = atomicrmw umin i32* %addr, i32 -1 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_umax_zero
|
|
; CHECK-NEXT: %res = load atomic i32, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @atomic_umax_zero(i32* %addr) {
|
|
%res = atomicrmw umax i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_min_smax_char
|
|
; CHECK-NEXT: %res = load atomic i8, i8* %addr monotonic, align 1
|
|
; CHECK-NEXT: ret i8 %res
|
|
define i8 @atomic_min_smax_char(i8* %addr) {
|
|
%res = atomicrmw min i8* %addr, i8 127 monotonic
|
|
ret i8 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_max_smin_char
|
|
; CHECK-NEXT: %res = load atomic i8, i8* %addr monotonic, align 1
|
|
; CHECK-NEXT: ret i8 %res
|
|
define i8 @atomic_max_smin_char(i8* %addr) {
|
|
%res = atomicrmw max i8* %addr, i8 -128 monotonic
|
|
ret i8 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_fsub
|
|
; CHECK-NEXT: %res = load atomic float, float* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret float %res
|
|
define float @atomic_fsub_zero(float* %addr) {
|
|
%res = atomicrmw fsub float* %addr, float 0.0 monotonic
|
|
ret float %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_fadd
|
|
; CHECK-NEXT: %res = load atomic float, float* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret float %res
|
|
define float @atomic_fadd_zero(float* %addr) {
|
|
%res = atomicrmw fadd float* %addr, float -0.0 monotonic
|
|
ret float %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_fsub_canon
|
|
; CHECK-NEXT: %res = atomicrmw fadd float* %addr, float -0.000000e+00 release
|
|
; CHECK-NEXT: ret float %res
|
|
define float @atomic_fsub_canon(float* %addr) {
|
|
%res = atomicrmw fsub float* %addr, float 0.0 release
|
|
ret float %res
|
|
}
|
|
; CHECK-LABEL: atomic_fadd_canon
|
|
; CHECK-NEXT: %res = atomicrmw fadd float* %addr, float -0.000000e+00 release
|
|
; CHECK-NEXT: ret float %res
|
|
define float @atomic_fadd_canon(float* %addr) {
|
|
%res = atomicrmw fadd float* %addr, float -0.0 release
|
|
ret float %res
|
|
}
|
|
|
|
; Can't replace a volatile w/a load; this would eliminate a volatile store.
|
|
; CHECK-LABEL: atomic_sub_zero_volatile
|
|
; CHECK-NEXT: %res = atomicrmw volatile sub i64* %addr, i64 0 acquire
|
|
; CHECK-NEXT: ret i64 %res
|
|
define i64 @atomic_sub_zero_volatile(i64* %addr) {
|
|
%res = atomicrmw volatile sub i64* %addr, i64 0 acquire
|
|
ret i64 %res
|
|
}
|
|
|
|
|
|
; Check that the transformation properly preserve the syncscope.
|
|
; CHECK-LABEL: atomic_syncscope
|
|
; CHECK-NEXT: %res = load atomic i16, i16* %addr syncscope("some_syncscope") acquire, align 2
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_syncscope(i16* %addr) {
|
|
%res = atomicrmw or i16* %addr, i16 0 syncscope("some_syncscope") acquire
|
|
ret i16 %res
|
|
}
|
|
|
|
; By eliminating the store part of the atomicrmw, we would get rid of the
|
|
; release semantic, which is incorrect. We can canonicalize the operation.
|
|
; CHECK-LABEL: atomic_seq_cst
|
|
; CHECK-NEXT: %res = atomicrmw or i16* %addr, i16 0 seq_cst
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_seq_cst(i16* %addr) {
|
|
%res = atomicrmw add i16* %addr, i16 0 seq_cst
|
|
ret i16 %res
|
|
}
|
|
|
|
; Check that the transformation does not apply when the value is changed by
|
|
; the atomic operation (non zero constant).
|
|
; CHECK-LABEL: atomic_add_non_zero
|
|
; CHECK-NEXT: %res = atomicrmw add i16* %addr, i16 2 monotonic
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_add_non_zero(i16* %addr) {
|
|
%res = atomicrmw add i16* %addr, i16 2 monotonic
|
|
ret i16 %res
|
|
}
|
|
|
|
; CHECK-LABEL: atomic_xor_zero
|
|
; CHECK-NEXT: %res = load atomic i16, i16* %addr monotonic, align 2
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_xor_zero(i16* %addr) {
|
|
%res = atomicrmw xor i16* %addr, i16 0 monotonic
|
|
ret i16 %res
|
|
}
|
|
|
|
; Check that the transformation does not apply when the ordering is
|
|
; incompatible with a load (release). Do canonicalize.
|
|
; CHECK-LABEL: atomic_release
|
|
; CHECK-NEXT: %res = atomicrmw or i16* %addr, i16 0 release
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_release(i16* %addr) {
|
|
%res = atomicrmw sub i16* %addr, i16 0 release
|
|
ret i16 %res
|
|
}
|
|
|
|
; Check that the transformation does not apply when the ordering is
|
|
; incompatible with a load (acquire, release). Do canonicalize.
|
|
; CHECK-LABEL: atomic_acq_rel
|
|
; CHECK-NEXT: %res = atomicrmw or i16* %addr, i16 0 acq_rel
|
|
; CHECK-NEXT: ret i16 %res
|
|
define i16 @atomic_acq_rel(i16* %addr) {
|
|
%res = atomicrmw xor i16* %addr, i16 0 acq_rel
|
|
ret i16 %res
|
|
}
|
|
|
|
|
|
; CHECK-LABEL: sat_or_allones
|
|
; CHECK-NEXT: %res = atomicrmw xchg i32* %addr, i32 -1 monotonic
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @sat_or_allones(i32* %addr) {
|
|
%res = atomicrmw or i32* %addr, i32 -1 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_and_zero
|
|
; CHECK-NEXT: %res = atomicrmw xchg i32* %addr, i32 0 monotonic
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @sat_and_zero(i32* %addr) {
|
|
%res = atomicrmw and i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
; CHECK-LABEL: sat_umin_uint_min
|
|
; CHECK-NEXT: %res = atomicrmw xchg i32* %addr, i32 0 monotonic
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @sat_umin_uint_min(i32* %addr) {
|
|
%res = atomicrmw umin i32* %addr, i32 0 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_umax_uint_max
|
|
; CHECK-NEXT: %res = atomicrmw xchg i32* %addr, i32 -1 monotonic
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @sat_umax_uint_max(i32* %addr) {
|
|
%res = atomicrmw umax i32* %addr, i32 -1 monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_min_smin_char
|
|
; CHECK-NEXT: %res = atomicrmw xchg i8* %addr, i8 -128 monotonic
|
|
; CHECK-NEXT: ret i8 %res
|
|
define i8 @sat_min_smin_char(i8* %addr) {
|
|
%res = atomicrmw min i8* %addr, i8 -128 monotonic
|
|
ret i8 %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_max_smax_char
|
|
; CHECK-NEXT: %res = atomicrmw xchg i8* %addr, i8 127 monotonic
|
|
; CHECK-NEXT: ret i8 %res
|
|
define i8 @sat_max_smax_char(i8* %addr) {
|
|
%res = atomicrmw max i8* %addr, i8 127 monotonic
|
|
ret i8 %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_fadd_nan
|
|
; CHECK-NEXT: %res = atomicrmw xchg double* %addr, double 0x7FF00000FFFFFFFF release
|
|
; CHECK-NEXT: ret double %res
|
|
define double @sat_fadd_nan(double* %addr) {
|
|
%res = atomicrmw fadd double* %addr, double 0x7FF00000FFFFFFFF release
|
|
ret double %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_fsub_nan
|
|
; CHECK-NEXT: %res = atomicrmw xchg double* %addr, double 0x7FF00000FFFFFFFF release
|
|
; CHECK-NEXT: ret double %res
|
|
define double @sat_fsub_nan(double* %addr) {
|
|
%res = atomicrmw fsub double* %addr, double 0x7FF00000FFFFFFFF release
|
|
ret double %res
|
|
}
|
|
|
|
; CHECK-LABEL: sat_fsub_nan_unused
|
|
; CHECK-NEXT: store atomic double 0x7FF00000FFFFFFFF, double* %addr monotonic, align 8
|
|
; CHECK-NEXT: ret void
|
|
define void @sat_fsub_nan_unused(double* %addr) {
|
|
atomicrmw fsub double* %addr, double 0x7FF00000FFFFFFFF monotonic
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: xchg_unused_monotonic
|
|
; CHECK-NEXT: store atomic i32 0, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret void
|
|
define void @xchg_unused_monotonic(i32* %addr) {
|
|
atomicrmw xchg i32* %addr, i32 0 monotonic
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: xchg_unused_release
|
|
; CHECK-NEXT: store atomic i32 -1, i32* %addr release, align 4
|
|
; CHECK-NEXT: ret void
|
|
define void @xchg_unused_release(i32* %addr) {
|
|
atomicrmw xchg i32* %addr, i32 -1 release
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: xchg_unused_seq_cst
|
|
; CHECK-NEXT: atomicrmw xchg i32* %addr, i32 0 seq_cst
|
|
; CHECK-NEXT: ret void
|
|
define void @xchg_unused_seq_cst(i32* %addr) {
|
|
atomicrmw xchg i32* %addr, i32 0 seq_cst
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: xchg_unused_volatile
|
|
; CHECK-NEXT: atomicrmw volatile xchg i32* %addr, i32 0 monotonic
|
|
; CHECK-NEXT: ret void
|
|
define void @xchg_unused_volatile(i32* %addr) {
|
|
atomicrmw volatile xchg i32* %addr, i32 0 monotonic
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: sat_or_allones_unused
|
|
; CHECK-NEXT: store atomic i32 -1, i32* %addr monotonic, align 4
|
|
; CHECK-NEXT: ret void
|
|
define void @sat_or_allones_unused(i32* %addr) {
|
|
atomicrmw or i32* %addr, i32 -1 monotonic
|
|
ret void
|
|
}
|
|
|
|
|
|
; CHECK-LABEL: undef_operand_unused
|
|
; CHECK-NEXT: atomicrmw or i32* %addr, i32 undef monotonic
|
|
; CHECK-NEXT: ret void
|
|
define void @undef_operand_unused(i32* %addr) {
|
|
atomicrmw or i32* %addr, i32 undef monotonic
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: undef_operand_used
|
|
; CHECK-NEXT: %res = atomicrmw or i32* %addr, i32 undef monotonic
|
|
; CHECK-NEXT: ret i32 %res
|
|
define i32 @undef_operand_used(i32* %addr) {
|
|
%res = atomicrmw or i32* %addr, i32 undef monotonic
|
|
ret i32 %res
|
|
}
|
|
|
|
|
|
|