| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 |
| ; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefix=X64 |
| ; RUN: llc < %s -mtriple=i686-unknown-linux-gnu | FileCheck %s --check-prefix=X86 |
| |
| ; Compares of atomic loads should fold the load into the compare's memory |
| ; operand, like compares of regular loads do: the folded form still performs |
| ; a single full-width load, and on x86 an ordinary load already provides |
| ; every ordering up to seq_cst. |
| |
| define i1 @cmp8_eq0_monotonic(ptr %p) { |
| ; X64-LABEL: cmp8_eq0_monotonic: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpb $0, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp8_eq0_monotonic: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpb $0, (%eax) |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i8, ptr %p monotonic, align 1 |
| %c = icmp eq i8 %v, 0 |
| ret i1 %c |
| } |
| |
| define i1 @cmp16_eq0_acquire(ptr %p) { |
| ; X64-LABEL: cmp16_eq0_acquire: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpw $0, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp16_eq0_acquire: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpw $0, (%eax) |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i16, ptr %p acquire, align 2 |
| %c = icmp eq i16 %v, 0 |
| ret i1 %c |
| } |
| |
| define i1 @cmp32_eq0_seq_cst(ptr %p) { |
| ; X64-LABEL: cmp32_eq0_seq_cst: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl $0, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_eq0_seq_cst: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpl $0, (%eax) |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p seq_cst, align 4 |
| %c = icmp eq i32 %v, 0 |
| ret i1 %c |
| } |
| |
| define i1 @cmp64_eq0_unordered(ptr %p) { |
| ; X64-LABEL: cmp64_eq0_unordered: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpq $0, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp64_eq0_unordered: |
| ; X86: # %bb.0: |
| ; X86-NEXT: subl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 16 |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: fildll (%eax) |
| ; X86-NEXT: fistpll (%esp) |
| ; X86-NEXT: movl (%esp), %eax |
| ; X86-NEXT: orl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: addl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 4 |
| ; X86-NEXT: retl |
| %v = load atomic i64, ptr %p unordered, align 8 |
| %c = icmp eq i64 %v, 0 |
| ret i1 %c |
| } |
| |
| define i1 @cmp32_imm_acquire(ptr %p) { |
| ; X64-LABEL: cmp32_imm_acquire: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl $42, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_imm_acquire: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpl $42, (%eax) |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p acquire, align 4 |
| %c = icmp eq i32 %v, 42 |
| ret i1 %c |
| } |
| |
| define i1 @cmp32_imm_large(ptr %p) { |
| ; X64-LABEL: cmp32_imm_large: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl $305419896, (%rdi) # imm = 0x12345678 |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_imm_large: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpl $305419896, (%eax) # imm = 0x12345678 |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p monotonic, align 4 |
| %c = icmp eq i32 %v, 305419896 ; 0x12345678 |
| ret i1 %c |
| } |
| |
| define i1 @cmp64_imm_sext32(ptr %p) { |
| ; X64-LABEL: cmp64_imm_sext32: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpq $-100, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp64_imm_sext32: |
| ; X86: # %bb.0: |
| ; X86-NEXT: subl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 16 |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: fildll (%eax) |
| ; X86-NEXT: fistpll (%esp) |
| ; X86-NEXT: movl (%esp), %eax |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx |
| ; X86-NEXT: notl %ecx |
| ; X86-NEXT: xorl $-100, %eax |
| ; X86-NEXT: orl %ecx, %eax |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: addl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 4 |
| ; X86-NEXT: retl |
| %v = load atomic i64, ptr %p monotonic, align 8 |
| %c = icmp eq i64 %v, -100 |
| ret i1 %c |
| } |
| |
| ; The immediate does not fit CMP64mi32, so the load stays in a register. |
| define i1 @cmp64_imm_too_wide(ptr %p) { |
| ; X64-LABEL: cmp64_imm_too_wide: |
| ; X64: # %bb.0: |
| ; X64-NEXT: movabsq $4886718345, %rax # imm = 0x123456789 |
| ; X64-NEXT: cmpq %rax, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp64_imm_too_wide: |
| ; X86: # %bb.0: |
| ; X86-NEXT: subl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 16 |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: fildll (%eax) |
| ; X86-NEXT: fistpll (%esp) |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl $591751049, %ecx # imm = 0x23456789 |
| ; X86-NEXT: xorl (%esp), %ecx |
| ; X86-NEXT: xorl $1, %eax |
| ; X86-NEXT: orl %ecx, %eax |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: addl $12, %esp |
| ; X86-NEXT: .cfi_def_cfa_offset 4 |
| ; X86-NEXT: retl |
| %v = load atomic i64, ptr %p monotonic, align 8 |
| %c = icmp eq i64 %v, 4886718345 ; 0x123456789 |
| ret i1 %c |
| } |
| |
| define i1 @cmp32_reg(ptr %p, i32 %x) { |
| ; X64-LABEL: cmp32_reg: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl %esi, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_reg: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %eax |
| ; X86-NEXT: cmpl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p acquire, align 4 |
| %c = icmp eq i32 %v, %x |
| ret i1 %c |
| } |
| |
| ; The loaded value sits on the compare's RHS. |
| define i1 @cmp32_reg_rhs(ptr %p, i32 %x) { |
| ; X64-LABEL: cmp32_reg_rhs: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl (%rdi), %esi |
| ; X64-NEXT: setl %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_reg_rhs: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %eax |
| ; X86-NEXT: cmpl %eax, {{[0-9]+}}(%esp) |
| ; X86-NEXT: setl %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p acquire, align 4 |
| %c = icmp slt i32 %x, %v |
| ret i1 %c |
| } |
| |
| ; Relaxed load of a level, compare, branch (the log-level-check shape). |
| define i32 @cmp32_sge_branch(ptr %p, i32 %lvl) { |
| ; X64-LABEL: cmp32_sge_branch: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl %esi, (%rdi) |
| ; X64-NEXT: jl .LBB10_2 |
| ; X64-NEXT: # %bb.1: # %log |
| ; X64-NEXT: movl $1, %eax |
| ; X64-NEXT: retq |
| ; X64-NEXT: .LBB10_2: # %skip |
| ; X64-NEXT: xorl %eax, %eax |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_sge_branch: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %eax |
| ; X86-NEXT: cmpl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: jl .LBB10_2 |
| ; X86-NEXT: # %bb.1: # %log |
| ; X86-NEXT: movl $1, %eax |
| ; X86-NEXT: retl |
| ; X86-NEXT: .LBB10_2: # %skip |
| ; X86-NEXT: xorl %eax, %eax |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p monotonic, align 4 |
| %c = icmp sge i32 %v, %lvl |
| br i1 %c, label %log, label %skip |
| log: |
| ret i32 1 |
| skip: |
| ret i32 0 |
| } |
| |
| define i1 @test32_mask_bit(ptr %p) { |
| ; X64-LABEL: test32_mask_bit: |
| ; X64: # %bb.0: |
| ; X64-NEXT: movl (%rdi), %eax |
| ; X64-NEXT: andl $8, %eax |
| ; X64-NEXT: shrl $3, %eax |
| ; X64-NEXT: # kill: def $al killed $al killed $eax |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: test32_mask_bit: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %eax |
| ; X86-NEXT: andl $8, %eax |
| ; X86-NEXT: shrl $3, %eax |
| ; X86-NEXT: # kill: def $al killed $al killed $eax |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p monotonic, align 4 |
| %a = and i32 %v, 8 |
| %c = icmp ne i32 %a, 0 |
| ret i1 %c |
| } |
| |
| ; The mask fits neither i8 nor i16 and is not a shifted mask, so the |
| ; masked compare cannot be narrowed and keeps its full width. |
| define i1 @test32_mask_wide(ptr %p) { |
| ; X64-LABEL: test32_mask_wide: |
| ; X64: # %bb.0: |
| ; X64-NEXT: movl (%rdi), %eax |
| ; X64-NEXT: testl $65792, %eax # imm = 0x10100 |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: test32_mask_wide: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %eax |
| ; X86-NEXT: testl $65792, %eax # imm = 0x10100 |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p monotonic, align 4 |
| %a = and i32 %v, 65792 ; 0x10100 |
| %c = icmp eq i32 %a, 0 |
| ret i1 %c |
| } |
| |
| ; The loaded value has a second use: the load must stay. |
| define i32 @no_fold_multiuse(ptr %p) { |
| ; X64-LABEL: no_fold_multiuse: |
| ; X64: # %bb.0: |
| ; X64-NEXT: movl (%rdi), %ecx |
| ; X64-NEXT: cmpl $7, %ecx |
| ; X64-NEXT: movl $100, %eax |
| ; X64-NEXT: cmovnel %ecx, %eax |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: no_fold_multiuse: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl (%eax), %ecx |
| ; X86-NEXT: cmpl $7, %ecx |
| ; X86-NEXT: movl $100, %eax |
| ; X86-NEXT: je .LBB13_2 |
| ; X86-NEXT: # %bb.1: |
| ; X86-NEXT: movl %ecx, %eax |
| ; X86-NEXT: .LBB13_2: |
| ; X86-NEXT: retl |
| %v = load atomic i32, ptr %p acquire, align 4 |
| %c = icmp eq i32 %v, 7 |
| %r = select i1 %c, i32 100, i32 %v |
| ret i32 %r |
| } |
| |
| ; Two atomic loads: at most one side can fold. |
| define i1 @cmp_two_loads(ptr %p, ptr %q) { |
| ; X64-LABEL: cmp_two_loads: |
| ; X64: # %bb.0: |
| ; X64-NEXT: movl (%rdi), %eax |
| ; X64-NEXT: cmpl (%rsi), %eax |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp_two_loads: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx |
| ; X86-NEXT: movl (%ecx), %ecx |
| ; X86-NEXT: cmpl (%eax), %ecx |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %a = load atomic i32, ptr %p acquire, align 4 |
| %b = load atomic i32, ptr %q acquire, align 4 |
| %c = icmp eq i32 %a, %b |
| ret i1 %c |
| } |
| |
| ; Volatile atomic load: folding keeps the single access, matching how |
| ; compares of volatile non-atomic loads are handled. |
| define i1 @cmp32_volatile(ptr %p) { |
| ; X64-LABEL: cmp32_volatile: |
| ; X64: # %bb.0: |
| ; X64-NEXT: cmpl $0, (%rdi) |
| ; X64-NEXT: sete %al |
| ; X64-NEXT: retq |
| ; |
| ; X86-LABEL: cmp32_volatile: |
| ; X86: # %bb.0: |
| ; X86-NEXT: movl {{[0-9]+}}(%esp), %eax |
| ; X86-NEXT: cmpl $0, (%eax) |
| ; X86-NEXT: sete %al |
| ; X86-NEXT: retl |
| %v = load atomic volatile i32, ptr %p acquire, align 4 |
| %c = icmp eq i32 %v, 0 |
| ret i1 %c |
| } |