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