1; RUN: llc < %s -mtriple=sparcv9 -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=HARD 2; RUN: llc < %s -mtriple=sparcv9 -disable-sparc-delay-filler -disable-sparc-leaf-proc -mattr=soft-float | FileCheck %s --check-prefix=CHECK --check-prefix=SOFT 3 4; CHECK-LABEL: intarg: 5; The save/restore frame is not strictly necessary here, but we would need to 6; refer to %o registers instead. 7; CHECK: save %sp, -128, %sp 8; CHECK: ldx [%fp+2231], [[R2:%[gilo][0-7]]] 9; CHECK: ld [%fp+2227], [[R1:%[gilo][0-7]]] 10; CHECK: stb %i0, [%i4] 11; CHECK: stb %i1, [%i4] 12; CHECK: sth %i2, [%i4] 13; CHECK: st %i3, [%i4] 14; CHECK: stx %i4, [%i4] 15; CHECK: st %i5, [%i4] 16; CHECK: st [[R1]], [%i4] 17; CHECK: stx [[R2]], [%i4] 18; CHECK: restore 19define void @intarg(i8 %a0, ; %i0 20 i8 %a1, ; %i1 21 i16 %a2, ; %i2 22 i32 %a3, ; %i3 23 ptr %a4, ; %i4 24 i32 %a5, ; %i5 25 i32 signext %a6, ; [%fp+BIAS+176] 26 ptr %a7) { ; [%fp+BIAS+184] 27 store volatile i8 %a0, ptr %a4 28 store volatile i8 %a1, ptr %a4 29 %p16 = bitcast ptr %a4 to ptr 30 store volatile i16 %a2, ptr %p16 31 %p32 = bitcast ptr %a4 to ptr 32 store volatile i32 %a3, ptr %p32 33 %pp = bitcast ptr %a4 to ptr 34 store volatile ptr %a4, ptr %pp 35 store volatile i32 %a5, ptr %p32 36 store volatile i32 %a6, ptr %p32 37 store volatile ptr %a7, ptr %pp 38 ret void 39} 40 41; CHECK-LABEL: call_intarg: 42; 16 saved + 8 args. 43; CHECK: save %sp, -192, %sp 44; Sign-extend and store the full 64 bits. 45; CHECK: sra %i0, 0, [[R:%[gilo][0-7]]] 46; Use %o0-%o5 for outgoing arguments 47; CHECK: mov 5, %o5 48; CHECK: stx [[R]], [%sp+2223] 49; CHECK: call intarg 50; CHECK-NOT: add %sp 51; CHECK: restore 52define void @call_intarg(i32 %i0, ptr %i1) { 53 call void @intarg(i8 0, i8 1, i16 2, i32 3, ptr undef, i32 5, i32 signext %i0, ptr %i1) 54 ret void 55} 56 57; CHECK-LABEL: floatarg: 58; HARD: save %sp, -128, %sp 59; HARD: ld [%fp+2307], [[F:%f[0-9]+]] 60; HARD: fstod %f1, 61; HARD: faddd %f2, 62; HARD: faddd %f4, 63; HARD: faddd %f6, 64; HARD: fadds %f31, [[F]] 65; SOFT: save %sp, -176, %sp 66; SOFT: ld [%fp+2299], %i4 67; SOFT: ld [%fp+2307], %i5 68; SOFT: srl %i0, 0, %o0 69; SOFT-NEXT: call __extendsfdf2 70; SOFT: mov %o0, %o1 71; SOFT: mov %i1, %o0 72; SOFT: mov %i2, %o0 73; SOFT: mov %i3, %o0 74define double @floatarg(float %a0, ; %f1 75 double %a1, ; %d2 76 double %a2, ; %d4 77 double %a3, ; %d6 78 float %a4, ; %f9 79 float %a5, ; %f11 80 float %a6, ; %f13 81 float %a7, ; %f15 82 float %a8, ; %f17 83 float %a9, ; %f19 84 float %a10, ; %f21 85 float %a11, ; %f23 86 float %a12, ; %f25 87 float %a13, ; %f27 88 float %a14, ; %f29 89 float %a15, ; %f31 90 float %a16, ; [%fp+BIAS+256] (using 8 bytes) 91 double %a17) { ; [%fp+BIAS+264] (using 8 bytes) 92 %d0 = fpext float %a0 to double 93 %s1 = fadd double %a1, %d0 94 %s2 = fadd double %a2, %s1 95 %s3 = fadd double %a3, %s2 96 %s16 = fadd float %a15, %a16 97 %d16 = fpext float %s16 to double 98 %s17 = fadd double %d16, %s3 99 ret double %s17 100} 101 102; CHECK-LABEL: call_floatarg: 103; CHECK: save %sp, -272, %sp 104; Store 8 bytes in full slot. 105; HARD: std %f2, [%sp+2311] 106; Store 4 bytes, right-aligned in slot. 107; HARD: st %f1, [%sp+2307] 108; HARD: fmovd %f2, %f4 109; SOFT: stx %i1, [%sp+2311] 110; SOFT: stx %i0, [%sp+2303] 111; SOFT: stx %i2, [%sp+2295] 112; SOFT: stx %i2, [%sp+2287] 113; SOFT: stx %i2, [%sp+2279] 114; SOFT: stx %i2, [%sp+2271] 115; SOFT: stx %i2, [%sp+2263] 116; SOFT: stx %i2, [%sp+2255] 117; SOFT: stx %i2, [%sp+2247] 118; SOFT: stx %i2, [%sp+2239] 119; SOFT: stx %i2, [%sp+2231] 120; SOFT: stx %i2, [%sp+2223] 121; SOFT: mov %i2, %o0 122; SOFT: mov %i1, %o1 123; SOFT: mov %i1, %o2 124; SOFT: mov %i1, %o3 125; SOFT: mov %i2, %o4 126; SOFT: mov %i2, %o5 127; CHECK: call floatarg 128; CHECK-NOT: add %sp 129; CHECK: restore 130 131define void @call_floatarg(float %f1, double %d2, float %f5, ptr %p) { 132 %r = call double @floatarg(float %f5, double %d2, double %d2, double %d2, 133 float %f5, float %f5, float %f5, float %f5, 134 float %f5, float %f5, float %f5, float %f5, 135 float %f5, float %f5, float %f5, float %f5, 136 float %f1, double %d2) 137 store double %r, ptr %p 138 ret void 139} 140 141; CHECK-LABEL: mixedarg: 142; CHECK: ldx [%fp+2247] 143; CHECK: ldx [%fp+2231] 144; SOFT: ldx [%fp+2239], %i0 145; HARD: fstod %f3 146; HARD: faddd %f6 147; HARD: faddd %f16 148; SOFT: mov %o0, %o1 149; SOFT-NEXT: mov %i3, %o0 150; SOFT-NEXT: call __adddf3 151; SOFT: mov %o0, %o1 152; SOFT-NEXT: mov %i0, %o0 153; SOFT-NEXT: call __adddf3 154; HARD: std %f0, [%i1] 155; SOFT: stx %o0, [%i5] 156 157define void @mixedarg(i8 %a0, ; %i0 158 float %a1, ; %f3 159 i16 %a2, ; %i2 160 double %a3, ; %d6 161 i13 %a4, ; %i4 162 float %a5, ; %f11 163 i64 %a6, ; [%fp+BIAS+176] 164 ptr %a7, ; [%fp+BIAS+184] 165 double %a8, ; %d16 166 ptr %a9) { ; [%fp+BIAS+200] 167 %d1 = fpext float %a1 to double 168 %s3 = fadd double %a3, %d1 169 %s8 = fadd double %a8, %s3 170 store double %s8, ptr %a7 171 store i16 %a2, ptr %a9 172 ret void 173} 174 175; CHECK-LABEL: call_mixedarg: 176; CHECK: stx %i2, [%sp+2247] 177; SOFT: stx %i1, [%sp+2239] 178; CHECK: stx %i0, [%sp+2223] 179; HARD: fmovd %f2, %f6 180; HARD: fmovd %f2, %f16 181; SOFT: mov %i1, %o3 182; CHECK: call mixedarg 183; CHECK-NOT: add %sp 184; CHECK: restore 185 186define void @call_mixedarg(i64 %i0, double %f2, ptr %i2) { 187 call void @mixedarg(i8 undef, 188 float undef, 189 i16 undef, 190 double %f2, 191 i13 undef, 192 float undef, 193 i64 %i0, 194 ptr undef, 195 double %f2, 196 ptr %i2) 197 ret void 198} 199 200; The inreg attribute is used to indicate 32-bit sized struct elements that 201; share an 8-byte slot. 202; CHECK-LABEL: inreg_fi: 203; SOFT: srlx %i0, 32, [[R:%[gilo][0-7]]] 204; HARD: fstoi %f1 205; SOFT: call __fixsfsi 206; HARD: srlx %i0, 32, [[R:%[gilo][0-7]]] 207; CHECK: sub [[R]], 208define i32 @inreg_fi(i32 inreg %a0, ; high bits of %i0 209 float inreg %a1) { ; %f1 210 %b1 = fptosi float %a1 to i32 211 %rv = sub i32 %a0, %b1 212 ret i32 %rv 213} 214 215; CHECK-LABEL: call_inreg_fi: 216; Allocate space for 6 arguments, even when only 2 are used. 217; CHECK: save %sp, -176, %sp 218; HARD-DAG: sllx %i1, 32, %o0 219; HARD-DAG: fmovs %f5, %f1 220; SOFT: srl %i2, 0, %i0 221; SOFT: sllx %i1, 32, %i1 222; SOFT: or %i1, %i0, %o0 223; CHECK: call inreg_fi 224define void @call_inreg_fi(ptr %p, i32 %i1, float %f5) { 225 %x = call i32 @inreg_fi(i32 inreg %i1, float inreg %f5) 226 ret void 227} 228 229; CHECK-LABEL: inreg_ff: 230; HARD: fsubs %f0, %f1, %f0 231; SOFT: srlx %i0, 32, %o0 232; SOFT: srl %i0, 0, %o1 233; SOFT: call __subsf3 234define float @inreg_ff(float inreg %a0, ; %f0 235 float inreg %a1) { ; %f1 236 %rv = fsub float %a0, %a1 237 ret float %rv 238} 239 240; CHECK-LABEL: call_inreg_ff: 241; HARD-DAG: fmovs %f3, %f0 242; HARD-DAG: fmovs %f5, %f1 243; SOFT: srl %i2, 0, %i0 244; SOFT: sllx %i1, 32, %i1 245; SOFT: or %i1, %i0, %o0 246; CHECK: call inreg_ff 247define void @call_inreg_ff(ptr %p, float %f3, float %f5) { 248 %x = call float @inreg_ff(float inreg %f3, float inreg %f5) 249 ret void 250} 251 252; CHECK-LABEL: inreg_if: 253; HARD: fstoi %f0 254; SOFT: srlx %i0, 32, %o0 255; SOFT: call __fixsfsi 256; CHECK: sub %i0 257define i32 @inreg_if(float inreg %a0, ; %f0 258 i32 inreg %a1) { ; low bits of %i0 259 %b0 = fptosi float %a0 to i32 260 %rv = sub i32 %a1, %b0 261 ret i32 %rv 262} 263 264; CHECK-LABEL: call_inreg_if: 265; HARD: fmovs %f3, %f0 266; HARD: mov %i2, %o0 267; SOFT: srl %i2, 0, %i0 268; SOFT: sllx %i1, 32, %i1 269; SOFT: or %i1, %i0, %o0 270; CHECK: call inreg_if 271define void @call_inreg_if(ptr %p, float %f3, i32 %i2) { 272 %x = call i32 @inreg_if(float inreg %f3, i32 inreg %i2) 273 ret void 274} 275 276; The frontend shouldn't do this. Just pass i64 instead. 277; CHECK-LABEL: inreg_ii: 278; CHECK: srlx %i0, 32, [[R:%[gilo][0-7]]] 279; CHECK: sub %i0, [[R]], %i0 280define i32 @inreg_ii(i32 inreg %a0, ; high bits of %i0 281 i32 inreg %a1) { ; low bits of %i0 282 %rv = sub i32 %a1, %a0 283 ret i32 %rv 284} 285 286; CHECK-LABEL: call_inreg_ii: 287; CHECK: srl %i2, 0, [[R2:%[gilo][0-7]]] 288; CHECK: sllx %i1, 32, [[R1:%[gilo][0-7]]] 289; CHECK: or [[R1]], [[R2]], %o0 290; CHECK: call inreg_ii 291define void @call_inreg_ii(ptr %p, i32 %i1, i32 %i2) { 292 %x = call i32 @inreg_ii(i32 inreg %i1, i32 inreg %i2) 293 ret void 294} 295 296; This is not a C struct, the i32 member uses 8 bytes, but the float only 4. 297; CHECK-LABEL: ret_i32_float_pair: 298; CHECK: ld [%i2], %i0 299; HARD: ld [%i3], %f2 300; SOFT: ld [%i3], %i1 301define { i32, float } @ret_i32_float_pair(i32 %a0, i32 %a1, 302 ptr %p, ptr %q) { 303 %r1 = load i32, ptr %p 304 %rv1 = insertvalue { i32, float } undef, i32 %r1, 0 305 store i32 0, ptr %p 306 %r2 = load float, ptr %q 307 %rv2 = insertvalue { i32, float } %rv1, float %r2, 1 308 ret { i32, float } %rv2 309} 310 311; CHECK-LABEL: call_ret_i32_float_pair: 312; CHECK: call ret_i32_float_pair 313; CHECK: st %o0, [%i0] 314; HARD: st %f2, [%i1] 315; SOFT: st %o1, [%i1] 316define void @call_ret_i32_float_pair(ptr %i0, ptr %i1) { 317 %rv = call { i32, float } @ret_i32_float_pair(i32 undef, i32 undef, 318 ptr undef, ptr undef) 319 %e0 = extractvalue { i32, float } %rv, 0 320 store i32 %e0, ptr %i0 321 %e1 = extractvalue { i32, float } %rv, 1 322 store float %e1, ptr %i1 323 ret void 324} 325 326; This is a C struct, each member uses 4 bytes. 327; CHECK-LABEL: ret_i32_float_packed: 328; CHECK: ld [%i2], [[R:%[gilo][0-7]]] 329; HARD: ld [%i3], %f1 330; SOFT: ld [%i3], %i1 331; CHECK: sllx [[R]], 32, %i0 332define inreg { i32, float } @ret_i32_float_packed(i32 %a0, i32 %a1, 333 ptr %p, ptr %q) { 334 %r1 = load i32, ptr %p 335 %rv1 = insertvalue { i32, float } undef, i32 %r1, 0 336 store i32 0, ptr %p 337 %r2 = load float, ptr %q 338 %rv2 = insertvalue { i32, float } %rv1, float %r2, 1 339 ret { i32, float } %rv2 340} 341 342; CHECK-LABEL: call_ret_i32_float_packed: 343; CHECK: call ret_i32_float_packed 344; CHECK: srlx %o0, 32, [[R:%[gilo][0-7]]] 345; CHECK: st [[R]], [%i0] 346; HARD: st %f1, [%i1] 347; SOFT: st %o0, [%i1] 348define void @call_ret_i32_float_packed(ptr %i0, ptr %i1) { 349 %rv = call { i32, float } @ret_i32_float_packed(i32 undef, i32 undef, 350 ptr undef, ptr undef) 351 %e0 = extractvalue { i32, float } %rv, 0 352 store i32 %e0, ptr %i0 353 %e1 = extractvalue { i32, float } %rv, 1 354 store float %e1, ptr %i1 355 ret void 356} 357 358; The C frontend should use i64 to return { i32, i32 } structs, but verify that 359; we don't miscompile thi case where both struct elements are placed in %i0. 360; CHECK-LABEL: ret_i32_packed: 361; CHECK: ld [%i2], [[R1:%[gilo][0-7]]] 362; CHECK: ld [%i3], [[R2:%[gilo][0-7]]] 363; CHECK: sllx [[R2]], 32, [[R3:%[gilo][0-7]]] 364; CHECK: or [[R3]], [[R1]], %i0 365define inreg { i32, i32 } @ret_i32_packed(i32 %a0, i32 %a1, 366 ptr %p, ptr %q) { 367 %r1 = load i32, ptr %p 368 %rv1 = insertvalue { i32, i32 } undef, i32 %r1, 1 369 store i32 0, ptr %p 370 %r2 = load i32, ptr %q 371 %rv2 = insertvalue { i32, i32 } %rv1, i32 %r2, 0 372 ret { i32, i32 } %rv2 373} 374 375; CHECK-LABEL: call_ret_i32_packed: 376; CHECK: call ret_i32_packed 377; CHECK: srlx %o0, 32, [[R:%[gilo][0-7]]] 378; CHECK: st [[R]], [%i0] 379; CHECK: st %o0, [%i1] 380define void @call_ret_i32_packed(ptr %i0, ptr %i1) { 381 %rv = call { i32, i32 } @ret_i32_packed(i32 undef, i32 undef, 382 ptr undef, ptr undef) 383 %e0 = extractvalue { i32, i32 } %rv, 0 384 store i32 %e0, ptr %i0 385 %e1 = extractvalue { i32, i32 } %rv, 1 386 store i32 %e1, ptr %i1 387 ret void 388} 389 390; The return value must be sign-extended to 64 bits. 391; CHECK-LABEL: ret_sext: 392; CHECK: sra %i0, 0, %i0 393define signext i32 @ret_sext(i32 %a0) { 394 ret i32 %a0 395} 396 397; CHECK-LABEL: ret_zext: 398; CHECK: srl %i0, 0, %i0 399define zeroext i32 @ret_zext(i32 %a0) { 400 ret i32 %a0 401} 402 403; CHECK-LABEL: ret_nosext: 404; CHECK-NOT: sra 405define signext i32 @ret_nosext(i32 signext %a0) { 406 ret i32 %a0 407} 408 409; CHECK-LABEL: ret_nozext: 410; CHECK-NOT: srl 411define signext i32 @ret_nozext(i32 signext %a0) { 412 ret i32 %a0 413} 414 415; CHECK-LABEL: test_register_directive: 416; CHECK: .register %g2, #scratch 417; CHECK: .register %g3, #scratch 418; CHECK: add %i0, 2, %g2 419; CHECK: add %i0, 3, %g3 420define i32 @test_register_directive(i32 %i0) { 421entry: 422 %0 = add nsw i32 %i0, 2 423 %1 = add nsw i32 %i0, 3 424 tail call void asm sideeffect "", "r,r,~{l0},~{l1},~{l2},~{l3},~{l4},~{l5},~{l6},~{l7},~{i0},~{i1},~{i2},~{i3},~{i4},~{i5},~{i6},~{i7},~{o0},~{o1},~{o2},~{o3},~{o4},~{o5},~{o6},~{o7},~{g1},~{g4},~{g5},~{g6},~{g7}"(i32 %0, i32 %1) 425 %2 = add nsw i32 %0, %1 426 ret i32 %2 427} 428 429; CHECK-LABEL: test_large_stack: 430 431; CHECK: sethi 16, %g1 432; CHECK: xor %g1, -176, %g1 433; CHECK: save %sp, %g1, %sp 434 435; CHECK: sethi 14, %g1 436; CHECK: xor %g1, -1, %g1 437; CHECK: add %g1, %fp, %g1 438; CHECK: call use_buf 439 440define i32 @test_large_stack() { 441entry: 442 %buffer1 = alloca [16384 x i8], align 8 443 %buffer1.sub = getelementptr inbounds [16384 x i8], ptr %buffer1, i32 0, i32 0 444 %0 = call i32 @use_buf(i32 16384, ptr %buffer1.sub) 445 ret i32 %0 446} 447 448declare i32 @use_buf(i32, ptr) 449 450; CHECK-LABEL: test_fp128_args: 451; HARD-DAG: std %f0, [%fp+{{.+}}] 452; HARD-DAG: std %f2, [%fp+{{.+}}] 453; HARD-DAG: std %f6, [%fp+{{.+}}] 454; HARD-DAG: std %f4, [%fp+{{.+}}] 455; HARD: add %fp, [[Offset:[0-9]+]], %o0 456; HARD: call _Qp_add 457; HARD: ldd [%fp+[[Offset]]], %f0 458; SOFT-DAG: mov %i0, %o0 459; SOFT-DAG: mov %i1, %o1 460; SOFT-DAG: mov %i2, %o2 461; SOFT-DAG: mov %i3, %o3 462; SOFT: call __addtf3 463; SOFT: mov %o0, %i0 464; SOFT: mov %o1, %i1 465 466define fp128 @test_fp128_args(fp128 %a, fp128 %b) { 467entry: 468 %0 = fadd fp128 %a, %b 469 ret fp128 %0 470} 471 472declare i64 @receive_fp128(i64 %a, ...) 473 474; CHECK-LABEL: test_fp128_variable_args: 475; HARD-DAG: std %f4, [%sp+[[Offset0:[0-9]+]]] 476; HARD-DAG: std %f6, [%sp+[[Offset1:[0-9]+]]] 477; HARD-DAG: ldx [%sp+[[Offset0]]], %o2 478; HARD-DAG: ldx [%sp+[[Offset1]]], %o3 479; SOFT-DAG: mov %i0, %o0 480; SOFT-DAG: mov %i1, %o1 481; SOFT-DAG: mov %i2, %o2 482; CHECK: call receive_fp128 483define i64 @test_fp128_variable_args(i64 %a, fp128 %b) { 484entry: 485 %0 = call i64 (i64, ...) @receive_fp128(i64 %a, fp128 %b) 486 ret i64 %0 487} 488 489; CHECK-LABEL: test_call_libfunc: 490; HARD: st %f1, [%fp+[[Offset0:[0-9]+]]] 491; HARD: fmovs %f3, %f1 492; SOFT: srl %i1, 0, %o0 493; CHECK: call cosf 494; HARD: st %f0, [%fp+[[Offset1:[0-9]+]]] 495; HARD: ld [%fp+[[Offset0]]], %f1 496; SOFT: mov %o0, %i1 497; SOFT: srl %i0, 0, %o0 498; CHECK: call sinf 499; HARD: ld [%fp+[[Offset1]]], %f1 500; HARD: fmuls %f1, %f0, %f0 501; SOFT: mov %o0, %o1 502; SOFT: mov %i1, %o0 503; SOFT: call __mulsf3 504; SOFT: sllx %o0, 32, %i0 505 506define inreg float @test_call_libfunc(float %arg0, float %arg1) { 507entry: 508 %0 = tail call inreg float @cosf(float %arg1) 509 %1 = tail call inreg float @sinf(float %arg0) 510 %2 = fmul float %0, %1 511 ret float %2 512} 513 514declare inreg float @cosf(float %arg) readnone nounwind 515declare inreg float @sinf(float %arg) readnone nounwind 516