Improve and enable folding of conditional branches with tail calls. 1. Make it so that conditional tail calls can be emitted even when there are multiple predecessors. 2. Don't guard the transformation behind -Os. The rationale for guarding it was static-prediction can be affected by whether the branch is forward of backward. This is no longer true for almost any X86 cpus (anything newer than `SnB`) so is no longer a meaningful concern. Reviewed By: pengfei Differential Revision: https://reviews.llvm.org/D140931
987 lines
26 KiB
LLVM
987 lines
26 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc -verify-machineinstrs < %s -mtriple=i686-linux -mcpu=core2 -mattr=+sse2 | FileCheck %s --check-prefix=X86
|
|
; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-linux -mcpu=core2 -mattr=+sse2 | FileCheck %s --check-prefix=X64
|
|
; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-linux-gnux32 -mcpu=core2 -mattr=+sse2 | FileCheck %s --check-prefix=X32
|
|
|
|
define dso_local void @t1(i32 %x) nounwind ssp {
|
|
; X86-LABEL: t1:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmp foo # TAILCALL
|
|
;
|
|
; X64-LABEL: t1:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmp foo # TAILCALL
|
|
;
|
|
; X32-LABEL: t1:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmp foo # TAILCALL
|
|
tail call void @foo() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local void @foo()
|
|
|
|
define dso_local void @t2() nounwind ssp {
|
|
; X86-LABEL: t2:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmp foo2 # TAILCALL
|
|
;
|
|
; X64-LABEL: t2:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmp foo2 # TAILCALL
|
|
;
|
|
; X32-LABEL: t2:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmp foo2 # TAILCALL
|
|
%t0 = tail call i32 @foo2() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local i32 @foo2()
|
|
|
|
define dso_local void @t3() nounwind ssp {
|
|
; X86-LABEL: t3:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmp foo3 # TAILCALL
|
|
;
|
|
; X64-LABEL: t3:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmp foo3 # TAILCALL
|
|
;
|
|
; X32-LABEL: t3:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmp foo3 # TAILCALL
|
|
%t0 = tail call i32 @foo3() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local i32 @foo3()
|
|
|
|
define dso_local void @t4(ptr nocapture %x) nounwind ssp {
|
|
; X86-LABEL: t4:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movl $0, (%esp)
|
|
; X86-NEXT: calll *{{[0-9]+}}(%esp)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t4:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: movq %rdi, %rax
|
|
; X64-NEXT: xorl %edi, %edi
|
|
; X64-NEXT: jmpq *%rax # TAILCALL
|
|
;
|
|
; X32-LABEL: t4:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: movq %rdi, %rax
|
|
; X32-NEXT: xorl %edi, %edi
|
|
; X32-NEXT: jmpq *%rax # TAILCALL
|
|
tail call void %x(i32 0) nounwind
|
|
ret void
|
|
}
|
|
|
|
define dso_local void @t5(ptr nocapture %x) nounwind ssp {
|
|
; X86-LABEL: t5:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmpl *{{[0-9]+}}(%esp) # TAILCALL
|
|
;
|
|
; X64-LABEL: t5:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmpq *%rdi # TAILCALL
|
|
;
|
|
; X32-LABEL: t5:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmpq *%rdi # TAILCALL
|
|
tail call void %x() nounwind
|
|
ret void
|
|
}
|
|
|
|
; Basically the same test as t5, except pass the function pointer on the stack
|
|
; for x86_64.
|
|
|
|
define dso_local void @t5_x64(i32, i32, i32, i32, i32, i32, ptr nocapture %x) nounwind ssp {
|
|
; X86-LABEL: t5_x64:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmpl *{{[0-9]+}}(%esp) # TAILCALL
|
|
;
|
|
; X64-LABEL: t5_x64:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmpq *{{[0-9]+}}(%rsp) # TAILCALL
|
|
;
|
|
; X32-LABEL: t5_x64:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X32-NEXT: jmpq *%rax # TAILCALL
|
|
tail call void %x() nounwind
|
|
ret void
|
|
}
|
|
|
|
|
|
define dso_local i32 @t6(i32 %x) nounwind ssp {
|
|
; X86-LABEL: t6:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: cmpl $9, %eax
|
|
; X86-NEXT: jg .LBB6_2
|
|
; X86-NEXT: # %bb.1: # %bb
|
|
; X86-NEXT: decl %eax
|
|
; X86-NEXT: movl %eax, (%esp)
|
|
; X86-NEXT: calll t6
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
; X86-NEXT: .LBB6_2: # %bb1
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: jmp bar # TAILCALL
|
|
;
|
|
; X64-LABEL: t6:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: cmpl $9, %edi
|
|
; X64-NEXT: jg bar # TAILCALL
|
|
; X64-NEXT: # %bb.1: # %bb
|
|
; X64-NEXT: decl %edi
|
|
; X64-NEXT: jmp t6 # TAILCALL
|
|
;
|
|
; X32-LABEL: t6:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: cmpl $9, %edi
|
|
; X32-NEXT: jg bar # TAILCALL
|
|
; X32-NEXT: # %bb.1: # %bb
|
|
; X32-NEXT: decl %edi
|
|
; X32-NEXT: jmp t6 # TAILCALL
|
|
%t0 = icmp slt i32 %x, 10
|
|
br i1 %t0, label %bb, label %bb1
|
|
|
|
bb:
|
|
%t1 = add nsw i32 %x, -1
|
|
%t2 = tail call i32 @t6(i32 %t1) nounwind ssp
|
|
ret i32 %t2
|
|
|
|
bb1:
|
|
%t3 = tail call i32 @bar(i32 %x) nounwind
|
|
ret i32 %t3
|
|
}
|
|
|
|
declare dso_local i32 @bar(i32)
|
|
|
|
define dso_local i32 @t7(i32 %a, i32 %b, i32 %c) nounwind ssp {
|
|
; X86-LABEL: t7:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: jmp bar2 # TAILCALL
|
|
;
|
|
; X64-LABEL: t7:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmp bar2 # TAILCALL
|
|
;
|
|
; X32-LABEL: t7:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmp bar2 # TAILCALL
|
|
%t0 = tail call i32 @bar2(i32 %a, i32 %b, i32 %c) nounwind
|
|
ret i32 %t0
|
|
}
|
|
|
|
declare dso_local i32 @bar2(i32, i32, i32)
|
|
|
|
define signext i16 @t8() nounwind ssp {
|
|
; X86-LABEL: t8:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: jmp bar3 # TAILCALL
|
|
;
|
|
; X64-LABEL: t8:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: jmp bar3 # TAILCALL
|
|
;
|
|
; X32-LABEL: t8:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: jmp bar3 # TAILCALL
|
|
entry:
|
|
%0 = tail call signext i16 @bar3() nounwind ; <i16> [#uses=1]
|
|
ret i16 %0
|
|
}
|
|
|
|
declare dso_local signext i16 @bar3()
|
|
|
|
define signext i16 @t9(ptr nocapture %x) nounwind ssp {
|
|
; X86-LABEL: t9:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movl $0, (%esp)
|
|
; X86-NEXT: calll *{{[0-9]+}}(%esp)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t9:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: movq %rdi, %rax
|
|
; X64-NEXT: xorl %edi, %edi
|
|
; X64-NEXT: jmpq *%rax # TAILCALL
|
|
;
|
|
; X32-LABEL: t9:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: movq %rdi, %rax
|
|
; X32-NEXT: xorl %edi, %edi
|
|
; X32-NEXT: jmpq *%rax # TAILCALL
|
|
entry:
|
|
%0 = tail call signext i16 %x(i32 0) nounwind
|
|
ret i16 %0
|
|
}
|
|
|
|
define dso_local void @t10() nounwind ssp {
|
|
; X86-LABEL: t10:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: calll foo4
|
|
;
|
|
; X64-LABEL: t10:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: pushq %rax
|
|
; X64-NEXT: callq foo4
|
|
;
|
|
; X32-LABEL: t10:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: pushq %rax
|
|
; X32-NEXT: callq foo4
|
|
entry:
|
|
%0 = tail call i32 @foo4() noreturn nounwind
|
|
unreachable
|
|
}
|
|
|
|
declare dso_local i32 @foo4()
|
|
|
|
; In 32-bit mode, it's emitting a bunch of dead loads that are not being
|
|
; eliminated currently.
|
|
|
|
define dso_local i32 @t11(i32 %x, i32 %y, i32 %z.0, i32 %z.1, i32 %z.2) nounwind ssp {
|
|
; X86-LABEL: t11:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: cmpl $0, {{[0-9]+}}(%esp)
|
|
; X86-NEXT: jne foo5 # TAILCALL
|
|
; X86-NEXT: # %bb.1: # %bb6
|
|
; X86-NEXT: xorl %eax, %eax
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t11:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: testl %edi, %edi
|
|
; X64-NEXT: jne foo5 # TAILCALL
|
|
; X64-NEXT: # %bb.1: # %bb6
|
|
; X64-NEXT: xorl %eax, %eax
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t11:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: testl %edi, %edi
|
|
; X32-NEXT: jne foo5 # TAILCALL
|
|
; X32-NEXT: # %bb.1: # %bb6
|
|
; X32-NEXT: xorl %eax, %eax
|
|
; X32-NEXT: retq
|
|
entry:
|
|
%0 = icmp eq i32 %x, 0
|
|
br i1 %0, label %bb6, label %bb
|
|
|
|
bb:
|
|
%1 = tail call i32 @foo5(i32 %x, i32 %y, i32 %z.0, i32 %z.1, i32 %z.2) nounwind
|
|
ret i32 %1
|
|
|
|
bb6:
|
|
ret i32 0
|
|
}
|
|
|
|
declare dso_local i32 @foo5(i32, i32, i32, i32, i32)
|
|
|
|
%struct.t = type { i32, i32, i32, i32, i32 }
|
|
|
|
define dso_local i32 @t12(i32 %x, i32 %y, ptr byval(%struct.t) align 4 %z) nounwind ssp {
|
|
; X86-LABEL: t12:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: cmpl $0, {{[0-9]+}}(%esp)
|
|
; X86-NEXT: jne foo6 # TAILCALL
|
|
; X86-NEXT: # %bb.1: # %bb2
|
|
; X86-NEXT: xorl %eax, %eax
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t12:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: testl %edi, %edi
|
|
; X64-NEXT: jne foo6 # TAILCALL
|
|
; X64-NEXT: # %bb.1: # %bb2
|
|
; X64-NEXT: xorl %eax, %eax
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t12:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: testl %edi, %edi
|
|
; X32-NEXT: jne foo6 # TAILCALL
|
|
; X32-NEXT: # %bb.1: # %bb2
|
|
; X32-NEXT: xorl %eax, %eax
|
|
; X32-NEXT: retq
|
|
entry:
|
|
%0 = icmp eq i32 %x, 0
|
|
br i1 %0, label %bb2, label %bb
|
|
|
|
bb:
|
|
%1 = tail call i32 @foo6(i32 %x, i32 %y, ptr byval(%struct.t) align 4 %z) nounwind
|
|
ret i32 %1
|
|
|
|
bb2:
|
|
ret i32 0
|
|
}
|
|
|
|
declare dso_local i32 @foo6(i32, i32, ptr byval(%struct.t) align 4)
|
|
|
|
; rdar://r7717598
|
|
%struct.ns = type { i32, i32 }
|
|
%struct.cp = type { float, float, float, float, float }
|
|
|
|
define ptr @t13(ptr %yy) nounwind ssp {
|
|
; X86-LABEL: t13:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $28, %esp
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: movl 16(%eax), %ecx
|
|
; X86-NEXT: movl %ecx, {{[0-9]+}}(%esp)
|
|
; X86-NEXT: movsd {{.*#+}} xmm0 = mem[0],zero
|
|
; X86-NEXT: movsd {{.*#+}} xmm1 = mem[0],zero
|
|
; X86-NEXT: movsd %xmm1, {{[0-9]+}}(%esp)
|
|
; X86-NEXT: movsd %xmm0, (%esp)
|
|
; X86-NEXT: xorl %ecx, %ecx
|
|
; X86-NEXT: calll foo7
|
|
; X86-NEXT: addl $28, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t13:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: pushq %rax
|
|
; X64-NEXT: subq $8, %rsp
|
|
; X64-NEXT: movl 16(%rdi), %eax
|
|
; X64-NEXT: movq (%rdi), %rcx
|
|
; X64-NEXT: movq 8(%rdi), %rdx
|
|
; X64-NEXT: xorl %edi, %edi
|
|
; X64-NEXT: pushq %rax
|
|
; X64-NEXT: pushq %rdx
|
|
; X64-NEXT: pushq %rcx
|
|
; X64-NEXT: callq foo7
|
|
; X64-NEXT: addq $32, %rsp
|
|
; X64-NEXT: popq %rcx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t13:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: pushq %rax
|
|
; X32-NEXT: subl $8, %esp
|
|
; X32-NEXT: movl 16(%edi), %eax
|
|
; X32-NEXT: movq (%edi), %rcx
|
|
; X32-NEXT: movq 8(%edi), %rdx
|
|
; X32-NEXT: xorl %edi, %edi
|
|
; X32-NEXT: pushq %rax
|
|
; X32-NEXT: pushq %rdx
|
|
; X32-NEXT: pushq %rcx
|
|
; X32-NEXT: callq foo7
|
|
; X32-NEXT: addl $32, %esp
|
|
; X32-NEXT: movl %eax, %eax
|
|
; X32-NEXT: popq %rcx
|
|
; X32-NEXT: retq
|
|
entry:
|
|
%0 = tail call fastcc ptr @foo7(ptr byval(%struct.cp) align 4 %yy, i8 signext 0) nounwind
|
|
ret ptr %0
|
|
}
|
|
|
|
; rdar://6195379
|
|
; llvm can't do sibcall for this in 32-bit mode (yet).
|
|
declare dso_local fastcc ptr @foo7(ptr byval(%struct.cp) align 4, i8 signext) nounwind ssp
|
|
|
|
%struct.__block_descriptor = type { i64, i64 }
|
|
%struct.__block_descriptor_withcopydispose = type { i64, i64, ptr, ptr }
|
|
%struct.__block_literal_1 = type { ptr, i32, i32, ptr, ptr }
|
|
%struct.__block_literal_2 = type { ptr, i32, i32, ptr, ptr, ptr }
|
|
|
|
define dso_local void @t14(ptr nocapture %.block_descriptor) nounwind ssp {
|
|
; X86-LABEL: t14:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: movl 20(%eax), %eax
|
|
; X86-NEXT: movl %eax, (%esp)
|
|
; X86-NEXT: calll *12(%eax)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t14:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: movq 32(%rdi), %rdi
|
|
; X64-NEXT: jmpq *16(%rdi) # TAILCALL
|
|
;
|
|
; X32-LABEL: t14:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: movl 20(%edi), %edi
|
|
; X32-NEXT: movl 12(%edi), %eax
|
|
; X32-NEXT: jmpq *%rax # TAILCALL
|
|
entry:
|
|
%0 = getelementptr inbounds %struct.__block_literal_2, ptr %.block_descriptor, i64 0, i32 5 ; <ptr> [#uses=1]
|
|
%1 = load ptr, ptr %0, align 8 ; <ptr> [#uses=2]
|
|
%2 = getelementptr inbounds %struct.__block_literal_1, ptr %1, i64 0, i32 3 ; <ptr> [#uses=1]
|
|
%3 = load ptr, ptr %2, align 8 ; <ptr> [#uses=1]
|
|
tail call void %3(ptr %1) nounwind
|
|
ret void
|
|
}
|
|
|
|
; rdar://7726868
|
|
%struct.foo = type { [4 x i32] }
|
|
|
|
define dso_local void @t15(ptr noalias sret(%struct.foo) %agg.result) nounwind {
|
|
; X86-LABEL: t15:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
|
|
; X86-NEXT: movl %esi, %ecx
|
|
; X86-NEXT: calll f
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl $4
|
|
;
|
|
; X64-LABEL: t15:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: callq f
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t15:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: callq f
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @f(ptr noalias sret(%struct.foo) %agg.result) nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local void @f(ptr noalias sret(%struct.foo)) nounwind
|
|
|
|
define dso_local void @t16() nounwind ssp {
|
|
; X86-LABEL: t16:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: calll bar4
|
|
; X86-NEXT: fstp %st(0)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t16:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: jmp bar4 # TAILCALL
|
|
;
|
|
; X32-LABEL: t16:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: jmp bar4 # TAILCALL
|
|
entry:
|
|
%0 = tail call double @bar4() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local double @bar4()
|
|
|
|
; rdar://6283267
|
|
define dso_local void @t17() nounwind ssp {
|
|
; X86-LABEL: t17:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: jmp bar5 # TAILCALL
|
|
;
|
|
; X64-LABEL: t17:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: xorl %eax, %eax
|
|
; X64-NEXT: jmp bar5 # TAILCALL
|
|
;
|
|
; X32-LABEL: t17:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: xorl %eax, %eax
|
|
; X32-NEXT: jmp bar5 # TAILCALL
|
|
entry:
|
|
tail call void (...) @bar5() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local void @bar5(...)
|
|
|
|
; rdar://7774847
|
|
define dso_local void @t18() nounwind ssp {
|
|
; X86-LABEL: t18:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: calll bar6
|
|
; X86-NEXT: fstp %st(0)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t18:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: xorl %eax, %eax
|
|
; X64-NEXT: jmp bar6 # TAILCALL
|
|
;
|
|
; X32-LABEL: t18:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: xorl %eax, %eax
|
|
; X32-NEXT: jmp bar6 # TAILCALL
|
|
entry:
|
|
%0 = tail call double (...) @bar6() nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local double @bar6(...)
|
|
|
|
define dso_local void @t19() alignstack(32) nounwind {
|
|
; X86-LABEL: t19:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: pushl %ebp
|
|
; X86-NEXT: movl %esp, %ebp
|
|
; X86-NEXT: andl $-32, %esp
|
|
; X86-NEXT: subl $32, %esp
|
|
; X86-NEXT: calll foo
|
|
; X86-NEXT: movl %ebp, %esp
|
|
; X86-NEXT: popl %ebp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t19:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: pushq %rbp
|
|
; X64-NEXT: movq %rsp, %rbp
|
|
; X64-NEXT: andq $-32, %rsp
|
|
; X64-NEXT: subq $32, %rsp
|
|
; X64-NEXT: callq foo
|
|
; X64-NEXT: movq %rbp, %rsp
|
|
; X64-NEXT: popq %rbp
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t19:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: pushq %rbp
|
|
; X32-NEXT: movl %esp, %ebp
|
|
; X32-NEXT: andl $-32, %esp
|
|
; X32-NEXT: subl $32, %esp
|
|
; X32-NEXT: callq foo
|
|
; X32-NEXT: movl %ebp, %esp
|
|
; X32-NEXT: popq %rbp
|
|
; X32-NEXT: retq
|
|
entry:
|
|
tail call void @foo() nounwind
|
|
ret void
|
|
}
|
|
|
|
; If caller / callee calling convention mismatch then check if the return
|
|
; values are returned in the same registers.
|
|
; rdar://7874780
|
|
|
|
define dso_local double @t20(double %x) nounwind {
|
|
; X86-LABEL: t20:
|
|
; X86: # %bb.0: # %entry
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movsd {{.*#+}} xmm0 = mem[0],zero
|
|
; X86-NEXT: calll foo20
|
|
; X86-NEXT: movsd %xmm0, (%esp)
|
|
; X86-NEXT: fldl (%esp)
|
|
; X86-NEXT: addl $12, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t20:
|
|
; X64: # %bb.0: # %entry
|
|
; X64-NEXT: jmp foo20 # TAILCALL
|
|
;
|
|
; X32-LABEL: t20:
|
|
; X32: # %bb.0: # %entry
|
|
; X32-NEXT: jmp foo20 # TAILCALL
|
|
entry:
|
|
%0 = tail call fastcc double @foo20(double %x) nounwind
|
|
ret double %0
|
|
}
|
|
|
|
declare dso_local fastcc double @foo20(double) nounwind
|
|
|
|
; bug 28417
|
|
define fastcc void @t21_sret_to_sret(ptr noalias sret(%struct.foo) %agg.result) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: calll t21_f_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: callq t21_f_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: callq t21_f_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @t21_f_sret(ptr noalias sret(%struct.foo) %agg.result) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_more_args(ptr noalias sret(%struct.foo) %agg.result, i32 %a, i32 %b) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_more_args:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: movl %eax, (%esp)
|
|
; X86-NEXT: calll f_sret@PLT
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_more_args:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: callq f_sret@PLT
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_more_args:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: callq f_sret@PLT
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @f_sret(ptr noalias sret(%struct.foo) %agg.result, i32 %a, i32 %b) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_second_arg_sret(ptr noalias %agg.result, ptr noalias sret(%struct.foo) %ret) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_second_arg_sret:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %edx, %esi
|
|
; X86-NEXT: movl %edx, %ecx
|
|
; X86-NEXT: calll t21_f_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_second_arg_sret:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rsi, %rbx
|
|
; X64-NEXT: movq %rsi, %rdi
|
|
; X64-NEXT: callq t21_f_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_second_arg_sret:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rsi, %rbx
|
|
; X32-NEXT: movq %rsi, %rdi
|
|
; X32-NEXT: callq t21_f_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @t21_f_sret(ptr noalias sret(%struct.foo) %ret) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_more_args2(ptr noalias sret(%struct.foo) %agg.result, i32 %a, i32 %b) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_more_args2:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: movl %edx, (%esp)
|
|
; X86-NEXT: movl %eax, %edx
|
|
; X86-NEXT: calll f_sret@PLT
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_more_args2:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movl %esi, %eax
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: movl %edx, %esi
|
|
; X64-NEXT: movl %eax, %edx
|
|
; X64-NEXT: callq f_sret@PLT
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_more_args2:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movl %esi, %eax
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: movl %edx, %esi
|
|
; X32-NEXT: movl %eax, %edx
|
|
; X32-NEXT: callq f_sret@PLT
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @f_sret(ptr noalias sret(%struct.foo) %agg.result, i32 %b, i32 %a) nounwind
|
|
ret void
|
|
}
|
|
|
|
|
|
define fastcc void @t21_sret_to_sret_args_mismatch(ptr noalias sret(%struct.foo) %agg.result, ptr noalias %ret) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_args_mismatch:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: movl %edx, %ecx
|
|
; X86-NEXT: calll t21_f_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_args_mismatch:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: movq %rsi, %rdi
|
|
; X64-NEXT: callq t21_f_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_args_mismatch:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: movq %rsi, %rdi
|
|
; X32-NEXT: callq t21_f_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @t21_f_sret(ptr noalias sret(%struct.foo) %ret) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_args_mismatch2(ptr noalias sret(%struct.foo) %agg.result, ptr noalias %ret) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_args_mismatch2:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: movl %edx, %ecx
|
|
; X86-NEXT: calll t21_f_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_args_mismatch2:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: movq %rsi, %rdi
|
|
; X64-NEXT: callq t21_f_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_args_mismatch2:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: movq %rsi, %rdi
|
|
; X32-NEXT: callq t21_f_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @t21_f_sret(ptr noalias sret(%struct.foo) %ret) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_arg_mismatch(ptr noalias sret(%struct.foo) %agg.result) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_arg_mismatch:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: calll ret_struct@PLT
|
|
; X86-NEXT: movl %eax, %ecx
|
|
; X86-NEXT: calll t21_f_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_arg_mismatch:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: callq ret_struct@PLT
|
|
; X64-NEXT: movq %rax, %rdi
|
|
; X64-NEXT: callq t21_f_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_arg_mismatch:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: callq ret_struct@PLT
|
|
; X32-NEXT: movl %eax, %edi
|
|
; X32-NEXT: callq t21_f_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
%a = call fastcc ptr @ret_struct()
|
|
tail call fastcc void @t21_f_sret(ptr noalias sret(%struct.foo) %a) nounwind
|
|
ret void
|
|
}
|
|
|
|
define fastcc void @t21_sret_to_sret_structs_mismatch(ptr noalias sret(%struct.foo) %agg.result, ptr noalias %a) nounwind {
|
|
; X86-LABEL: t21_sret_to_sret_structs_mismatch:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %edi
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: pushl %eax
|
|
; X86-NEXT: movl %edx, %esi
|
|
; X86-NEXT: movl %ecx, %edi
|
|
; X86-NEXT: calll ret_struct@PLT
|
|
; X86-NEXT: movl %esi, %ecx
|
|
; X86-NEXT: movl %eax, %edx
|
|
; X86-NEXT: calll t21_f_sret2
|
|
; X86-NEXT: movl %edi, %eax
|
|
; X86-NEXT: addl $4, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: popl %edi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_sret_structs_mismatch:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %r14
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: pushq %rax
|
|
; X64-NEXT: movq %rsi, %rbx
|
|
; X64-NEXT: movq %rdi, %r14
|
|
; X64-NEXT: callq ret_struct@PLT
|
|
; X64-NEXT: movq %rbx, %rdi
|
|
; X64-NEXT: movq %rax, %rsi
|
|
; X64-NEXT: callq t21_f_sret2
|
|
; X64-NEXT: movq %r14, %rax
|
|
; X64-NEXT: addq $8, %rsp
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: popq %r14
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_sret_structs_mismatch:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %r14
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: pushq %rax
|
|
; X32-NEXT: movq %rsi, %rbx
|
|
; X32-NEXT: movq %rdi, %r14
|
|
; X32-NEXT: callq ret_struct@PLT
|
|
; X32-NEXT: movl %eax, %esi
|
|
; X32-NEXT: movq %rbx, %rdi
|
|
; X32-NEXT: callq t21_f_sret2
|
|
; X32-NEXT: movl %r14d, %eax
|
|
; X32-NEXT: addl $8, %esp
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: popq %r14
|
|
; X32-NEXT: retq
|
|
%b = call fastcc ptr @ret_struct()
|
|
tail call fastcc void @t21_f_sret2(ptr noalias sret(%struct.foo) %a, ptr noalias %b) nounwind
|
|
ret void
|
|
}
|
|
|
|
declare ccc ptr @ret_struct() nounwind
|
|
|
|
|
|
define fastcc void @t21_sret_to_non_sret(ptr noalias sret(%struct.foo) %agg.result) nounwind {
|
|
; X86-LABEL: t21_sret_to_non_sret:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: pushl %esi
|
|
; X86-NEXT: subl $8, %esp
|
|
; X86-NEXT: movl %ecx, %esi
|
|
; X86-NEXT: calll t21_f_non_sret
|
|
; X86-NEXT: movl %esi, %eax
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: popl %esi
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t21_sret_to_non_sret:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: pushq %rbx
|
|
; X64-NEXT: movq %rdi, %rbx
|
|
; X64-NEXT: callq t21_f_non_sret
|
|
; X64-NEXT: movq %rbx, %rax
|
|
; X64-NEXT: popq %rbx
|
|
; X64-NEXT: retq
|
|
;
|
|
; X32-LABEL: t21_sret_to_non_sret:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: pushq %rbx
|
|
; X32-NEXT: movq %rdi, %rbx
|
|
; X32-NEXT: callq t21_f_non_sret
|
|
; X32-NEXT: movl %ebx, %eax
|
|
; X32-NEXT: popq %rbx
|
|
; X32-NEXT: retq
|
|
tail call fastcc void @t21_f_non_sret(ptr %agg.result) nounwind
|
|
ret void
|
|
}
|
|
|
|
define ccc void @t22_non_sret_to_sret(ptr %agg.result) nounwind {
|
|
; i686 not tailcallable, as sret is callee-pop here.
|
|
; X86-LABEL: t22_non_sret_to_sret:
|
|
; X86: # %bb.0:
|
|
; X86-NEXT: subl $12, %esp
|
|
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
; X86-NEXT: movl %eax, (%esp)
|
|
; X86-NEXT: calll t22_f_sret@PLT
|
|
; X86-NEXT: addl $8, %esp
|
|
; X86-NEXT: retl
|
|
;
|
|
; X64-LABEL: t22_non_sret_to_sret:
|
|
; X64: # %bb.0:
|
|
; X64-NEXT: jmp t22_f_sret@PLT # TAILCALL
|
|
;
|
|
; X32-LABEL: t22_non_sret_to_sret:
|
|
; X32: # %bb.0:
|
|
; X32-NEXT: jmp t22_f_sret@PLT # TAILCALL
|
|
tail call ccc void @t22_f_sret(ptr noalias sret(%struct.foo) %agg.result) nounwind
|
|
ret void
|
|
}
|
|
|
|
declare dso_local fastcc void @t21_f_sret(ptr noalias sret(%struct.foo)) nounwind
|
|
declare dso_local fastcc void @t21_f_sret2(ptr noalias sret(%struct.foo), ptr noalias) nounwind
|
|
declare dso_local fastcc void @t21_f_non_sret(ptr) nounwind
|
|
|
|
declare ccc void @t22_f_sret(ptr noalias sret(%struct.foo)) nounwind
|
|
|
|
declare ccc void @f_sret(ptr noalias sret(%struct.foo), i32, i32) nounwind
|