1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc < %s -mtriple=sparc -verify-machineinstrs | FileCheck %s --check-prefix=V8 3; RUN: llc < %s -mtriple=sparcv9 -verify-machineinstrs | FileCheck %s --check-prefix=V9 4 5define i32 @simple_leaf(i32 %i) #0 { 6; V8-LABEL: simple_leaf: 7; V8: ! %bb.0: ! %entry 8; V8-NEXT: mov %o7, %g1 9; V8-NEXT: call foo 10; V8-NEXT: mov %g1, %o7 11; 12; V9-LABEL: simple_leaf: 13; V9: ! %bb.0: ! %entry 14; V9-NEXT: mov %o7, %g1 15; V9-NEXT: call foo 16; V9-NEXT: mov %g1, %o7 17entry: 18 %call = tail call i32 @foo(i32 %i) 19 ret i32 %call 20} 21 22define i32 @simple_standard(i32 %i) #1 { 23; V8-LABEL: simple_standard: 24; V8: ! %bb.0: ! %entry 25; V8-NEXT: save %sp, -96, %sp 26; V8-NEXT: call foo 27; V8-NEXT: restore 28; 29; V9-LABEL: simple_standard: 30; V9: ! %bb.0: ! %entry 31; V9-NEXT: save %sp, -128, %sp 32; V9-NEXT: call foo 33; V9-NEXT: restore 34entry: 35 %call = tail call i32 @foo(i32 %i) 36 ret i32 %call 37} 38 39define i32 @extra_arg_leaf(i32 %i) #0 { 40; V8-LABEL: extra_arg_leaf: 41; V8: ! %bb.0: ! %entry 42; V8-NEXT: mov 12, %o1 43; V8-NEXT: mov %o7, %g1 44; V8-NEXT: call foo2 45; V8-NEXT: mov %g1, %o7 46; 47; V9-LABEL: extra_arg_leaf: 48; V9: ! %bb.0: ! %entry 49; V9-NEXT: mov 12, %o1 50; V9-NEXT: mov %o7, %g1 51; V9-NEXT: call foo2 52; V9-NEXT: mov %g1, %o7 53entry: 54 %call = tail call i32 @foo2(i32 %i, i32 12) 55 ret i32 %call 56} 57 58define i32 @extra_arg_standard(i32 %i) #1 { 59; V8-LABEL: extra_arg_standard: 60; V8: ! %bb.0: ! %entry 61; V8-NEXT: save %sp, -96, %sp 62; V8-NEXT: call foo2 63; V8-NEXT: restore %g0, 12, %o1 64; 65; V9-LABEL: extra_arg_standard: 66; V9: ! %bb.0: ! %entry 67; V9-NEXT: save %sp, -128, %sp 68; V9-NEXT: call foo2 69; V9-NEXT: restore %g0, 12, %o1 70entry: 71 %call = tail call i32 @foo2(i32 %i, i32 12) 72 ret i32 %call 73} 74 75; Perform tail call optimization for external symbol. 76 77define void @caller_extern(ptr %src) optsize #0 { 78; V8-LABEL: caller_extern: 79; V8: ! %bb.0: ! %entry 80; V8-NEXT: sethi %hi(dest), %o1 81; V8-NEXT: add %o1, %lo(dest), %o1 82; V8-NEXT: mov 7, %o2 83; V8-NEXT: mov %o0, %o3 84; V8-NEXT: mov %o1, %o0 85; V8-NEXT: mov %o3, %o1 86; V8-NEXT: mov %o7, %g1 87; V8-NEXT: call memcpy 88; V8-NEXT: mov %g1, %o7 89; 90; V9-LABEL: caller_extern: 91; V9: ! %bb.0: ! %entry 92; V9-NEXT: sethi %h44(dest), %o1 93; V9-NEXT: add %o1, %m44(dest), %o1 94; V9-NEXT: sllx %o1, 12, %o1 95; V9-NEXT: add %o1, %l44(dest), %o1 96; V9-NEXT: mov 7, %o2 97; V9-NEXT: mov %o0, %o3 98; V9-NEXT: mov %o1, %o0 99; V9-NEXT: mov %o3, %o1 100; V9-NEXT: mov %o7, %g1 101; V9-NEXT: call memcpy 102; V9-NEXT: mov %g1, %o7 103entry: 104 tail call void @llvm.memcpy.p0.p0.i32( 105 ptr @dest, 106 ptr %src, i32 7, i1 false) 107 ret void 108} 109 110; Perform tail call optimization for function pointer. 111 112define i32 @func_ptr_test(ptr nocapture %func_ptr) #0 { 113; V8-LABEL: func_ptr_test: 114; V8: ! %bb.0: ! %entry 115; V8-NEXT: jmp %o0 116; V8-NEXT: nop 117; 118; V9-LABEL: func_ptr_test: 119; V9: ! %bb.0: ! %entry 120; V9-NEXT: jmp %o0 121; V9-NEXT: nop 122entry: 123 %call = tail call i32 %func_ptr() #1 124 ret i32 %call 125} 126 127define i32 @func_ptr_test2(ptr nocapture %func_ptr, 128; V8-LABEL: func_ptr_test2: 129; V8: ! %bb.0: ! %entry 130; V8-NEXT: save %sp, -96, %sp 131; V8-NEXT: mov 10, %i3 132; V8-NEXT: mov %i0, %i4 133; V8-NEXT: mov %i1, %i0 134; V8-NEXT: jmp %i4 135; V8-NEXT: restore %g0, %i3, %o1 136; 137; V9-LABEL: func_ptr_test2: 138; V9: ! %bb.0: ! %entry 139; V9-NEXT: save %sp, -128, %sp 140; V9-NEXT: mov 10, %i3 141; V9-NEXT: mov %i0, %i4 142; V9-NEXT: mov %i1, %i0 143; V9-NEXT: jmp %i4 144; V9-NEXT: restore %g0, %i3, %o1 145 i32 %r, i32 %q) #1 { 146entry: 147 %call = tail call i32 %func_ptr(i32 %r, i32 10, i32 %q) #1 148 ret i32 %call 149} 150 151 152; Do not tail call optimize if stack is used to pass parameters. 153 154define i32 @caller_args() #0 { 155; V8-LABEL: caller_args: 156; V8: ! %bb.0: ! %entry 157; V8-NEXT: save %sp, -104, %sp 158; V8-NEXT: mov 6, %i0 159; V8-NEXT: mov 1, %o1 160; V8-NEXT: mov 2, %o2 161; V8-NEXT: mov 3, %o3 162; V8-NEXT: mov 4, %o4 163; V8-NEXT: mov 5, %o5 164; V8-NEXT: st %i0, [%sp+92] 165; V8-NEXT: call foo7 166; V8-NEXT: mov %g0, %o0 167; V8-NEXT: ret 168; V8-NEXT: restore %g0, %o0, %o0 169; 170; V9-LABEL: caller_args: 171; V9: ! %bb.0: ! %entry 172; V9-NEXT: save %sp, -192, %sp 173; V9-NEXT: mov 6, %i0 174; V9-NEXT: mov 1, %o1 175; V9-NEXT: mov 2, %o2 176; V9-NEXT: mov 3, %o3 177; V9-NEXT: mov 4, %o4 178; V9-NEXT: mov 5, %o5 179; V9-NEXT: stx %i0, [%sp+2223] 180; V9-NEXT: call foo7 181; V9-NEXT: mov %g0, %o0 182; V9-NEXT: ret 183; V9-NEXT: restore %g0, %o0, %o0 184entry: 185 %r = tail call i32 @foo7(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) 186 ret i32 %r 187} 188 189; Byval parameters hand the function a pointer directly into the stack area 190; we want to reuse during a tail call. Do not tail call optimize functions with 191; byval parameters. 192 193define i32 @caller_byval() #0 { 194; V8-LABEL: caller_byval: 195; V8: ! %bb.0: ! %entry 196; V8-NEXT: save %sp, -104, %sp 197; V8-NEXT: ld [%fp+-4], %i0 198; V8-NEXT: st %i0, [%fp+-8] 199; V8-NEXT: call callee_byval 200; V8-NEXT: add %fp, -8, %o0 201; V8-NEXT: ret 202; V8-NEXT: restore %g0, %o0, %o0 203; 204; V9-LABEL: caller_byval: 205; V9: ! %bb.0: ! %entry 206; V9-NEXT: save %sp, -192, %sp 207; V9-NEXT: call callee_byval 208; V9-NEXT: add %fp, 2039, %o0 209; V9-NEXT: ret 210; V9-NEXT: restore %g0, %o0, %o0 211entry: 212 %a = alloca ptr 213 %r = tail call i32 @callee_byval(ptr byval(ptr) %a) 214 ret i32 %r 215} 216 217; Perform tail call optimization for sret function. 218 219define void @sret_test(ptr noalias sret(%struct.a) %agg.result) #0 { 220; V8-LABEL: sret_test: 221; V8: ! %bb.0: ! %entry 222; V8-NEXT: mov %o7, %g1 223; V8-NEXT: call sret_func 224; V8-NEXT: mov %g1, %o7 225; 226; V9-LABEL: sret_test: 227; V9: ! %bb.0: ! %entry 228; V9-NEXT: mov %o7, %g1 229; V9-NEXT: call sret_func 230; V9-NEXT: mov %g1, %o7 231entry: 232 tail call void @sret_func(ptr sret(%struct.a) %agg.result) 233 ret void 234} 235 236; Do not tail call if either caller or callee returns 237; a struct and the other does not. Returning a large 238; struct will generate a memcpy as the tail function. 239 240define void @ret_large_struct(ptr noalias sret(%struct.big) %agg.result) #0 { 241; V8-LABEL: ret_large_struct: 242; V8: ! %bb.0: ! %entry 243; V8-NEXT: save %sp, -96, %sp 244; V8-NEXT: ld [%fp+64], %i0 245; V8-NEXT: sethi %hi(bigstruct), %i1 246; V8-NEXT: add %i1, %lo(bigstruct), %o1 247; V8-NEXT: mov 400, %o2 248; V8-NEXT: call memcpy 249; V8-NEXT: mov %i0, %o0 250; V8-NEXT: jmp %i7+12 251; V8-NEXT: restore 252; 253; V9-LABEL: ret_large_struct: 254; V9: ! %bb.0: ! %entry 255; V9-NEXT: save %sp, -176, %sp 256; V9-NEXT: sethi %h44(bigstruct), %i1 257; V9-NEXT: add %i1, %m44(bigstruct), %i1 258; V9-NEXT: sllx %i1, 12, %i1 259; V9-NEXT: add %i1, %l44(bigstruct), %o1 260; V9-NEXT: mov 400, %o2 261; V9-NEXT: call memcpy 262; V9-NEXT: mov %i0, %o0 263; V9-NEXT: ret 264; V9-NEXT: restore 265entry: 266 %0 = bitcast ptr %agg.result to ptr 267 tail call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 @bigstruct, i32 400, i1 false) 268 ret void 269} 270 271; Test register + immediate pattern. 272 273define void @addri_test(i32 %ptr) #0 { 274; V8-LABEL: addri_test: 275; V8: ! %bb.0: ! %entry 276; V8-NEXT: jmp %o0+4 277; V8-NEXT: nop 278; 279; V9-LABEL: addri_test: 280; V9: ! %bb.0: ! %entry 281; V9-NEXT: add %o0, 4, %o0 282; V9-NEXT: srl %o0, 0, %o0 283; V9-NEXT: jmp %o0 284; V9-NEXT: nop 285entry: 286 %add = add nsw i32 %ptr, 4 287 %0 = inttoptr i32 %add to ptr 288 tail call void %0() #1 289 ret void 290} 291 292%struct.a = type { i32, i32 } 293@dest = global [2 x i8] zeroinitializer 294 295%struct.big = type { [100 x i32] } 296@bigstruct = global %struct.big zeroinitializer 297 298declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1) 299declare void @sret_func(ptr sret(%struct.a)) 300declare i32 @callee_byval(ptr byval(ptr) %a) 301declare i32 @foo(i32) 302declare i32 @foo2(i32, i32) 303declare i32 @foo7(i32, i32, i32, i32, i32, i32, i32) 304 305attributes #0 = { nounwind "disable-tail-calls"="false" 306 "frame-pointer"="none" } 307attributes #1 = { nounwind "disable-tail-calls"="false" 308 "frame-pointer"="all" } 309