|
|
@@ -0,0 +1,280 @@ |
|
|
; RUN: llc -mtriple=i686-windows < %s | FileCheck %s |
|
|
|
|
|
declare void @addrof_i32(i32*) |
|
|
declare void @addrof_i64(i64*) |
|
|
declare void @addrof_i128(i128*) |
|
|
declare void @addrof_i32_x3(i32*, i32*, i32*) |
|
|
|
|
|
define void @simple(i32 %x) { |
|
|
entry: |
|
|
%x.addr = alloca i32 |
|
|
store i32 %x, i32* %x.addr |
|
|
call void @addrof_i32(i32* %x.addr) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _simple: |
|
|
; CHECK: leal 4(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; We need to load %x before calling addrof_i32 now because it could mutate %x in |
|
|
; place. |
|
|
|
|
|
define i32 @use_arg(i32 %x) { |
|
|
entry: |
|
|
%x.addr = alloca i32 |
|
|
store i32 %x, i32* %x.addr |
|
|
call void @addrof_i32(i32* %x.addr) |
|
|
ret i32 %x |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _use_arg: |
|
|
; CHECK: pushl %[[csr:[^ ]*]] |
|
|
; CHECK-DAG: movl 8(%esp), %[[csr]] |
|
|
; CHECK-DAG: leal 8(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: movl %[[csr]], %eax |
|
|
; CHECK: popl %[[csr]] |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
define i64 @split_i64(i64 %x) { |
|
|
entry: |
|
|
%x.addr = alloca i64, align 4 |
|
|
store i64 %x, i64* %x.addr, align 4 |
|
|
call void @addrof_i64(i64* %x.addr) |
|
|
ret i64 %x |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _split_i64: |
|
|
; CHECK: pushl %ebp |
|
|
; CHECK: movl %esp, %ebp |
|
|
; CHECK: pushl %[[csr2:[^ ]*]] |
|
|
; CHECK: pushl %[[csr1:[^ ]*]] |
|
|
; CHECK: andl $-8, %esp |
|
|
; CHECK-DAG: movl 8(%ebp), %[[csr1]] |
|
|
; CHECK-DAG: movl 12(%ebp), %[[csr2]] |
|
|
; CHECK-DAG: leal 8(%ebp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i64 |
|
|
; CHECK-DAG: movl %[[csr1]], %eax |
|
|
; CHECK-DAG: movl %[[csr2]], %edx |
|
|
; CHECK: leal -8(%ebp), %esp |
|
|
; CHECK: popl %[[csr1]] |
|
|
; CHECK: popl %[[csr2]] |
|
|
; CHECK: popl %ebp |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; We can't copy elide when an i64 is split between registers and memory in a |
|
|
; fastcc function. |
|
|
|
|
|
define fastcc i64 @fastcc_split_i64(i64* %p, i64 %x) { |
|
|
entry: |
|
|
%x.addr = alloca i64, align 4 |
|
|
store i64 %x, i64* %x.addr, align 4 |
|
|
call void @addrof_i64(i64* %x.addr) |
|
|
ret i64 %x |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _fastcc_split_i64: |
|
|
; CHECK: pushl %ebp |
|
|
; CHECK: movl %esp, %ebp |
|
|
; CHECK-DAG: movl %edx, %[[r1:[^ ]*]] |
|
|
; CHECK-DAG: movl 8(%ebp), %[[r2:[^ ]*]] |
|
|
; CHECK-DAG: movl %[[r2]], 4(%esp) |
|
|
; CHECK-DAG: movl %[[r1]], (%esp) |
|
|
; CHECK: movl %esp, %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i64 |
|
|
; CHECK: popl %ebp |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; We can't copy elide when it would reduce the user requested alignment. |
|
|
|
|
|
define void @high_alignment(i32 %x) { |
|
|
entry: |
|
|
%x.p = alloca i32, align 128 |
|
|
store i32 %x, i32* %x.p |
|
|
call void @addrof_i32(i32* %x.p) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _high_alignment: |
|
|
; CHECK: andl $-128, %esp |
|
|
; CHECK: movl 8(%ebp), %[[reg:[^ ]*]] |
|
|
; CHECK: movl %[[reg]], (%esp) |
|
|
; CHECK: movl %esp, %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; 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) { |
|
|
entry: |
|
|
%x.p = alloca i64 |
|
|
store i64 %x, i64* %x.p |
|
|
call void @addrof_i64(i64* %x.p) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _abi_alignment: |
|
|
; CHECK: andl $-8, %esp |
|
|
; CHECK: movl 8(%ebp), %[[reg:[^ ]*]] |
|
|
; CHECK: movl %[[reg]], (%esp) |
|
|
; CHECK: movl %esp, %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i64 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; The code we generate for this is unimportant. This is mostly a crash test. |
|
|
|
|
|
define void @split_i128(i128* %sret, i128 %x) { |
|
|
entry: |
|
|
%x.addr = alloca i128 |
|
|
store i128 %x, i128* %x.addr |
|
|
call void @addrof_i128(i128* %x.addr) |
|
|
store i128 %x, i128* %sret |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _split_i128: |
|
|
; CHECK: pushl %ebp |
|
|
; CHECK: calll _addrof_i128 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; Check that we load all of x, y, and z before the call. |
|
|
|
|
|
define i32 @three_args(i32 %x, i32 %y, i32 %z) { |
|
|
entry: |
|
|
%z.addr = alloca i32, align 4 |
|
|
%y.addr = alloca i32, align 4 |
|
|
%x.addr = alloca i32, align 4 |
|
|
store i32 %z, i32* %z.addr, align 4 |
|
|
store i32 %y, i32* %y.addr, align 4 |
|
|
store i32 %x, i32* %x.addr, align 4 |
|
|
call void @addrof_i32_x3(i32* %x.addr, i32* %y.addr, i32* %z.addr) |
|
|
%s1 = add i32 %x, %y |
|
|
%sum = add i32 %s1, %z |
|
|
ret i32 %sum |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _three_args: |
|
|
; CHECK: pushl %[[csr:[^ ]*]] |
|
|
; CHECK-DAG: movl {{[0-9]+}}(%esp), %[[csr]] |
|
|
; CHECK-DAG: addl {{[0-9]+}}(%esp), %[[csr]] |
|
|
; CHECK-DAG: addl {{[0-9]+}}(%esp), %[[csr]] |
|
|
; CHECK-DAG: leal 8(%esp), %[[x:[^ ]*]] |
|
|
; CHECK-DAG: leal 12(%esp), %[[y:[^ ]*]] |
|
|
; CHECK-DAG: leal 16(%esp), %[[z:[^ ]*]] |
|
|
; CHECK: pushl %[[z]] |
|
|
; CHECK: pushl %[[y]] |
|
|
; CHECK: pushl %[[x]] |
|
|
; CHECK: calll _addrof_i32_x3 |
|
|
; CHECK: movl %[[csr]], %eax |
|
|
; CHECK: popl %[[csr]] |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
define void @two_args_same_alloca(i32 %x, i32 %y) { |
|
|
entry: |
|
|
%x.addr = alloca i32 |
|
|
store i32 %x, i32* %x.addr |
|
|
store i32 %y, i32* %x.addr |
|
|
call void @addrof_i32(i32* %x.addr) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _two_args_same_alloca: |
|
|
; CHECK: movl 8(%esp), {{.*}} |
|
|
; CHECK: movl {{.*}}, 4(%esp) |
|
|
; CHECK: leal 4(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
define void @avoid_byval(i32* byval %x) { |
|
|
entry: |
|
|
%x.p.p = alloca i32* |
|
|
store i32* %x, i32** %x.p.p |
|
|
call void @addrof_i32(i32* %x) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _avoid_byval: |
|
|
; CHECK: leal {{[0-9]+}}(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
define void @avoid_inalloca(i32* inalloca %x) { |
|
|
entry: |
|
|
%x.p.p = alloca i32* |
|
|
store i32* %x, i32** %x.p.p |
|
|
call void @addrof_i32(i32* %x) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _avoid_inalloca: |
|
|
; CHECK: leal {{[0-9]+}}(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: retl |
|
|
|
|
|
|
|
|
; Don't elide the copy when the alloca is escaped with a store. |
|
|
|
|
|
define void @escape_with_store(i32 %x) { |
|
|
%x1 = alloca i32 |
|
|
%x2 = alloca i32* |
|
|
store i32* %x1, i32** %x2 |
|
|
%x3 = load i32*, i32** %x2 |
|
|
store i32 0, i32* %x3 |
|
|
store i32 %x, i32* %x1 |
|
|
call void @addrof_i32(i32* %x1) |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _escape_with_store: |
|
|
; CHECK-DAG: movl {{.*}}(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK-DAG: movl $0, [[offs:[0-9]*]](%esp) |
|
|
; CHECK: movl %[[reg]], [[offs]](%esp) |
|
|
; CHECK: calll _addrof_i32 |
|
|
|
|
|
|
|
|
; This test case exposed issues with the use of TokenFactor. |
|
|
|
|
|
define void @sret_and_elide(i32* sret %sret, i32 %v) { |
|
|
%v.p = alloca i32 |
|
|
store i32 %v, i32* %v.p |
|
|
call void @addrof_i32(i32* %v.p) |
|
|
store i32 %v, i32* %sret |
|
|
ret void |
|
|
} |
|
|
|
|
|
; CHECK-LABEL: _sret_and_elide: |
|
|
; CHECK: pushl |
|
|
; CHECK: pushl |
|
|
; CHECK: movl 12(%esp), %[[sret:[^ ]*]] |
|
|
; CHECK: movl 16(%esp), %[[v:[^ ]*]] |
|
|
; CHECK: leal 16(%esp), %[[reg:[^ ]*]] |
|
|
; CHECK: pushl %[[reg]] |
|
|
; CHECK: calll _addrof_i32 |
|
|
; CHECK: movl %[[v]], (%[[sret]]) |
|
|
; CHECK: movl %[[sret]], %eax |
|
|
; CHECK: popl |
|
|
; CHECK: popl |
|
|
; CHECK: retl |