560 lines · c
1// RUN: %clang_cc1 -triple loongarch64 -target-feature +f -target-feature +d -target-abi lp64d \2// RUN: -emit-llvm %s -o - | FileCheck %s3 4/// This test checks the calling convention of the lp64d ABI.5 6#include <stddef.h>7#include <stdint.h>8 9/// Part 0: C Data Types and Alignment.10 11/// `char` datatype is signed by default.12/// In most cases, the unsigned integer data types are zero-extended when stored13/// in general-purpose register, and the signed integer data types are14/// sign-extended. However, in the LP64D ABI, unsigned 32-bit types, such as15/// unsigned int, are stored in general-purpose registers as proper sign16/// extensions of their 32-bit values.17 18// CHECK-LABEL: define{{.*}} zeroext i1 @check_bool()19_Bool check_bool() { return 0; }20 21// CHECK-LABEL: define{{.*}} signext i8 @check_char()22char check_char() { return 0; }23 24// CHECK-LABEL: define{{.*}} signext i16 @check_short()25short check_short() { return 0; }26 27// CHECK-LABEL: define{{.*}} signext i32 @check_int()28int check_int() { return 0; }29 30// CHECK-LABEL: define{{.*}} i64 @check_long()31long check_long() { return 0; }32 33// CHECK-LABEL: define{{.*}} i64 @check_longlong()34long long check_longlong() { return 0; }35 36// CHECK-LABEL: define{{.*}} zeroext i8 @check_uchar()37unsigned char check_uchar() { return 0; }38 39// CHECK-LABEL: define{{.*}} zeroext i16 @check_ushort()40unsigned short check_ushort() { return 0; }41 42// CHECK-LABEL: define{{.*}} signext i32 @check_uint()43unsigned int check_uint() { return 0; }44 45// CHECK-LABEL: define{{.*}} i64 @check_ulong()46unsigned long check_ulong() { return 0; }47 48// CHECK-LABEL: define{{.*}} i64 @check_ulonglong()49unsigned long long check_ulonglong() { return 0; }50 51// CHECK-LABEL: define{{.*}} half @check_float16()52_Float16 check_float16() { return 0; }53 54// CHECK-LABEL: define{{.*}} float @check_float()55float check_float() { return 0; }56 57// CHECK-LABEL: define{{.*}} double @check_double()58double check_double() { return 0; }59 60// CHECK-LABEL: define{{.*}} fp128 @check_longdouble()61long double check_longdouble() { return 0; }62 63/// Part 1: Scalar arguments and return value.64 65/// 1. 1 < WOA <= GRLEN66/// a. Argument is passed in a single argument register, or on the stack by67/// value if none is available.68/// i. If the argument is floating-point type, the argument is passed in FAR. if69/// no FAR is available, it’s passed in GAR. If no GAR is available, it’s70/// passed on the stack. When passed in registers or on the stack,71/// floating-point types narrower than GRLEN bits are widened to GRLEN bits,72/// with the upper bits undefined.73/// ii. If the argument is integer or pointer type, the argument is passed in74/// GAR. If no GAR is available, it’s passed on the stack. When passed in75/// registers or on the stack, the unsigned integer scalars narrower than GRLEN76/// bits are zero-extended to GRLEN bits, and the signed integer scalars are77/// sign-extended.78/// 2. GRLEN < WOA ≤ 2 × GRLEN79/// a. The argument is passed in a pair of GAR, with the low-order GRLEN bits in80/// the lower-numbered register and the high-order GRLEN bits in the81/// higher-numbered register. If exactly one register is available, the82/// low-order GRLEN bits are passed in the register and the high-order GRLEN83/// bits are passed on the stack. If no GAR is available, it’s passed on the84/// stack.85 86/// Note that most of these conventions are handled by the backend, so here we87/// only check the correctness of argument (or return value)'s sign/zero88/// extension attribute.89 90// CHECK-LABEL: define{{.*}} signext i32 @f_scalar(i1 noundef zeroext %a, i8 noundef signext %b, i8 noundef zeroext %c, i16 noundef signext %d, i16 noundef zeroext %e, i32 noundef signext %f, i32 noundef signext %g, i64 noundef %h, i1 noundef zeroext %i, i8 noundef signext %j, i8 noundef zeroext %k, i16 noundef signext %l, i16 noundef zeroext %m, i32 noundef signext %n, i32 noundef signext %o, i64 noundef %p)91int f_scalar(_Bool a, int8_t b, uint8_t c, int16_t d, uint16_t e, int32_t f,92 uint32_t g, int64_t h, _Bool i, int8_t j, uint8_t k, int16_t l,93 uint16_t m, int32_t n, uint32_t o, int64_t p) {94 return 0;95}96 97/// Part 2: Structure arguments and return value.98 99/// Empty structures are ignored by C compilers which support them as a100/// non-standard extension(same as union arguments and return values). Bits101/// unused due to padding, and bits past the end of a structure whose size in102/// bits is not divisible by GRLEN, are undefined. And the layout of the103/// structure on the stack is consistent with that in memory.104 105/// Check empty structs are ignored.106 107struct empty_s {};108 109// CHECK-LABEL: define{{.*}} void @f_empty_s()110struct empty_s f_empty_s(struct empty_s x) {111 return x;112}113 114/// 1. 0 < WOA ≤ GRLEN115/// a. The structure has only fixed-point members. If there is an available GAR,116/// the structure is passed through the GAR by value passing; If no GAR is117/// available, it’s passed on the stack.118 119struct i16x4_s {120 int16_t a, b, c, d;121};122 123// CHECK-LABEL: define{{.*}} i64 @f_i16x4_s(i64 %x.coerce)124struct i16x4_s f_i16x4_s(struct i16x4_s x) {125 return x;126}127 128/// b. The structure has only floating-point members:129/// i. One floating-point member. The argument is passed in a FAR; If no FAR is130/// available, the value is passed in a GAR; if no GAR is available, the value131/// is passed on the stack.132 133struct f16x1_s {134 __fp16 a;135};136 137struct float16x1_s {138 _Float16 a;139};140 141struct f32x1_s {142 float a;143};144 145struct f64x1_s {146 double a;147};148 149// CHECK-LABEL: define{{.*}} half @f_f16x1_s(half %0)150struct f16x1_s f_f16x1_s(struct f16x1_s x) {151 return x;152}153 154// CHECK-LABEL: define{{.*}} half @f_float16x1_s(half %0)155struct float16x1_s f_float16x1_s(struct float16x1_s x) {156 return x;157}158 159// CHECK-LABEL: define{{.*}} float @f_f32x1_s(float %0)160struct f32x1_s f_f32x1_s(struct f32x1_s x) {161 return x;162}163 164// CHECK-LABEL: define{{.*}} double @f_f64x1_s(double %0)165struct f64x1_s f_f64x1_s(struct f64x1_s x) {166 return x;167}168 169/// ii. Two floating-point members. The argument is passed in a pair of170/// available FAR, with the low-order float member bits in the lower-numbered171/// FAR and the high-order float member bits in the higher-numbered FAR. If the172/// number of available FAR is less than 2, it’s passed in a GAR, and passed on173/// the stack if no GAR is available.174 175struct f16x2_s {176 __fp16 a;177 _Float16 b;178};179 180struct f32x2_s {181 float a, b;182};183 184// CHECK-LABEL: define{{.*}} { half, half } @f_f16x2_s(half %0, half %1)185struct f16x2_s f_f16x2_s(struct f16x2_s x) {186 return x;187}188 189// CHECK-LABEL: define{{.*}} { float, float } @f_f32x2_s(float %0, float %1)190struct f32x2_s f_f32x2_s(struct f32x2_s x) {191 return x;192}193 194/// c. The structure has both fixed-point and floating-point members, i.e. the195/// structure has one float member and...196/// i. Multiple fixed-point members. If there are available GAR, the structure197/// is passed in a GAR, and passed on the stack if no GAR is available.198 199struct f16x1_i16x2_s {200 _Float16 a;201 int16_t b, c;202};203 204struct f32x1_i16x2_s {205 float a;206 int16_t b, c;207};208 209// CHECK-LABEL: define{{.*}} i64 @f_f16x1_i16x2_s(i64 %x.coerce)210struct f16x1_i16x2_s f_f16x1_i16x2_s(struct f16x1_i16x2_s x) {211 return x;212}213 214// CHECK-LABEL: define{{.*}} i64 @f_f32x1_i16x2_s(i64 %x.coerce)215struct f32x1_i16x2_s f_f32x1_i16x2_s(struct f32x1_i16x2_s x) {216 return x;217}218 219/// ii. Only one fixed-point member. If one FAR and one GAR are available, the220/// floating-point member of the structure is passed in the FAR, and the integer221/// member of the structure is passed in the GAR; If no floating-point register222/// but one GAR is available, it’s passed in GAR; If no GAR is available, it’s223/// passed on the stack.224 225struct f16x1_i32x1_s {226 _Float16 a;227 int32_t b;228};229 230struct f32x1_i32x1_s {231 float a;232 int32_t b;233};234 235// CHECK-LABEL: define{{.*}} { half, i32 } @f_f16x1_i32x1_s(half %0, i32 %1)236struct f16x1_i32x1_s f_f16x1_i32x1_s(struct f16x1_i32x1_s x) {237 return x;238}239 240// CHECK-LABEL: define{{.*}} { float, i32 } @f_f32x1_i32x1_s(float %0, i32 %1)241struct f32x1_i32x1_s f_f32x1_i32x1_s(struct f32x1_i32x1_s x) {242 return x;243}244 245/// 2. GRLEN < WOA ≤ 2 × GRLEN246/// a. Only fixed-point members.247/// i. The argument is passed in a pair of available GAR, with the low-order248/// bits in the lower-numbered GAR and the high-order bits in the249/// higher-numbered GAR. If only one GAR is available, the low-order bits are in250/// the GAR and the high-order bits are on the stack, and passed on the stack if251/// no GAR is available.252 253struct i64x2_s {254 int64_t a, b;255};256 257// CHECK-LABEL: define{{.*}} [2 x i64] @f_i64x2_s([2 x i64] %x.coerce)258struct i64x2_s f_i64x2_s(struct i64x2_s x) {259 return x;260}261 262/// b. Only floating-point members.263/// i. The structure has one long double member or one double member and two264/// adjacent float members or 3-4 float members. The argument is passed in a265/// pair of available GAR, with the low-order bits in the lower-numbered GAR and266/// the high-order bits in the higher-numbered GAR. If only one GAR is267/// available, the low-order bits are in the GAR and the high-order bits are on268/// the stack, and passed on the stack if no GAR is available.269 270struct f128x1_s {271 long double a;272};273 274// CHECK-LABEL: define{{.*}} i128 @f_f128x1_s(i128 %x.coerce)275struct f128x1_s f_f128x1_s(struct f128x1_s x) {276 return x;277}278 279struct f64x1_f32x2_s {280 double a;281 float b, c;282};283 284// CHECK-LABEL: define{{.*}} [2 x i64] @f_f64x1_f32x2_s([2 x i64] %x.coerce)285struct f64x1_f32x2_s f_f64x1_f32x2_s(struct f64x1_f32x2_s x) {286 return x;287}288 289struct f32x3_s {290 float a, b, c;291};292 293// CHECK-LABEL: define{{.*}} [2 x i64] @f_f32x3_s([2 x i64] %x.coerce)294struct f32x3_s f_f32x3_s(struct f32x3_s x) {295 return x;296}297 298struct f32x4_s {299 float a, b, c, d;300};301 302// CHECK-LABEL: define{{.*}} [2 x i64] @f_f32x4_s([2 x i64] %x.coerce)303struct f32x4_s f_f32x4_s(struct f32x4_s x) {304 return x;305}306 307struct f16x5_s {308 _Float16 a, b, c, d;309 __fp16 e;310};311 312// CHECK-LABEL: define{{.*}} [2 x i64] @f_f16x5_s([2 x i64] %x.coerce)313struct f16x5_s f_f16x5_s(struct f16x5_s x) {314 return x;315}316 317/// ii. The structure with two double members is passed in a pair of available318/// FARs. If no a pair of available FARs, it’s passed in GARs. A structure with319/// one double member and one float member is same.320 321struct f64x2_s {322 double a, b;323};324 325// CHECK-LABEL: define{{.*}} { double, double } @f_f64x2_s(double %0, double %1)326struct f64x2_s f_f64x2_s(struct f64x2_s x) {327 return x;328}329 330/// c. Both fixed-point and floating-point members.331/// i. The structure has one double member and only one fixed-point member.332/// A. If one FAR and one GAR are available, the floating-point member of the333/// structure is passed in the FAR, and the integer member of the structure is334/// passed in the GAR; If no floating-point registers but two GARs are335/// available, it’s passed in the two GARs; If only one GAR is available, the336/// low-order bits are in the GAR and the high-order bits are on the stack; And337/// it’s passed on the stack if no GAR is available.338 339struct f64x1_i64x1_s {340 double a;341 int64_t b;342};343 344// CHECK-LABEL: define{{.*}} { double, i64 } @f_f64x1_i64x1_s(double %0, i64 %1)345struct f64x1_i64x1_s f_f64x1_i64x1_s(struct f64x1_i64x1_s x) {346 return x;347}348 349/// ii. Others350/// A. The argument is passed in a pair of available GAR, with the low-order351/// bits in the lower-numbered GAR and the high-order bits in the352/// higher-numbered GAR. If only one GAR is available, the low-order bits are in353/// the GAR and the high-order bits are on the stack, and passed on the stack if354/// no GAR is available.355 356struct f64x1_i32x2_s {357 double a;358 int32_t b, c;359};360 361// CHECK-LABEL: define{{.*}} [2 x i64] @f_f64x1_i32x2_s([2 x i64] %x.coerce)362struct f64x1_i32x2_s f_f64x1_i32x2_s(struct f64x1_i32x2_s x) {363 return x;364}365 366struct f32x2_i32x2_s {367 float a, b;368 int32_t c, d;369};370 371// CHECK-LABEL: define{{.*}} [2 x i64] @f_f32x2_i32x2_s([2 x i64] %x.coerce)372struct f32x2_i32x2_s f_f32x2_i32x2_s(struct f32x2_i32x2_s x) {373 return x;374}375 376struct f16x4_i32x2_s {377 _Float16 a, b, c, d;378 int32_t e, f;379};380 381// CHECK-LABEL: define{{.*}} [2 x i64] @f_f16x4_i32x2_s([2 x i64] %x.coerce)382struct f16x4_i32x2_s f_f16x4_i32x2_s(struct f16x4_i32x2_s x) {383 return x;384}385 386/// 3. WOA > 2 × GRLEN387/// a. It’s passed by reference and are replaced in the argument list with the388/// address. If there is an available GAR, the reference is passed in the GAR,389/// and passed on the stack if no GAR is available.390 391struct i64x4_s {392 int64_t a, b, c, d;393};394 395// CHECK-LABEL: define{{.*}} void @f_i64x4_s(ptr{{.*}} sret(%struct.i64x4_s) align 8 %agg.result, ptr{{.*}} %x)396struct i64x4_s f_i64x4_s(struct i64x4_s x) {397 return x;398}399 400struct f64x4_s {401 double a, b, c, d;402};403 404// CHECK-LABEL: define{{.*}} void @f_f64x4_s(ptr{{.*}} sret(%struct.f64x4_s) align 8 %agg.result, ptr{{.*}} %x)405struct f64x4_s f_f64x4_s(struct f64x4_s x) {406 return x;407}408 409/// Part 3: Union arguments and return value.410 411/// Check empty unions are ignored.412 413union empty_u {};414 415// CHECK-LABEL: define{{.*}} void @f_empty_u()416union empty_u f_empty_u(union empty_u x) {417 return x;418}419 420/// Union is passed in GAR or stack.421/// 1. 0 < WOA ≤ GRLEN422/// a. The argument is passed in a GAR, or on the stack by value if no GAR is423/// available.424 425union i32_f32_u {426 int32_t a;427 float b;428};429 430// CHECK-LABEL: define{{.*}} i64 @f_i32_f32_u(i64 %x.coerce)431union i32_f32_u f_i32_f32_u(union i32_f32_u x) {432 return x;433}434 435union i64_f64_u {436 int64_t a;437 double b;438};439 440// CHECK-LABEL: define{{.*}} i64 @f_i64_f64_u(i64 %x.coerce)441union i64_f64_u f_i64_f64_u(union i64_f64_u x) {442 return x;443}444 445/// 2. GRLEN < WOA ≤ 2 × GRLEN446/// a. The argument is passed in a pair of available GAR, with the low-order447/// bits in the lower-numbered GAR and the high-order bits in the448/// higher-numbered GAR. If only one GAR is available, the low-order bits are in449/// the GAR and the high-order bits are on the stack. The arguments are passed450/// on the stack when no GAR is available.451 452union i128_f128_u {453 __int128_t a;454 long double b;455};456 457// CHECK-LABEL: define{{.*}} i128 @f_i128_f128_u(i128 %x.coerce)458union i128_f128_u f_i128_f128_u(union i128_f128_u x) {459 return x;460}461 462/// 3. WOA > 2 × GRLEN463/// a. It’s passed by reference and are replaced in the argument list with the464/// address. If there is an available GAR, the reference is passed in the GAR,465/// and passed on the stack if no GAR is available.466 467union i64_arr3_u {468 int64_t a[3];469};470 471// CHECK-LABEL: define{{.*}} void @f_i64_arr3_u(ptr{{.*}} sret(%union.i64_arr3_u) align 8 %agg.result, ptr{{.*}} %x)472union i64_arr3_u f_i64_arr3_u(union i64_arr3_u x) {473 return x;474}475 476/// Part 4: Complex number arguments and return value.477 478/// A complex floating-point number, or a structure containing just one complex479/// floating-point number, is passed as though it were a structure containing480/// two floating-point reals.481 482// CHECK-LABEL: define{{.*}} { float, float } @f_floatcomplex(float noundef %x.coerce0, float noundef %x.coerce1)483float __complex__ f_floatcomplex(float __complex__ x) { return x; }484 485// CHECK-LABEL: define{{.*}} { double, double } @f_doublecomplex(double noundef %x.coerce0, double noundef %x.coerce1)486double __complex__ f_doublecomplex(double __complex__ x) { return x; }487 488struct floatcomplex_s {489 float __complex__ c;490};491// CHECK-LABEL: define{{.*}} { float, float } @f_floatcomplex_s(float %0, float %1)492struct floatcomplex_s f_floatcomplex_s(struct floatcomplex_s x) {493 return x;494}495 496struct doublecomplex_s {497 double __complex__ c;498};499// CHECK-LABEL: define{{.*}} { double, double } @f_doublecomplex_s(double %0, double %1)500struct doublecomplex_s f_doublecomplex_s(struct doublecomplex_s x) {501 return x;502}503 504/// Part 5: Variadic arguments.505 506/// Variadic arguments are passed in GARs in the same manner as named arguments.507 508int f_va_callee(int, ...);509 510// CHECK-LABEL: define{{.*}} void @f_va_caller()511// CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double noundef 4.000000e+00, double noundef 5.000000e+00, i64 {{.*}}, [2 x i64] {{.*}})512void f_va_caller(void) {513 f_va_callee(1, 2, 3LL, 4.0f, 5.0, (struct i16x4_s){6, 7, 8, 9},514 (struct i64x2_s){10, 11});515}516 517// CHECK-LABEL: @f_va_int(518// CHECK-NEXT: entry:519// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8520// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8521// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4522// CHECK-NEXT: store ptr [[FMT:%.*]], ptr [[FMT_ADDR]], align 8523// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[VA]])524// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8525// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8526// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8527// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 8528// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4529// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[VA]])530// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[V]], align 4531// CHECK-NEXT: ret i32 [[TMP1]]532int f_va_int(char *fmt, ...) {533 __builtin_va_list va;534 __builtin_va_start(va, fmt);535 int v = __builtin_va_arg(va, int);536 __builtin_va_end(va);537 return v;538}539 540/// Part 6. Structures with zero size fields (bitfields or arrays).541 542/// Check that zero size fields in structure are ignored.543/// Note that this rule is not explicitly documented in ABI spec but it matches544/// GCC's behavior.545 546struct f64x2_zsfs_s {547 double a;548 int : 0;549 __int128_t : 0;550 int b[0];551 __int128_t c[0];552 double d;553};554 555// CHECK-LABEL: define{{.*}} { double, double } @f_f64x2_zsfs_s(double %0, double %1)556struct f64x2_zsfs_s f_f64x2_zsfs_s(struct f64x2_zsfs_s x) {557 return x;558}559 560