| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart | |
| ; RUN: opt -passes='require<libcall-lowering-info>,atomic-expand' -S -mtriple=thumbv7--none-eabi %s | FileCheck %s | |
| ;; With the "thread-model"="single" module flag, AtomicExpand lowers | |
| ;; atomic operations to their non-atomic form. fences are dropped and | |
| ;; atomic loads/stores/rmw/cmpxchg become plain memory operations, | |
| ;; with no ldrex/strex or dmb barriers emitted. | |
| define i32 @load(ptr %p) { | |
| ; CHECK-LABEL: @load( | |
| ; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret i32 [[V]] | |
| ; | |
| %v = load atomic i32, ptr %p seq_cst, align 4 | |
| ret i32 %v | |
| } | |
| define void @store(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @store( | |
| ; CHECK-NEXT: store i32 [[V:%.*]], ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| store atomic i32 %v, ptr %p seq_cst, align 4 | |
| ret void | |
| } | |
| define i32 @rmw(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @rmw( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]] | |
| ; CHECK-NEXT: store i32 [[NEW]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: ret i32 [[TMP1]] | |
| ; | |
| %old = atomicrmw add ptr %p, i32 %v seq_cst | |
| ret i32 %old | |
| } | |
| define { i32, i1 } @cmpxchg(ptr %p, i32 %cmp, i32 %new) !prof !1 { | |
| ; CHECK-LABEL: @cmpxchg( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]] | |
| ; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 [[TMP1]], !prof [[PROF2:![0-9]+]] | |
| ; CHECK-NEXT: store i32 [[TMP3]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 1 | |
| ; CHECK-NEXT: ret { i32, i1 } [[TMP5]] | |
| ; | |
| %res = cmpxchg ptr %p, i32 %cmp, i32 %new seq_cst seq_cst | |
| ret { i32, i1 } %res | |
| } | |
| define void @fence() { | |
| ; CHECK-LABEL: @fence( | |
| ; CHECK-NEXT: ret void | |
| ; | |
| fence seq_cst | |
| ret void | |
| } | |
| ;; The volatile qualifier is preserved | |
| define i32 @load_volatile(ptr %p) { | |
| ; CHECK-LABEL: @load_volatile( | |
| ; CHECK-NEXT: [[V:%.*]] = load volatile i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret i32 [[V]] | |
| ; | |
| %v = load atomic volatile i32, ptr %p seq_cst, align 4 | |
| ret i32 %v | |
| } | |
| define void @store_volatile(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @store_volatile( | |
| ; CHECK-NEXT: store volatile i32 [[V:%.*]], ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| store atomic volatile i32 %v, ptr %p seq_cst, align 4 | |
| ret void | |
| } | |
| define i32 @rmw_volatile(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @rmw_volatile( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load volatile i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]] | |
| ; CHECK-NEXT: store volatile i32 [[NEW]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: ret i32 [[TMP1]] | |
| ; | |
| %old = atomicrmw volatile add ptr %p, i32 %v seq_cst | |
| ret i32 %old | |
| } | |
| define { i32, i1 } @cmpxchg_volatile(ptr %p, i32 %cmp, i32 %new) { | |
| ; CHECK-LABEL: @cmpxchg_volatile( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load volatile i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]] | |
| ; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 [[TMP1]] | |
| ; CHECK-NEXT: store volatile i32 [[TMP3]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 1 | |
| ; CHECK-NEXT: ret { i32, i1 } [[TMP5]] | |
| ; | |
| %res = cmpxchg volatile ptr %p, i32 %cmp, i32 %new seq_cst seq_cst | |
| ret { i32, i1 } %res | |
| } | |
| ;; Elementwise atomic loads/stores/rmw also lose their atomicity, and the | |
| ;; elementwise marker is dropped along with it. | |
| define <2 x i32> @load_elementwise(ptr %p) { | |
| ; CHECK-LABEL: @load_elementwise( | |
| ; CHECK-NEXT: [[V:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8 | |
| ; CHECK-NEXT: ret <2 x i32> [[V]] | |
| ; | |
| %v = load atomic elementwise <2 x i32>, ptr %p monotonic, align 8 | |
| ret <2 x i32> %v | |
| } | |
| define void @store_elementwise(ptr %p, <2 x i32> %v) { | |
| ; CHECK-LABEL: @store_elementwise( | |
| ; CHECK-NEXT: store <2 x i32> [[V:%.*]], ptr [[P:%.*]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| store atomic elementwise <2 x i32> %v, ptr %p monotonic, align 8 | |
| ret void | |
| } | |
| define <2 x i32> @rmw_elementwise(ptr %p, <2 x i32> %v) { | |
| ; CHECK-LABEL: @rmw_elementwise( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8 | |
| ; CHECK-NEXT: [[NEW:%.*]] = add <2 x i32> [[TMP1]], [[V:%.*]] | |
| ; CHECK-NEXT: store <2 x i32> [[NEW]], ptr [[P]], align 8 | |
| ; CHECK-NEXT: ret <2 x i32> [[TMP1]] | |
| ; | |
| %old = atomicrmw elementwise add ptr %p, <2 x i32> %v monotonic, align 8 | |
| ret <2 x i32> %old | |
| } | |
| ;; A non-system syncscope is dropped along with the atomicity: the resulting | |
| ;; non-atomic load/store/rmw must not carry a syncscope (the verifier rejects a | |
| ;; non-atomic access with a synchronization scope). | |
| define i32 @load_syncscope(ptr %p) { | |
| ; CHECK-LABEL: @load_syncscope( | |
| ; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret i32 [[V]] | |
| ; | |
| %v = load atomic i32, ptr %p syncscope("singlethread") seq_cst, align 4 | |
| ret i32 %v | |
| } | |
| define void @store_syncscope(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @store_syncscope( | |
| ; CHECK-NEXT: store i32 [[V:%.*]], ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| store atomic i32 %v, ptr %p syncscope("singlethread") seq_cst, align 4 | |
| ret void | |
| } | |
| define i32 @rmw_syncscope(ptr %p, i32 %v) { | |
| ; CHECK-LABEL: @rmw_syncscope( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]] | |
| ; CHECK-NEXT: store i32 [[NEW]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: ret i32 [[TMP1]] | |
| ; | |
| %old = atomicrmw add ptr %p, i32 %v syncscope("agent") seq_cst | |
| ret i32 %old | |
| } | |
| define { i32, i1 } @cmpxchg_syncscope(ptr %p, i32 %cmp, i32 %new) { | |
| ; CHECK-LABEL: @cmpxchg_syncscope( | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]] | |
| ; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 [[TMP1]] | |
| ; CHECK-NEXT: store i32 [[TMP3]], ptr [[P]], align 4 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 1 | |
| ; CHECK-NEXT: ret { i32, i1 } [[TMP5]] | |
| ; | |
| %res = cmpxchg ptr %p, i32 %cmp, i32 %new syncscope("agent") seq_cst seq_cst | |
| ret { i32, i1 } %res | |
| } | |
| !llvm.module.flags = !{!0} | |
| !0 = !{i32 1, !"thread-model", !"single"} | |
| !1 = !{!"function_entry_count", i32 10} | |
| ;. | |
| ; CHECK: [[PROF2]] = !{!"unknown", !"loweratomic"} | |
| ;. |