1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc --mtriple=loongarch64 --target-abi=lp64s < %s | FileCheck %s 3; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64d < %s | FileCheck %s 4 5;; This file contains tests that should have identical output for all ABIs, i.e. 6;; where no arguments are passed via floating point registers. 7 8;; Check that on LA64, i128 is passed in a pair of GPRs. 9define i64 @callee_i128_in_regs(i64 %a, i128 %b) nounwind { 10; CHECK-LABEL: callee_i128_in_regs: 11; CHECK: # %bb.0: 12; CHECK-NEXT: add.d $a0, $a0, $a1 13; CHECK-NEXT: ret 14 %b_trunc = trunc i128 %b to i64 15 %1 = add i64 %a, %b_trunc 16 ret i64 %1 17} 18 19define i64 @caller_i128_in_regs() nounwind { 20; CHECK-LABEL: caller_i128_in_regs: 21; CHECK: # %bb.0: 22; CHECK-NEXT: addi.d $sp, $sp, -16 23; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill 24; CHECK-NEXT: ori $a0, $zero, 1 25; CHECK-NEXT: ori $a1, $zero, 2 26; CHECK-NEXT: move $a2, $zero 27; CHECK-NEXT: bl %plt(callee_i128_in_regs) 28; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload 29; CHECK-NEXT: addi.d $sp, $sp, 16 30; CHECK-NEXT: ret 31 %1 = call i64 @callee_i128_in_regs(i64 1, i128 2) 32 ret i64 %1 33} 34 35;; Check that the stack is used once the GPRs are exhausted. 36define i64 @callee_many_scalars(i8 %a, i16 %b, i32 %c, i64 %d, i128 %e, i64 %f, i128 %g, i64 %h) nounwind { 37; CHECK-LABEL: callee_many_scalars: 38; CHECK: # %bb.0: 39; CHECK-NEXT: ld.d $t0, $sp, 8 40; CHECK-NEXT: ld.d $t1, $sp, 0 41; CHECK-NEXT: andi $a0, $a0, 255 42; CHECK-NEXT: bstrpick.d $a1, $a1, 15, 0 43; CHECK-NEXT: bstrpick.d $a2, $a2, 31, 0 44; CHECK-NEXT: add.d $a0, $a0, $a1 45; CHECK-NEXT: add.d $a0, $a0, $a2 46; CHECK-NEXT: add.d $a0, $a0, $a3 47; CHECK-NEXT: xor $a1, $a5, $t1 48; CHECK-NEXT: xor $a2, $a4, $a7 49; CHECK-NEXT: or $a1, $a2, $a1 50; CHECK-NEXT: sltui $a1, $a1, 1 51; CHECK-NEXT: add.d $a0, $a1, $a0 52; CHECK-NEXT: add.d $a0, $a0, $a6 53; CHECK-NEXT: add.d $a0, $a0, $t0 54; CHECK-NEXT: ret 55 %a_ext = zext i8 %a to i64 56 %b_ext = zext i16 %b to i64 57 %c_ext = zext i32 %c to i64 58 %1 = add i64 %a_ext, %b_ext 59 %2 = add i64 %1, %c_ext 60 %3 = add i64 %2, %d 61 %4 = icmp eq i128 %e, %g 62 %5 = zext i1 %4 to i64 63 %6 = add i64 %5, %3 64 %7 = add i64 %6, %f 65 %8 = add i64 %7, %h 66 ret i64 %8 67} 68 69define i64 @caller_many_scalars() nounwind { 70; CHECK-LABEL: caller_many_scalars: 71; CHECK: # %bb.0: 72; CHECK-NEXT: addi.d $sp, $sp, -32 73; CHECK-NEXT: st.d $ra, $sp, 24 # 8-byte Folded Spill 74; CHECK-NEXT: ori $a0, $zero, 8 75; CHECK-NEXT: st.d $a0, $sp, 8 76; CHECK-NEXT: ori $a0, $zero, 1 77; CHECK-NEXT: ori $a1, $zero, 2 78; CHECK-NEXT: ori $a2, $zero, 3 79; CHECK-NEXT: ori $a3, $zero, 4 80; CHECK-NEXT: ori $a4, $zero, 5 81; CHECK-NEXT: ori $a6, $zero, 6 82; CHECK-NEXT: ori $a7, $zero, 7 83; CHECK-NEXT: st.d $zero, $sp, 0 84; CHECK-NEXT: move $a5, $zero 85; CHECK-NEXT: bl %plt(callee_many_scalars) 86; CHECK-NEXT: ld.d $ra, $sp, 24 # 8-byte Folded Reload 87; CHECK-NEXT: addi.d $sp, $sp, 32 88; CHECK-NEXT: ret 89 %1 = call i64 @callee_many_scalars(i8 1, i16 2, i32 3, i64 4, i128 5, i64 6, i128 7, i64 8) 90 ret i64 %1 91} 92 93;; Check that i256 is passed indirectly. 94 95define i64 @callee_large_scalars(i256 %a, i256 %b) nounwind { 96; CHECK-LABEL: callee_large_scalars: 97; CHECK: # %bb.0: 98; CHECK-NEXT: ld.d $a2, $a1, 0 99; CHECK-NEXT: ld.d $a3, $a0, 0 100; CHECK-NEXT: ld.d $a4, $a1, 8 101; CHECK-NEXT: ld.d $a5, $a1, 24 102; CHECK-NEXT: ld.d $a6, $a0, 24 103; CHECK-NEXT: ld.d $a7, $a0, 8 104; CHECK-NEXT: ld.d $a1, $a1, 16 105; CHECK-NEXT: ld.d $a0, $a0, 16 106; CHECK-NEXT: xor $a5, $a6, $a5 107; CHECK-NEXT: xor $a4, $a7, $a4 108; CHECK-NEXT: or $a4, $a4, $a5 109; CHECK-NEXT: xor $a0, $a0, $a1 110; CHECK-NEXT: xor $a1, $a3, $a2 111; CHECK-NEXT: or $a0, $a1, $a0 112; CHECK-NEXT: or $a0, $a0, $a4 113; CHECK-NEXT: sltui $a0, $a0, 1 114; CHECK-NEXT: ret 115 %1 = icmp eq i256 %a, %b 116 %2 = zext i1 %1 to i64 117 ret i64 %2 118} 119 120define i64 @caller_large_scalars() nounwind { 121; CHECK-LABEL: caller_large_scalars: 122; CHECK: # %bb.0: 123; CHECK-NEXT: addi.d $sp, $sp, -80 124; CHECK-NEXT: st.d $ra, $sp, 72 # 8-byte Folded Spill 125; CHECK-NEXT: st.d $zero, $sp, 24 126; CHECK-NEXT: vrepli.b $vr0, 0 127; CHECK-NEXT: vst $vr0, $sp, 8 128; CHECK-NEXT: ori $a0, $zero, 2 129; CHECK-NEXT: st.d $a0, $sp, 0 130; CHECK-NEXT: st.d $zero, $sp, 56 131; CHECK-NEXT: vst $vr0, $sp, 40 132; CHECK-NEXT: ori $a2, $zero, 1 133; CHECK-NEXT: addi.d $a0, $sp, 32 134; CHECK-NEXT: addi.d $a1, $sp, 0 135; CHECK-NEXT: st.d $a2, $sp, 32 136; CHECK-NEXT: bl %plt(callee_large_scalars) 137; CHECK-NEXT: ld.d $ra, $sp, 72 # 8-byte Folded Reload 138; CHECK-NEXT: addi.d $sp, $sp, 80 139; CHECK-NEXT: ret 140 %1 = call i64 @callee_large_scalars(i256 1, i256 2) 141 ret i64 %1 142} 143 144;; Check that arguments larger than 2*GRLen are handled correctly when their 145;; address is passed on the stack rather than in memory. 146 147;; Must keep define on a single line due to an update_llc_test_checks.py limitation 148define i64 @callee_large_scalars_exhausted_regs(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i64 %g, i256 %h, i64 %i, i256 %j) nounwind { 149; CHECK-LABEL: callee_large_scalars_exhausted_regs: 150; CHECK: # %bb.0: 151; CHECK-NEXT: ld.d $a0, $sp, 8 152; CHECK-NEXT: ld.d $a1, $a0, 0 153; CHECK-NEXT: ld.d $a2, $a7, 0 154; CHECK-NEXT: ld.d $a3, $a0, 8 155; CHECK-NEXT: ld.d $a4, $a0, 24 156; CHECK-NEXT: ld.d $a5, $a7, 24 157; CHECK-NEXT: ld.d $a6, $a7, 8 158; CHECK-NEXT: ld.d $a0, $a0, 16 159; CHECK-NEXT: ld.d $a7, $a7, 16 160; CHECK-NEXT: xor $a4, $a5, $a4 161; CHECK-NEXT: xor $a3, $a6, $a3 162; CHECK-NEXT: or $a3, $a3, $a4 163; CHECK-NEXT: xor $a0, $a7, $a0 164; CHECK-NEXT: xor $a1, $a2, $a1 165; CHECK-NEXT: or $a0, $a1, $a0 166; CHECK-NEXT: or $a0, $a0, $a3 167; CHECK-NEXT: sltui $a0, $a0, 1 168; CHECK-NEXT: ret 169 %1 = icmp eq i256 %h, %j 170 %2 = zext i1 %1 to i64 171 ret i64 %2 172} 173 174define i64 @caller_large_scalars_exhausted_regs() nounwind { 175; CHECK-LABEL: caller_large_scalars_exhausted_regs: 176; CHECK: # %bb.0: 177; CHECK-NEXT: addi.d $sp, $sp, -96 178; CHECK-NEXT: st.d $ra, $sp, 88 # 8-byte Folded Spill 179; CHECK-NEXT: addi.d $a0, $sp, 16 180; CHECK-NEXT: st.d $a0, $sp, 8 181; CHECK-NEXT: ori $a0, $zero, 9 182; CHECK-NEXT: st.d $a0, $sp, 0 183; CHECK-NEXT: st.d $zero, $sp, 40 184; CHECK-NEXT: vrepli.b $vr0, 0 185; CHECK-NEXT: vst $vr0, $sp, 24 186; CHECK-NEXT: ori $a0, $zero, 10 187; CHECK-NEXT: st.d $a0, $sp, 16 188; CHECK-NEXT: st.d $zero, $sp, 72 189; CHECK-NEXT: ori $a0, $zero, 8 190; CHECK-NEXT: st.d $a0, $sp, 48 191; CHECK-NEXT: ori $a0, $zero, 1 192; CHECK-NEXT: ori $a1, $zero, 2 193; CHECK-NEXT: ori $a2, $zero, 3 194; CHECK-NEXT: ori $a3, $zero, 4 195; CHECK-NEXT: ori $a4, $zero, 5 196; CHECK-NEXT: ori $a5, $zero, 6 197; CHECK-NEXT: ori $a6, $zero, 7 198; CHECK-NEXT: addi.d $a7, $sp, 48 199; CHECK-NEXT: vst $vr0, $sp, 56 200; CHECK-NEXT: bl %plt(callee_large_scalars_exhausted_regs) 201; CHECK-NEXT: ld.d $ra, $sp, 88 # 8-byte Folded Reload 202; CHECK-NEXT: addi.d $sp, $sp, 96 203; CHECK-NEXT: ret 204 %1 = call i64 @callee_large_scalars_exhausted_regs( 205 i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i256 8, i64 9, 206 i256 10) 207 ret i64 %1 208} 209 210;; Check large struct arguments, which are passed byval 211 212%struct.large = type { i64, i64, i64, i64 } 213 214define i64 @callee_large_struct(ptr byval(%struct.large) align 8 %a) nounwind { 215; CHECK-LABEL: callee_large_struct: 216; CHECK: # %bb.0: 217; CHECK-NEXT: ld.d $a1, $a0, 0 218; CHECK-NEXT: ld.d $a0, $a0, 24 219; CHECK-NEXT: add.d $a0, $a1, $a0 220; CHECK-NEXT: ret 221 %1 = getelementptr inbounds %struct.large, ptr %a, i64 0, i32 0 222 %2 = getelementptr inbounds %struct.large, ptr %a, i64 0, i32 3 223 %3 = load i64, ptr %1 224 %4 = load i64, ptr %2 225 %5 = add i64 %3, %4 226 ret i64 %5 227} 228 229define i64 @caller_large_struct() nounwind { 230; CHECK-LABEL: caller_large_struct: 231; CHECK: # %bb.0: 232; CHECK-NEXT: addi.d $sp, $sp, -80 233; CHECK-NEXT: st.d $ra, $sp, 72 # 8-byte Folded Spill 234; CHECK-NEXT: ori $a0, $zero, 1 235; CHECK-NEXT: st.d $a0, $sp, 40 236; CHECK-NEXT: ori $a1, $zero, 2 237; CHECK-NEXT: st.d $a1, $sp, 48 238; CHECK-NEXT: ori $a2, $zero, 3 239; CHECK-NEXT: st.d $a2, $sp, 56 240; CHECK-NEXT: ori $a3, $zero, 4 241; CHECK-NEXT: st.d $a3, $sp, 64 242; CHECK-NEXT: st.d $a0, $sp, 8 243; CHECK-NEXT: st.d $a1, $sp, 16 244; CHECK-NEXT: st.d $a2, $sp, 24 245; CHECK-NEXT: st.d $a3, $sp, 32 246; CHECK-NEXT: addi.d $a0, $sp, 8 247; CHECK-NEXT: bl %plt(callee_large_struct) 248; CHECK-NEXT: ld.d $ra, $sp, 72 # 8-byte Folded Reload 249; CHECK-NEXT: addi.d $sp, $sp, 80 250; CHECK-NEXT: ret 251 %ls = alloca %struct.large, align 8 252 %a = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 0 253 store i64 1, ptr %a 254 %b = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 1 255 store i64 2, ptr %b 256 %c = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 2 257 store i64 3, ptr %c 258 %d = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 3 259 store i64 4, ptr %d 260 %1 = call i64 @callee_large_struct(ptr byval(%struct.large) align 8 %ls) 261 ret i64 %1 262} 263 264;; Check return scalar which size is 2*GRLen. 265 266define i128 @callee_small_scalar_ret() nounwind { 267; CHECK-LABEL: callee_small_scalar_ret: 268; CHECK: # %bb.0: 269; CHECK-NEXT: addi.w $a0, $zero, -1 270; CHECK-NEXT: move $a1, $a0 271; CHECK-NEXT: ret 272 ret i128 -1 273} 274 275define i64 @caller_small_scalar_ret() nounwind { 276; CHECK-LABEL: caller_small_scalar_ret: 277; CHECK: # %bb.0: 278; CHECK-NEXT: addi.d $sp, $sp, -16 279; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill 280; CHECK-NEXT: bl %plt(callee_small_scalar_ret) 281; CHECK-NEXT: addi.w $a2, $zero, -2 282; CHECK-NEXT: xor $a0, $a0, $a2 283; CHECK-NEXT: orn $a0, $a0, $a1 284; CHECK-NEXT: sltui $a0, $a0, 1 285; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload 286; CHECK-NEXT: addi.d $sp, $sp, 16 287; CHECK-NEXT: ret 288 %1 = call i128 @callee_small_scalar_ret() 289 %2 = icmp eq i128 -2, %1 290 %3 = zext i1 %2 to i64 291 ret i64 %3 292} 293 294;; Check return struct which size is 2*GRLen. 295 296%struct.small = type { i64, ptr } 297 298define %struct.small @callee_small_struct_ret() nounwind { 299; CHECK-LABEL: callee_small_struct_ret: 300; CHECK: # %bb.0: 301; CHECK-NEXT: ori $a0, $zero, 1 302; CHECK-NEXT: move $a1, $zero 303; CHECK-NEXT: ret 304 ret %struct.small { i64 1, ptr null } 305} 306 307define i64 @caller_small_struct_ret() nounwind { 308; CHECK-LABEL: caller_small_struct_ret: 309; CHECK: # %bb.0: 310; CHECK-NEXT: addi.d $sp, $sp, -16 311; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill 312; CHECK-NEXT: bl %plt(callee_small_struct_ret) 313; CHECK-NEXT: add.d $a0, $a0, $a1 314; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload 315; CHECK-NEXT: addi.d $sp, $sp, 16 316; CHECK-NEXT: ret 317 %1 = call %struct.small @callee_small_struct_ret() 318 %2 = extractvalue %struct.small %1, 0 319 %3 = extractvalue %struct.small %1, 1 320 %4 = ptrtoint ptr %3 to i64 321 %5 = add i64 %2, %4 322 ret i64 %5 323} 324 325;; Check return scalar which size is more than 2*GRLen. 326 327define i256 @callee_large_scalar_ret() nounwind { 328; CHECK-LABEL: callee_large_scalar_ret: 329; CHECK: # %bb.0: 330; CHECK-NEXT: addi.w $a1, $zero, -1 331; CHECK-NEXT: st.d $a1, $a0, 24 332; CHECK-NEXT: st.d $a1, $a0, 16 333; CHECK-NEXT: st.d $a1, $a0, 8 334; CHECK-NEXT: lu12i.w $a1, -30141 335; CHECK-NEXT: ori $a1, $a1, 747 336; CHECK-NEXT: st.d $a1, $a0, 0 337; CHECK-NEXT: ret 338 ret i256 -123456789 339} 340 341define void @caller_large_scalar_ret() nounwind { 342; CHECK-LABEL: caller_large_scalar_ret: 343; CHECK: # %bb.0: 344; CHECK-NEXT: addi.d $sp, $sp, -48 345; CHECK-NEXT: st.d $ra, $sp, 40 # 8-byte Folded Spill 346; CHECK-NEXT: addi.d $a0, $sp, 0 347; CHECK-NEXT: bl %plt(callee_large_scalar_ret) 348; CHECK-NEXT: ld.d $ra, $sp, 40 # 8-byte Folded Reload 349; CHECK-NEXT: addi.d $sp, $sp, 48 350; CHECK-NEXT: ret 351 %1 = call i256 @callee_large_scalar_ret() 352 ret void 353} 354 355;; Check return struct which size is more than 2*GRLen. 356 357define void @callee_large_struct_ret(ptr noalias sret(%struct.large) %agg.result) nounwind { 358; CHECK-LABEL: callee_large_struct_ret: 359; CHECK: # %bb.0: 360; CHECK-NEXT: ori $a1, $zero, 1 361; CHECK-NEXT: st.d $a1, $a0, 0 362; CHECK-NEXT: ori $a1, $zero, 2 363; CHECK-NEXT: st.d $a1, $a0, 8 364; CHECK-NEXT: ori $a1, $zero, 3 365; CHECK-NEXT: st.d $a1, $a0, 16 366; CHECK-NEXT: ori $a1, $zero, 4 367; CHECK-NEXT: st.d $a1, $a0, 24 368; CHECK-NEXT: ret 369 %a = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 0 370 store i64 1, ptr %a, align 4 371 %b = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 1 372 store i64 2, ptr %b, align 4 373 %c = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 2 374 store i64 3, ptr %c, align 4 375 %d = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 3 376 store i64 4, ptr %d, align 4 377 ret void 378} 379 380define i64 @caller_large_struct_ret() nounwind { 381; CHECK-LABEL: caller_large_struct_ret: 382; CHECK: # %bb.0: 383; CHECK-NEXT: addi.d $sp, $sp, -48 384; CHECK-NEXT: st.d $ra, $sp, 40 # 8-byte Folded Spill 385; CHECK-NEXT: addi.d $a0, $sp, 8 386; CHECK-NEXT: bl %plt(callee_large_struct_ret) 387; CHECK-NEXT: ld.d $a0, $sp, 8 388; CHECK-NEXT: ld.d $a1, $sp, 32 389; CHECK-NEXT: add.d $a0, $a0, $a1 390; CHECK-NEXT: ld.d $ra, $sp, 40 # 8-byte Folded Reload 391; CHECK-NEXT: addi.d $sp, $sp, 48 392; CHECK-NEXT: ret 393 %1 = alloca %struct.large 394 call void @callee_large_struct_ret(ptr sret(%struct.large) %1) 395 %2 = getelementptr inbounds %struct.large, ptr %1, i64 0, i32 0 396 %3 = load i64, ptr %2 397 %4 = getelementptr inbounds %struct.large, ptr %1, i64 0, i32 3 398 %5 = load i64, ptr %4 399 %6 = add i64 %3, %5 400 ret i64 %6 401} 402