Index: clang/test/CodeGen/RISCV/riscv-abi.cpp =================================================================== --- clang/test/CodeGen/RISCV/riscv-abi.cpp +++ clang/test/CodeGen/RISCV/riscv-abi.cpp @@ -1,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - \ // RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32-ILP32F,ILP32 %s // RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ @@ -25,8 +26,14 @@ int32_t i2; }; -// ILP32-ILP32F-ILP32D-LABEL: define{{.*}} [2 x i32] @_Z30int32_int32_struct_inheritance14child1_int32_s([2 x i32] %a.coerce) -// LP64-LP64F-LP64D-LABEL: define{{.*}} i64 @_Z30int32_int32_struct_inheritance14child1_int32_s(i64 %a.coerce) +// ILP32-ILP32F-ILP32D-LABEL: define dso_local [2 x i32] @_Z30int32_int32_struct_inheritance14child1_int32_s +// ILP32-ILP32F-ILP32D-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// ILP32-ILP32F-ILP32D: entry: +// +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @_Z30int32_int32_struct_inheritance14child1_int32_s +// LP64-LP64F-LP64D-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// LP64-LP64F-LP64D: entry: +// struct child1_int32_s int32_int32_struct_inheritance(struct child1_int32_s a) { return a; } @@ -39,10 +46,22 @@ float f1; }; -// ILP32: define{{.*}} [2 x i32] @_Z30int32_float_struct_inheritance14child2_float_s([2 x i32] %a.coerce) -// ILP32F-ILP32D: define{{.*}} { i32, float } @_Z30int32_float_struct_inheritance14child2_float_s(i32 %0, float %1) -// LP64: define{{.*}} i64 @_Z30int32_float_struct_inheritance14child2_float_s(i64 %a.coerce) -// LP64F-LP64D: define{{.*}} { i32, float } @_Z30int32_float_struct_inheritance14child2_float_s(i32 %0, float %1) +// ILP32-LABEL: define dso_local [2 x i32] @_Z30int32_float_struct_inheritance14child2_float_s +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { i32, float } @_Z30int32_float_struct_inheritance14child2_float_s +// ILP32F-ILP32D-SAME: (i32 [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +// LP64-LABEL: define dso_local i64 @_Z30int32_float_struct_inheritance14child2_float_s +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { i32, float } @_Z30int32_float_struct_inheritance14child2_float_s +// LP64F-LP64D-SAME: (i32 [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// struct child2_float_s int32_float_struct_inheritance(struct child2_float_s a) { return a; } @@ -55,9 +74,18 @@ int64_t i1; }; -// ILP32-ILP32F-ILP32D-LABEL: define{{.*}} void @_Z30float_int64_struct_inheritance14child3_int64_s(ptr noalias sret(%struct.child3_int64_s) -// LP64-LABEL: define{{.*}} [2 x i64] @_Z30float_int64_struct_inheritance14child3_int64_s([2 x i64] %a.coerce) -// LP64F-LP64D-LABEL: define{{.*}} { float, i64 } @_Z30float_int64_struct_inheritance14child3_int64_s(float %0, i64 %1) +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @_Z30float_int64_struct_inheritance14child3_int64_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_CHILD3_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +// LP64-LABEL: define dso_local [2 x i64] @_Z30float_int64_struct_inheritance14child3_int64_s +// LP64-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i64 } @_Z30float_int64_struct_inheritance14child3_int64_s +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// struct child3_int64_s float_int64_struct_inheritance(struct child3_int64_s a) { return a; } @@ -70,10 +98,22 @@ double d1; }; -// ILP32-ILP32F-LABEL: define{{.*}} void @_Z32double_double_struct_inheritance15child4_double_s(ptr noalias sret(%struct.child4_double_s) -// ILP32D-LABEL: define{{.*}} { double, double } @_Z32double_double_struct_inheritance15child4_double_s(double %0, double %1) -// LP64-LP64F-LABEL: define{{.*}} [2 x i64] @_Z32double_double_struct_inheritance15child4_double_s([2 x i64] %a.coerce) -// LP64D-LABEL: define{{.*}} { double, double } @_Z32double_double_struct_inheritance15child4_double_s(double %0, double %1) +// ILP32-ILP32F-LABEL: define dso_local void @_Z32double_double_struct_inheritance15child4_double_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_CHILD4_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @_Z32double_double_struct_inheritance15child4_double_s +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +// LP64-LP64F-LABEL: define dso_local [2 x i64] @_Z32double_double_struct_inheritance15child4_double_s +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @_Z32double_double_struct_inheritance15child4_double_s +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// struct child4_double_s double_double_struct_inheritance(struct child4_double_s a) { return a; } @@ -89,8 +129,14 @@ float f1; }; -// ILP32-ILP32F-ILP32D-LABEL: define{{.*}} void @_ZN16child5_virtual_sC1EOS_(ptr noundef nonnull align 4 dereferenceable(8) %this, ptr noundef nonnull align 4 dereferenceable(8) %0) -// LP64-LP64F-LP64D-LABEL: define{{.*}} void @_ZN16child5_virtual_sC1EOS_(ptr noundef nonnull align 8 dereferenceable(12) %this, ptr noundef nonnull align 8 dereferenceable(12) %0) +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @_Z38int32_float_virtual_struct_inheritance16child5_virtual_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_CHILD5_VIRTUAL_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +// LP64-LP64F-LP64D-LABEL: define dso_local void @_Z38int32_float_virtual_struct_inheritance16child5_virtual_s +// LP64-LP64F-LP64D-SAME: (ptr noalias sret([[STRUCT_CHILD5_VIRTUAL_S:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[A:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// struct child5_virtual_s int32_float_virtual_struct_inheritance(struct child5_virtual_s a) { return a; } @@ -110,14 +156,25 @@ struct grandchild_6_s : child6a_s, child6b_s { }; -// ILP32: define{{.*}} [2 x i32] @_Z38float_float_diamond_struct_inheritance14grandchild_6_s([2 x i32] %a.coerce) -// ILP32F-ILP64D: define{{.*}} { float, float } @_Z38float_float_diamond_struct_inheritance14grandchild_6_s(float %0, float %1) -// LP64: define{{.*}} i64 @_Z38float_float_diamond_struct_inheritance14grandchild_6_s(i64 %a.coerce) -// LP64F-LP64D: define{{.*}} { float, float } @_Z38float_float_diamond_struct_inheritance14grandchild_6_s(float %0, float %1) +// ILP32-LABEL: define dso_local [2 x i32] @_Z38float_float_diamond_struct_inheritance14grandchild_6_s +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @_Z38float_float_diamond_struct_inheritance14grandchild_6_s +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +// LP64-LABEL: define dso_local i64 @_Z38float_float_diamond_struct_inheritance14grandchild_6_s +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @_Z38float_float_diamond_struct_inheritance14grandchild_6_s +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// struct grandchild_6_s float_float_diamond_struct_inheritance(struct grandchild_6_s a) { return a; } - -// NOTE: These prefixes are unused. Do not add tests below this line: +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: // ILP32F: {{.*}} // LP64F: {{.*}} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32-abi.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -emit-llvm %s -o - | FileCheck %s +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" +// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s // This file contains test cases that will have different output for ilp32 vs // the other 32-bit ABIs. @@ -25,7 +26,10 @@ // Scalars passed on the stack should not have signext/zeroext attributes // (they are anyext). -// CHECK-LABEL: define{{.*}} i32 @f_scalar_stack_1(i32 noundef %a, i64 noundef %b, float noundef %c, double noundef %d, fp128 noundef %e, i8 noundef zeroext %f, i8 noundef %g, i8 noundef %h) +// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], float noundef [[C:%.*]], double noundef [[D:%.*]], fp128 noundef [[E:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef [[G:%.*]], i8 noundef [[H:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// int f_scalar_stack_1(int32_t a, int64_t b, float c, double d, long double e, uint8_t f, int8_t g, uint8_t h) { return g + h; @@ -35,7 +39,10 @@ // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(%struct.large* noalias sret(%struct.large) align 4 %agg.result, float noundef %a, i64 noundef %b, double noundef %c, fp128 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_2 +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], float noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef [[F:%.*]], i8 noundef [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_2(float a, int64_t b, double c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; @@ -44,10 +51,16 @@ // Aggregates and >=XLen scalars passed on the stack should be lowered just as // they would be if passed via registers. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_3(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 noundef %e, i64 noundef %f, float noundef %g, double noundef %h, fp128 noundef %i) +// CHECK-LABEL: define dso_local void @f_scalar_stack_3 +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], float noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_scalar_stack_3(double a, int64_t b, double c, int64_t d, int e, int64_t f, float g, double h, long double i) {} -// CHECK-LABEL: define{{.*}} void @f_agg_stack(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 %e.coerce, [2 x i32] %f.coerce, i64 %g.coerce, %struct.large* noundef %h) +// CHECK-LABEL: define dso_local void @f_agg_stack +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, struct small f, struct small_aligned g, struct large h) {} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-abi.c @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" +// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ // RUN: | FileCheck %s // This file contains test cases that will have the same output for the ilp32 @@ -27,7 +28,10 @@ // Scalars passed on the stack should not have signext/zeroext attributes // (they are anyext). -// CHECK-LABEL: define{{.*}} i32 @f_scalar_stack_1(i32 noundef %a, i64 noundef %b, i32 noundef %c, double noundef %d, fp128 noundef %e, i8 noundef zeroext %f, i8 noundef %g, i8 noundef %h) +// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i32 noundef [[C:%.*]], double noundef [[D:%.*]], fp128 noundef [[E:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef [[G:%.*]], i8 noundef [[H:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// int f_scalar_stack_1(int32_t a, int64_t b, int32_t c, double d, long double e, uint8_t f, int8_t g, uint8_t h) { return g + h; @@ -37,7 +41,10 @@ // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(%struct.large* noalias sret(%struct.large) align 4 %agg.result, i32 noundef %a, i64 noundef %b, double noundef %c, fp128 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_2 +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef [[F:%.*]], i8 noundef [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_2(int32_t a, int64_t b, double c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; @@ -46,10 +53,16 @@ // Aggregates and >=XLen scalars passed on the stack should be lowered just as // they would be if passed via registers. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_3(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 noundef %e, i64 noundef %f, i32 noundef %g, double noundef %h, fp128 noundef %i) +// CHECK-LABEL: define dso_local void @f_scalar_stack_3 +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], i32 noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_scalar_stack_3(double a, int64_t b, double c, int64_t d, int e, int64_t f, int32_t g, double h, long double i) {} -// CHECK-LABEL: define{{.*}} void @f_agg_stack(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 %e.coerce, [2 x i32] %f.coerce, i64 %g.coerce, %struct.large* noundef %h) +// CHECK-LABEL: define dso_local void @f_agg_stack +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, struct small f, struct small_aligned g, struct large h) {} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-ilp32d-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-ilp32d-abi.c @@ -1,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" // RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -emit-llvm -fforce-enable-int128 %s -o - \ // RUN: | FileCheck %s -check-prefixes=CHECK,CHECK-FORCEINT128 @@ -12,57 +13,93 @@ #include #include -// CHECK-LABEL: define{{.*}} void @f_void() +// CHECK-LABEL: define dso_local void @f_void +// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// void f_void(void) {} // Scalar arguments and return values smaller than the word size are extended // according to the sign of their type, up to 32 bits -// CHECK-LABEL: define{{.*}} zeroext i1 @f_scalar_0(i1 noundef zeroext %x) +// CHECK-LABEL: define dso_local zeroext i1 @f_scalar_0 +// CHECK-SAME: (i1 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// _Bool f_scalar_0(_Bool x) { return x; } -// CHECK-LABEL: define{{.*}} signext i8 @f_scalar_1(i8 noundef signext %x) +// CHECK-LABEL: define dso_local signext i8 @f_scalar_1 +// CHECK-SAME: (i8 noundef signext [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// int8_t f_scalar_1(int8_t x) { return x; } -// CHECK-LABEL: define{{.*}} zeroext i8 @f_scalar_2(i8 noundef zeroext %x) +// CHECK-LABEL: define dso_local zeroext i8 @f_scalar_2 +// CHECK-SAME: (i8 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// uint8_t f_scalar_2(uint8_t x) { return x; } -// CHECK-LABEL: define{{.*}} i32 @f_scalar_3(i32 noundef %x) +// CHECK-LABEL: define dso_local i32 @f_scalar_3 +// CHECK-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// int32_t f_scalar_3(int32_t x) { return x; } -// CHECK-LABEL: define{{.*}} i64 @f_scalar_4(i64 noundef %x) +// CHECK-LABEL: define dso_local i64 @f_scalar_4 +// CHECK-SAME: (i64 noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// int64_t f_scalar_4(int64_t x) { return x; } #ifdef __SIZEOF_INT128__ -// CHECK-FORCEINT128-LABEL: define{{.*}} i128 @f_scalar_5(i128 noundef %x) +// CHECK-FORCEINT128-LABEL: define dso_local i128 @f_scalar_5 +// CHECK-FORCEINT128-SAME: (i128 noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK-FORCEINT128: entry: +// __int128_t f_scalar_5(__int128_t x) { return x; } #endif -// CHECK-LABEL: define{{.*}} float @f_fp_scalar_1(float noundef %x) +// CHECK-LABEL: define dso_local float @f_fp_scalar_1 +// CHECK-SAME: (float noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// float f_fp_scalar_1(float x) { return x; } -// CHECK-LABEL: define{{.*}} double @f_fp_scalar_2(double noundef %x) +// CHECK-LABEL: define dso_local double @f_fp_scalar_2 +// CHECK-SAME: (double noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// double f_fp_scalar_2(double x) { return x; } // Scalars larger than 2*xlen are passed/returned indirect. However, the // RISC-V LLVM backend can handle this fine, so the function doesn't need to // be modified. -// CHECK-LABEL: define{{.*}} fp128 @f_fp_scalar_3(fp128 noundef %x) +// CHECK-LABEL: define dso_local fp128 @f_fp_scalar_3 +// CHECK-SAME: (fp128 noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// long double f_fp_scalar_3(long double x) { return x; } // Empty structs or unions are ignored. struct empty_s {}; -// CHECK-LABEL: define{{.*}} void @f_agg_empty_struct() +// CHECK-LABEL: define dso_local void @f_agg_empty_struct +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct empty_s f_agg_empty_struct(struct empty_s x) { return x; } union empty_u {}; -// CHECK-LABEL: define{{.*}} void @f_agg_empty_union() +// CHECK-LABEL: define dso_local void @f_agg_empty_union +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// union empty_u f_agg_empty_union(union empty_u x) { return x; } @@ -74,13 +111,19 @@ uint8_t a, b, c, d; }; -// CHECK-LABEL: define{{.*}} void @f_agg_tiny(i32 %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_tiny +// CHECK-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_tiny(struct tiny x) { x.a += x.b; x.c += x.d; } -// CHECK-LABEL: define{{.*}} i32 @f_agg_tiny_ret() +// CHECK-LABEL: define dso_local i32 @f_agg_tiny_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct tiny f_agg_tiny_ret(void) { return (struct tiny){1, 2, 3, 4}; } @@ -88,23 +131,35 @@ typedef uint8_t v4i8 __attribute__((vector_size(4))); typedef int32_t v1i32 __attribute__((vector_size(4))); -// CHECK-LABEL: define{{.*}} void @f_vec_tiny_v4i8(i32 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_tiny_v4i8 +// CHECK-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_tiny_v4i8(v4i8 x) { x[0] = x[1]; x[2] = x[3]; } -// CHECK-LABEL: define{{.*}} i32 @f_vec_tiny_v4i8_ret() +// CHECK-LABEL: define dso_local i32 @f_vec_tiny_v4i8_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v4i8 f_vec_tiny_v4i8_ret(void) { return (v4i8){1, 2, 3, 4}; } -// CHECK-LABEL: define{{.*}} void @f_vec_tiny_v1i32(i32 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_tiny_v1i32 +// CHECK-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_tiny_v1i32(v1i32 x) { x[0] = 114; } -// CHECK-LABEL: define{{.*}} i32 @f_vec_tiny_v1i32_ret() +// CHECK-LABEL: define dso_local i32 @f_vec_tiny_v1i32_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v1i32 f_vec_tiny_v1i32_ret(void) { return (v1i32){1}; } @@ -113,13 +168,19 @@ int32_t a, *b; }; -// CHECK-LABEL: define{{.*}} void @f_agg_small([2 x i32] %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_small +// CHECK-SAME: ([2 x i32] [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_small(struct small x) { x.a += *x.b; x.b = &x.a; } -// CHECK-LABEL: define{{.*}} [2 x i32] @f_agg_small_ret() +// CHECK-LABEL: define dso_local [2 x i32] @f_agg_small_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct small f_agg_small_ret(void) { return (struct small){1, 0}; } @@ -127,22 +188,34 @@ typedef uint8_t v8i8 __attribute__((vector_size(8))); typedef int64_t v1i64 __attribute__((vector_size(8))); -// CHECK-LABEL: define{{.*}} void @f_vec_small_v8i8(i64 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_small_v8i8 +// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_small_v8i8(v8i8 x) { x[0] = x[7]; } -// CHECK-LABEL: define{{.*}} i64 @f_vec_small_v8i8_ret() +// CHECK-LABEL: define dso_local i64 @f_vec_small_v8i8_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v8i8 f_vec_small_v8i8_ret(void) { return (v8i8){1, 2, 3, 4, 5, 6, 7, 8}; } -// CHECK-LABEL: define{{.*}} void @f_vec_small_v1i64(i64 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_small_v1i64 +// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_small_v1i64(v1i64 x) { x[0] = 114; } -// CHECK-LABEL: define{{.*}} i64 @f_vec_small_v1i64_ret() +// CHECK-LABEL: define dso_local i64 @f_vec_small_v1i64_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v1i64 f_vec_small_v1i64_ret(void) { return (v1i64){1}; } @@ -155,12 +228,18 @@ int64_t a; }; -// CHECK-LABEL: define{{.*}} void @f_agg_small_aligned(i64 %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_small_aligned +// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_small_aligned(struct small_aligned x) { x.a += x.a; } -// CHECK-LABEL: define{{.*}} i64 @f_agg_small_aligned_ret(i64 %x.coerce) +// CHECK-LABEL: define dso_local i64 @f_agg_small_aligned_ret +// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { return (struct small_aligned){10}; } @@ -170,26 +249,38 @@ int32_t a, b, c, d; }; -// CHECK-LABEL: define{{.*}} void @f_agg_large(%struct.large* noundef %x) +// CHECK-LABEL: define dso_local void @f_agg_large +// CHECK-SAME: (%struct.large* noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_large(struct large x) { x.a = x.b + x.c + x.d; } // The address where the struct should be written to will be the first // argument -// CHECK-LABEL: define{{.*}} void @f_agg_large_ret(%struct.large* noalias sret(%struct.large) align 4 %agg.result, i32 noundef %i, i8 noundef signext %j) +// CHECK-LABEL: define dso_local void @f_agg_large_ret +// CHECK-SAME: (%struct.large* noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[I:%.*]], i8 noundef signext [[J:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_agg_large_ret(int32_t i, int8_t j) { return (struct large){1, 2, 3, 4}; } typedef unsigned char v16i8 __attribute__((vector_size(16))); -// CHECK-LABEL: define{{.*}} void @f_vec_large_v16i8(<16 x i8>* noundef %0) +// CHECK-LABEL: define dso_local void @f_vec_large_v16i8 +// CHECK-SAME: (<16 x i8>* noundef [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_large_v16i8(v16i8 x) { x[0] = x[7]; } -// CHECK-LABEL: define{{.*}} void @f_vec_large_v16i8_ret(<16 x i8>* noalias sret(<16 x i8>) align 16 %agg.result) +// CHECK-LABEL: define dso_local void @f_vec_large_v16i8_ret +// CHECK-SAME: (<16 x i8>* noalias sret(<16 x i8>) align 16 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// v16i8 f_vec_large_v16i8_ret(void) { return (v16i8){1, 2, 3, 4, 5, 6, 7, 8}; } @@ -197,7 +288,10 @@ // Scalars passed on the stack should not have signext/zeroext attributes // (they are anyext). -// CHECK-LABEL: define{{.*}} i32 @f_scalar_stack_1(i32 %a.coerce, [2 x i32] %b.coerce, i64 %c.coerce, %struct.large* noundef %d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef %g, i8 noundef %h) +// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 [[A_COERCE:%.*]], [2 x i32] [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], %struct.large* noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef [[G:%.*]], i8 noundef [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, struct large d, uint8_t e, int8_t f, uint8_t g, int8_t h) { return g + h; @@ -207,13 +301,19 @@ // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(%struct.large* noalias sret(%struct.large) align 4 %agg.result, i32 noundef %a, i64 noundef %b, i64 noundef %c, fp128 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_2 +// CHECK-SAME: (%struct.large* noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef [[F:%.*]], i8 noundef [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_2(int32_t a, int64_t b, int64_t c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; } -// CHECK-LABEL: define{{.*}} fp128 @f_scalar_stack_4(i32 noundef %a, i64 noundef %b, i64 noundef %c, fp128 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g) +// CHECK-LABEL: define dso_local fp128 @f_scalar_stack_4 +// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef [[F:%.*]], i8 noundef [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// long double f_scalar_stack_4(int32_t a, int64_t b, int64_t c, long double d, uint8_t e, int8_t f, uint8_t g) { return d; @@ -222,210 +322,16 @@ // Aggregates and >=XLen scalars passed on the stack should be lowered just as // they would be if passed via registers. -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_5(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 noundef %e, i64 noundef %f, float noundef %g, double noundef %h, fp128 noundef %i) +// CHECK-LABEL: define dso_local void @f_scalar_stack_5 +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], float noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_scalar_stack_5(double a, int64_t b, double c, int64_t d, int e, int64_t f, float g, double h, long double i) {} -// CHECK-LABEL: define{{.*}} void @f_agg_stack(double noundef %a, i64 noundef %b, double noundef %c, i64 noundef %d, i32 %e.coerce, [2 x i32] %f.coerce, i64 %g.coerce, %struct.large* noundef %h) +// CHECK-LABEL: define dso_local void @f_agg_stack +// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], %struct.large* noundef [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, struct small f, struct small_aligned g, struct large h) {} - -// Ensure that ABI lowering happens as expected for vararg calls. For RV32 -// with the base integer calling convention there will be no observable -// differences in the lowered IR for a call with varargs vs without. - -int f_va_callee(int, ...); - -// CHECK-LABEL: define{{.*}} void @f_va_caller() -// CHECK: call i32 (i32, ...) @f_va_callee(i32 noundef 1, i32 noundef 2, i64 noundef 3, double noundef 4.000000e+00, double noundef 5.000000e+00, i32 {{%.*}}, [2 x i32] {{%.*}}, i64 {{%.*}}, %struct.large* noundef {{%.*}}) -void f_va_caller(void) { - f_va_callee(1, 2, 3LL, 4.0f, 5.0, (struct tiny){6, 7, 8, 9}, - (struct small){10, NULL}, (struct small_aligned){11}, - (struct large){12, 13, 14, 15}); -} - -// CHECK-LABEL: define{{.*}} i32 @f_va_1(i8* noundef %fmt, ...) {{.*}} { -// CHECK: [[FMT_ADDR:%.*]] = alloca i8*, align 4 -// CHECK: [[VA:%.*]] = alloca i8*, align 4 -// CHECK: [[V:%.*]] = alloca i32, align 4 -// CHECK: store i8* %fmt, i8** [[FMT_ADDR]], align 4 -// CHECK: [[VA1:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK: call void @llvm.va_start(i8* [[VA1]]) -// CHECK: [[ARGP_CUR:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR]], i32 4 -// CHECK: store i8* [[ARGP_NEXT]], i8** [[VA]], align 4 -// CHECK: [[TMP0:%.*]] = bitcast i8* [[ARGP_CUR]] to i32* -// CHECK: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4 -// CHECK: store i32 [[TMP1]], i32* [[V]], align 4 -// CHECK: [[VA2:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK: call void @llvm.va_end(i8* [[VA2]]) -// CHECK: [[TMP2:%.*]] = load i32, i32* [[V]], align 4 -// CHECK: ret i32 [[TMP2]] -// CHECK: } -int f_va_1(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - int v = __builtin_va_arg(va, int); - __builtin_va_end(va); - - return v; -} - -// An "aligned" register pair (where the first register is even-numbered) is -// used to pass varargs with 2x xlen alignment and 2x xlen size. Ensure the -// correct offsets are used. - -// CHECK-LABEL: @f_va_2( -// CHECK: [[FMT_ADDR:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[VA:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[V:%.*]] = alloca double, align 8 -// CHECK-NEXT: store i8* [[FMT:%.*]], i8** [[FMT_ADDR]], align 4 -// CHECK-NEXT: [[VA1:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_start(i8* [[VA1]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint i8* [[ARGP_CUR]] to i32 -// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], 7 -// CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -8 -// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i32 [[TMP2]] to i8* -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR_ALIGNED]], i32 8 -// CHECK-NEXT: store i8* [[ARGP_NEXT]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP3:%.*]] = bitcast i8* [[ARGP_CUR_ALIGNED]] to double* -// CHECK-NEXT: [[TMP4:%.*]] = load double, double* [[TMP3]], align 8 -// CHECK-NEXT: store double [[TMP4]], double* [[V]], align 8 -// CHECK-NEXT: [[VA2:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_end(i8* [[VA2]]) -// CHECK-NEXT: [[TMP5:%.*]] = load double, double* [[V]], align 8 -// CHECK-NEXT: ret double [[TMP5]] -double f_va_2(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - double v = __builtin_va_arg(va, double); - __builtin_va_end(va); - - return v; -} - -// Two "aligned" register pairs. - -// CHECK-LABEL: @f_va_3( -// CHECK: [[FMT_ADDR:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[VA:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[V:%.*]] = alloca double, align 8 -// CHECK-NEXT: [[W:%.*]] = alloca i32, align 4 -// CHECK-NEXT: [[X:%.*]] = alloca double, align 8 -// CHECK-NEXT: store i8* [[FMT:%.*]], i8** [[FMT_ADDR]], align 4 -// CHECK-NEXT: [[VA1:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_start(i8* [[VA1]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint i8* [[ARGP_CUR]] to i32 -// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], 7 -// CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -8 -// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i32 [[TMP2]] to i8* -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR_ALIGNED]], i32 8 -// CHECK-NEXT: store i8* [[ARGP_NEXT]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP3:%.*]] = bitcast i8* [[ARGP_CUR_ALIGNED]] to double* -// CHECK-NEXT: [[TMP4:%.*]] = load double, double* [[TMP3]], align 8 -// CHECK-NEXT: store double [[TMP4]], double* [[V]], align 8 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR2]], i32 4 -// CHECK-NEXT: store i8* [[ARGP_NEXT3]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP5:%.*]] = bitcast i8* [[ARGP_CUR2]] to i32* -// CHECK-NEXT: [[TMP6:%.*]] = load i32, i32* [[TMP5]], align 4 -// CHECK-NEXT: store i32 [[TMP6]], i32* [[W]], align 4 -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP7:%.*]] = ptrtoint i8* [[ARGP_CUR4]] to i32 -// CHECK-NEXT: [[TMP8:%.*]] = add i32 [[TMP7]], 7 -// CHECK-NEXT: [[TMP9:%.*]] = and i32 [[TMP8]], -8 -// CHECK-NEXT: [[ARGP_CUR4_ALIGNED:%.*]] = inttoptr i32 [[TMP9]] to i8* -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR4_ALIGNED]], i32 8 -// CHECK-NEXT: store i8* [[ARGP_NEXT5]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP10:%.*]] = bitcast i8* [[ARGP_CUR4_ALIGNED]] to double* -// CHECK-NEXT: [[TMP11:%.*]] = load double, double* [[TMP10]], align 8 -// CHECK-NEXT: store double [[TMP11]], double* [[X]], align 8 -// CHECK-NEXT: [[VA6:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_end(i8* [[VA6]]) -// CHECK-NEXT: [[TMP12:%.*]] = load double, double* [[V]], align 8 -// CHECK-NEXT: [[TMP13:%.*]] = load double, double* [[X]], align 8 -// CHECK-NEXT: [[ADD:%.*]] = fadd double [[TMP12]], [[TMP13]] -// CHECK-NEXT: ret double [[ADD]] -double f_va_3(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - double v = __builtin_va_arg(va, double); - int w = __builtin_va_arg(va, int); - double x = __builtin_va_arg(va, double); - __builtin_va_end(va); - - return v + x; -} - -// CHECK-LABEL: define{{.*}} i32 @f_va_4(i8* noundef %fmt, ...) {{.*}} { -// CHECK: [[FMT_ADDR:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[VA:%.*]] = alloca i8*, align 4 -// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 -// CHECK-NEXT: [[LD:%.*]] = alloca fp128, align 16 -// CHECK-NEXT: [[TS:%.*]] = alloca [[STRUCT_TINY:%.*]], align 1 -// CHECK-NEXT: [[SS:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 4 -// CHECK-NEXT: [[LS:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 4 -// CHECK-NEXT: [[RET:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i8* [[FMT:%.*]], i8** [[FMT_ADDR]], align 4 -// CHECK-NEXT: [[VA1:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_start(i8* [[VA1]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR]], i32 4 -// CHECK-NEXT: store i8* [[ARGP_NEXT]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = bitcast i8* [[ARGP_CUR]] to i32* -// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4 -// CHECK-NEXT: store i32 [[TMP1]], i32* [[V]], align 4 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR2]], i32 4 -// CHECK-NEXT: store i8* [[ARGP_NEXT3]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP2:%.*]] = bitcast i8* [[ARGP_CUR2]] to fp128** -// CHECK-NEXT: [[TMP3:%.*]] = load fp128*, fp128** [[TMP2]], align 4 -// CHECK-NEXT: [[TMP4:%.*]] = load fp128, fp128* [[TMP3]], align 16 -// CHECK-NEXT: store fp128 [[TMP4]], fp128* [[LD]], align 16 -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR4]], i32 4 -// CHECK-NEXT: store i8* [[ARGP_NEXT5]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP5:%.*]] = bitcast i8* [[ARGP_CUR4]] to %struct.tiny* -// CHECK-NEXT: [[TMP6:%.*]] = bitcast %struct.tiny* [[TS]] to i8* -// CHECK-NEXT: [[TMP7:%.*]] = bitcast %struct.tiny* [[TMP5]] to i8* -// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[TMP6]], i8* align 4 [[TMP7]], i32 4, i1 false) -// CHECK-NEXT: [[ARGP_CUR6:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT7:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR6]], i32 8 -// CHECK-NEXT: store i8* [[ARGP_NEXT7]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP8:%.*]] = bitcast i8* [[ARGP_CUR6]] to %struct.small* -// CHECK-NEXT: [[TMP9:%.*]] = bitcast %struct.small* [[SS]] to i8* -// CHECK-NEXT: [[TMP10:%.*]] = bitcast %struct.small* [[TMP8]] to i8* -// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[TMP9]], i8* align 4 [[TMP10]], i32 8, i1 false) -// CHECK-NEXT: [[ARGP_CUR8:%.*]] = load i8*, i8** [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT9:%.*]] = getelementptr inbounds i8, i8* [[ARGP_CUR8]], i32 4 -// CHECK-NEXT: store i8* [[ARGP_NEXT9]], i8** [[VA]], align 4 -// CHECK-NEXT: [[TMP11:%.*]] = bitcast i8* [[ARGP_CUR8]] to %struct.large** -// CHECK-NEXT: [[TMP12:%.*]] = load %struct.large*, %struct.large** [[TMP11]], align 4 -// CHECK-NEXT: [[TMP13:%.*]] = bitcast %struct.large* [[LS]] to i8* -// CHECK-NEXT: [[TMP14:%.*]] = bitcast %struct.large* [[TMP12]] to i8* -// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[TMP13]], i8* align 4 [[TMP14]], i32 16, i1 false) -// CHECK-NEXT: [[VA10:%.*]] = bitcast i8** [[VA]] to i8* -// CHECK-NEXT: call void @llvm.va_end(i8* [[VA10]]) -int f_va_4(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - int v = __builtin_va_arg(va, int); - long double ld = __builtin_va_arg(va, long double); - struct tiny ts = __builtin_va_arg(va, struct tiny); - struct small ss = __builtin_va_arg(va, struct small); - struct large ls = __builtin_va_arg(va, struct large); - __builtin_va_end(va); - - int ret = (int)((long double)v + ld); - ret = ret + ts.a + ts.b + ts.c + ts.d; - ret = ret + ss.a + (int)ss.b; - ret = ret + ls.a + ls.b + ls.c + ls.d; - - return ret; -} Index: clang/test/CodeGen/RISCV/riscv32-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32d-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32d-abi.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" +// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ // RUN: | FileCheck %s #include @@ -9,7 +10,10 @@ // Doubles are passed in FPRs, so argument 'i' will be passed zero-extended // because it will be passed in a GPR. -// CHECK: define{{.*}} void @f_fpr_tracking(double noundef %a, double noundef %b, double noundef %c, double noundef %d, double noundef %e, double noundef %f, double noundef %g, double noundef %h, i8 noundef zeroext %i) +// CHECK-LABEL: define dso_local void @f_fpr_tracking +// CHECK-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// void f_fpr_tracking(double a, double b, double c, double d, double e, double f, double g, double h, uint8_t i) {} @@ -25,10 +29,16 @@ struct double_s { double f; }; -// CHECK: define{{.*}} void @f_double_s_arg(double %0) +// CHECK-LABEL: define dso_local void @f_double_s_arg +// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_s_arg(struct double_s a) {} -// CHECK: define{{.*}} double @f_ret_double_s() +// CHECK-LABEL: define dso_local double @f_ret_double_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_s f_ret_double_s(void) { return (struct double_s){1.0}; } @@ -39,18 +49,30 @@ struct zbf_double_s { int : 0; double f; }; struct zbf_double_zbf_s { int : 0; double f; int : 0; }; -// CHECK: define{{.*}} void @f_zbf_double_s_arg(double %0) +// CHECK-LABEL: define dso_local void @f_zbf_double_s_arg +// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_zbf_double_s_arg(struct zbf_double_s a) {} -// CHECK: define{{.*}} double @f_ret_zbf_double_s() +// CHECK-LABEL: define dso_local double @f_ret_zbf_double_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct zbf_double_s f_ret_zbf_double_s(void) { return (struct zbf_double_s){1.0}; } -// CHECK: define{{.*}} void @f_zbf_double_zbf_s_arg(double %0) +// CHECK-LABEL: define dso_local void @f_zbf_double_zbf_s_arg +// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {} -// CHECK: define{{.*}} double @f_ret_zbf_double_zbf_s() +// CHECK-LABEL: define dso_local double @f_ret_zbf_double_zbf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct zbf_double_zbf_s f_ret_zbf_double_zbf_s(void) { return (struct zbf_double_zbf_s){1.0}; } @@ -61,23 +83,38 @@ struct double_double_s { double f; double g; }; struct double_float_s { double f; float g; }; -// CHECK: define{{.*}} void @f_double_double_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_double_double_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_double_s_arg(struct double_double_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_double_double_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_double_double_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_double_s f_ret_double_double_s(void) { return (struct double_double_s){1.0, 2.0}; } -// CHECK: define{{.*}} void @f_double_float_s_arg(double %0, float %1) +// CHECK-LABEL: define dso_local void @f_double_float_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_float_s_arg(struct double_float_s a) {} -// CHECK: define{{.*}} { double, float } @f_ret_double_float_s() +// CHECK-LABEL: define dso_local { double, float } @f_ret_double_float_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_float_s f_ret_double_float_s(void) { return (struct double_float_s){1.0, 2.0}; } -// CHECK: define{{.*}} void @f_double_double_s_arg_insufficient_fprs(float noundef %a, double noundef %b, double noundef %c, double noundef %d, double noundef %e, double noundef %f, double noundef %g, %struct.double_double_s* noundef %h) +// CHECK-LABEL: define dso_local void @f_double_double_s_arg_insufficient_fprs +// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d, double e, double f, double g, struct double_double_s h) {} @@ -92,42 +129,72 @@ struct double_int64bf_s { double f; int64_t i : 32; }; struct double_int8_zbf_s { double f; int8_t i; int : 0; }; -// CHECK: define{{.*}} void @f_double_int8_s_arg(double %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_double_int8_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int8_s_arg(struct double_int8_s a) {} -// CHECK: define{{.*}} { double, i8 } @f_ret_double_int8_s() +// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_int8_s f_ret_double_int8_s(void) { return (struct double_int8_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_double_uint8_s_arg(double %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_double_uint8_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_uint8_s_arg(struct double_uint8_s a) {} -// CHECK: define{{.*}} { double, i8 } @f_ret_double_uint8_s() +// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_uint8_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_uint8_s f_ret_double_uint8_s(void) { return (struct double_uint8_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_double_int32_s_arg(double %0, i32 %1) +// CHECK-LABEL: define dso_local void @f_double_int32_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int32_s_arg(struct double_int32_s a) {} -// CHECK: define{{.*}} { double, i32 } @f_ret_double_int32_s() +// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_int32_s f_ret_double_int32_s(void) { return (struct double_int32_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_double_int64_s_arg(%struct.double_int64_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_double_int64_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int64_s_arg(struct double_int64_s a) {} -// CHECK: define{{.*}} void @f_ret_double_int64_s(%struct.double_int64_s* noalias sret(%struct.double_int64_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_double_int64_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct double_int64_s f_ret_double_int64_s(void) { return (struct double_int64_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_double_int64bf_s_arg(double %0, i32 %1) +// CHECK-LABEL: define dso_local void @f_double_int64bf_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int64bf_s_arg(struct double_int64bf_s a) {} -// CHECK: define{{.*}} { double, i32 } @f_ret_double_int64bf_s() +// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int64bf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_int64bf_s f_ret_double_int64bf_s(void) { return (struct double_int64bf_s){1.0, 2}; } @@ -135,39 +202,63 @@ // The zero-width bitfield means the struct can't be passed according to the // floating point calling convention. -// CHECK: define{{.*}} void @f_double_int8_zbf_s(double %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_double_int8_zbf_s +// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int8_zbf_s(struct double_int8_zbf_s a) {} -// CHECK: define{{.*}} { double, i8 } @f_ret_double_int8_zbf_s() +// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_zbf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_int8_zbf_s f_ret_double_int8_zbf_s(void) { return (struct double_int8_zbf_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_double_int8_s_arg_insufficient_gprs(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d, i32 noundef %e, i32 noundef %f, i32 noundef %g, i32 noundef %h, %struct.double_int8_s* noundef %i) +// CHECK-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs +// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, int f, int g, int h, struct double_int8_s i) {} -// CHECK: define{{.*}} void @f_struct_double_int8_insufficient_fprs(float noundef %a, double noundef %b, double noundef %c, double noundef %d, double noundef %e, double noundef %f, double noundef %g, double noundef %h, %struct.double_int8_s* noundef %i) +// CHECK-LABEL: define dso_local void @f_struct_double_int8_insufficient_fprs +// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d, double e, double f, double g, double h, struct double_int8_s i) {} // Complex floating-point values or structs containing a single complex // floating-point value should be passed as if it were an fp+fp struct. -// CHECK: define{{.*}} void @f_doublecomplex(double noundef %a.coerce0, double noundef %a.coerce1) +// CHECK-LABEL: define dso_local void @f_doublecomplex +// CHECK-SAME: (double noundef [[A_COERCE0:%.*]], double noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublecomplex(double __complex__ a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublecomplex() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// double __complex__ f_ret_doublecomplex(void) { return 1.0; } struct doublecomplex_s { double __complex__ c; }; -// CHECK: define{{.*}} void @f_doublecomplex_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublecomplex_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublecomplex_s_arg(struct doublecomplex_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublecomplex_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublecomplex_s f_ret_doublecomplex_s(void) { return (struct doublecomplex_s){1.0}; } @@ -177,60 +268,96 @@ struct doublearr1_s { double a[1]; }; -// CHECK: define{{.*}} void @f_doublearr1_s_arg(double %0) +// CHECK-LABEL: define dso_local void @f_doublearr1_s_arg +// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr1_s_arg(struct doublearr1_s a) {} -// CHECK: define{{.*}} double @f_ret_doublearr1_s() +// CHECK-LABEL: define dso_local double @f_ret_doublearr1_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr1_s f_ret_doublearr1_s(void) { return (struct doublearr1_s){{1.0}}; } struct doublearr2_s { double a[2]; }; -// CHECK: define{{.*}} void @f_doublearr2_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublearr2_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr2_s_arg(struct doublearr2_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublearr2_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr2_s f_ret_doublearr2_s(void) { return (struct doublearr2_s){{1.0, 2.0}}; } struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_doublearr2_tricky1_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublearr2_tricky1_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky1_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s(void) { return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}}; } struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_doublearr2_tricky2_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublearr2_tricky2_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky2_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s(void) { return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; } struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_doublearr2_tricky3_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublearr2_tricky3_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky3_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s(void) { return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; } struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_doublearr2_tricky4_s_arg(double %0, double %1) +// CHECK-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {} -// CHECK: define{{.*}} { double, double } @f_ret_doublearr2_tricky4_s() +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky4_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s(void) { return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; } @@ -240,30 +367,48 @@ struct int_double_int_s { int a; double b; int c; }; -// CHECK: define{{.*}} void @f_int_double_int_s_arg(%struct.int_double_int_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_int_double_int_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int_double_int_s_arg(struct int_double_int_s a) {} -// CHECK: define{{.*}} void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret(%struct.int_double_int_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_int_double_int_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_INT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct int_double_int_s f_ret_int_double_int_s(void) { return (struct int_double_int_s){1, 2.0, 3}; } struct int64_double_s { int64_t a; double b; }; -// CHECK: define{{.*}} void @f_int64_double_s_arg(%struct.int64_double_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_int64_double_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int64_double_s_arg(struct int64_double_s a) {} -// CHECK: define{{.*}} void @f_ret_int64_double_s(%struct.int64_double_s* noalias sret(%struct.int64_double_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_int64_double_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_INT64_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct int64_double_s f_ret_int64_double_s(void) { return (struct int64_double_s){1, 2.0}; } struct char_char_double_s { char a; char b; double c; }; -// CHECK-LABEL: define{{.*}} void @f_char_char_double_s_arg(%struct.char_char_double_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_char_char_double_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_char_char_double_s_arg(struct char_char_double_s a) {} -// CHECK: define{{.*}} void @f_ret_char_char_double_s(%struct.char_char_double_s* noalias sret(%struct.char_char_double_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_char_char_double_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_CHAR_CHAR_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct char_char_double_s f_ret_char_char_double_s(void) { return (struct char_char_double_s){1, 2, 3.0}; } @@ -273,10 +418,16 @@ union double_u { double a; }; -// CHECK: define{{.*}} void @f_double_u_arg(i64 %a.coerce) +// CHECK-LABEL: define dso_local void @f_double_u_arg +// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_u_arg(union double_u a) {} -// CHECK: define{{.*}} i64 @f_ret_double_u() +// CHECK-LABEL: define dso_local i64 @f_ret_double_u +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// union double_u f_ret_double_u(void) { return (union double_u){1.0}; } @@ -287,19 +438,28 @@ // returned in registers). This includes complex doubles, which are treated as // double+double structs by the ABI. -// CHECK: define{{.*}} { double, i32 } @f_ret_double_int32_s_double_int32_s_just_sufficient_gprs(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d, i32 noundef %e, i32 noundef %f, i32 noundef %g, double %0, i32 %1) +// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s_double_int32_s_just_sufficient_gprs +// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct double_int32_s f_ret_double_int32_s_double_int32_s_just_sufficient_gprs( int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { return (struct double_int32_s){1.0, 2}; } -// CHECK: define{{.*}} { double, double } @f_ret_double_double_s_double_int32_s_just_sufficient_gprs(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d, i32 noundef %e, i32 noundef %f, i32 noundef %g, double %0, i32 %1) +// CHECK-LABEL: define dso_local { double, double } @f_ret_double_double_s_double_int32_s_just_sufficient_gprs +// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct double_double_s f_ret_double_double_s_double_int32_s_just_sufficient_gprs( int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { return (struct double_double_s){1.0, 2.0}; } -// CHECK: define{{.*}} { double, double } @f_ret_doublecomplex_double_int32_s_just_sufficient_gprs(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d, i32 noundef %e, i32 noundef %f, i32 noundef %g, double %0, i32 %1) +// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex_double_int32_s_just_sufficient_gprs +// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// double __complex__ f_ret_doublecomplex_double_int32_s_just_sufficient_gprs( int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { return 1.0; Index: clang/test/CodeGen/RISCV/riscv32-ilp32f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32f-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32f-abi.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ // RUN: | FileCheck %s #include @@ -6,27 +7,42 @@ // Doubles are still passed in GPRs, so the 'e' argument will be anyext as // GPRs are exhausted. -// CHECK: define{{.*}} void @f_fpr_tracking(double noundef %a, double noundef %b, double noundef %c, double noundef %d, i8 noundef %e) +// CHECK-LABEL: define dso_local void @f_fpr_tracking +// CHECK-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], i8 noundef [[E:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// void f_fpr_tracking(double a, double b, double c, double d, int8_t e) {} // Lowering for doubles is unnmodified, as 64 > FLEN. struct double_s { double d; }; -// CHECK: define{{.*}} void @f_double_s_arg(i64 %a.coerce) +// CHECK-LABEL: define dso_local void @f_double_s_arg +// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_s_arg(struct double_s a) {} -// CHECK: define{{.*}} i64 @f_ret_double_s() +// CHECK-LABEL: define dso_local i64 @f_ret_double_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_s f_ret_double_s(void) { return (struct double_s){1.0}; } struct double_double_s { double d; double e; }; -// CHECK: define{{.*}} void @f_double_double_s_arg(%struct.double_double_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_double_double_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_double_s_arg(struct double_double_s a) {} -// CHECK: define{{.*}} void @f_ret_double_double_s(%struct.double_double_s* noalias sret(%struct.double_double_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_double_double_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_DOUBLE_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct double_double_s f_ret_double_double_s(void) { return (struct double_double_s){1.0, 2.0}; } @@ -35,10 +51,16 @@ struct int_double_s { int a; double b; }; -// CHECK: define{{.*}} void @f_int_double_s_arg(%struct.int_double_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_int_double_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int_double_s_arg(struct int_double_s a) {} -// CHECK: define{{.*}} void @f_ret_int_double_s(%struct.int_double_s* noalias sret(%struct.int_double_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_int_double_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct int_double_s f_ret_int_double_s(void) { return (struct int_double_s){1, 2.0}; } Index: clang/test/CodeGen/RISCV/riscv32-ilp32f-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32f-ilp32d-abi.c +++ clang/test/CodeGen/RISCV/riscv32-ilp32f-ilp32d-abi.c @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ // RUN: | FileCheck %s -// RUN: %clang_cc1 -no-opaque-pointers -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ +// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ // RUN: | FileCheck %s #include @@ -11,7 +12,10 @@ // Floats are passed in FPRs, so argument 'i' will be passed zero-extended // because it will be passed in a GPR. -// CHECK: define{{.*}} void @f_fpr_tracking(float noundef %a, float noundef %b, float noundef %c, float noundef %d, float noundef %e, float noundef %f, float noundef %g, float noundef %h, i8 noundef zeroext %i) +// CHECK-LABEL: define dso_local void @f_fpr_tracking +// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// void f_fpr_tracking(float a, float b, float c, float d, float e, float f, float g, float h, uint8_t i) {} @@ -27,10 +31,16 @@ struct float_s { float f; }; -// CHECK: define{{.*}} void @f_float_s_arg(float %0) +// CHECK-LABEL: define dso_local void @f_float_s_arg +// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_s_arg(struct float_s a) {} -// CHECK: define{{.*}} float @f_ret_float_s() +// CHECK-LABEL: define dso_local float @f_ret_float_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_s f_ret_float_s(void) { return (struct float_s){1.0}; } @@ -41,18 +51,30 @@ struct zbf_float_s { int : 0; float f; }; struct zbf_float_zbf_s { int : 0; float f; int : 0; }; -// CHECK: define{{.*}} void @f_zbf_float_s_arg(float %0) +// CHECK-LABEL: define dso_local void @f_zbf_float_s_arg +// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_zbf_float_s_arg(struct zbf_float_s a) {} -// CHECK: define{{.*}} float @f_ret_zbf_float_s() +// CHECK-LABEL: define dso_local float @f_ret_zbf_float_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct zbf_float_s f_ret_zbf_float_s(void) { return (struct zbf_float_s){1.0}; } -// CHECK: define{{.*}} void @f_zbf_float_zbf_s_arg(float %0) +// CHECK-LABEL: define dso_local void @f_zbf_float_zbf_s_arg +// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {} -// CHECK: define{{.*}} float @f_ret_zbf_float_zbf_s() +// CHECK-LABEL: define dso_local float @f_ret_zbf_float_zbf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct zbf_float_zbf_s f_ret_zbf_float_zbf_s(void) { return (struct zbf_float_zbf_s){1.0}; } @@ -62,15 +84,24 @@ struct float_float_s { float f; float g; }; -// CHECK: define{{.*}} void @f_float_float_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_float_float_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_float_s_arg(struct float_float_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_float_float_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_float_float_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_float_s f_ret_float_float_s(void) { return (struct float_float_s){1.0, 2.0}; } -// CHECK: define{{.*}} void @f_float_float_s_arg_insufficient_fprs(float noundef %a, float noundef %b, float noundef %c, float noundef %d, float noundef %e, float noundef %f, float noundef %g, [2 x i32] %h.coerce) +// CHECK-LABEL: define dso_local void @f_float_float_s_arg_insufficient_fprs +// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], [2 x i32] [[H_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, float e, float f, float g, struct float_float_s h) {} @@ -85,42 +116,72 @@ struct float_int64bf_s { float f; int64_t i : 32; }; struct float_int8_zbf_s { float f; int8_t i; int : 0; }; -// CHECK: define{{.*}} void @f_float_int8_s_arg(float %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_float_int8_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int8_s_arg(struct float_int8_s a) {} -// CHECK: define{{.*}} { float, i8 } @f_ret_float_int8_s() +// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_int8_s f_ret_float_int8_s(void) { return (struct float_int8_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_float_uint8_s_arg(float %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_float_uint8_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_uint8_s_arg(struct float_uint8_s a) {} -// CHECK: define{{.*}} { float, i8 } @f_ret_float_uint8_s() +// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_uint8_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_uint8_s f_ret_float_uint8_s(void) { return (struct float_uint8_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_float_int32_s_arg(float %0, i32 %1) +// CHECK-LABEL: define dso_local void @f_float_int32_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int32_s_arg(struct float_int32_s a) {} -// CHECK: define{{.*}} { float, i32 } @f_ret_float_int32_s() +// CHECK-LABEL: define dso_local { float, i32 } @f_ret_float_int32_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_int32_s f_ret_float_int32_s(void) { return (struct float_int32_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_float_int64_s_arg(%struct.float_int64_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_float_int64_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int64_s_arg(struct float_int64_s a) {} -// CHECK: define{{.*}} void @f_ret_float_int64_s(%struct.float_int64_s* noalias sret(%struct.float_int64_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_float_int64_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_FLOAT_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct float_int64_s f_ret_float_int64_s(void) { return (struct float_int64_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_float_int64bf_s_arg(float %0, i32 %1) +// CHECK-LABEL: define dso_local void @f_float_int64bf_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int64bf_s_arg(struct float_int64bf_s a) {} -// CHECK: define{{.*}} { float, i32 } @f_ret_float_int64bf_s() +// CHECK-LABEL: define dso_local { float, i32 } @f_ret_float_int64bf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_int64bf_s f_ret_float_int64bf_s(void) { return (struct float_int64bf_s){1.0, 2}; } @@ -128,39 +189,63 @@ // The zero-width bitfield means the struct can't be passed according to the // floating point calling convention. -// CHECK: define{{.*}} void @f_float_int8_zbf_s(float %0, i8 %1) +// CHECK-LABEL: define dso_local void @f_float_int8_zbf_s +// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int8_zbf_s(struct float_int8_zbf_s a) {} -// CHECK: define{{.*}} { float, i8 } @f_ret_float_int8_zbf_s() +// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_zbf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_int8_zbf_s f_ret_float_int8_zbf_s(void) { return (struct float_int8_zbf_s){1.0, 2}; } -// CHECK: define{{.*}} void @f_float_int8_s_arg_insufficient_gprs(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d, i32 noundef %e, i32 noundef %f, i32 noundef %g, i32 noundef %h, [2 x i32] %i.coerce) +// CHECK-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs +// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, int f, int g, int h, struct float_int8_s i) {} -// CHECK: define{{.*}} void @f_struct_float_int8_insufficient_fprs(float noundef %a, float noundef %b, float noundef %c, float noundef %d, float noundef %e, float noundef %f, float noundef %g, float noundef %h, [2 x i32] %i.coerce) +// CHECK-LABEL: define dso_local void @f_struct_float_int8_insufficient_fprs +// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d, float e, float f, float g, float h, struct float_int8_s i) {} // Complex floating-point values or structs containing a single complex // floating-point value should be passed as if it were an fp+fp struct. -// CHECK: define{{.*}} void @f_floatcomplex(float noundef %a.coerce0, float noundef %a.coerce1) +// CHECK-LABEL: define dso_local void @f_floatcomplex +// CHECK-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex(float __complex__ a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatcomplex() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// float __complex__ f_ret_floatcomplex(void) { return 1.0; } struct floatcomplex_s { float __complex__ c; }; -// CHECK: define{{.*}} void @f_floatcomplex_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_s_arg(struct floatcomplex_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatcomplex_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatcomplex_s f_ret_floatcomplex_s(void) { return (struct floatcomplex_s){1.0}; } @@ -170,60 +255,96 @@ struct floatarr1_s { float a[1]; }; -// CHECK: define{{.*}} void @f_floatarr1_s_arg(float %0) +// CHECK-LABEL: define dso_local void @f_floatarr1_s_arg +// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr1_s_arg(struct floatarr1_s a) {} -// CHECK: define{{.*}} float @f_ret_floatarr1_s() +// CHECK-LABEL: define dso_local float @f_ret_floatarr1_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr1_s f_ret_floatarr1_s(void) { return (struct floatarr1_s){{1.0}}; } struct floatarr2_s { float a[2]; }; -// CHECK: define{{.*}} void @f_floatarr2_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatarr2_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr2_s_arg(struct floatarr2_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatarr2_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr2_s f_ret_floatarr2_s(void) { return (struct floatarr2_s){{1.0, 2.0}}; } struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_floatarr2_tricky1_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatarr2_tricky1_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky1_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s(void) { return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}}; } struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_floatarr2_tricky2_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatarr2_tricky2_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky2_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s(void) { return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; } struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_floatarr2_tricky3_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatarr2_tricky3_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky3_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s(void) { return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; } struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; }; -// CHECK: define{{.*}} void @f_floatarr2_tricky4_s_arg(float %0, float %1) +// CHECK-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {} -// CHECK: define{{.*}} { float, float } @f_ret_floatarr2_tricky4_s() +// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky4_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s(void) { return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; } @@ -233,30 +354,48 @@ struct int_float_int_s { int a; float b; int c; }; -// CHECK: define{{.*}} void @f_int_float_int_s_arg(%struct.int_float_int_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_int_float_int_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int_float_int_s_arg(struct int_float_int_s a) {} -// CHECK: define{{.*}} void @f_ret_int_float_int_s(%struct.int_float_int_s* noalias sret(%struct.int_float_int_s) align 4 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_int_float_int_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_FLOAT_INT_S:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct int_float_int_s f_ret_int_float_int_s(void) { return (struct int_float_int_s){1, 2.0, 3}; } struct int64_float_s { int64_t a; float b; }; -// CHECK: define{{.*}} void @f_int64_float_s_arg(%struct.int64_float_s* noundef %a) +// CHECK-LABEL: define dso_local void @f_int64_float_s_arg +// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int64_float_s_arg(struct int64_float_s a) {} -// CHECK: define{{.*}} void @f_ret_int64_float_s(%struct.int64_float_s* noalias sret(%struct.int64_float_s) align 8 %agg.result) +// CHECK-LABEL: define dso_local void @f_ret_int64_float_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_INT64_FLOAT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct int64_float_s f_ret_int64_float_s(void) { return (struct int64_float_s){1, 2.0}; } struct char_char_float_s { char a; char b; float c; }; -// CHECK-LABEL: define{{.*}} void @f_char_char_float_s_arg([2 x i32] %a.coerce) +// CHECK-LABEL: define dso_local void @f_char_char_float_s_arg +// CHECK-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_char_char_float_s_arg(struct char_char_float_s a) {} -// CHECK: define{{.*}} [2 x i32] @f_ret_char_char_float_s() +// CHECK-LABEL: define dso_local [2 x i32] @f_ret_char_char_float_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct char_char_float_s f_ret_char_char_float_s(void) { return (struct char_char_float_s){1, 2, 3.0}; } @@ -266,10 +405,16 @@ union float_u { float a; }; -// CHECK: define{{.*}} void @f_float_u_arg(i32 %a.coerce) +// CHECK-LABEL: define dso_local void @f_float_u_arg +// CHECK-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_u_arg(union float_u a) {} -// CHECK: define{{.*}} i32 @f_ret_float_u() +// CHECK-LABEL: define dso_local i32 @f_ret_float_u +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// union float_u f_ret_float_u(void) { return (union float_u){1.0}; } Index: clang/test/CodeGen/RISCV/riscv32-vararg.c =================================================================== --- /dev/null +++ clang/test/CodeGen/RISCV/riscv32-vararg.c @@ -0,0 +1,294 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature +// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ +// RUN: | FileCheck %s +// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ +// RUN: | FileCheck %s + +#include +#include + +struct tiny { + uint8_t a, b, c, d; +}; +struct small { + int32_t a, *b; +}; +struct small_aligned { + int64_t a; +}; +struct large { + int32_t a, b, c, d; +}; + +// Ensure that ABI lowering happens as expected for vararg calls. For RV32 +// with the base integer calling convention there will be no observable +// differences in the lowered IR for a call with varargs vs without. + +int f_va_callee(int, ...); + +// CHECK-LABEL: define dso_local void @f_va_caller +// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_TINY:%.*]], align 1 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL1:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL4:%.*]] = alloca [[STRUCT_SMALL_ALIGNED:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL6:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 4 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[STRUCT_LARGE]], align 4 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i8 6, ptr [[A]], align 1 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i8 7, ptr [[B]], align 1 +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i8 8, ptr [[C]], align 1 +// CHECK-NEXT: [[D:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: store i8 9, ptr [[D]], align 1 +// CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL1]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[A2]], align 4 +// CHECK-NEXT: [[B3:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL1]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[B3]], align 4 +// CHECK-NEXT: [[A5:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL4]], i32 0, i32 0 +// CHECK-NEXT: store i64 11, ptr [[A5]], align 8 +// CHECK-NEXT: [[A7:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 0 +// CHECK-NEXT: store i32 12, ptr [[A7]], align 4 +// CHECK-NEXT: [[B8:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 1 +// CHECK-NEXT: store i32 13, ptr [[B8]], align 4 +// CHECK-NEXT: [[C9:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 2 +// CHECK-NEXT: store i32 14, ptr [[C9]], align 4 +// CHECK-NEXT: [[D10:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 3 +// CHECK-NEXT: store i32 15, ptr [[D10]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DOTCOMPOUNDLITERAL]], align 1 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i32], ptr [[DOTCOMPOUNDLITERAL1]], align 4 +// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[COERCE_DIVE]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[BYVAL_TEMP]], ptr align 4 [[DOTCOMPOUNDLITERAL6]], i32 16, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call i32 (i32, ...) @f_va_callee(i32 noundef 1, i32 noundef 2, i64 noundef 3, double noundef 4.000000e+00, double noundef 5.000000e+00, i32 [[TMP0]], [2 x i32] [[TMP1]], i64 [[TMP2]], ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void f_va_caller(void) { + f_va_callee(1, 2, 3LL, 4.0f, 5.0, (struct tiny){6, 7, 8, 9}, + (struct small){10, NULL}, (struct small_aligned){11}, + (struct large){12, 13, 14, 15}); +} + +// CHECK-LABEL: define dso_local i32 @f_va_1 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4 +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[V]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int f_va_1(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + int v = __builtin_va_arg(va, int); + __builtin_va_end(va); + + return v; +} + +// An "aligned" register pair (where the first register is even-numbered) is +// used to pass varargs with 2x xlen alignment and 2x xlen size. Ensure the +// correct offsets are used. + +// CHECK-LABEL: define dso_local double @f_va_2 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[V:%.*]] = alloca double, align 8 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i32 +// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], 7 +// CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -8 +// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i32 [[TMP2]] to ptr +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 +// CHECK-NEXT: store double [[TMP3]], ptr [[V]], align 8 +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP4:%.*]] = load double, ptr [[V]], align 8 +// CHECK-NEXT: ret double [[TMP4]] +// +double f_va_2(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + double v = __builtin_va_arg(va, double); + __builtin_va_end(va); + + return v; +} + +// Two "aligned" register pairs. + +// CHECK-LABEL: define dso_local double @f_va_3 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[V:%.*]] = alloca double, align 8 +// CHECK-NEXT: [[W:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca double, align 8 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i32 +// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], 7 +// CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -8 +// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i32 [[TMP2]] to ptr +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 +// CHECK-NEXT: store double [[TMP3]], ptr [[V]], align 8 +// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[W]], align 4 +// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[ARGP_CUR3]] to i32 +// CHECK-NEXT: [[TMP6:%.*]] = add i32 [[TMP5]], 7 +// CHECK-NEXT: [[TMP7:%.*]] = and i32 [[TMP6]], -8 +// CHECK-NEXT: [[ARGP_CUR3_ALIGNED:%.*]] = inttoptr i32 [[TMP7]] to ptr +// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3_ALIGNED]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP8:%.*]] = load double, ptr [[ARGP_CUR3_ALIGNED]], align 8 +// CHECK-NEXT: store double [[TMP8]], ptr [[X]], align 8 +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP9:%.*]] = load double, ptr [[V]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load double, ptr [[X]], align 8 +// CHECK-NEXT: [[ADD:%.*]] = fadd double [[TMP9]], [[TMP10]] +// CHECK-NEXT: ret double [[ADD]] +// +double f_va_3(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + double v = __builtin_va_arg(va, double); + int w = __builtin_va_arg(va, int); + double x = __builtin_va_arg(va, double); + __builtin_va_end(va); + + return v + x; +} + +// CHECK-LABEL: define dso_local i32 @f_va_4 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[LD:%.*]] = alloca fp128, align 16 +// CHECK-NEXT: [[TS:%.*]] = alloca [[STRUCT_TINY:%.*]], align 1 +// CHECK-NEXT: [[SS:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 4 +// CHECK-NEXT: [[LS:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 4 +// CHECK-NEXT: [[RET:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4 +// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR1]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load fp128, ptr [[TMP1]], align 16 +// CHECK-NEXT: store fp128 [[TMP2]], ptr [[LD]], align 16 +// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[VA]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TS]], ptr align 4 [[ARGP_CUR3]], i32 4, i1 false) +// CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[VA]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[SS]], ptr align 4 [[ARGP_CUR5]], i32 8, i1 false) +// CHECK-NEXT: [[ARGP_CUR7:%.*]] = load ptr, ptr [[VA]], align 4 +// CHECK-NEXT: [[ARGP_NEXT8:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR7]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT8]], ptr [[VA]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[ARGP_CUR7]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[LS]], ptr align 4 [[TMP3]], i32 16, i1 false) +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[V]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sitofp i32 [[TMP4]] to fp128 +// CHECK-NEXT: [[TMP5:%.*]] = load fp128, ptr [[LD]], align 16 +// CHECK-NEXT: [[ADD:%.*]] = fadd fp128 [[CONV]], [[TMP5]] +// CHECK-NEXT: [[CONV9:%.*]] = fptosi fp128 [[ADD]] to i32 +// CHECK-NEXT: store i32 [[CONV9]], ptr [[RET]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[RET]], align 4 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[A]], align 1 +// CHECK-NEXT: [[CONV10:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: [[ADD11:%.*]] = add nsw i32 [[TMP6]], [[CONV10]] +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[B]], align 1 +// CHECK-NEXT: [[CONV12:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: [[ADD13:%.*]] = add nsw i32 [[ADD11]], [[CONV12]] +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 2 +// CHECK-NEXT: [[TMP9:%.*]] = load i8, ptr [[C]], align 1 +// CHECK-NEXT: [[CONV14:%.*]] = zext i8 [[TMP9]] to i32 +// CHECK-NEXT: [[ADD15:%.*]] = add nsw i32 [[ADD13]], [[CONV14]] +// CHECK-NEXT: [[D:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 3 +// CHECK-NEXT: [[TMP10:%.*]] = load i8, ptr [[D]], align 1 +// CHECK-NEXT: [[CONV16:%.*]] = zext i8 [[TMP10]] to i32 +// CHECK-NEXT: [[ADD17:%.*]] = add nsw i32 [[ADD15]], [[CONV16]] +// CHECK-NEXT: store i32 [[ADD17]], ptr [[RET]], align 4 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[RET]], align 4 +// CHECK-NEXT: [[A18:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[SS]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[A18]], align 4 +// CHECK-NEXT: [[ADD19:%.*]] = add nsw i32 [[TMP11]], [[TMP12]] +// CHECK-NEXT: [[B20:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[SS]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[B20]], align 4 +// CHECK-NEXT: [[TMP14:%.*]] = ptrtoint ptr [[TMP13]] to i32 +// CHECK-NEXT: [[ADD21:%.*]] = add nsw i32 [[ADD19]], [[TMP14]] +// CHECK-NEXT: store i32 [[ADD21]], ptr [[RET]], align 4 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[RET]], align 4 +// CHECK-NEXT: [[A22:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[A22]], align 4 +// CHECK-NEXT: [[ADD23:%.*]] = add nsw i32 [[TMP15]], [[TMP16]] +// CHECK-NEXT: [[B24:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[B24]], align 4 +// CHECK-NEXT: [[ADD25:%.*]] = add nsw i32 [[ADD23]], [[TMP17]] +// CHECK-NEXT: [[C26:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load i32, ptr [[C26]], align 4 +// CHECK-NEXT: [[ADD27:%.*]] = add nsw i32 [[ADD25]], [[TMP18]] +// CHECK-NEXT: [[D28:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 3 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[D28]], align 4 +// CHECK-NEXT: [[ADD29:%.*]] = add nsw i32 [[ADD27]], [[TMP19]] +// CHECK-NEXT: store i32 [[ADD29]], ptr [[RET]], align 4 +// CHECK-NEXT: [[TMP20:%.*]] = load i32, ptr [[RET]], align 4 +// CHECK-NEXT: ret i32 [[TMP20]] +// +int f_va_4(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + int v = __builtin_va_arg(va, int); + long double ld = __builtin_va_arg(va, long double); + struct tiny ts = __builtin_va_arg(va, struct tiny); + struct small ss = __builtin_va_arg(va, struct small); + struct large ls = __builtin_va_arg(va, struct large); + __builtin_va_end(va); + + int ret = (int)((long double)v + ld); + ret = ret + ts.a + ts.b + ts.c + ts.d; + ret = ret + ss.a + (int)ss.b; + ret = ret + ls.a + ls.b + ls.c + ls.d; + + return ret; +}