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