Index: clang/test/CodeGen/RISCV/riscv-abi.cpp =================================================================== --- clang/test/CodeGen/RISCV/riscv-abi.cpp +++ clang/test/CodeGen/RISCV/riscv-abi.cpp @@ -1,16 +1,17 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - \ -// RUN: | FileCheck -check-prefixes=ILP32,ILP32-ILP32F,ILP32-ILP32F-ILP32D %s +// 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 - \ -// RUN: | FileCheck -check-prefixes=ILP32F,ILP32-ILP32F,ILP32F-ILP32D,ILP32-ILP32F-ILP32D %s +// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32-ILP32F,ILP32F %s // RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \ -// RUN: | FileCheck -check-prefixes=ILP32D,ILP32F-ILP32D,ILP32-ILP32F-ILP32D %s +// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32D %s // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - \ -// RUN: | FileCheck -check-prefixes=LP64,LP64-LP64F,LP64-LP64F-LP64D %s +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64-LP64F,LP64 %s // RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ -// RUN: | FileCheck -check-prefixes=LP64F,LP64-LP64F,LP64F-LP64D,LP64-LP64F-LP64D %s +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64F-LP64D,LP64-LP64F,LP64F %s // RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-feature +d -target-abi lp64d -emit-llvm %s -o - \ -// RUN: | FileCheck -check-prefixes=LP64D,LP64F-LP64D,LP64-LP64F-LP64D %s +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64F-LP64D,LP64D %s #include @@ -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,28 @@ 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,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s // This file contains test cases that will have different output for ilp32 vs @@ -25,13 +26,19 @@ // Scalars passed on the stack should have signext/zeroext attributes, just as // if they were passed in registers. -// 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 signext %g, i8 noundef zeroext %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 signext [[G:%.*]], i8 noundef zeroext [[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; } -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(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 signext %f, i8 noundef zeroext %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 signext [[F:%.*]], i8 noundef zeroext [[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}; @@ -40,10 +47,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, ptr 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,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // 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 @@ -27,13 +28,19 @@ // Scalars passed on the stack should have signext/zeroext attributes, just as // if they were passed in registers. -// 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 signext %g, i8 noundef zeroext %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 signext [[G:%.*]], i8 noundef zeroext [[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; } -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(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 signext %f, i8 noundef zeroext %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 signext [[F:%.*]], i8 noundef zeroext [[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}; @@ -42,10 +49,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, ptr 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:" --version 2 // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -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(ptr noundef %x) +// CHECK-LABEL: define dso_local void @f_agg_large +// CHECK-SAME: (ptr 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(ptr 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: (ptr 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(ptr noundef %0) +// CHECK-LABEL: define dso_local void @f_vec_large_v16i8 +// CHECK-SAME: (ptr 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(ptr noalias sret(<16 x i8>) align 16 %agg.result) +// CHECK-LABEL: define dso_local void @f_vec_large_v16i8_ret +// CHECK-SAME: (ptr 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 have signext/zeroext attributes, just as // if they were passed in registers -// CHECK-LABEL: define{{.*}} i32 @f_scalar_stack_1(i32 %a.coerce, [2 x i32] %b.coerce, i64 %c.coerce, ptr noundef %d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef zeroext %g, i8 noundef signext %h) +// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 [[A_COERCE:%.*]], [2 x i32] [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[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(ptr 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 signext %f, i8 noundef zeroext %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:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[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 signext %f, i8 noundef zeroext %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 signext [[F:%.*]], i8 noundef zeroext [[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,186 +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, ptr 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) {} - -// 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 {{%.*}}, ptr 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(ptr noundef %fmt, ...) {{.*}} { -// CHECK: [[FMT_ADDR:%.*]] = alloca ptr, align 4 -// CHECK: [[VA:%.*]] = alloca ptr, align 4 -// CHECK: [[V:%.*]] = alloca i32, align 4 -// CHECK: store ptr %fmt, ptr [[FMT_ADDR]], align 4 -// CHECK: call void @llvm.va_start(ptr [[VA]]) -// CHECK: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 -// CHECK: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4 -// CHECK: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 -// CHECK: store i32 [[TMP1]], ptr [[V]], align 4 -// CHECK: call void @llvm.va_end(ptr [[VA]]) -// CHECK: [[TMP2:%.*]] = load i32, ptr [[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 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: [[TMP4:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 -// CHECK-NEXT: store double [[TMP4]], ptr [[V]], align 8 -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -// CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[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 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: [[TMP4:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 -// CHECK-NEXT: store double [[TMP4]], ptr [[V]], align 8 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2]], i32 4 -// CHECK-NEXT: store ptr [[ARGP_NEXT3]], ptr [[VA]], align 4 -// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[ARGP_CUR2]], align 4 -// CHECK-NEXT: store i32 [[TMP6]], ptr [[W]], align 4 -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[TMP7:%.*]] = ptrtoint ptr [[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 ptr -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR4_ALIGNED]], i32 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT5]], ptr [[VA]], align 4 -// CHECK-NEXT: [[TMP11:%.*]] = load double, ptr [[ARGP_CUR4_ALIGNED]], align 8 -// CHECK-NEXT: store double [[TMP11]], ptr [[X]], align 8 -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -// CHECK-NEXT: [[TMP12:%.*]] = load double, ptr [[V]], align 8 -// CHECK-NEXT: [[TMP13:%.*]] = load double, ptr [[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(ptr noundef %fmt, ...) {{.*}} { -// CHECK: [[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: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 -// CHECK-NEXT: store i32 [[TMP1]], ptr [[V]], align 4 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2]], i32 4 -// CHECK-NEXT: store ptr [[ARGP_NEXT3]], ptr [[VA]], align 4 -// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[ARGP_CUR2]], align 4 -// CHECK-NEXT: [[TMP4:%.*]] = load fp128, ptr [[TMP3]], align 16 -// CHECK-NEXT: store fp128 [[TMP4]], ptr [[LD]], align 16 -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR4]], i32 4 -// CHECK-NEXT: store ptr [[ARGP_NEXT5]], ptr [[VA]], align 4 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TS]], ptr align 4 [[ARGP_CUR4]], i32 4, i1 false) -// CHECK-NEXT: [[ARGP_CUR6:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT7:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR6]], i32 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT7]], ptr [[VA]], align 4 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[SS]], ptr align 4 [[ARGP_CUR6]], i32 8, i1 false) -// CHECK-NEXT: [[ARGP_CUR8:%.*]] = load ptr, ptr [[VA]], align 4 -// CHECK-NEXT: [[ARGP_NEXT9:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR8]], i32 4 -// CHECK-NEXT: store ptr [[ARGP_NEXT9]], ptr [[VA]], align 4 -// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[ARGP_CUR8]], align 4 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[LS]], ptr align 4 [[TMP12]], i32 16, i1 false) -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -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,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ // RUN: | FileCheck %s @@ -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, ptr 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(ptr 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(ptr 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, ptr 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, ptr 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(ptr 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(ptr 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(ptr 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(ptr 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(ptr 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(ptr 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,29 +1,45 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ // RUN: | FileCheck %s #include -// CHECK: define{{.*}} void @f_fpr_tracking(double noundef %a, double noundef %b, double noundef %c, double noundef %d, i8 noundef signext %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 signext [[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(ptr 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(ptr 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}; } @@ -32,10 +48,16 @@ struct int_double_s { int a; double b; }; -// CHECK: define{{.*}} void @f_int_double_s_arg(ptr 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(ptr 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,3 +1,4 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 // 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 - \ @@ -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(ptr 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(ptr 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(ptr 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(ptr 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(ptr 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(ptr 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 --version 2 +// 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; +} Index: clang/test/CodeGen/RISCV/riscv64-lp64-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-abi.c +++ clang/test/CodeGen/RISCV/riscv64-lp64-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:" --version 2 // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s // This file contains test cases that will have different output for lp64 vs @@ -15,13 +16,19 @@ // Scalars passed on the stack should have signext/zeroext attributes, just as // if they were passed in registers. -// CHECK-LABEL: define{{.*}} signext i32 @f_scalar_stack_1(i32 noundef signext %a, i128 noundef %b, float noundef %c, fp128 noundef %d, ptr noundef %0, i8 noundef zeroext %f, i8 noundef signext %g, i8 noundef zeroext %h) +// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], float noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// int f_scalar_stack_1(int32_t a, __int128_t b, float c, long double d, v32i8 e, uint8_t f, int8_t g, uint8_t h) { return g + h; } -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(ptr noalias sret(%struct.large) align 8 %agg.result, double noundef %a, i128 noundef %b, fp128 noundef %c, ptr noundef %0, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef zeroext %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_2 +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], double noundef [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; @@ -30,20 +37,32 @@ // Complex floating-point values or structs containing a single complex // floating-point value should be passed in a GPR. -// CHECK: define{{.*}} void @f_floatcomplex(i64 noundef %a.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex +// CHECK-SAME: (i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex(float __complex__ a) {} -// CHECK: define{{.*}} i64 @f_ret_floatcomplex() +// CHECK-LABEL: define dso_local i64 @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(i64 %a.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg +// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_s_arg(struct floatcomplex_s a) {} -// CHECK: define{{.*}} i64 @f_ret_floatcomplex_s() +// CHECK-LABEL: define dso_local i64 @f_ret_floatcomplex_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct floatcomplex_s f_ret_floatcomplex_s(void) { return (struct floatcomplex_s){1.0}; } Index: clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-abi.c +++ clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-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:" --version 2 // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ // RUN: | FileCheck %s @@ -17,13 +18,19 @@ // Scalars passed on the stack should have signext/zeroext attributes, just as // if they were passed in registers. -// CHECK-LABEL: define{{.*}} signext i32 @f_scalar_stack_1(i32 noundef signext %a, i128 noundef %b, double noundef %c, fp128 noundef %d, ptr noundef %0, i8 noundef zeroext %f, i8 noundef signext %g, i8 noundef zeroext %h) +// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 +// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// int f_scalar_stack_1(int32_t a, __int128_t b, double c, long double d, v32i8 e, uint8_t f, int8_t g, uint8_t h) { return g + h; } -// CHECK-LABEL: define{{.*}} void @f_scalar_stack_2(ptr noalias sret(%struct.large) align 8 %agg.result, double noundef %a, i128 noundef %b, fp128 noundef %c, ptr noundef %0, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef zeroext %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_2 +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], double noundef [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; Index: clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-lp64d-abi.c +++ clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-lp64d-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:" --version 2 // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ // RUN: | FileCheck %s @@ -10,48 +11,81 @@ #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{{.*}} signext i32 @f_scalar_3(i32 noundef signext %x) +// CHECK-LABEL: define dso_local signext i32 @f_scalar_3 +// CHECK-SAME: (i32 noundef signext [[X:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// uint32_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; } -// 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; } -// 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; } @@ -63,13 +97,19 @@ uint16_t a, b, c, d; }; -// CHECK-LABEL: define{{.*}} void @f_agg_tiny(i64 %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_tiny +// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_tiny(struct tiny x) { x.a += x.b; x.c += x.d; } -// CHECK-LABEL: define{{.*}} i64 @f_agg_tiny_ret() +// CHECK-LABEL: define dso_local i64 @f_agg_tiny_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct tiny f_agg_tiny_ret(void) { return (struct tiny){1, 2, 3, 4}; } @@ -77,23 +117,35 @@ typedef uint16_t v4i16 __attribute__((vector_size(8))); typedef int64_t v1i64 __attribute__((vector_size(8))); -// CHECK-LABEL: define{{.*}} void @f_vec_tiny_v4i16(i64 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_tiny_v4i16 +// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_tiny_v4i16(v4i16 x) { x[0] = x[1]; x[2] = x[3]; } -// CHECK-LABEL: define{{.*}} i64 @f_vec_tiny_v4i16_ret() +// CHECK-LABEL: define dso_local i64 @f_vec_tiny_v4i16_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v4i16 f_vec_tiny_v4i16_ret(void) { return (v4i16){1, 2, 3, 4}; } -// CHECK-LABEL: define{{.*}} void @f_vec_tiny_v1i64(i64 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_tiny_v1i64 +// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_tiny_v1i64(v1i64 x) { x[0] = 114; } -// CHECK-LABEL: define{{.*}} i64 @f_vec_tiny_v1i64_ret() +// CHECK-LABEL: define dso_local i64 @f_vec_tiny_v1i64_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v1i64 f_vec_tiny_v1i64_ret(void) { return (v1i64){1}; } @@ -102,13 +154,19 @@ int64_t a, *b; }; -// CHECK-LABEL: define{{.*}} void @f_agg_small([2 x i64] %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_small +// CHECK-SAME: ([2 x i64] [[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 i64] @f_agg_small_ret() +// CHECK-LABEL: define dso_local [2 x i64] @f_agg_small_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct small f_agg_small_ret(void) { return (struct small){1, 0}; } @@ -116,22 +174,34 @@ typedef uint16_t v8i16 __attribute__((vector_size(16))); typedef __int128_t v1i128 __attribute__((vector_size(16))); -// CHECK-LABEL: define{{.*}} void @f_vec_small_v8i16(i128 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_small_v8i16 +// CHECK-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_small_v8i16(v8i16 x) { x[0] = x[7]; } -// CHECK-LABEL: define{{.*}} i128 @f_vec_small_v8i16_ret() +// CHECK-LABEL: define dso_local i128 @f_vec_small_v8i16_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v8i16 f_vec_small_v8i16_ret(void) { return (v8i16){1, 2, 3, 4, 5, 6, 7, 8}; } -// CHECK-LABEL: define{{.*}} void @f_vec_small_v1i128(i128 noundef %x.coerce) +// CHECK-LABEL: define dso_local void @f_vec_small_v1i128 +// CHECK-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_small_v1i128(v1i128 x) { x[0] = 114; } -// CHECK-LABEL: define{{.*}} i128 @f_vec_small_v1i128_ret() +// CHECK-LABEL: define dso_local i128 @f_vec_small_v1i128_ret +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// v1i128 f_vec_small_v1i128_ret(void) { return (v1i128){1}; } @@ -144,12 +214,18 @@ __int128_t a; }; -// CHECK-LABEL: define{{.*}} void @f_agg_small_aligned(i128 %x.coerce) +// CHECK-LABEL: define dso_local void @f_agg_small_aligned +// CHECK-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_agg_small_aligned(struct small_aligned x) { x.a += x.a; } -// CHECK-LABEL: define{{.*}} i128 @f_agg_small_aligned_ret(i128 %x.coerce) +// CHECK-LABEL: define dso_local i128 @f_agg_small_aligned_ret +// CHECK-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { return (struct small_aligned){10}; } @@ -159,26 +235,38 @@ int64_t a, b, c, d; }; -// CHECK-LABEL: define{{.*}} void @f_agg_large(ptr noundef %x) +// CHECK-LABEL: define dso_local void @f_agg_large +// CHECK-SAME: (ptr 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(ptr noalias sret(%struct.large) align 8 %agg.result, i32 noundef signext %i, i8 noundef signext %j) +// CHECK-LABEL: define dso_local void @f_agg_large_ret +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[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 v32i8 __attribute__((vector_size(32))); -// CHECK-LABEL: define{{.*}} void @f_vec_large_v32i8(ptr noundef %0) +// CHECK-LABEL: define dso_local void @f_vec_large_v32i8 +// CHECK-SAME: (ptr noundef [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_vec_large_v32i8(v32i8 x) { x[0] = x[7]; } -// CHECK-LABEL: define{{.*}} void @f_vec_large_v32i8_ret(ptr noalias sret(<32 x i8>) align 32 %agg.result) +// CHECK-LABEL: define dso_local void @f_vec_large_v32i8_ret +// CHECK-SAME: (ptr noalias sret(<32 x i8>) align 32 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// v32i8 f_vec_large_v32i8_ret(void) { return (v32i8){1, 2, 3, 4, 5, 6, 7, 8}; } @@ -186,13 +274,19 @@ // Scalars passed on the stack should have signext/zeroext attributes, just as // if they were passed in registers. -// CHECK-LABEL: define{{.*}} signext i32 @f_scalar_stack_1(i64 %a.coerce, [2 x i64] %b.coerce, i128 %c.coerce, ptr noundef %d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef zeroext %g, i8 noundef signext %h) +// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 +// CHECK-SAME: (i64 [[A_COERCE:%.*]], [2 x i64] [[B_COERCE:%.*]], i128 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[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; } -// CHECK-LABEL: define{{.*}} signext i32 @f_scalar_stack_2(i32 noundef signext %a, i128 noundef %b, i64 noundef %c, fp128 noundef %d, ptr noundef %0, i8 noundef zeroext %f, i8 noundef signext %g, i8 noundef zeroext %h) +// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_2 +// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// int f_scalar_stack_2(int32_t a, __int128_t b, int64_t c, long double d, v32i8 e, uint8_t f, int8_t g, uint8_t h) { return g + h; @@ -202,202 +296,11 @@ // 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_3(ptr noalias sret(%struct.large) align 8 %agg.result, i32 noundef signext %a, i128 noundef %b, fp128 noundef %c, ptr noundef %0, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef zeroext %g) +// CHECK-LABEL: define dso_local void @f_scalar_stack_3 +// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// struct large f_scalar_stack_3(uint32_t a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; } - -// Ensure that ABI lowering happens as expected for vararg calls. - -int f_va_callee(int, ...); - -// CHECK-LABEL: define{{.*}} void @f_va_caller() -void f_va_caller(void) { - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double noundef 4.000000e+00, double noundef 5.000000e+00, i64 {{%.*}}, [2 x i64] {{%.*}}, i128 {{%.*}}, ptr noundef {{%.*}}) - 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: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, fp128 noundef 0xL00000000000000004001400000000000, i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5.0L, 6, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i128 {{%.*}}, i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, (struct small_aligned){5}, 6, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, [2 x i64] {{%.*}}, i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, (struct small){5, NULL}, 6, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, fp128 noundef 0xL00000000000000004001800000000000, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, 6.0L, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i128 {{%.*}}, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, (struct small_aligned){6}, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, [2 x i64] {{%.*}}, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, (struct small){6, NULL}, 7, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, fp128 noundef 0xL00000000000000004001C00000000000, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, 6, 7.0L, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, i128 {{%.*}}, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, 6, (struct small_aligned){7}, 8, 9); - // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, [2 x i64] {{.*}}, i32 noundef signext 8, i32 noundef signext 9) - f_va_callee(1, 2, 3, 4, 5, 6, (struct small){7, NULL}, 8, 9); -} - -// CHECK-LABEL: define{{.*}} signext i32 @f_va_1(ptr noundef %fmt, ...) {{.*}} { -// CHECK: [[FMT_ADDR:%.*]] = alloca ptr, align 8 -// CHECK: [[VA:%.*]] = alloca ptr, align 8 -// CHECK: [[V:%.*]] = alloca i32, align 4 -// CHECK: store ptr %fmt, ptr [[FMT_ADDR]], align 8 -// CHECK: call void @llvm.va_start(ptr [[VA]]) -// CHECK: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 -// CHECK: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 -// CHECK: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 8 -// CHECK: store i32 [[TMP1]], ptr [[V]], align 4 -// CHECK: call void @llvm.va_end(ptr [[VA]]) -// CHECK: [[TMP2:%.*]] = load i32, ptr [[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 ptr, align 8 -// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[V:%.*]] = alloca fp128, align 16 -// CHECK-NEXT: store ptr [[FMT:%.*]], ptr [[FMT_ADDR]], align 8 -// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i64 -// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], 15 -// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], -16 -// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i64 [[TMP2]] to ptr -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 -// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP4:%.*]] = load fp128, ptr [[ARGP_CUR_ALIGNED]], align 16 -// CHECK-NEXT: store fp128 [[TMP4]], ptr [[V]], align 16 -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -// CHECK-NEXT: [[TMP5:%.*]] = load fp128, ptr [[V]], align 16 -// CHECK-NEXT: ret fp128 [[TMP5]] -long double f_va_2(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - long double v = __builtin_va_arg(va, long double); - __builtin_va_end(va); - - return v; -} - -// Two "aligned" register pairs. - -// CHECK-LABEL: @f_va_3( -// CHECK: [[FMT_ADDR:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[V:%.*]] = alloca fp128, align 16 -// CHECK-NEXT: [[W:%.*]] = alloca i32, align 4 -// CHECK-NEXT: [[X:%.*]] = alloca fp128, align 16 -// CHECK-NEXT: store ptr [[FMT:%.*]], ptr [[FMT_ADDR]], align 8 -// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i64 -// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], 15 -// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], -16 -// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i64 [[TMP2]] to ptr -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 -// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP4:%.*]] = load fp128, ptr [[ARGP_CUR_ALIGNED]], align 16 -// CHECK-NEXT: store fp128 [[TMP4]], ptr [[V]], align 16 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2]], i64 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT3]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[ARGP_CUR2]], align 8 -// CHECK-NEXT: store i32 [[TMP6]], ptr [[W]], align 4 -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP7:%.*]] = ptrtoint ptr [[ARGP_CUR4]] to i64 -// CHECK-NEXT: [[TMP8:%.*]] = add i64 [[TMP7]], 15 -// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], -16 -// CHECK-NEXT: [[ARGP_CUR4_ALIGNED:%.*]] = inttoptr i64 [[TMP9]] to ptr -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR4_ALIGNED]], i64 16 -// CHECK-NEXT: store ptr [[ARGP_NEXT5]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP11:%.*]] = load fp128, ptr [[ARGP_CUR4_ALIGNED]], align 16 -// CHECK-NEXT: store fp128 [[TMP11]], ptr [[X]], align 16 -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -// CHECK-NEXT: [[TMP12:%.*]] = load fp128, ptr [[V]], align 16 -// CHECK-NEXT: [[TMP13:%.*]] = load fp128, ptr [[X]], align 16 -// CHECK-NEXT: [[ADD:%.*]] = fadd fp128 [[TMP12]], [[TMP13]] -// CHECK-NEXT: ret fp128 [[ADD]] -long double f_va_3(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - long double v = __builtin_va_arg(va, long double); - int w = __builtin_va_arg(va, int); - long double x = __builtin_va_arg(va, long double); - __builtin_va_end(va); - - return v + x; -} - -// CHECK-LABEL: @f_va_4( -// CHECK: [[FMT_ADDR:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 -// CHECK-NEXT: [[TS:%.*]] = alloca [[STRUCT_TINY:%.*]], align 2 -// CHECK-NEXT: [[SS:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 8 -// CHECK-NEXT: [[LS:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 8 -// CHECK-NEXT: [[RET:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store ptr [[FMT:%.*]], ptr [[FMT_ADDR]], align 8 -// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) -// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 8 -// CHECK-NEXT: store i32 [[TMP1]], ptr [[V]], align 4 -// CHECK-NEXT: [[ARGP_CUR2:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2]], i64 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT3]], ptr [[VA]], align 8 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 2 [[TS]], ptr align 8 [[ARGP_CUR2]], i64 8, i1 false) -// CHECK-NEXT: [[ARGP_CUR4:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[ARGP_NEXT5:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR4]], i64 16 -// CHECK-NEXT: store ptr [[ARGP_NEXT5]], ptr [[VA]], align 8 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[SS]], ptr align 8 [[ARGP_CUR4]], i64 16, i1 false) -// CHECK-NEXT: [[ARGP_CUR6:%.*]] = load ptr, ptr [[VA]], align 8 -// CHECK-NEXT: [[ARGP_NEXT7:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR6]], i64 8 -// CHECK-NEXT: store ptr [[ARGP_NEXT7]], ptr [[VA]], align 8 -// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[ARGP_CUR6]], align 8 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[LS]], ptr align 8 [[TMP9]], i64 32, i1 false) -// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) -// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 0 -// CHECK-NEXT: [[TMP12:%.*]] = load i16, ptr [[A]], align 2 -// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP12]] to i64 -// CHECK-NEXT: [[A9:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[SS]], i32 0, i32 0 -// CHECK-NEXT: [[TMP13:%.*]] = load i64, ptr [[A9]], align 8 -// CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[CONV]], [[TMP13]] -// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 2 -// CHECK-NEXT: [[TMP14:%.*]] = load i64, ptr [[C]], align 8 -// CHECK-NEXT: [[ADD10:%.*]] = add nsw i64 [[ADD]], [[TMP14]] -// CHECK-NEXT: [[CONV11:%.*]] = trunc i64 [[ADD10]] to i32 -// CHECK-NEXT: store i32 [[CONV11]], ptr [[RET]], align 4 -// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[RET]], align 4 -// CHECK-NEXT: ret i32 [[TMP15]] -int f_va_4(char *fmt, ...) { - __builtin_va_list va; - - __builtin_va_start(va, fmt); - int v = __builtin_va_arg(va, int); - 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 = ts.a + ss.a + ls.c; - - return ret; -} Index: clang/test/CodeGen/RISCV/riscv64-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64d-abi.c +++ clang/test/CodeGen/RISCV/riscv64-lp64d-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:" --version 2 // RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ // RUN: | FileCheck %s @@ -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, [2 x i64] %h.coerce) +// 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:%.*]], [2 x i64] [[H_COERCE:%.*]]) #[[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_int128bf_s { double f; __int128_t i : 64; }; 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(double %0, i64 %1) +// CHECK-LABEL: define dso_local void @f_double_int64_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int64_s_arg(struct double_int64_s a) {} -// CHECK: define{{.*}} { double, i64 } @f_ret_double_int64_s() +// CHECK-LABEL: define dso_local { double, i64 } @f_ret_double_int64_s +// CHECK-SAME: () #[[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_int128bf_s_arg(double %0, i64 %1) +// CHECK-LABEL: define dso_local void @f_double_int128bf_s_arg +// CHECK-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_double_int128bf_s_arg(struct double_int128bf_s a) {} -// CHECK: define{{.*}} { double, i64 } @f_ret_double_int128bf_s() +// CHECK-LABEL: define dso_local { double, i64 } @f_ret_double_int128bf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct double_int128bf_s f_ret_double_int128bf_s(void) { return (struct double_int128bf_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 signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, [2 x i64] %i.coerce) +// CHECK-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs +// CHECK-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[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, [2 x i64] %i.coerce) +// 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:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[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,20 +367,32 @@ struct int_double_int_s { int a; double b; int c; }; -// CHECK: define{{.*}} void @f_int_double_int_s_arg(ptr 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(ptr 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 char_char_double_s { char a; char b; double c; }; -// CHECK-LABEL: define{{.*}} void @f_char_char_double_s_arg([2 x i64] %a.coerce) +// CHECK-LABEL: define dso_local void @f_char_char_double_s_arg +// CHECK-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_char_char_double_s_arg(struct char_char_double_s a) {} -// CHECK: define{{.*}} [2 x i64] @f_ret_char_char_double_s() +// CHECK-LABEL: define dso_local [2 x i64] @f_ret_char_char_double_s +// CHECK-SAME: () #[[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}; } @@ -263,10 +402,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}; } Index: clang/test/CodeGen/RISCV/riscv64-lp64f-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64f-lp64d-abi.c +++ clang/test/CodeGen/RISCV/riscv64-lp64f-lp64d-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:" --version 2 // RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ // RUN: | FileCheck %s // RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ @@ -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, i64 %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:%.*]], i64 [[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_int128bf_s { float f; __int128_t i : 64; }; 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(float %0, i64 %1) +// CHECK-LABEL: define dso_local void @f_float_int64_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int64_s_arg(struct float_int64_s a) {} -// CHECK: define{{.*}} { float, i64 } @f_ret_float_int64_s() +// CHECK-LABEL: define dso_local { float, i64 } @f_ret_float_int64_s +// CHECK-SAME: () #[[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_int128bf_s_arg(float %0, i64 %1) +// CHECK-LABEL: define dso_local void @f_float_int128bf_s_arg +// CHECK-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_int128bf_s_arg(struct float_int128bf_s a) {} -// CHECK: define{{.*}} <{ float, i64 }> @f_ret_float_int128bf_s() +// CHECK-LABEL: define dso_local <{ float, i64 }> @f_ret_float_int128bf_s +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: entry: +// struct float_int128bf_s f_ret_float_int128bf_s(void) { return (struct float_int128bf_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 signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, i64 %i.coerce) +// CHECK-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs +// CHECK-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], i64 [[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, i64 %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:%.*]], i64 [[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}; } @@ -168,26 +253,38 @@ // Complex floating-point values or structs containing a single complex // floating-point value should be passed in GPRs if no two FPRs is available. -// CHECK: define{{.*}} void @f_floatcomplex_insufficient_fprs1(float noundef %a.coerce0, float noundef %a.coerce1, float noundef %b.coerce0, float noundef %b.coerce1, float noundef %c.coerce0, float noundef %c.coerce1, float noundef %d.coerce0, float noundef %d.coerce1, i64 noundef %e.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs1 +// CHECK-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_insufficient_fprs1(float __complex__ a, float __complex__ b, float __complex__ c, float __complex__ d, float __complex__ e) {} -// CHECK: define{{.*}} void @f_floatcomplex_s_arg_insufficient_fprs1(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i64 %e.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs1 +// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], float [[TMP6:%.*]], float [[TMP7:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_s_arg_insufficient_fprs1(struct floatcomplex_s a, struct floatcomplex_s b, struct floatcomplex_s c, struct floatcomplex_s d, struct floatcomplex_s e) {} -// CHECK: define{{.*}} void @f_floatcomplex_insufficient_fprs2(float noundef %a, float noundef %b.coerce0, float noundef %b.coerce1, float noundef %c.coerce0, float noundef %c.coerce1, float noundef %d.coerce0, float noundef %d.coerce1, i64 noundef %e.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs2 +// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_insufficient_fprs2(float a, float __complex__ b, float __complex__ c, float __complex__ d, float __complex__ e) {} -// CHECK: define{{.*}} void @f_floatcomplex_s_arg_insufficient_fprs2(float noundef %a, float %0, float %1, float %2, float %3, float %4, float %5, i64 %e.coerce) +// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs2 +// CHECK-SAME: (float noundef [[A:%.*]], float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_floatcomplex_s_arg_insufficient_fprs2(float a, struct floatcomplex_s b, struct floatcomplex_s c, @@ -199,60 +296,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}}}}; } @@ -262,20 +395,32 @@ struct int_float_int_s { int a; float b; int c; }; -// CHECK: define{{.*}} void @f_int_float_int_s_arg([2 x i64] %a.coerce) +// CHECK-LABEL: define dso_local void @f_int_float_int_s_arg +// CHECK-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_int_float_int_s_arg(struct int_float_int_s a) {} -// CHECK: define{{.*}} [2 x i64] @f_ret_int_float_int_s() +// CHECK-LABEL: define dso_local [2 x i64] @f_ret_int_float_int_s +// CHECK-SAME: () #[[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 char_char_float_s { char a; char b; float c; }; -// CHECK-LABEL: define{{.*}} void @f_char_char_float_s_arg(i64 %a.coerce) +// CHECK-LABEL: define dso_local void @f_char_char_float_s_arg +// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_char_char_float_s_arg(struct char_char_float_s a) {} -// CHECK: define{{.*}} i64 @f_ret_char_char_float_s() +// CHECK-LABEL: define dso_local i64 @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}; } @@ -285,10 +430,16 @@ union float_u { float a; }; -// CHECK: define{{.*}} void @f_float_u_arg(i64 %a.coerce) +// CHECK-LABEL: define dso_local void @f_float_u_arg +// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// CHECK: entry: +// void f_float_u_arg(union float_u a) {} -// CHECK: define{{.*}} i64 @f_ret_float_u() +// CHECK-LABEL: define dso_local i64 @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/riscv64-vararg.c =================================================================== --- /dev/null +++ clang/test/CodeGen/RISCV/riscv64-vararg.c @@ -0,0 +1,302 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --version 2 +// RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ +// RUN: | FileCheck %s +// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ +// RUN: | FileCheck %s + +#include +#include + +struct tiny { + uint16_t a, b, c, d; +}; + +struct small { + int64_t a, *b; +}; + +struct small_aligned { + __int128_t a; +}; + +struct large { + int64_t a, b, c, d; +}; + +// Ensure that ABI lowering happens as expected for vararg calls. +// Specifically, ensure that signext is emitted for varargs that will be +// passed in registers but not on the stack. Ensure this takes into account +// the use of "aligned" register pairs for varargs with 2*xlen alignment. + +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 2 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL1:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL4:%.*]] = alloca [[STRUCT_SMALL_ALIGNED:%.*]], align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL6:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[STRUCT_LARGE]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL12:%.*]] = alloca [[STRUCT_SMALL_ALIGNED]], align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL16:%.*]] = alloca [[STRUCT_SMALL]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL21:%.*]] = alloca [[STRUCT_SMALL_ALIGNED]], align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL25:%.*]] = alloca [[STRUCT_SMALL]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL30:%.*]] = alloca [[STRUCT_SMALL_ALIGNED]], align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL34:%.*]] = alloca [[STRUCT_SMALL]], align 8 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i16 6, ptr [[A]], align 2 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i16 7, ptr [[B]], align 2 +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i16 8, ptr [[C]], align 2 +// CHECK-NEXT: [[D:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: store i16 9, ptr [[D]], align 2 +// CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL1]], i32 0, i32 0 +// CHECK-NEXT: store i64 10, ptr [[A2]], align 8 +// CHECK-NEXT: [[B3:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL1]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[B3]], align 8 +// CHECK-NEXT: [[A5:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL4]], i32 0, i32 0 +// CHECK-NEXT: store i128 11, ptr [[A5]], align 16 +// CHECK-NEXT: [[A7:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 0 +// CHECK-NEXT: store i64 12, ptr [[A7]], align 8 +// CHECK-NEXT: [[B8:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 1 +// CHECK-NEXT: store i64 13, ptr [[B8]], align 8 +// CHECK-NEXT: [[C9:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 2 +// CHECK-NEXT: store i64 14, ptr [[C9]], align 8 +// CHECK-NEXT: [[D10:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[DOTCOMPOUNDLITERAL6]], i32 0, i32 3 +// CHECK-NEXT: store i64 15, ptr [[D10]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[DOTCOMPOUNDLITERAL]], align 2 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL1]], align 8 +// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i128, ptr [[COERCE_DIVE]], align 16 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL6]], i64 32, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double noundef 4.000000e+00, double noundef 5.000000e+00, i64 [[TMP0]], [2 x i64] [[TMP1]], i128 [[TMP2]], ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: [[CALL11:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, fp128 noundef 0xL00000000000000004001400000000000, i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A13:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL12]], i32 0, i32 0 +// CHECK-NEXT: store i128 5, ptr [[A13]], align 16 +// CHECK-NEXT: [[COERCE_DIVE14:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL12]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i128, ptr [[COERCE_DIVE14]], align 16 +// CHECK-NEXT: [[CALL15:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i128 [[TMP3]], i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A17:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL16]], i32 0, i32 0 +// CHECK-NEXT: store i64 5, ptr [[A17]], align 8 +// CHECK-NEXT: [[B18:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL16]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[B18]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL16]], align 8 +// CHECK-NEXT: [[CALL19:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, [2 x i64] [[TMP4]], i32 noundef signext 6, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[CALL20:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, fp128 noundef 0xL00000000000000004001800000000000, i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A22:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL21]], i32 0, i32 0 +// CHECK-NEXT: store i128 6, ptr [[A22]], align 16 +// CHECK-NEXT: [[COERCE_DIVE23:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL21]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i128, ptr [[COERCE_DIVE23]], align 16 +// CHECK-NEXT: [[CALL24:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i128 [[TMP5]], i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A26:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL25]], i32 0, i32 0 +// CHECK-NEXT: store i64 6, ptr [[A26]], align 8 +// CHECK-NEXT: [[B27:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL25]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[B27]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL25]], align 8 +// CHECK-NEXT: [[CALL28:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, [2 x i64] [[TMP6]], i32 noundef signext 7, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[CALL29:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, fp128 noundef 0xL00000000000000004001C00000000000, i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A31:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL30]], i32 0, i32 0 +// CHECK-NEXT: store i128 7, ptr [[A31]], align 16 +// CHECK-NEXT: [[COERCE_DIVE32:%.*]] = getelementptr inbounds [[STRUCT_SMALL_ALIGNED]], ptr [[DOTCOMPOUNDLITERAL30]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load i128, ptr [[COERCE_DIVE32]], align 16 +// CHECK-NEXT: [[CALL33:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, i128 [[TMP7]], i32 noundef signext 8, i32 noundef signext 9) +// CHECK-NEXT: [[A35:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL34]], i32 0, i32 0 +// CHECK-NEXT: store i64 7, ptr [[A35]], align 8 +// CHECK-NEXT: [[B36:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[DOTCOMPOUNDLITERAL34]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[B36]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL34]], align 8 +// CHECK-NEXT: [[CALL37:%.*]] = call signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i32 noundef signext 5, i32 noundef signext 6, [2 x i64] [[TMP8]], i32 noundef signext 8, i32 noundef signext 9) +// 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}); + f_va_callee(1, 2, 3, 4, 5.0L, 6, 7, 8, 9); + f_va_callee(1, 2, 3, 4, (struct small_aligned){5}, 6, 7, 8, 9); + f_va_callee(1, 2, 3, 4, (struct small){5, NULL}, 6, 7, 8, 9); + f_va_callee(1, 2, 3, 4, 5, 6.0L, 7, 8, 9); + f_va_callee(1, 2, 3, 4, 5, (struct small_aligned){6}, 7, 8, 9); + f_va_callee(1, 2, 3, 4, 5, (struct small){6, NULL}, 7, 8, 9); + f_va_callee(1, 2, 3, 4, 5, 6, 7.0L, 8, 9); + f_va_callee(1, 2, 3, 4, 5, 6, (struct small_aligned){7}, 8, 9); + f_va_callee(1, 2, 3, 4, 5, 6, (struct small){7, NULL}, 8, 9); +} + +// CHECK-LABEL: define dso_local signext i32 @f_va_1 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 8 +// 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 fp128 @f_va_2 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[V:%.*]] = alloca fp128, align 16 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], 15 +// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], -16 +// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i64 [[TMP2]] to ptr +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load fp128, ptr [[ARGP_CUR_ALIGNED]], align 16 +// CHECK-NEXT: store fp128 [[TMP3]], ptr [[V]], align 16 +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP4:%.*]] = load fp128, ptr [[V]], align 16 +// CHECK-NEXT: ret fp128 [[TMP4]] +// +long double f_va_2(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + long double v = __builtin_va_arg(va, long double); + __builtin_va_end(va); + + return v; +} + +// Two "aligned" register pairs. + +// CHECK-LABEL: define dso_local fp128 @f_va_3 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[V:%.*]] = alloca fp128, align 16 +// CHECK-NEXT: [[W:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca fp128, align 16 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARGP_CUR]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], 15 +// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], -16 +// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = inttoptr i64 [[TMP2]] to ptr +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load fp128, ptr [[ARGP_CUR_ALIGNED]], align 16 +// CHECK-NEXT: store fp128 [[TMP3]], ptr [[V]], align 16 +// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i64 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARGP_CUR1]], align 8 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[W]], align 4 +// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[ARGP_CUR3]] to i64 +// CHECK-NEXT: [[TMP6:%.*]] = add i64 [[TMP5]], 15 +// CHECK-NEXT: [[TMP7:%.*]] = and i64 [[TMP6]], -16 +// CHECK-NEXT: [[ARGP_CUR3_ALIGNED:%.*]] = inttoptr i64 [[TMP7]] to ptr +// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3_ALIGNED]], i64 16 +// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load fp128, ptr [[ARGP_CUR3_ALIGNED]], align 16 +// CHECK-NEXT: store fp128 [[TMP8]], ptr [[X]], align 16 +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[TMP9:%.*]] = load fp128, ptr [[V]], align 16 +// CHECK-NEXT: [[TMP10:%.*]] = load fp128, ptr [[X]], align 16 +// CHECK-NEXT: [[ADD:%.*]] = fadd fp128 [[TMP9]], [[TMP10]] +// CHECK-NEXT: ret fp128 [[ADD]] +// +long double f_va_3(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + long double v = __builtin_va_arg(va, long double); + int w = __builtin_va_arg(va, int); + long double x = __builtin_va_arg(va, long double); + __builtin_va_end(va); + + return v + x; +} + +// CHECK-LABEL: define dso_local signext i32 @f_va_4 +// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TS:%.*]] = alloca [[STRUCT_TINY:%.*]], align 2 +// CHECK-NEXT: [[SS:%.*]] = alloca [[STRUCT_SMALL:%.*]], align 8 +// CHECK-NEXT: [[LS:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 8 +// CHECK-NEXT: [[RET:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]]) +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 8 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4 +// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i64 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 2 [[TS]], ptr align 8 [[ARGP_CUR1]], i64 8, i1 false) +// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i64 16 +// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[VA]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[SS]], ptr align 8 [[ARGP_CUR3]], i64 16, i1 false) +// CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[VA]], align 8 +// CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i64 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[VA]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[LS]], ptr align 8 [[TMP1]], i64 32, i1 false) +// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]]) +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_TINY]], ptr [[TS]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[A]], align 2 +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP2]] to i64 +// CHECK-NEXT: [[A7:%.*]] = getelementptr inbounds [[STRUCT_SMALL]], ptr [[SS]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[A7]], align 8 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[CONV]], [[TMP3]] +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_LARGE]], ptr [[LS]], i32 0, i32 2 +// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr [[C]], align 8 +// CHECK-NEXT: [[ADD8:%.*]] = add nsw i64 [[ADD]], [[TMP4]] +// CHECK-NEXT: [[CONV9:%.*]] = trunc i64 [[ADD8]] to i32 +// CHECK-NEXT: store i32 [[CONV9]], ptr [[RET]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[RET]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int f_va_4(char *fmt, ...) { + __builtin_va_list va; + + __builtin_va_start(va, fmt); + int v = __builtin_va_arg(va, int); + 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 = ts.a + ss.a + ls.c; + + return ret; +}