| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -passes=instcombine -S -o - %s | FileCheck %s |
| |
| ; Check that we don't replace `atomicrmw <op> LHS, 0` with `load atomic LHS`. |
| ; Doing that would lose the store semantic of the `atomicrmw` operation. |
| ; This may enable some other optimizations that would otherwise be illegal when |
| ; the store semantic was present (e.g., like dropping a fence). |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i32 @atomic_add_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_add_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw add ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| define i32 @atomic_or_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_or_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw add ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i32 @atomic_sub_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_sub_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw sub ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i32 @atomic_and_allones(ptr %addr) { |
| ; CHECK-LABEL: @atomic_and_allones( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw and ptr %addr, i32 -1 monotonic |
| ret i32 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i32 @atomic_umin_uint_max(ptr %addr) { |
| ; CHECK-LABEL: @atomic_umin_uint_max( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw umin ptr %addr, i32 -1 monotonic |
| ret i32 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i32 @atomic_umax_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_umax_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw umax ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i8 @atomic_min_smax_char(ptr %addr) { |
| ; CHECK-LABEL: @atomic_min_smax_char( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i8 0 monotonic, align 1 |
| ; CHECK-NEXT: ret i8 [[RES]] |
| ; |
| %res = atomicrmw min ptr %addr, i8 127 monotonic |
| ret i8 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i8 @atomic_max_smin_char(ptr %addr) { |
| ; CHECK-LABEL: @atomic_max_smin_char( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i8 0 monotonic, align 1 |
| ; CHECK-NEXT: ret i8 [[RES]] |
| ; |
| %res = atomicrmw max ptr %addr, i8 -128 monotonic |
| ret i8 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define float @atomic_fsub_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_fsub_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 monotonic, align 4 |
| ; CHECK-NEXT: ret float [[RES]] |
| ; |
| %res = atomicrmw fsub ptr %addr, float 0.0 monotonic |
| ret float %res |
| } |
| |
| define float @atomic_fadd_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_fadd_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 monotonic, align 4 |
| ; CHECK-NEXT: ret float [[RES]] |
| ; |
| %res = atomicrmw fadd ptr %addr, float -0.0 monotonic |
| ret float %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define float @atomic_fsub_canon(ptr %addr) { |
| ; CHECK-LABEL: @atomic_fsub_canon( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 release, align 4 |
| ; CHECK-NEXT: ret float [[RES]] |
| ; |
| %res = atomicrmw fsub ptr %addr, float 0.0 release |
| ret float %res |
| } |
| |
| define float @atomic_fadd_canon(ptr %addr) { |
| ; CHECK-LABEL: @atomic_fadd_canon( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 release, align 4 |
| ; CHECK-NEXT: ret float [[RES]] |
| ; |
| %res = atomicrmw fadd ptr %addr, float -0.0 release |
| ret float %res |
| } |
| |
| ; Can't replace a volatile w/a load; this would eliminate a volatile store. |
| define i64 @atomic_sub_zero_volatile(ptr %addr) { |
| ; CHECK-LABEL: @atomic_sub_zero_volatile( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw volatile sub ptr [[ADDR:%.*]], i64 0 acquire, align 8 |
| ; CHECK-NEXT: ret i64 [[RES]] |
| ; |
| %res = atomicrmw volatile sub ptr %addr, i64 0 acquire |
| ret i64 %res |
| } |
| |
| |
| ; Check that the transformation properly preserve the syncscope. |
| ; Idempotent atomicrmw are still canonicalized. |
| define i16 @atomic_syncscope(ptr %addr) { |
| ; CHECK-LABEL: @atomic_syncscope( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 syncscope("some_syncscope") acquire, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw or ptr %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. |
| define i16 @atomic_seq_cst(ptr %addr) { |
| ; CHECK-LABEL: @atomic_seq_cst( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 seq_cst, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw add ptr %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). |
| define i16 @atomic_add_non_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_add_non_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw add ptr [[ADDR:%.*]], i16 2 monotonic, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw add ptr %addr, i16 2 monotonic |
| ret i16 %res |
| } |
| |
| ; Idempotent atomicrmw are still canonicalized. |
| define i16 @atomic_xor_zero(ptr %addr) { |
| ; CHECK-LABEL: @atomic_xor_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 monotonic, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw xor ptr %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. |
| define i16 @atomic_release(ptr %addr) { |
| ; CHECK-LABEL: @atomic_release( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 release, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw sub ptr %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. |
| define i16 @atomic_acq_rel(ptr %addr) { |
| ; CHECK-LABEL: @atomic_acq_rel( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 acq_rel, align 2 |
| ; CHECK-NEXT: ret i16 [[RES]] |
| ; |
| %res = atomicrmw xor ptr %addr, i16 0 acq_rel |
| ret i16 %res |
| } |
| |
| define i32 @sat_or_allones(ptr %addr) { |
| ; CHECK-LABEL: @sat_or_allones( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw or ptr %addr, i32 -1 monotonic |
| ret i32 %res |
| } |
| |
| define i32 @sat_and_zero(ptr %addr) { |
| ; CHECK-LABEL: @sat_and_zero( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw and ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| define i32 @sat_umin_uint_min(ptr %addr) { |
| ; CHECK-LABEL: @sat_umin_uint_min( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw umin ptr %addr, i32 0 monotonic |
| ret i32 %res |
| } |
| |
| define i32 @sat_umax_uint_max(ptr %addr) { |
| ; CHECK-LABEL: @sat_umax_uint_max( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw umax ptr %addr, i32 -1 monotonic |
| ret i32 %res |
| } |
| |
| define i8 @sat_min_smin_char(ptr %addr) { |
| ; CHECK-LABEL: @sat_min_smin_char( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i8 -128 monotonic, align 1 |
| ; CHECK-NEXT: ret i8 [[RES]] |
| ; |
| %res = atomicrmw min ptr %addr, i8 -128 monotonic |
| ret i8 %res |
| } |
| |
| define i8 @sat_max_smax_char(ptr %addr) { |
| ; CHECK-LABEL: @sat_max_smax_char( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i8 127 monotonic, align 1 |
| ; CHECK-NEXT: ret i8 [[RES]] |
| ; |
| %res = atomicrmw max ptr %addr, i8 127 monotonic |
| ret i8 %res |
| } |
| |
| define double @sat_fadd_nan(ptr %addr) { |
| ; CHECK-LABEL: @sat_fadd_nan( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF release, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fadd ptr %addr, double 0x7FF00000FFFFFFFF release |
| ret double %res |
| } |
| |
| define double @sat_fsub_nan(ptr %addr) { |
| ; CHECK-LABEL: @sat_fsub_nan( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF release, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fsub ptr %addr, double 0x7FF00000FFFFFFFF release |
| ret double %res |
| } |
| |
| define void @sat_fsub_nan_unused(ptr %addr) { |
| ; CHECK-LABEL: @sat_fsub_nan_unused( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF monotonic, align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw fsub ptr %addr, double 0x7FF00000FFFFFFFF monotonic |
| ret void |
| } |
| |
| define void @xchg_unused_monotonic(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_monotonic( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw xchg ptr %addr, i32 0 monotonic |
| ret void |
| } |
| |
| define void @xchg_unused_release(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_release( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw xchg ptr %addr, i32 -1 release |
| ret void |
| } |
| |
| define void @xchg_unused_under_aligned(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_under_aligned( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 1 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw xchg ptr %addr, i32 -1 release, align 1 |
| ret void |
| } |
| |
| define void @xchg_unused_over_aligned(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_over_aligned( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw xchg ptr %addr, i32 -1 release, align 8 |
| ret void |
| } |
| |
| define void @xchg_unused_seq_cst(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_seq_cst( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 seq_cst, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw xchg ptr %addr, i32 0 seq_cst |
| ret void |
| } |
| |
| define void @xchg_unused_volatile(ptr %addr) { |
| ; CHECK-LABEL: @xchg_unused_volatile( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw volatile xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw volatile xchg ptr %addr, i32 0 monotonic |
| ret void |
| } |
| |
| define void @sat_or_allones_unused(ptr %addr) { |
| ; CHECK-LABEL: @sat_or_allones_unused( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw or ptr %addr, i32 -1 monotonic |
| ret void |
| } |
| |
| define void @undef_operand_unused(ptr %addr) { |
| ; CHECK-LABEL: @undef_operand_unused( |
| ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 undef monotonic, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| atomicrmw or ptr %addr, i32 undef monotonic |
| ret void |
| } |
| |
| define i32 @undef_operand_used(ptr %addr) { |
| ; CHECK-LABEL: @undef_operand_used( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 undef monotonic, align 4 |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %res = atomicrmw or ptr %addr, i32 undef monotonic |
| ret i32 %res |
| } |
| |
| define double @sat_fmax_inf(ptr %addr) { |
| ; CHECK-LABEL: @sat_fmax_inf( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF0000000000000 monotonic, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fmax ptr %addr, double 0x7FF0000000000000 monotonic |
| ret double %res |
| } |
| |
| define double @no_sat_fmax_inf(ptr %addr) { |
| ; CHECK-LABEL: @no_sat_fmax_inf( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fmax ptr [[ADDR:%.*]], double 1.000000e-01 monotonic, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fmax ptr %addr, double 1.000000e-01 monotonic |
| ret double %res |
| } |
| |
| define double @sat_fmin_inf(ptr %addr) { |
| ; CHECK-LABEL: @sat_fmin_inf( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0xFFF0000000000000 monotonic, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fmin ptr %addr, double 0xFFF0000000000000 monotonic |
| ret double %res |
| } |
| |
| define double @no_sat_fmin_inf(ptr %addr) { |
| ; CHECK-LABEL: @no_sat_fmin_inf( |
| ; CHECK-NEXT: [[RES:%.*]] = atomicrmw fmin ptr [[ADDR:%.*]], double 1.000000e-01 monotonic, align 8 |
| ; CHECK-NEXT: ret double [[RES]] |
| ; |
| %res = atomicrmw fmin ptr %addr, double 1.000000e-01 monotonic |
| ret double %res |
| } |