blob: 9a488bb0ca1dae4be9a1d66e7078f61771cc54a3 [file] [edit]
; 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"}
;.