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