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