311 lines
8.2 KiB
LLVM
311 lines
8.2 KiB
LLVM
; 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(i8* %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.p0i8.p0i8.i32(
|
|
i8* getelementptr inbounds ([2 x i8],
|
|
[2 x i8]* @dest, i32 0, i32 0),
|
|
i8* %src, i32 7, i1 false)
|
|
ret void
|
|
}
|
|
|
|
; Perform tail call optimization for function pointer.
|
|
|
|
define i32 @func_ptr_test(i32 ()* 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(i32 (i32, i32, i32)* 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 %g0, %o0
|
|
; 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: call foo7
|
|
; V8-NEXT: st %i0, [%sp+92]
|
|
; 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 0, %o0
|
|
; 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: call foo7
|
|
; V9-NEXT: stx %i0, [%sp+2223]
|
|
; 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 i32*
|
|
%r = tail call i32 @callee_byval(i32** byval(i32*) %a)
|
|
ret i32 %r
|
|
}
|
|
|
|
; Perform tail call optimization for sret function.
|
|
|
|
define void @sret_test(%struct.a* 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 bitcast (void (%struct.a*)* @sret_func to
|
|
void (%struct.a*)*)(%struct.a* 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(%struct.big* 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 %struct.big* %agg.result to i8*
|
|
tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%struct.big* @bigstruct to i8*), 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 void ()*
|
|
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.p0i8.p0i8.i32(i8*, i8*, i32, i1)
|
|
declare void @sret_func(%struct.a* sret(%struct.a))
|
|
declare i32 @callee_byval(i32** byval(i32*) %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" }
|