blob: 71a800e6fd81be301eab00292f690e36873ffdee [file] [log] [blame]
; This tests each of the supported NaCl atomic instructions for every
; size allowed.
; RUN: %p2i -i %s --filetype=obj --disassemble --args -O2 \
; RUN: -allow-externally-defined-symbols | FileCheck %s
; RUN: %p2i -i %s --filetype=obj --disassemble --args -O2 \
; RUN: -allow-externally-defined-symbols | FileCheck --check-prefix=O2 %s
; RUN: %p2i -i %s --filetype=obj --disassemble --args -Om1 \
; RUN: -allow-externally-defined-symbols | FileCheck %s
; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
; RUN: --target arm32 -i %s --args -O2 \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
; RUN: --check-prefix=ARM32
; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
; RUN: --target arm32 -i %s --args -O2 \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
; RUN: --check-prefix=ARM32O2
; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
; RUN: --target arm32 -i %s --args -Om1 \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
; RUN: --check-prefix=ARM32
declare i8 @llvm.nacl.atomic.load.i8(i8*, i32)
declare i16 @llvm.nacl.atomic.load.i16(i16*, i32)
declare i32 @llvm.nacl.atomic.load.i32(i32*, i32)
declare i64 @llvm.nacl.atomic.load.i64(i64*, i32)
declare void @llvm.nacl.atomic.store.i8(i8, i8*, i32)
declare void @llvm.nacl.atomic.store.i16(i16, i16*, i32)
declare void @llvm.nacl.atomic.store.i32(i32, i32*, i32)
declare void @llvm.nacl.atomic.store.i64(i64, i64*, i32)
declare i8 @llvm.nacl.atomic.rmw.i8(i32, i8*, i8, i32)
declare i16 @llvm.nacl.atomic.rmw.i16(i32, i16*, i16, i32)
declare i32 @llvm.nacl.atomic.rmw.i32(i32, i32*, i32, i32)
declare i64 @llvm.nacl.atomic.rmw.i64(i32, i64*, i64, i32)
declare i8 @llvm.nacl.atomic.cmpxchg.i8(i8*, i8, i8, i32, i32)
declare i16 @llvm.nacl.atomic.cmpxchg.i16(i16*, i16, i16, i32, i32)
declare i32 @llvm.nacl.atomic.cmpxchg.i32(i32*, i32, i32, i32, i32)
declare i64 @llvm.nacl.atomic.cmpxchg.i64(i64*, i64, i64, i32, i32)
declare void @llvm.nacl.atomic.fence(i32)
declare void @llvm.nacl.atomic.fence.all()
declare i1 @llvm.nacl.atomic.is.lock.free(i32, i8*)
@SzGlobal8 = internal global [1 x i8] zeroinitializer, align 1
@SzGlobal16 = internal global [2 x i8] zeroinitializer, align 2
@SzGlobal32 = internal global [4 x i8] zeroinitializer, align 4
@SzGlobal64 = internal global [8 x i8] zeroinitializer, align 8
; NOTE: The LLC equivalent for 16-bit atomic operations are expanded
; as 32-bit operations. For Subzero, assume that real 16-bit operations
; will be usable (the validator will be fixed):
; https://code.google.com/p/nativeclient/issues/detail?id=2981
;;; Load
; x86 guarantees load/store to be atomic if naturally aligned.
; The PNaCl IR requires all atomic accesses to be naturally aligned.
define internal i32 @test_atomic_load_8(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
; parameter value "6" is for the sequential consistency memory order.
%i = call i8 @llvm.nacl.atomic.load.i8(i8* %ptr, i32 6)
%i2 = sub i8 %i, 0
%r = zext i8 %i2 to i32
ret i32 %r
}
; CHECK-LABEL: test_atomic_load_8
; CHECK: mov {{.*}},DWORD
; CHECK: mov {{.*}},BYTE
; ARM32-LABEL: test_atomic_load_8
; ARM32: ldrb r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i32 @test_atomic_load_16(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i16*
%i = call i16 @llvm.nacl.atomic.load.i16(i16* %ptr, i32 6)
%i2 = sub i16 %i, 0
%r = zext i16 %i2 to i32
ret i32 %r
}
; CHECK-LABEL: test_atomic_load_16
; CHECK: mov {{.*}},DWORD
; CHECK: mov {{.*}},WORD
; ARM32-LABEL: test_atomic_load_16
; ARM32: ldrh r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i32 @test_atomic_load_32(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%r = call i32 @llvm.nacl.atomic.load.i32(i32* %ptr, i32 6)
ret i32 %r
}
; CHECK-LABEL: test_atomic_load_32
; CHECK: mov {{.*}},DWORD
; CHECK: mov {{.*}},DWORD
; ARM32-LABEL: test_atomic_load_32
; ARM32: ldr r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i64 @test_atomic_load_64(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%r = call i64 @llvm.nacl.atomic.load.i64(i64* %ptr, i32 6)
ret i64 %r
}
; CHECK-LABEL: test_atomic_load_64
; CHECK: movq x{{.*}},QWORD
; CHECK: movq QWORD {{.*}},x{{.*}}
; ARM32-LABEL: test_atomic_load_64
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i32 @test_atomic_load_32_with_arith(i32 %iptr) {
entry:
br label %next
next:
%ptr = inttoptr i32 %iptr to i32*
%r = call i32 @llvm.nacl.atomic.load.i32(i32* %ptr, i32 6)
%r2 = sub i32 32, %r
ret i32 %r2
}
; CHECK-LABEL: test_atomic_load_32_with_arith
; CHECK: mov {{.*}},DWORD
; The next instruction may be a separate load or folded into an add.
;
; In O2 mode, we know that the load and sub are going to be fused.
; O2-LABEL: test_atomic_load_32_with_arith
; O2: mov {{.*}},DWORD
; O2: sub {{.*}},DWORD
; ARM32-LABEL: test_atomic_load_32_with_arith
; ARM32: ldr r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i32 @test_atomic_load_32_ignored(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.load.i32(i32* %ptr, i32 6)
ret i32 0
}
; CHECK-LABEL: test_atomic_load_32_ignored
; CHECK: mov {{.*}},DWORD
; CHECK: mov {{.*}},DWORD
; O2-LABEL: test_atomic_load_32_ignored
; O2: mov {{.*}},DWORD
; O2: mov {{.*}},DWORD
; ARM32-LABEL: test_atomic_load_32_ignored
; ARM32: ldr r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal i64 @test_atomic_load_64_ignored(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%ignored = call i64 @llvm.nacl.atomic.load.i64(i64* %ptr, i32 6)
ret i64 0
}
; CHECK-LABEL: test_atomic_load_64_ignored
; CHECK: movq x{{.*}},QWORD
; CHECK: movq QWORD {{.*}},x{{.*}}
; ARM32-LABEL: test_atomic_load_64_ignored
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
;;; Store
define internal void @test_atomic_store_8(i32 %iptr, i32 %v) {
entry:
%truncv = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
call void @llvm.nacl.atomic.store.i8(i8 %truncv, i8* %ptr, i32 6)
ret void
}
; CHECK-LABEL: test_atomic_store_8
; CHECK: mov BYTE
; CHECK: mfence
; ARM32-LABEL: test_atomic_store_8
; ARM32: dmb
; ARM32: strb r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal void @test_atomic_store_16(i32 %iptr, i32 %v) {
entry:
%truncv = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
call void @llvm.nacl.atomic.store.i16(i16 %truncv, i16* %ptr, i32 6)
ret void
}
; CHECK-LABEL: test_atomic_store_16
; CHECK: mov WORD
; CHECK: mfence
; ARM32-LABEL: test_atomic_store_16
; ARM32: dmb
; ARM32: strh r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal void @test_atomic_store_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
call void @llvm.nacl.atomic.store.i32(i32 %v, i32* %ptr, i32 6)
ret void
}
; CHECK-LABEL: test_atomic_store_32
; CHECK: mov DWORD
; CHECK: mfence
; ARM32-LABEL: test_atomic_store_32
; ARM32: dmb
; ARM32: str r{{[0-9]+}}, [r{{[0-9]+}}
; ARM32: dmb
define internal void @test_atomic_store_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
call void @llvm.nacl.atomic.store.i64(i64 %v, i64* %ptr, i32 6)
ret void
}
; CHECK-LABEL: test_atomic_store_64
; CHECK: movq x{{.*}},QWORD
; CHECK: movq QWORD {{.*}},x{{.*}}
; CHECK: mfence
; ARM32-LABEL: test_atomic_store_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [[MEM:.*]]
; ARM32: strexd [[S:r[0-9]+]], r{{[0-9]+}}, r{{[0-9]+}}, [[MEM]]
; ARM32: cmp [[S]], #0
; ARM32: bne
; ARM32: dmb
define internal void @test_atomic_store_64_const(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i64*
call void @llvm.nacl.atomic.store.i64(i64 12345678901234, i64* %ptr, i32 6)
ret void
}
; CHECK-LABEL: test_atomic_store_64_const
; CHECK: mov {{.*}},0x73ce2ff2
; CHECK: mov {{.*}},0xb3a
; CHECK: movq x{{.*}},QWORD
; CHECK: movq QWORD {{.*}},x{{.*}}
; CHECK: mfence
; ARM32-LABEL: test_atomic_store_64_const
; ARM32: movw [[T0:r[0-9]+]], #12274
; ARM32: movt [[T0]], #29646
; ARM32: movw r{{[0-9]+}}, #2874
; ARM32: dmb
; ARM32: .L[[RETRY:.*]]:
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [[MEM:.*]]
; ARM32: strexd [[S:r[0-9]+]], r{{[0-9]+}}, r{{[0-9]+}}, [[MEM]]
; ARM32: cmp [[S]], #0
; ARM32: bne .L[[RETRY]]
; ARM32: dmb
;;; RMW
;; add
define internal i32 @test_atomic_rmw_add_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
; "1" is an atomic add, and "6" is sequential consistency.
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 1, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_add_8
; CHECK: lock xadd BYTE {{.*}},[[REG:.*]]
; CHECK: {{mov|movzx}} {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_add_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: add
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_add_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 1, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_add_16
; CHECK: lock xadd WORD {{.*}},[[REG:.*]]
; CHECK: {{mov|movzx}} {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_add_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: add
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_add_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_add_32
; CHECK: lock xadd DWORD {{.*}},[[REG:.*]]
; CHECK: mov {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_add_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: add
; ARM32: strex
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_add_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_add_64
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: [[LABEL:[^ ]*]]: {{.*}} mov ebx,eax
; RHS of add cannot be any of the e[abcd]x regs because they are
; clobbered in the loop, and the RHS needs to be remain live.
; CHECK: add ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: adc ecx,{{.*e.[^x]}}
; Ptr cannot be eax, ebx, ecx, or edx (used up for the expected and desired).
; It can be esi, edi, or ebp though, for example (so we need to be careful
; about rejecting eb* and ed*.)
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK: jne [[LABEL]]
; ARM32-LABEL: test_atomic_rmw_add_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: adds
; ARM32: adc
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
; Same test as above, but with a global address to test FakeUse issues.
define internal i64 @test_atomic_rmw_add_64_global(i64 %v) {
entry:
%ptr = bitcast [8 x i8]* @SzGlobal64 to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_add_64_global
; ARM32-LABEL: test_atomic_rmw_add_64_global
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: adds
; ARM32: adc
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
; Test with some more register pressure. When we have an alloca, ebp is
; used to manage the stack frame, so it cannot be used as a register either.
declare void @use_ptr(i32 %iptr)
define internal i64 @test_atomic_rmw_add_64_alloca(i32 %iptr, i64 %v) {
entry:
br label %eblock ; Disable alloca optimization
eblock:
%alloca_ptr = alloca i8, i32 16, align 16
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr, i64 %v, i32 6)
store i8 0, i8* %alloca_ptr, align 1
store i8 1, i8* %alloca_ptr, align 1
store i8 2, i8* %alloca_ptr, align 1
store i8 3, i8* %alloca_ptr, align 1
%__5 = ptrtoint i8* %alloca_ptr to i32
call void @use_ptr(i32 %__5)
ret i64 %old
}
; CHECK-LABEL: test_atomic_rmw_add_64_alloca
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; Ptr cannot be eax, ebx, ecx, or edx (used up for the expected and desired).
; It also cannot be ebp since we use that for alloca. Also make sure it's
; not esp, since that's the stack pointer and mucking with it will break
; the later use_ptr function call.
; That pretty much leaves esi, or edi as the only viable registers.
; CHECK: lock cmpxchg8b QWORD PTR [e{{[ds]}}i]
; CHECK: call {{.*}} R_{{.*}} use_ptr
; ARM32-LABEL: test_atomic_rmw_add_64_alloca
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: adds
; ARM32: adc
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_add_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; Technically this could use "lock add" instead of "lock xadd", if liveness
; tells us that the destination variable is dead.
; CHECK-LABEL: test_atomic_rmw_add_32_ignored
; CHECK: lock xadd DWORD {{.*}},[[REG:.*]]
; ARM32-LABEL: test_atomic_rmw_add_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: add
; ARM32: strex
; ARM32: bne
; ARM32: dmb
; Atomic RMW 64 needs to be expanded into its own loop.
; Make sure that works w/ non-trivial function bodies.
define internal i64 @test_atomic_rmw_add_64_loop(i32 %iptr, i64 %v) {
entry:
%x = icmp ult i64 %v, 100
br i1 %x, label %err, label %loop
loop:
%v_next = phi i64 [ %v, %entry ], [ %next, %loop ]
%ptr = inttoptr i32 %iptr to i64*
%next = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr, i64 %v_next, i32 6)
%success = icmp eq i64 %next, 100
br i1 %success, label %done, label %loop
done:
ret i64 %next
err:
ret i64 0
}
; CHECK-LABEL: test_atomic_rmw_add_64_loop
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: [[LABEL:[^ ]*]]: {{.*}} mov ebx,eax
; CHECK: add ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: adc ecx,{{.*e.[^x]}}
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}+0x0]
; CHECK: jne [[LABEL]]
; ARM32-LABEL: test_atomic_rmw_add_64_loop
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: adds
; ARM32: adc
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
; ARM32: b
;; sub
define internal i32 @test_atomic_rmw_sub_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 2, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_sub_8
; CHECK: neg [[REG:.*]]
; CHECK: lock xadd BYTE {{.*}},[[REG]]
; CHECK: {{mov|movzx}} {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_sub_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: sub
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_sub_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 2, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_sub_16
; CHECK: neg [[REG:.*]]
; CHECK: lock xadd WORD {{.*}},[[REG]]
; CHECK: {{mov|movzx}} {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_sub_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: sub
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_sub_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 2, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_sub_32
; CHECK: neg [[REG:.*]]
; CHECK: lock xadd DWORD {{.*}},[[REG]]
; CHECK: mov {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_sub_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: sub
; ARM32: strex
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_sub_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 2, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_sub_64
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: [[LABEL:[^ ]*]]: {{.*}} mov ebx,eax
; CHECK: sub ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: sbb ecx,{{.*e.[^x]}}
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK: jne [[LABEL]]
; ARM32-LABEL: test_atomic_rmw_sub_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: subs
; ARM32: sbc
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_sub_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 2, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; Could use "lock sub" instead of "neg; lock xadd"
; CHECK-LABEL: test_atomic_rmw_sub_32_ignored
; CHECK: neg [[REG:.*]]
; CHECK: lock xadd DWORD {{.*}},[[REG]]
; ARM32-LABEL: test_atomic_rmw_sub_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: sub
; ARM32: strex
; ARM32: bne
; ARM32: dmb
;; or
define internal i32 @test_atomic_rmw_or_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 3, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_or_8
; CHECK: mov al,BYTE PTR
; Dest cannot be eax here, because eax is used for the old value. Also want
; to make sure that cmpxchg's source is the same register.
; CHECK: or [[REG:[^a].]]
; CHECK: lock cmpxchg BYTE PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_or_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: orr
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
; Same test as above, but with a global address to test FakeUse issues.
define internal i32 @test_atomic_rmw_or_8_global(i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = bitcast [1 x i8]* @SzGlobal8 to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 3, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_or_8_global
; ARM32-LABEL: test_atomic_rmw_or_8_global
; ARM32: dmb
; ARM32: movw [[PTR:r[0-9]+]], #:lower16:SzGlobal8
; ARM32: movt [[PTR]], #:upper16:SzGlobal8
; ARM32: ldrexb r{{[0-9]+}}, {{[[]}}[[PTR]]{{[]]}}
; ARM32: orr
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_or_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 3, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_or_16
; CHECK: mov ax,WORD PTR
; CHECK: or [[REG:[^a].]]
; CHECK: lock cmpxchg WORD PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_or_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: orr
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
; Same test as above, but with a global address to test FakeUse issues.
define internal i32 @test_atomic_rmw_or_16_global(i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = bitcast [2 x i8]* @SzGlobal16 to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 3, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_or_16_global
; ARM32-LABEL: test_atomic_rmw_or_16_global
; ARM32: dmb
; ARM32: movw [[PTR:r[0-9]+]], #:lower16:SzGlobal16
; ARM32: movt [[PTR]], #:upper16:SzGlobal16
; ARM32: ldrexh r{{[0-9]+}}, {{[[]}}[[PTR]]{{[]]}}
; ARM32: orr
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_or_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 3, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_or_32
; CHECK: mov eax,DWORD PTR
; CHECK: or [[REG:e[^a].]]
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_or_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: orr
; ARM32: strex
; ARM32: bne
; ARM32: dmb
; Same test as above, but with a global address to test FakeUse issues.
define internal i32 @test_atomic_rmw_or_32_global(i32 %v) {
entry:
%ptr = bitcast [4 x i8]* @SzGlobal32 to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 3, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_or_32_global
; ARM32-LABEL: test_atomic_rmw_or_32_global
; ARM32: dmb
; ARM32: movw [[PTR:r[0-9]+]], #:lower16:SzGlobal32
; ARM32: movt [[PTR]], #:upper16:SzGlobal32
; ARM32: ldrex r{{[0-9]+}}, {{[[]}}[[PTR]]{{[]]}}
; ARM32: orr
; ARM32: strex
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_or_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 3, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_or_64
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: [[LABEL:[^ ]*]]: {{.*}} mov ebx,eax
; CHECK: or ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: or ecx,{{.*e.[^x]}}
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK: jne [[LABEL]]
; ARM32-LABEL: test_atomic_rmw_or_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: orr
; ARM32: orr
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_or_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 3, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; CHECK-LABEL: test_atomic_rmw_or_32_ignored
; Could just "lock or", if we inspect the liveness information first.
; Would also need a way to introduce "lock"'edness to binary
; operators without introducing overhead on the more common binary ops.
; CHECK: mov eax,DWORD PTR
; CHECK: or [[REG:e[^a].]]
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_or_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: orr
; ARM32: strex
; ARM32: bne
; ARM32: dmb
;; and
define internal i32 @test_atomic_rmw_and_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 4, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_and_8
; CHECK: mov al,BYTE PTR
; CHECK: and [[REG:[^a].]]
; CHECK: lock cmpxchg BYTE PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_and_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: and
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_and_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 4, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_and_16
; CHECK: mov ax,WORD PTR
; CHECK: and
; CHECK: lock cmpxchg WORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_and_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: and
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_and_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 4, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_and_32
; CHECK: mov eax,DWORD PTR
; CHECK: and
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_and_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: and
; ARM32: strex
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_and_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 4, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_and_64
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: [[LABEL:[^ ]*]]: {{.*}} mov ebx,eax
; CHECK: and ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: and ecx,{{.*e.[^x]}}
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK: jne [[LABEL]]
; ARM32-LABEL: test_atomic_rmw_and_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: and
; ARM32: and
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_and_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 4, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; CHECK-LABEL: test_atomic_rmw_and_32_ignored
; Could just "lock and"
; CHECK: mov eax,DWORD PTR
; CHECK: and
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_and_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: and
; ARM32: strex
; ARM32: bne
; ARM32: dmb
;; xor
define internal i32 @test_atomic_rmw_xor_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 5, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_xor_8
; CHECK: mov al,BYTE PTR
; CHECK: xor [[REG:[^a].]]
; CHECK: lock cmpxchg BYTE PTR [e{{[^a].}}],[[REG]]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xor_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: eor
; ARM32: strexb
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xor_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 5, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_xor_16
; CHECK: mov ax,WORD PTR
; CHECK: xor
; CHECK: lock cmpxchg WORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xor_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: eor
; ARM32: strexh
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xor_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 5, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_xor_32
; CHECK: mov eax,DWORD PTR
; CHECK: xor
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xor_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: eor
; ARM32: strex
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_xor_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 5, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_xor_64
; CHECK: push ebx
; CHECK: mov eax,DWORD PTR [{{.*}}]
; CHECK: mov edx,DWORD PTR [{{.*}}+0x4]
; CHECK: mov ebx,eax
; CHECK: or ebx,{{.*e.[^x]}}
; CHECK: mov ecx,edx
; CHECK: or ecx,{{.*e.[^x]}}
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xor_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: eor
; ARM32: eor
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, [r{{[0-9]+}}]
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xor_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 5, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; CHECK-LABEL: test_atomic_rmw_xor_32_ignored
; CHECK: mov eax,DWORD PTR
; CHECK: xor
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}]
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xor_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: eor
; ARM32: strex
; ARM32: bne
; ARM32: dmb
;; exchange
define internal i32 @test_atomic_rmw_xchg_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 6, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_xchg_8
; CHECK: xchg BYTE PTR {{.*}},[[REG:.*]]
; ARM32-LABEL: test_atomic_rmw_xchg_8
; ARM32: dmb
; ARM32: ldrexb
; ARM32: strexb
; ARM32: cmp
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xchg_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 6, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-LABEL: test_atomic_rmw_xchg_16
; CHECK: xchg WORD PTR {{.*}},[[REG:.*]]
; ARM32-LABEL: test_atomic_rmw_xchg_16
; ARM32: dmb
; ARM32: ldrexh
; ARM32: strexh
; ARM32: cmp
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xchg_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 6, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-LABEL: test_atomic_rmw_xchg_32
; CHECK: xchg DWORD PTR {{.*}},[[REG:.*]]
; ARM32-LABEL: test_atomic_rmw_xchg_32
; ARM32: dmb
; ARM32: ldrex
; ARM32: strex
; ARM32: cmp
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_rmw_xchg_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 6, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-LABEL: test_atomic_rmw_xchg_64
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; CHECK: lock cmpxchg8b QWORD PTR [{{e.[^x]}}
; CHECK: jne
; ARM32-LABEL: test_atomic_rmw_xchg_64
; ARM32: dmb
; ARM32: ldrexd r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[PTR:r[0-9]+]]{{[]]}}
; ARM32: strexd r{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[PTR]]{{[]]}}
; ARM32: cmp
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_rmw_xchg_32_ignored(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.rmw.i32(i32 6, i32* %ptr, i32 %v, i32 6)
ret i32 %v
}
; In this case, ignoring the return value doesn't help. The xchg is
; used to do an atomic store.
; CHECK-LABEL: test_atomic_rmw_xchg_32_ignored
; CHECK: xchg DWORD PTR {{.*}},[[REG:.*]]
; ARM32-LABEL: test_atomic_rmw_xchg_32_ignored
; ARM32: dmb
; ARM32: ldrex
; ARM32: strex
; ARM32: cmp
; ARM32: bne
; ARM32: dmb
;;;; Cmpxchg
define internal i32 @test_atomic_cmpxchg_8(i32 %iptr, i32 %expected,
i32 %desired) {
entry:
%trunc_exp = trunc i32 %expected to i8
%trunc_des = trunc i32 %desired to i8
%ptr = inttoptr i32 %iptr to i8*
%old = call i8 @llvm.nacl.atomic.cmpxchg.i8(i8* %ptr, i8 %trunc_exp,
i8 %trunc_des, i32 6, i32 6)
%old_ext = zext i8 %old to i32
ret i32 %old_ext
}
; CHECK-LABEL: test_atomic_cmpxchg_8
; CHECK: mov eax,{{.*}}
; Need to check that eax isn't used as the address register or the desired.
; since it is already used as the *expected* register.
; CHECK: lock cmpxchg BYTE PTR [e{{[^a].}}],{{[^a]}}l
; ARM32-LABEL: test_atomic_cmpxchg_8
; ARM32: dmb
; ARM32: ldrexb [[V:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: lsl [[VV:r[0-9]+]], [[V]], #24
; ARM32: cmp [[VV]], {{r[0-9]+}}, lsl #24
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexbeq [[SUCCESS]], {{r[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_cmpxchg_16(i32 %iptr, i32 %expected,
i32 %desired) {
entry:
%trunc_exp = trunc i32 %expected to i16
%trunc_des = trunc i32 %desired to i16
%ptr = inttoptr i32 %iptr to i16*
%old = call i16 @llvm.nacl.atomic.cmpxchg.i16(i16* %ptr, i16 %trunc_exp,
i16 %trunc_des, i32 6, i32 6)
%old_ext = zext i16 %old to i32
ret i32 %old_ext
}
; CHECK-LABEL: test_atomic_cmpxchg_16
; CHECK: mov {{ax|eax}},{{.*}}
; CHECK: lock cmpxchg WORD PTR [e{{[^a].}}],{{[^a]}}x
; ARM32-LABEL: test_atomic_cmpxchg_16
; ARM32: dmb
; ARM32: ldrexh [[V:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: lsl [[VV:r[0-9]+]], [[V]], #16
; ARM32: cmp [[VV]], {{r[0-9]+}}, lsl #16
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexheq [[SUCCESS]], {{r[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_cmpxchg_32(i32 %iptr, i32 %expected,
i32 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%old = call i32 @llvm.nacl.atomic.cmpxchg.i32(i32* %ptr, i32 %expected,
i32 %desired, i32 6, i32 6)
ret i32 %old
}
; CHECK-LABEL: test_atomic_cmpxchg_32
; CHECK: mov eax,{{.*}}
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}],e{{[^a]}}
; ARM32-LABEL: test_atomic_cmpxchg_32
; ARM32: dmb
; ARM32: ldrex [[V:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexeq [[SUCCESS]], {{r[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_cmpxchg_64(i32 %iptr, i64 %expected,
i64 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 %expected,
i64 %desired, i32 6, i32 6)
ret i64 %old
}
; CHECK-LABEL: test_atomic_cmpxchg_64
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}+0x0]
; edx and eax are already the return registers, so they don't actually
; need to be reshuffled via movs. The next test stores the result
; somewhere, so in that case they do need to be mov'ed.
; ARM32-LABEL: test_atomic_cmpxchg_64
; ARM32: dmb
; ARM32: ldrexd [[V0:r[0-9]+]], [[V1:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V0]], {{r[0-9]+}}
; ARM32: cmpeq [[V1]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexdeq [[SUCCESS]], r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_cmpxchg_64_undef(i32 %iptr, i64 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 undef,
i64 %desired, i32 6, i32 6)
ret i64 %old
}
; CHECK-LABEL: test_atomic_cmpxchg_64_undef
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}+0x0]
; ARM32-LABEL: test_atomic_cmpxchg_64_undef
; ARM32: mov r{{[0-9]+}}, #0
; ARM32: mov r{{[0-9]+}}, #0
; ARM32: dmb
; ARM32: ldrexd [[V0:r[0-9]+]], [[V1:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V0]], {{r[0-9]+}}
; ARM32: cmpeq [[V1]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexdeq [[SUCCESS]], r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
; Test a case where %old really does need to be copied out of edx:eax.
define internal void @test_atomic_cmpxchg_64_store(
i32 %ret_iptr, i32 %iptr, i64 %expected, i64 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 %expected,
i64 %desired, i32 6, i32 6)
%__6 = inttoptr i32 %ret_iptr to i64*
store i64 %old, i64* %__6, align 1
ret void
}
; CHECK-LABEL: test_atomic_cmpxchg_64_store
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}
; CHECK-DAG: mov {{.*}},edx
; CHECK-DAG: mov {{.*}},eax
; ARM32-LABEL: test_atomic_cmpxchg_64_store
; ARM32: dmb
; ARM32: ldrexd [[V0:r[0-9]+]], [[V1:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V0]], {{r[0-9]+}}
; ARM32: cmpeq [[V1]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexdeq [[SUCCESS]], r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
; ARM32: str
; ARM32: str
; Test with some more register pressure. When we have an alloca, ebp is
; used to manage the stack frame, so it cannot be used as a register either.
define internal i64 @test_atomic_cmpxchg_64_alloca(i32 %iptr, i64 %expected,
i64 %desired) {
entry:
br label %eblock ; Disable alloca optimization
eblock:
%alloca_ptr = alloca i8, i32 16, align 16
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 %expected,
i64 %desired, i32 6, i32 6)
store i8 0, i8* %alloca_ptr, align 1
store i8 1, i8* %alloca_ptr, align 1
store i8 2, i8* %alloca_ptr, align 1
store i8 3, i8* %alloca_ptr, align 1
%__6 = ptrtoint i8* %alloca_ptr to i32
call void @use_ptr(i32 %__6)
ret i64 %old
}
; CHECK-LABEL: test_atomic_cmpxchg_64_alloca
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; Ptr cannot be eax, ebx, ecx, or edx (used up for the expected and desired).
; It also cannot be ebp since we use that for alloca. Also make sure it's
; not esp, since that's the stack pointer and mucking with it will break
; the later use_ptr function call.
; That pretty much leaves esi, or edi as the only viable registers.
; CHECK: lock cmpxchg8b QWORD PTR [e{{[ds]}}i]
; CHECK: call {{.*}} R_{{.*}} use_ptr
; ARM32-LABEL: test_atomic_cmpxchg_64_alloca
; ARM32: dmb
; ARM32: ldrexd [[V0:r[0-9]+]], [[V1:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V0]], {{r[0-9]+}}
; ARM32: cmpeq [[V1]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexdeq [[SUCCESS]], r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[A]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i32 @test_atomic_cmpxchg_32_ignored(i32 %iptr, i32 %expected,
i32 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%ignored = call i32 @llvm.nacl.atomic.cmpxchg.i32(i32* %ptr, i32 %expected,
i32 %desired, i32 6, i32 6)
ret i32 0
}
; CHECK-LABEL: test_atomic_cmpxchg_32_ignored
; CHECK: mov eax,{{.*}}
; CHECK: lock cmpxchg DWORD PTR [e{{[^a].}}]
; ARM32-LABEL: test_atomic_cmpxchg_32_ignored
; ARM32: dmb
; ARM32: ldrex [[V:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexeq [[SUCCESS]]
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
define internal i64 @test_atomic_cmpxchg_64_ignored(i32 %iptr, i64 %expected,
i64 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%ignored = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 %expected,
i64 %desired, i32 6, i32 6)
ret i64 0
}
; CHECK-LABEL: test_atomic_cmpxchg_64_ignored
; CHECK: push ebx
; CHECK-DAG: mov edx
; CHECK-DAG: mov eax
; CHECK-DAG: mov ecx
; CHECK-DAG: mov ebx
; CHECK: lock cmpxchg8b QWORD PTR [e{{.[^x]}}+0x0]
; ARM32-LABEL: test_atomic_cmpxchg_64_ignored
; ARM32: dmb
; ARM32: ldrexd [[V0:r[0-9]+]], [[V1:r[0-9]+]], {{[[]}}[[A:r[0-9]+]]{{[]]}}
; ARM32: cmp [[V0]], {{r[0-9]+}}
; ARM32: cmpeq [[V1]], {{r[0-9]+}}
; ARM32: movne [[SUCCESS:r[0-9]+]],
; ARM32: strexdeq [[SUCCESS]], r{{[0-9]+}}, r{{[0-9]+}}, {{[[]}}[[PTR]]{{[]]}}
; ARM32: cmp [[SUCCESS]], #0
; ARM32: bne
; ARM32: dmb
;;;; Fence and is-lock-free.
define internal void @test_atomic_fence() {
entry:
call void @llvm.nacl.atomic.fence(i32 6)
ret void
}
; CHECK-LABEL: test_atomic_fence
; CHECK: mfence
; ARM32-LABEL: test_atomic_fence
; ARM32: dmb sy
define internal void @test_atomic_fence_all() {
entry:
call void @llvm.nacl.atomic.fence.all()
ret void
}
; CHECK-LABEL: test_atomic_fence_all
; CHECK: mfence
; ARM32-LABEL: test_atomic_fence_all
; ARM32: dmb sy
define internal i32 @test_atomic_is_lock_free(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
%i = call i1 @llvm.nacl.atomic.is.lock.free(i32 4, i8* %ptr)
%r = zext i1 %i to i32
ret i32 %r
}
; CHECK-LABEL: test_atomic_is_lock_free
; CHECK: mov {{.*}},0x1
; ARM32-LABEL: test_atomic_is_lock_free
; ARM32: mov {{.*}}, #1
define internal i32 @test_not_lock_free(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
%i = call i1 @llvm.nacl.atomic.is.lock.free(i32 7, i8* %ptr)
%r = zext i1 %i to i32
ret i32 %r
}
; CHECK-LABEL: test_not_lock_free
; CHECK: mov {{.*}},0x0
; ARM32-LABEL: test_not_lock_free
; ARM32: mov {{.*}}, #0
define internal i32 @test_atomic_is_lock_free_ignored(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
%ignored = call i1 @llvm.nacl.atomic.is.lock.free(i32 4, i8* %ptr)
ret i32 0
}
; CHECK-LABEL: test_atomic_is_lock_free_ignored
; CHECK: mov {{.*}},0x0
; This can get optimized out, because it's side-effect-free.
; O2-LABEL: test_atomic_is_lock_free_ignored
; O2-NOT: mov {{.*}}, 1
; O2: mov {{.*}},0x0
; ARM32O2-LABEL: test_atomic_is_lock_free_ignored
; ARM32O2-NOT: mov {{.*}}, #1
; ARM32O2: mov {{.*}}, #0
; TODO(jvoung): at some point we can take advantage of the
; fact that nacl.atomic.is.lock.free will resolve to a constant
; (which adds DCE opportunities). Once we optimize, the test expectations
; for this case should change.
define internal i32 @test_atomic_is_lock_free_can_dce(i32 %iptr, i32 %x,
i32 %y) {
entry:
%ptr = inttoptr i32 %iptr to i8*
%i = call i1 @llvm.nacl.atomic.is.lock.free(i32 4, i8* %ptr)
%i_ext = zext i1 %i to i32
%cmp = icmp eq i32 %i_ext, 1
br i1 %cmp, label %lock_free, label %not_lock_free
lock_free:
ret i32 %i_ext
not_lock_free:
%z = add i32 %x, %y
ret i32 %z
}
; CHECK-LABEL: test_atomic_is_lock_free_can_dce
; CHECK: mov {{.*}},0x1
; CHECK: ret
; CHECK: add
; CHECK: ret
; Test the liveness / register allocation properties of the xadd instruction.
; Make sure we model that the Src register is modified and therefore it can't
; share a register with an overlapping live range, even if the result of the
; xadd instruction is unused.
define internal void @test_xadd_regalloc() {
entry:
br label %body
body:
%i = phi i32 [ 1, %entry ], [ %i_plus_1, %body ]
%g = bitcast [4 x i8]* @SzGlobal32 to i32*
%unused = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32* %g, i32 %i, i32 6)
%i_plus_1 = add i32 %i, 1
%cmp = icmp eq i32 %i_plus_1, 1001
br i1 %cmp, label %done, label %body
done:
ret void
}
; O2-LABEL: test_xadd_regalloc
;;; Some register will be used in the xadd instruction.
; O2: lock xadd DWORD PTR {{.*}},[[REG:e..]]
;;; Make sure that register isn't used again, e.g. as the induction variable.
; O2-NOT: ,[[REG]]
; O2: ret
; Do the same test for the xchg instruction instead of xadd.
define internal void @test_xchg_regalloc() {
entry:
br label %body
body:
%i = phi i32 [ 1, %entry ], [ %i_plus_1, %body ]
%g = bitcast [4 x i8]* @SzGlobal32 to i32*
%unused = call i32 @llvm.nacl.atomic.rmw.i32(i32 6, i32* %g, i32 %i, i32 6)
%i_plus_1 = add i32 %i, 1
%cmp = icmp eq i32 %i_plus_1, 1001
br i1 %cmp, label %done, label %body
done:
ret void
}
; O2-LABEL: test_xchg_regalloc
;;; Some register will be used in the xchg instruction.
; O2: xchg DWORD PTR {{.*}},[[REG:e..]]
;;; Make sure that register isn't used again, e.g. as the induction variable.
; O2-NOT: ,[[REG]]
; O2: ret
; Same test for cmpxchg.
define internal void @test_cmpxchg_regalloc() {
entry:
br label %body
body:
%i = phi i32 [ 1, %entry ], [ %i_plus_1, %body ]
%g = bitcast [4 x i8]* @SzGlobal32 to i32*
%unused = call i32 @llvm.nacl.atomic.cmpxchg.i32(i32* %g, i32 %i, i32 %i, i32 6, i32 6)
%i_plus_1 = add i32 %i, 1
%cmp = icmp eq i32 %i_plus_1, 1001
br i1 %cmp, label %done, label %body
done:
ret void
}
; O2-LABEL: test_cmpxchg_regalloc
;;; eax and some other register will be used in the cmpxchg instruction.
; O2: lock cmpxchg DWORD PTR {{.*}},[[REG:e..]]
;;; Make sure eax isn't used again, e.g. as the induction variable.
; O2-NOT: ,eax
; O2: ret
; Same test for cmpxchg8b.
define internal void @test_cmpxchg8b_regalloc() {
entry:
br label %body
body:
%i = phi i32 [ 1, %entry ], [ %i_plus_1, %body ]
%g = bitcast [8 x i8]* @SzGlobal64 to i64*
%i_64 = zext i32 %i to i64
%unused = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %g, i64 %i_64, i64 %i_64, i32 6, i32 6)
%i_plus_1 = add i32 %i, 1
%cmp = icmp eq i32 %i_plus_1, 1001
br i1 %cmp, label %done, label %body
done:
ret void
}
; O2-LABEL: test_cmpxchg8b_regalloc
;;; eax and some other register will be used in the cmpxchg instruction.
; O2: lock cmpxchg8b QWORD PTR
;;; Make sure eax/ecx/edx/ebx aren't used again, e.g. as the induction variable.
; O2-NOT: ,{{eax|ecx|edx|ebx}}
; O2: pop ebx