llvm/llvm/test/CodeGen/SPARC/tailcall.ll

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=sparc -verify-machineinstrs | FileCheck %s --check-prefix=V8
; RUN: llc < %s -mtriple=sparcv9 -verify-machineinstrs | FileCheck %s --check-prefix=V9

define i32 @simple_leaf(i32 %i) #0 {
; V8-LABEL: simple_leaf:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    mov %o7, %g1
; V8-NEXT:    call foo
; V8-NEXT:    mov %g1, %o7
;
; V9-LABEL: simple_leaf:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    mov %o7, %g1
; V9-NEXT:    call foo
; V9-NEXT:    mov %g1, %o7
entry:
  %call = tail call i32 @foo(i32 %i)
  ret i32 %call
}

define i32 @simple_standard(i32 %i) #1 {
; V8-LABEL: simple_standard:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -96, %sp
; V8-NEXT:    call foo
; V8-NEXT:    restore
;
; V9-LABEL: simple_standard:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -128, %sp
; V9-NEXT:    call foo
; V9-NEXT:    restore
entry:
  %call = tail call i32 @foo(i32 %i)
  ret i32 %call
}

define i32 @extra_arg_leaf(i32 %i) #0 {
; V8-LABEL: extra_arg_leaf:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    mov 12, %o1
; V8-NEXT:    mov %o7, %g1
; V8-NEXT:    call foo2
; V8-NEXT:    mov %g1, %o7
;
; V9-LABEL: extra_arg_leaf:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    mov 12, %o1
; V9-NEXT:    mov %o7, %g1
; V9-NEXT:    call foo2
; V9-NEXT:    mov %g1, %o7
entry:
  %call = tail call i32 @foo2(i32 %i, i32 12)
  ret i32 %call
}

define i32 @extra_arg_standard(i32 %i) #1 {
; V8-LABEL: extra_arg_standard:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -96, %sp
; V8-NEXT:    call foo2
; V8-NEXT:    restore %g0, 12, %o1
;
; V9-LABEL: extra_arg_standard:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -128, %sp
; V9-NEXT:    call foo2
; V9-NEXT:    restore %g0, 12, %o1
entry:
  %call = tail call i32 @foo2(i32 %i, i32 12)
  ret i32 %call
}

; Perform tail call optimization for external symbol.

define void @caller_extern(ptr %src) optsize #0 {
; V8-LABEL: caller_extern:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    sethi %hi(dest), %o1
; V8-NEXT:    add %o1, %lo(dest), %o1
; V8-NEXT:    mov 7, %o2
; V8-NEXT:    mov %o0, %o3
; V8-NEXT:    mov %o1, %o0
; V8-NEXT:    mov %o3, %o1
; V8-NEXT:    mov %o7, %g1
; V8-NEXT:    call memcpy
; V8-NEXT:    mov %g1, %o7
;
; V9-LABEL: caller_extern:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    sethi %h44(dest), %o1
; V9-NEXT:    add %o1, %m44(dest), %o1
; V9-NEXT:    sllx %o1, 12, %o1
; V9-NEXT:    add %o1, %l44(dest), %o1
; V9-NEXT:    mov 7, %o2
; V9-NEXT:    mov %o0, %o3
; V9-NEXT:    mov %o1, %o0
; V9-NEXT:    mov %o3, %o1
; V9-NEXT:    mov %o7, %g1
; V9-NEXT:    call memcpy
; V9-NEXT:    mov %g1, %o7
entry:
  tail call void @llvm.memcpy.p0.p0.i32(
    ptr @dest,
    ptr %src, i32 7, i1 false)
  ret void
}

; Perform tail call optimization for function pointer.

define i32 @func_ptr_test(ptr nocapture %func_ptr) #0 {
; V8-LABEL: func_ptr_test:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    jmp %o0
; V8-NEXT:    nop
;
; V9-LABEL: func_ptr_test:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    jmp %o0
; V9-NEXT:    nop
entry:
  %call = tail call i32 %func_ptr() #1
  ret i32 %call
}

define i32 @func_ptr_test2(ptr nocapture %func_ptr,
; V8-LABEL: func_ptr_test2:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -96, %sp
; V8-NEXT:    mov 10, %i3
; V8-NEXT:    mov %i0, %i4
; V8-NEXT:    mov %i1, %i0
; V8-NEXT:    jmp %i4
; V8-NEXT:    restore %g0, %i3, %o1
;
; V9-LABEL: func_ptr_test2:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -128, %sp
; V9-NEXT:    mov 10, %i3
; V9-NEXT:    mov %i0, %i4
; V9-NEXT:    mov %i1, %i0
; V9-NEXT:    jmp %i4
; V9-NEXT:    restore %g0, %i3, %o1
                           i32 %r, i32 %q) #1 {
entry:
  %call = tail call i32 %func_ptr(i32 %r, i32 10, i32 %q) #1
  ret i32 %call
}


; Do not tail call optimize if stack is used to pass parameters.

define i32 @caller_args() #0 {
; V8-LABEL: caller_args:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -104, %sp
; V8-NEXT:    mov 6, %i0
; V8-NEXT:    mov 1, %o1
; V8-NEXT:    mov 2, %o2
; V8-NEXT:    mov 3, %o3
; V8-NEXT:    mov 4, %o4
; V8-NEXT:    mov 5, %o5
; V8-NEXT:    st %i0, [%sp+92]
; V8-NEXT:    call foo7
; V8-NEXT:    mov %g0, %o0
; V8-NEXT:    ret
; V8-NEXT:    restore %g0, %o0, %o0
;
; V9-LABEL: caller_args:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -192, %sp
; V9-NEXT:    mov 6, %i0
; V9-NEXT:    mov 1, %o1
; V9-NEXT:    mov 2, %o2
; V9-NEXT:    mov 3, %o3
; V9-NEXT:    mov 4, %o4
; V9-NEXT:    mov 5, %o5
; V9-NEXT:    stx %i0, [%sp+2223]
; V9-NEXT:    call foo7
; V9-NEXT:    mov %g0, %o0
; V9-NEXT:    ret
; V9-NEXT:    restore %g0, %o0, %o0
entry:
  %r = tail call i32 @foo7(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6)
  ret i32 %r
}

; Byval parameters hand the function a pointer directly into the stack area
; we want to reuse during a tail call. Do not tail call optimize functions with
; byval parameters.

define i32 @caller_byval() #0 {
; V8-LABEL: caller_byval:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -104, %sp
; V8-NEXT:    ld [%fp+-4], %i0
; V8-NEXT:    st %i0, [%fp+-8]
; V8-NEXT:    call callee_byval
; V8-NEXT:    add %fp, -8, %o0
; V8-NEXT:    ret
; V8-NEXT:    restore %g0, %o0, %o0
;
; V9-LABEL: caller_byval:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -192, %sp
; V9-NEXT:    call callee_byval
; V9-NEXT:    add %fp, 2039, %o0
; V9-NEXT:    ret
; V9-NEXT:    restore %g0, %o0, %o0
entry:
  %a = alloca ptr
  %r = tail call i32 @callee_byval(ptr byval(ptr) %a)
  ret i32 %r
}

; Perform tail call optimization for sret function.

define void @sret_test(ptr noalias sret(%struct.a) %agg.result) #0 {
; V8-LABEL: sret_test:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    mov %o7, %g1
; V8-NEXT:    call sret_func
; V8-NEXT:    mov %g1, %o7
;
; V9-LABEL: sret_test:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    mov %o7, %g1
; V9-NEXT:    call sret_func
; V9-NEXT:    mov %g1, %o7
entry:
  tail call void @sret_func(ptr sret(%struct.a) %agg.result)
  ret void
}

; Do not tail call if either caller or callee returns
; a struct and the other does not. Returning a large
; struct will generate a memcpy as the tail function.

define void @ret_large_struct(ptr noalias sret(%struct.big) %agg.result) #0 {
; V8-LABEL: ret_large_struct:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    save %sp, -96, %sp
; V8-NEXT:    ld [%fp+64], %i0
; V8-NEXT:    sethi %hi(bigstruct), %i1
; V8-NEXT:    add %i1, %lo(bigstruct), %o1
; V8-NEXT:    mov 400, %o2
; V8-NEXT:    call memcpy
; V8-NEXT:    mov %i0, %o0
; V8-NEXT:    jmp %i7+12
; V8-NEXT:    restore
;
; V9-LABEL: ret_large_struct:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    save %sp, -176, %sp
; V9-NEXT:    sethi %h44(bigstruct), %i1
; V9-NEXT:    add %i1, %m44(bigstruct), %i1
; V9-NEXT:    sllx %i1, 12, %i1
; V9-NEXT:    add %i1, %l44(bigstruct), %o1
; V9-NEXT:    mov 400, %o2
; V9-NEXT:    call memcpy
; V9-NEXT:    mov %i0, %o0
; V9-NEXT:    ret
; V9-NEXT:    restore
entry:
  %0 = bitcast ptr %agg.result to ptr
  tail call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 @bigstruct, i32 400, i1 false)
  ret void
}

; Test register + immediate pattern.

define void @addri_test(i32 %ptr) #0 {
; V8-LABEL: addri_test:
; V8:       ! %bb.0: ! %entry
; V8-NEXT:    jmp %o0+4
; V8-NEXT:    nop
;
; V9-LABEL: addri_test:
; V9:       ! %bb.0: ! %entry
; V9-NEXT:    add %o0, 4, %o0
; V9-NEXT:    srl %o0, 0, %o0
; V9-NEXT:    jmp %o0
; V9-NEXT:    nop
entry:
  %add = add nsw i32 %ptr, 4
  %0 = inttoptr i32 %add to ptr
  tail call void %0() #1
  ret void
}

%struct.a = type { i32, i32 }
@dest = global [2 x i8] zeroinitializer

%struct.big = type { [100 x i32] }
@bigstruct = global %struct.big zeroinitializer

declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1)
declare void @sret_func(ptr sret(%struct.a))
declare i32 @callee_byval(ptr byval(ptr) %a)
declare i32 @foo(i32)
declare i32 @foo2(i32, i32)
declare i32 @foo7(i32, i32, i32, i32, i32, i32, i32)

attributes #0 = { nounwind "disable-tail-calls"="false"
                  "frame-pointer"="none" }
attributes #1 = { nounwind "disable-tail-calls"="false"
                  "frame-pointer"="all" }