llvm/llvm/test/CodeGen/X86/arg-copy-elide.ll

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=i686-windows < %s | FileCheck %s

declare void @addrof_i1(ptr)
declare void @addrof_i32(ptr)
declare void @addrof_i64(ptr)
declare void @addrof_i128(ptr)
declare void @addrof_i32_x3(ptr, ptr, ptr)

define void @simple(i32 %x) {
; CHECK-LABEL: simple:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i32
  store i32 %x, ptr %x.addr
  call void @addrof_i32(ptr %x.addr)
  ret void
}

; We need to load %x before calling addrof_i32 now because it could mutate %x in
; place.

define i32 @use_arg(i32 %x) {
; CHECK-LABEL: use_arg:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %esi, %eax
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i32
  store i32 %x, ptr %x.addr
  call void @addrof_i32(ptr %x.addr)
  ret i32 %x
}

; We won't copy elide for types needing legalization such as i64 or i1.

define i64 @split_i64(i64 %x) {
; CHECK-LABEL: split_i64:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %edi
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %edi
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i64
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %esi, %eax
; CHECK-NEXT:    movl %edi, %edx
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    popl %edi
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i64, align 4
  store i64 %x, ptr %x.addr, align 4
  call void @addrof_i64(ptr %x.addr)
  ret i64 %x
}

define i1 @i1_arg(i1 %x) {
; CHECK-LABEL: i1_arg:
; CHECK:       # %bb.0:
; CHECK-NEXT:    pushl %ebx
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    movzbl {{[0-9]+}}(%esp), %ebx
; CHECK-NEXT:    movl %ebx, %eax
; CHECK-NEXT:    andb $1, %al
; CHECK-NEXT:    movb %al, {{[0-9]+}}(%esp)
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i1
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %ebx, %eax
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    popl %ebx
; CHECK-NEXT:    retl
  %x.addr = alloca i1
  store i1 %x, ptr %x.addr
  call void @addrof_i1(ptr %x.addr)
  ret i1 %x
}

; We can't copy elide when an i64 is split between registers and memory in a
; fastcc function.

define fastcc i64 @fastcc_split_i64(ptr %p, i64 %x) {
; CHECK-LABEL: fastcc_split_i64:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %edi
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    subl $8, %esp
; CHECK-NEXT:    movl %edx, %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %edi
; CHECK-NEXT:    movl %edi, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %edx, (%esp)
; CHECK-NEXT:    movl %esp, %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i64
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %esi, %eax
; CHECK-NEXT:    movl %edi, %edx
; CHECK-NEXT:    addl $8, %esp
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    popl %edi
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i64, align 4
  store i64 %x, ptr %x.addr, align 4
  call void @addrof_i64(ptr %x.addr)
  ret i64 %x
}

; We can't copy elide when it would reduce the user requested alignment.

define void @high_alignment(i32 %x) {
; CHECK-LABEL: high_alignment:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %ebp
; CHECK-NEXT:    movl %esp, %ebp
; CHECK-NEXT:    andl $-128, %esp
; CHECK-NEXT:    subl $128, %esp
; CHECK-NEXT:    movl 8(%ebp), %eax
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    movl %esp, %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %ebp, %esp
; CHECK-NEXT:    popl %ebp
; CHECK-NEXT:    retl
entry:
  %x.p = alloca i32, align 128
  store i32 %x, ptr %x.p
  call void @addrof_i32(ptr %x.p)
  ret void
}

; We can't copy elide when it would reduce the ABI required alignment.
; FIXME: We should lower the ABI alignment of i64 on Windows, since MSVC
; doesn't guarantee it.

define void @abi_alignment(i64 %x) {
; CHECK-LABEL: abi_alignment:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %ebp
; CHECK-NEXT:    movl %esp, %ebp
; CHECK-NEXT:    andl $-8, %esp
; CHECK-NEXT:    subl $8, %esp
; CHECK-NEXT:    movl 8(%ebp), %eax
; CHECK-NEXT:    movl 12(%ebp), %ecx
; CHECK-NEXT:    movl %ecx, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    movl %esp, %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i64
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %ebp, %esp
; CHECK-NEXT:    popl %ebp
; CHECK-NEXT:    retl
entry:
  %x.p = alloca i64
  store i64 %x, ptr %x.p
  call void @addrof_i64(ptr %x.p)
  ret void
}

; The code we generate for this is unimportant. This is mostly a crash test.

define void @split_i128(ptr %sret, i128 %x) {
; CHECK-LABEL: split_i128:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %ebp
; CHECK-NEXT:    movl %esp, %ebp
; CHECK-NEXT:    pushl %ebx
; CHECK-NEXT:    pushl %edi
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    andl $-16, %esp
; CHECK-NEXT:    subl $48, %esp
; CHECK-NEXT:    movl 12(%ebp), %eax
; CHECK-NEXT:    movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
; CHECK-NEXT:    movl 16(%ebp), %ebx
; CHECK-NEXT:    movl 20(%ebp), %esi
; CHECK-NEXT:    movl 24(%ebp), %edi
; CHECK-NEXT:    movl %edi, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %esi, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %ebx, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %eax, {{[0-9]+}}(%esp)
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i128
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl 8(%ebp), %eax
; CHECK-NEXT:    movl %edi, 12(%eax)
; CHECK-NEXT:    movl %esi, 8(%eax)
; CHECK-NEXT:    movl %ebx, 4(%eax)
; CHECK-NEXT:    movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload
; CHECK-NEXT:    movl %ecx, (%eax)
; CHECK-NEXT:    leal -12(%ebp), %esp
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    popl %edi
; CHECK-NEXT:    popl %ebx
; CHECK-NEXT:    popl %ebp
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i128
  store i128 %x, ptr %x.addr
  call void @addrof_i128(ptr %x.addr)
  store i128 %x, ptr %sret
  ret void
}

; Check that we load all of x, y, and z before the call.

define i32 @three_args(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: three_args:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    addl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    addl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %ecx
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %edx
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    pushl %ecx
; CHECK-NEXT:    pushl %edx
; CHECK-NEXT:    calll _addrof_i32_x3
; CHECK-NEXT:    addl $12, %esp
; CHECK-NEXT:    movl %esi, %eax
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    retl
entry:
  %z.addr = alloca i32, align 4
  %y.addr = alloca i32, align 4
  %x.addr = alloca i32, align 4
  store i32 %z, ptr %z.addr, align 4
  store i32 %y, ptr %y.addr, align 4
  store i32 %x, ptr %x.addr, align 4
  call void @addrof_i32_x3(ptr %x.addr, ptr %y.addr, ptr %z.addr)
  %s1 = add i32 %x, %y
  %sum = add i32 %s1, %z
  ret i32 %sum
}

define void @two_args_same_alloca(i32 %x, i32 %y) {
; CHECK-LABEL: two_args_same_alloca:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    movl %eax, {{[0-9]+}}(%esp)
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    retl
entry:
  %x.addr = alloca i32
  store i32 %x, ptr %x.addr
  store i32 %y, ptr %x.addr
  call void @addrof_i32(ptr %x.addr)
  ret void
}

define void @avoid_byval(ptr byval(i32) %x) {
; CHECK-LABEL: avoid_byval:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    popl %eax
; CHECK-NEXT:    retl
entry:
  %x.p.p = alloca ptr
  store ptr %x, ptr %x.p.p
  call void @addrof_i32(ptr %x)
  ret void
}

define void @avoid_inalloca(ptr inalloca(i32) %x) {
; CHECK-LABEL: avoid_inalloca:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    popl %eax
; CHECK-NEXT:    retl
entry:
  %x.p.p = alloca ptr
  store ptr %x, ptr %x.p.p
  call void @addrof_i32(ptr %x)
  ret void
}

define void @avoid_preallocated(ptr preallocated(i32) %x) {
; CHECK-LABEL: avoid_preallocated:
; CHECK:       # %bb.0: # %entry
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    popl %eax
; CHECK-NEXT:    retl
entry:
  %x.p.p = alloca ptr
  store ptr %x, ptr %x.p.p
  call void @addrof_i32(ptr %x)
  ret void
}

; Don't elide the copy when the alloca is escaped with a store.
define void @escape_with_store(i32 %x) {
; CHECK-LABEL: escape_with_store:
; CHECK:       # %bb.0:
; CHECK-NEXT:    subl $8, %esp
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    movl %esp, %ecx
; CHECK-NEXT:    movl %ecx, {{[0-9]+}}(%esp)
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    pushl %ecx
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $12, %esp
; CHECK-NEXT:    retl
  %x1 = alloca i32
  %x2 = alloca ptr
  store ptr %x1, ptr %x2
  %x3 = load ptr, ptr %x2
  store i32 0, ptr %x3
  store i32 %x, ptr %x1
  call void @addrof_i32(ptr %x1)
  ret void
}

; This test case exposed issues with the use of TokenFactor.

define void @sret_and_elide(ptr sret(i32) %sret, i32 %v) {
; CHECK-LABEL: sret_and_elide:
; CHECK:       # %bb.0:
; CHECK-NEXT:    pushl %edi
; CHECK-NEXT:    pushl %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %esi
; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %edi
; CHECK-NEXT:    leal {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %edi, (%esi)
; CHECK-NEXT:    movl %esi, %eax
; CHECK-NEXT:    popl %esi
; CHECK-NEXT:    popl %edi
; CHECK-NEXT:    retl
  %v.p = alloca i32
  store i32 %v, ptr %v.p
  call void @addrof_i32(ptr %v.p)
  store i32 %v, ptr %sret
  ret void
}

define void @avoid_partially_initialized_alloca(i32 %x) {
; CHECK-LABEL: avoid_partially_initialized_alloca:
; CHECK:       # %bb.0:
; CHECK-NEXT:    pushl %ebp
; CHECK-NEXT:    movl %esp, %ebp
; CHECK-NEXT:    andl $-8, %esp
; CHECK-NEXT:    subl $8, %esp
; CHECK-NEXT:    movl 8(%ebp), %eax
; CHECK-NEXT:    movl %eax, (%esp)
; CHECK-NEXT:    movl %esp, %eax
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    calll _addrof_i32
; CHECK-NEXT:    addl $4, %esp
; CHECK-NEXT:    movl %ebp, %esp
; CHECK-NEXT:    popl %ebp
; CHECK-NEXT:    retl
  %a = alloca i64
  store i32 %x, ptr %a
  call void @addrof_i32(ptr %a)
  ret void
}

; Ensure no copy elision happens as the two i3 values fed into icmp may have
; garbage in the upper bits, a truncation is needed.

define i1 @use_i3(i3 %a1, i3 %a2) {
; CHECK-LABEL: use_i3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    pushl %eax
; CHECK-NEXT:    movzbl {{[0-9]+}}(%esp), %eax
; CHECK-NEXT:    andb $7, %al
; CHECK-NEXT:    movzbl {{[0-9]+}}(%esp), %ecx
; CHECK-NEXT:    andb $7, %cl
; CHECK-NEXT:    movb %cl, {{[0-9]+}}(%esp)
; CHECK-NEXT:    cmpb %cl, %al
; CHECK-NEXT:    sete %al
; CHECK-NEXT:    popl %ecx
; CHECK-NEXT:    retl
  %tmp = alloca i3
  store i3 %a2, ptr %tmp
  %val = load i3, ptr %tmp
  %res = icmp eq i3 %a1, %val
  ret i1 %res
}