; 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 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 }