================
@@ -1,54 +1,477 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value
-fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value
-fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,LLVMCIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value
-emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=LLVM,OGCG
struct Bar {
float f1;
float f2;
unsigned u;
};
-struct Bar varargs_aggregate(int count, ...) {
+struct Bar varargs_aggregate_mixed_pair(int count, ...) {
__builtin_va_list args;
__builtin_va_start(args, count);
struct Bar res = __builtin_va_arg(args, struct Bar);
__builtin_va_end(args);
return res;
}
+struct LongPair {
+ long a;
+ long b;
+};
+
+struct LongPair varargs_aggregate_gp_pair(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ struct LongPair res = __builtin_va_arg(args, struct LongPair);
+ __builtin_va_end(args);
+ return res;
+}
+
+struct DoublePair {
+ double a;
+ double b;
+};
+
+struct DoublePair varargs_aggregate_sse_pair(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ struct DoublePair res = __builtin_va_arg(args, struct DoublePair);
+ __builtin_va_end(args);
+ return res;
+}
+
+struct Big {
+ long a;
+ long b;
+ long c;
+};
+
+struct Big varargs_aggregate_memory(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ struct Big res = __builtin_va_arg(args, struct Big);
+ __builtin_va_end(args);
+ return res;
+}
-// CIR-LABEL: cir.func {{.*}} @varargs_aggregate(
-// CIR-SAME: -> !rec_anon_struct
-// CIR: %[[COERCE:.+]] = cir.alloca "coerce" {{.*}} :
!cir.ptr<!rec_anon_struct>
+// Mixed pair: one SSE eightbyte, one INTEGER eightbyte, reassembled via a
temp.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_mixed_pair(
+// CIR-SAME: -> !rec_anon_struct{{[0-9]*}}
+// CIR: %[[COERCE:.+]] = cir.alloca "coerce" {{.*}} :
!cir.ptr<!rec_anon_struct{{[0-9]*}}>
// CIR: %[[RET_ADDR:.+]] = cir.alloca "__retval" {{.*}} init :
!cir.ptr<!rec_Bar>
// CIR: %[[VAAREA:.+]] = cir.alloca "args" {{.*}} :
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>
// CIR: %[[TMP_ADDR:.+]] = cir.alloca "vaarg.tmp" {{.*}} : !cir.ptr<!rec_Bar>
// CIR: %[[VA_PTR0:.+]] = cir.cast array_to_ptrdecay %[[VAAREA]] :
!cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: cir.va_start %[[VA_PTR0]] : !cir.ptr<!rec___va_list_tag>
// CIR: %[[VA_PTR1:.+]] = cir.cast array_to_ptrdecay %[[VAAREA]] :
!cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
-// CIR: %[[VA_ARG:.+]] = cir.va_arg %[[VA_PTR1]] :
(!cir.ptr<!rec___va_list_tag>) -> !rec_Bar
-// CIR: cir.store{{.*}} %[[VA_ARG]], %[[TMP_ADDR]] : !rec_Bar,
!cir.ptr<!rec_Bar>
+// CIR: %[[GP_OFFSET_P:.+]] = cir.get_member %[[VA_PTR1]][0] {name =
"gp_offset"} : !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[GP_OFFSET:.+]] = cir.load %[[GP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[GP_LIMIT:.+]] = cir.const #cir.int<40> : !u32i
+// CIR: %[[FITS_GP:.+]] = cir.cmp le %[[GP_OFFSET]], %[[GP_LIMIT]] : !u32i
+// CIR: %[[FP_OFFSET_P:.+]] = cir.get_member %[[VA_PTR1]][1] {name =
"fp_offset"} : !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[FP_OFFSET:.+]] = cir.load %[[FP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[FP_LIMIT:.+]] = cir.const #cir.int<160> : !u32i
+// CIR: %[[FITS_FP:.+]] = cir.cmp le %[[FP_OFFSET]], %[[FP_LIMIT]] : !u32i
+// CIR: %[[IN_REGS:.+]] = cir.select if %[[FITS_GP]] then %[[FITS_FP]] else
%{{.+}} : (!cir.bool, !cir.bool, !cir.bool) -> !cir.bool
+// CIR: %[[REG_TMP:.+]] = cir.alloca "vaarg.reg" {{.*}} :
!cir.ptr<!rec_anon_struct{{[0-9]*}}>
+// CIR: %[[VA_ARG:.+]] = cir.ternary(%[[IN_REGS]], true {
+// CIR: %[[REG_SAVE_P:.+]] = cir.get_member %[[VA_PTR1]][3] {name =
"reg_save_area"}
+// CIR: %[[REG_SAVE:.+]] = cir.load %[[REG_SAVE_P]]
+// CIR: %[[REG_SAVE_B:.+]] = cir.cast bitcast %[[REG_SAVE]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[FP_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[FP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[FP_ADDR_V:.+]] = cir.cast bitcast %[[FP_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[SSE_VAL:.+]] = cir.load %[[FP_ADDR_V]] : !cir.ptr<!cir.vector<2
x !cir.float>>, !cir.vector<2 x !cir.float>
+// CIR: %[[TMP_SSE:.+]] = cir.get_member %[[REG_TMP]][0] {{.*}} ->
!cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: cir.store %[[SSE_VAL]], %[[TMP_SSE]] : !cir.vector<2 x
!cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[GP_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[GP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[GP_ADDR_V:.+]] = cir.cast bitcast %[[GP_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!u32i>
+// CIR: %[[INT_VAL:.+]] = cir.load %[[GP_ADDR_V]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[TMP_INT:.+]] = cir.get_member %[[REG_TMP]][1] {{.*}} ->
!cir.ptr<!u32i>
+// CIR: cir.store %[[INT_VAL]], %[[TMP_INT]] : !u32i, !cir.ptr<!u32i>
+// CIR: %[[REG_TMP_B:.+]] = cir.cast bitcast %[[REG_TMP]] :
!cir.ptr<!rec_anon_struct{{[0-9]*}}> -> !cir.ptr<!u8i>
+// CIR: %[[GP_BUMP:.+]] = cir.const #cir.int<8> : !u32i
+// CIR: %[[GP_NEXT:.+]] = cir.add %[[GP_OFFSET]], %[[GP_BUMP]] : !u32i
+// CIR: cir.store %[[GP_NEXT]], %[[GP_OFFSET_P]] : !u32i, !cir.ptr<!u32i>
+// CIR: %[[FP_BUMP:.+]] = cir.const #cir.int<16> : !u32i
+// CIR: %[[FP_NEXT:.+]] = cir.add %[[FP_OFFSET]], %[[FP_BUMP]] : !u32i
+// CIR: cir.store %[[FP_NEXT]], %[[FP_OFFSET_P]] : !u32i, !cir.ptr<!u32i>
+// CIR: cir.yield %[[REG_TMP_B]] : !cir.ptr<!u8i>
+// CIR: }, false {
+// CIR: %[[OVERFLOW_P:.+]] = cir.get_member %[[VA_PTR1]][2] {name =
"overflow_arg_area"}
+// CIR: %[[OVERFLOW:.+]] = cir.load %[[OVERFLOW_P]]
+// CIR: %[[OVERFLOW_B:.+]] = cir.cast bitcast %[[OVERFLOW]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[STRIDE:.+]] = cir.const #cir.int<16> : !s32i
+// CIR: %[[OVERFLOW_NEXT:.+]] = cir.ptr_stride %[[OVERFLOW_B]],
%[[STRIDE]] : (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR: cir.store %[[OVERFLOW_NEXT]], %{{.+}} : !cir.ptr<!u8i>,
!cir.ptr<!cir.ptr<!u8i>>
+// CIR: cir.yield %[[OVERFLOW_B]] : !cir.ptr<!u8i>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!u8i>
+// CIR: %[[VA_ARG_B:.+]] = cir.cast bitcast %[[VA_ARG]] : !cir.ptr<!u8i> ->
!cir.ptr<!rec_Bar>
+// CIR: %[[VA_ARG_V:.+]] = cir.load %[[VA_ARG_B]] : !cir.ptr<!rec_Bar>,
!rec_Bar
+// CIR: cir.store{{.*}} %[[VA_ARG_V]], %[[TMP_ADDR]] : !rec_Bar,
!cir.ptr<!rec_Bar>
// CIR: cir.copy %[[TMP_ADDR]] align(4) to %[[RET_ADDR]] align(4) :
!cir.ptr<!rec_Bar>
// CIR: %[[VA_PTR2:.+]] = cir.cast array_to_ptrdecay %[[VAAREA]] :
!cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: cir.va_end %[[VA_PTR2]] : !cir.ptr<!rec___va_list_tag>
// CIR: %[[RETVAL:.+]] = cir.load{{.*}} %[[RET_ADDR]] : !cir.ptr<!rec_Bar>,
!rec_Bar
-// CIR: %[[SLOT:.+]] = cir.cast bitcast %[[COERCE]] :
!cir.ptr<!rec_anon_struct> -> !cir.ptr<!rec_Bar>
+// CIR: %[[SLOT:.+]] = cir.cast bitcast %[[COERCE]] :
!cir.ptr<!rec_anon_struct{{[0-9]*}}> -> !cir.ptr<!rec_Bar>
// CIR: cir.store %[[RETVAL]], %[[SLOT]] : !rec_Bar, !cir.ptr<!rec_Bar>
-// CIR: %[[COERCED:.+]] = cir.load %[[COERCE]] : !cir.ptr<!rec_anon_struct>,
!rec_anon_struct
-// CIR: cir.return %[[COERCED]] : !rec_anon_struct
+// CIR: %[[COERCED:.+]] = cir.load %[[COERCE]] :
!cir.ptr<!rec_anon_struct{{[0-9]*}}>, !rec_anon_struct{{[0-9]*}}
+// CIR: cir.return %[[COERCED]] : !rec_anon_struct{{[0-9]*}}
+
+// GP pair: both eightbytes INTEGER, contiguous, so a single load suffices.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_gp_pair(
+// CIR: %[[GP_OFFSET_P:.+]] = cir.get_member %{{.+}}[0] {name = "gp_offset"}
: !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[GP_OFFSET:.+]] = cir.load %[[GP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[GP_LIMIT:.+]] = cir.const #cir.int<32> : !u32i
+// CIR: %[[FITS_GP:.+]] = cir.cmp le %[[GP_OFFSET]], %[[GP_LIMIT]] : !u32i
+// CIR: %[[VA_ARG:.+]] = cir.ternary(%[[FITS_GP]], true {
+// CIR: %[[REG_SAVE:.+]] = cir.load %{{.+}}
+// CIR: %[[REG_SAVE_B:.+]] = cir.cast bitcast %[[REG_SAVE]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[REG_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[GP_OFFSET]]
: (!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[GP_BUMP:.+]] = cir.const #cir.int<16> : !u32i
+// CIR: %[[GP_NEXT:.+]] = cir.add %[[GP_OFFSET]], %[[GP_BUMP]] : !u32i
+// CIR: cir.store %[[GP_NEXT]], %[[GP_OFFSET_P]] : !u32i, !cir.ptr<!u32i>
+// CIR: cir.yield %[[REG_ADDR]] : !cir.ptr<!u8i>
+// CIR: }, false {
+// CIR: %[[OVERFLOW:.+]] = cir.load %{{.+}}
+// CIR: %[[OVERFLOW_B:.+]] = cir.cast bitcast %[[OVERFLOW]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[STRIDE:.+]] = cir.const #cir.int<16> : !s32i
+// CIR: %[[OVERFLOW_NEXT:.+]] = cir.ptr_stride %[[OVERFLOW_B]],
%[[STRIDE]] : (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR: cir.yield %[[OVERFLOW_B]] : !cir.ptr<!u8i>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!u8i>
+// CIR: %[[VA_ARG_B:.+]] = cir.cast bitcast %[[VA_ARG]] : !cir.ptr<!u8i> ->
!cir.ptr<!rec_LongPair>
+// CIR: %[[VA_ARG_V:.+]] = cir.load %[[VA_ARG_B]] : !cir.ptr<!rec_LongPair>,
!rec_LongPair
+
+// SSE pair: both eightbytes SSE, 16 bytes apart, reassembled via a temp.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_sse_pair(
+// CIR: %[[FP_OFFSET_P:.+]] = cir.get_member %{{.+}}[1] {name = "fp_offset"}
: !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[FP_OFFSET:.+]] = cir.load %[[FP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[FP_LIMIT:.+]] = cir.const #cir.int<144> : !u32i
+// CIR: %[[FITS_FP:.+]] = cir.cmp le %[[FP_OFFSET]], %[[FP_LIMIT]] : !u32i
+// CIR: %[[REG_TMP:.+]] = cir.alloca "vaarg.reg" {{.*}} :
!cir.ptr<!rec_anon_struct{{[0-9]*}}>
+// CIR: %[[VA_ARG:.+]] = cir.ternary(%[[FITS_FP]], true {
+// CIR: %[[REG_SAVE:.+]] = cir.load %{{.+}}
+// CIR: %[[REG_SAVE_B:.+]] = cir.cast bitcast %[[REG_SAVE]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[LO_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[FP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[LO_ADDR_V:.+]] = cir.cast bitcast %[[LO_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!cir.double>
+// CIR: %[[LO_VAL:.+]] = cir.load %[[LO_ADDR_V]] : !cir.ptr<!cir.double>,
!cir.double
+// CIR: %[[TMP_LO:.+]] = cir.get_member %[[REG_TMP]][0] {{.*}} ->
!cir.ptr<!cir.double>
+// CIR: cir.store %[[LO_VAL]], %[[TMP_LO]] : !cir.double,
!cir.ptr<!cir.double>
+// CIR: %[[HI_BUMP:.+]] = cir.const #cir.int<16> : !u32i
+// CIR: %[[HI_OFF:.+]] = cir.add %[[FP_OFFSET]], %[[HI_BUMP]] : !u32i
+// CIR: %[[HI_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[HI_OFF]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[HI_ADDR_V:.+]] = cir.cast bitcast %[[HI_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!cir.double>
+// CIR: %[[HI_VAL:.+]] = cir.load %[[HI_ADDR_V]] : !cir.ptr<!cir.double>,
!cir.double
+// CIR: %[[TMP_HI:.+]] = cir.get_member %[[REG_TMP]][1] {{.*}} ->
!cir.ptr<!cir.double>
+// CIR: cir.store %[[HI_VAL]], %[[TMP_HI]] : !cir.double,
!cir.ptr<!cir.double>
+// CIR: %[[REG_TMP_B:.+]] = cir.cast bitcast %[[REG_TMP]] :
!cir.ptr<!rec_anon_struct{{[0-9]*}}> -> !cir.ptr<!u8i>
+// CIR: %[[FP_BUMP:.+]] = cir.const #cir.int<32> : !u32i
+// CIR: %[[FP_NEXT:.+]] = cir.add %[[FP_OFFSET]], %[[FP_BUMP]] : !u32i
+// CIR: cir.store %[[FP_NEXT]], %[[FP_OFFSET_P]] : !u32i, !cir.ptr<!u32i>
+// CIR: cir.yield %[[REG_TMP_B]] : !cir.ptr<!u8i>
+// CIR: }, false {
+// CIR: %[[OVERFLOW:.+]] = cir.load %{{.+}}
+// CIR: cir.yield %{{.+}} : !cir.ptr<!u8i>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!u8i>
+// CIR: %[[VA_ARG_B:.+]] = cir.cast bitcast %[[VA_ARG]] : !cir.ptr<!u8i> ->
!cir.ptr<!rec_DoublePair>
+// CIR: %[[VA_ARG_V:.+]] = cir.load %[[VA_ARG_B]] :
!cir.ptr<!rec_DoublePair>, !rec_DoublePair
-// LLVM-LABEL: define dso_local { <2 x float>, i32 } @varargs_aggregate(i32
noundef %{{.*}}, ...)
+// MEMORY class: three eightbytes never fit in registers.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_memory(
+// CIR: %[[OVERFLOW_P:.+]] = cir.get_member %{{.+}}[2] {name =
"overflow_arg_area"} : !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!cir.ptr<!void>>
+// CIR: %[[OVERFLOW:.+]] = cir.load %[[OVERFLOW_P]] :
!cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+// CIR: %[[OVERFLOW_B:.+]] = cir.cast bitcast %[[OVERFLOW]] :
!cir.ptr<!void> -> !cir.ptr<!u8i>
+// CIR: %[[STRIDE:.+]] = cir.const #cir.int<24> : !s32i
+// CIR: %[[OVERFLOW_NEXT:.+]] = cir.ptr_stride %[[OVERFLOW_B]], %[[STRIDE]]
: (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR: cir.store %[[OVERFLOW_NEXT]], %{{.+}} : !cir.ptr<!u8i>,
!cir.ptr<!cir.ptr<!u8i>>
+// CIR: %[[VA_ARG_B:.+]] = cir.cast bitcast %[[OVERFLOW_B]] : !cir.ptr<!u8i>
-> !cir.ptr<!rec_Big>
+// CIR: %[[VA_ARG_V:.+]] = cir.load %[[VA_ARG_B]] : !cir.ptr<!rec_Big>,
!rec_Big
+
+// LLVM-LABEL: define dso_local { <2 x float>, i32 }
@varargs_aggregate_mixed_pair(i32 noundef %{{.*}}, ...)
// LLVM: call void @llvm.va_start.p0(ptr %{{.*}})
-// LLVM: %[[VA_PTR1:.+]] = getelementptr %struct.__va_list_tag, ptr %{{.*}},
i32 0
-// LLVM: %[[VA_ARG:.+]] = va_arg ptr %[[VA_PTR1]], %struct.Bar
-// LLVM: store %struct.Bar %[[VA_ARG]], ptr %{{.*}}
+// LLVM: %[[GP_OFFSET_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 0
+// LLVM: %[[GP_OFFSET:.+]] = load i32, ptr %[[GP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: %[[FITS_GP:.+]] = icmp ule i32 %[[GP_OFFSET]], 40
+// LLVM: %[[FP_OFFSET_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 1
+// LLVM: %[[FP_OFFSET:.+]] = load i32, ptr %[[FP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: %[[FITS_FP:.+]] = icmp ule i32 %[[FP_OFFSET]], 160
+// LLVM: %[[IN_REGS:.+]] = and i1 %[[FITS_GP]], %[[FITS_FP]]
+// LLVMCIR: %[[REG_TMP:.+]] = alloca { <2 x float>, i32 }, align 8
+// LLVM: br i1 %[[IN_REGS]], label %[[REG_BB:.+]], label %[[MEM_BB:.+]]
+// LLVM: [[REG_BB]]:
+// LLVM: %[[REG_SAVE:.+]] = load ptr, ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[SSE_VAL:.+]] = load <2 x float>, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: store <2 x float> %[[SSE_VAL]], ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[INT_VAL:.+]] = load i32, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: store i32 %[[INT_VAL]], ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[GP_NEXT:.+]] = add i32 %[[GP_OFFSET]], 8
+// LLVM: store i32 %[[GP_NEXT]], ptr %[[GP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: %[[FP_NEXT:.+]] = add i32 %[[FP_OFFSET]], 16
+// LLVM: store i32 %[[FP_NEXT]], ptr %[[FP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: br label %[[END_BB:.+]]
+// LLVM: [[MEM_BB]]:
+// LLVM: %[[OVERFLOW_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 2
+// LLVM: %[[OVERFLOW:.+]] = load ptr, ptr %[[OVERFLOW_P]], align 8
+// LLVM: %[[OVERFLOW_NEXT:.+]] = getelementptr i8, ptr %[[OVERFLOW]],
i{{32|64}} 16
+// LLVM: store ptr %[[OVERFLOW_NEXT]], ptr %[[OVERFLOW_P]], align 8
+// LLVM: br label %[[END_BB]]
+// LLVM: [[END_BB]]:
+// The fetched value is materialized by loading the record on one side and by
+// copying it on the other, so only the address feeding it is shared.
+// LLVMCIR: %[[ADDR:.+]] = phi ptr [ %[[OVERFLOW]], %[[MEM_BB]] ], [
%[[REG_TMP]], %[[REG_BB]] ]
+// LLVMCIR: %[[VA_ARG:.+]] = load %struct.Bar, ptr %[[ADDR]], align 4
+// LLVMCIR: store %struct.Bar %[[VA_ARG]], ptr %{{.*}}, align 4
+// OGCG: %[[ADDR:.+]] = phi ptr [ %{{.*}}, %[[REG_BB]] ], [ %{{.*}},
%[[MEM_BB]] ]
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4
%{{.*}}, i64 12, i1 false)
-// OGCG-LABEL: define dso_local { <2 x float>, i32 } @varargs_aggregate
-// OGCG: call void @llvm.va_start.p0(ptr %{{.*}})
-// OGCG: %[[VAARG_ADDR:.+]] = phi ptr [ %{{.*}}, %vaarg.in_reg ], [ %{{.*}},
%vaarg.in_mem ]
-// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4
%[[VAARG_ADDR]], i64 12, i1 false)
+// LLVM-LABEL: define dso_local { i64, i64 } @varargs_aggregate_gp_pair(i32
noundef %{{.*}}, ...)
+// LLVM: %[[GP_OFFSET_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 0
+// LLVM: %[[GP_OFFSET:.+]] = load i32, ptr %[[GP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: %[[FITS_GP:.+]] = icmp ule i32 %[[GP_OFFSET]], 32
+// LLVM: br i1 %[[FITS_GP]], label %[[REG_BB:.+]], label %[[MEM_BB:.+]]
+// LLVM: [[REG_BB]]:
+// LLVM: %[[REG_SAVE:.+]] = load ptr, ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[REG_ADDR:.+]] = getelementptr i8, ptr %[[REG_SAVE]], i{{32|64}}
%{{.+}}
+// LLVM: %[[GP_NEXT:.+]] = add i32 %[[GP_OFFSET]], 16
+// LLVM: store i32 %[[GP_NEXT]], ptr %[[GP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: br label %[[END_BB:.+]]
+// LLVM: [[MEM_BB]]:
+// LLVM: %[[OVERFLOW_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 2
+// LLVM: %[[OVERFLOW:.+]] = load ptr, ptr %[[OVERFLOW_P]], align 8
+// LLVM: %[[OVERFLOW_NEXT:.+]] = getelementptr i8, ptr %[[OVERFLOW]],
i{{32|64}} 16
+// LLVM: store ptr %[[OVERFLOW_NEXT]], ptr %[[OVERFLOW_P]], align 8
+// LLVM: br label %[[END_BB]]
+// LLVM: [[END_BB]]:
+// LLVMCIR: %[[ADDR:.+]] = phi ptr [ %[[OVERFLOW]], %[[MEM_BB]] ], [
%[[REG_ADDR]], %[[REG_BB]] ]
+// LLVMCIR: %[[VA_ARG:.+]] = load %struct.LongPair, ptr %[[ADDR]], align 8
+// LLVMCIR: store %struct.LongPair %[[VA_ARG]], ptr %{{.*}}, align 8
+// OGCG: %[[ADDR:.+]] = phi ptr [ %{{.*}}, %[[REG_BB]] ], [ %{{.*}},
%[[MEM_BB]] ]
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %{{.*}}, ptr align 8
%[[ADDR]], i64 16, i1 false)
+
+// LLVM-LABEL: define dso_local { double, double }
@varargs_aggregate_sse_pair(i32 noundef %{{.*}}, ...)
+// LLVM: %[[FP_OFFSET_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 1
+// LLVM: %[[FP_OFFSET:.+]] = load i32, ptr %[[FP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: %[[FITS_FP:.+]] = icmp ule i32 %[[FP_OFFSET]], 144
+// LLVMCIR: %[[REG_TMP:.+]] = alloca { double, double }, align 8
+// LLVM: br i1 %[[FITS_FP]], label %[[REG_BB:.+]], label %[[MEM_BB:.+]]
+// LLVM: [[REG_BB]]:
+// LLVM: %[[REG_SAVE:.+]] = load ptr, ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[LO_VAL:.+]] = load double, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: store double %[[LO_VAL]], ptr %{{.*}}, align {{[0-9]+}}
+// The high eightbyte sits one 16-byte vector slot past the low one, reached by
+// bumping the offset on one side and by folding it into the address on the
+// other.
+// LLVMCIR: %[[HI_OFF:.+]] = add i32 %[[FP_OFFSET]], 16
+// LLVM: %[[HI_VAL:.+]] = load double, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: store double %[[HI_VAL]], ptr %{{.*}}, align {{[0-9]+}}
+// LLVM: %[[FP_NEXT:.+]] = add i32 %[[FP_OFFSET]], 32
+// LLVM: store i32 %[[FP_NEXT]], ptr %[[FP_OFFSET_P]], align {{[0-9]+}}
+// LLVM: br label %[[END_BB:.+]]
+// LLVM: [[MEM_BB]]:
+// LLVM: br label %[[END_BB]]
+// LLVM: [[END_BB]]:
+// LLVMCIR: %[[ADDR:.+]] = phi ptr [ %{{.*}}, %[[MEM_BB]] ], [ %[[REG_TMP]],
%[[REG_BB]] ]
+// LLVMCIR: %[[VA_ARG:.+]] = load %struct.DoublePair, ptr %[[ADDR]], align 8
+// LLVMCIR: store %struct.DoublePair %[[VA_ARG]], ptr %{{.*}}, align 8
+// OGCG: %[[ADDR:.+]] = phi ptr [ %{{.*}}, %[[REG_BB]] ], [ %{{.*}},
%[[MEM_BB]] ]
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %{{.*}}, ptr align 8
%[[ADDR]], i64 16, i1 false)
+
+// LLVM-LABEL: define dso_local void @varargs_aggregate_memory(ptr
dead_on_unwind noalias writable sret(%struct.Big) align 8 %{{.*}}, i32 noundef
%{{.*}}, ...)
+// LLVM: %[[OVERFLOW_P:.+]] = getelementptr inbounds nuw
%struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 2
+// LLVM: %[[OVERFLOW:.+]] = load ptr, ptr %[[OVERFLOW_P]], align 8
+// LLVM: %[[OVERFLOW_NEXT:.+]] = getelementptr i8, ptr %[[OVERFLOW]],
i{{32|64}} 24
+// LLVM: store ptr %[[OVERFLOW_NEXT]], ptr %[[OVERFLOW_P]], align 8
+// LLVMCIR: %[[VA_ARG:.+]] = load %struct.Big, ptr %[[OVERFLOW]], align 8
+// LLVMCIR: store %struct.Big %[[VA_ARG]], ptr %{{.*}}, align 8
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %{{.*}}, ptr align 8
%{{.*}}, i64 24, i1 false)
+
+struct RevMixed {
+ long a;
+ double b;
+};
+
+struct RevMixed varargs_aggregate_mixed_pair_rev(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ struct RevMixed res = __builtin_va_arg(args, struct RevMixed);
+ __builtin_va_end(args);
+ return res;
+}
+
+// The mirror of the mixed pair, with INTEGER in the low eightbyte and SSE in
+// the high one, so each half has to be read from the cursor for its own class
+// and stored to the member for that class. Reading the SSE half from
+// gp_offset would still produce a well-formed sequence, so each load is bound
+// to the cursor it came from.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_mixed_pair_rev(
+// CIR: %[[GP_OFFSET_P:.+]] = cir.get_member %{{.+}}[0] {name = "gp_offset"}
: !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[GP_OFFSET:.+]] = cir.load %[[GP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[GP_LIMIT:.+]] = cir.const #cir.int<40> : !u32i
+// CIR: %[[FITS_GP:.+]] = cir.cmp le %[[GP_OFFSET]], %[[GP_LIMIT]] : !u32i
+// CIR: %[[FP_OFFSET_P:.+]] = cir.get_member %{{.+}}[1] {name = "fp_offset"}
: !cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[FP_OFFSET:.+]] = cir.load %[[FP_OFFSET_P]] : !cir.ptr<!u32i>,
!u32i
+// CIR: %[[FP_LIMIT:.+]] = cir.const #cir.int<160> : !u32i
+// CIR: %[[FITS_FP:.+]] = cir.cmp le %[[FP_OFFSET]], %[[FP_LIMIT]] : !u32i
+// CIR: %[[REG_TMP:.+]] = cir.alloca "vaarg.reg" {{.*}} :
!cir.ptr<!rec_anon_struct{{[0-9]*}}>
+// CIR: %[[REG_SAVE_B:.+]] = cir.cast bitcast %{{.+}} : !cir.ptr<!void> ->
!cir.ptr<!u8i>
+// CIR: %[[LO_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[GP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[LO_ADDR_V:.+]] = cir.cast bitcast %[[LO_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!s64i>
+// CIR: %[[LO_VAL:.+]] = cir.load %[[LO_ADDR_V]] : !cir.ptr<!s64i>, !s64i
+// CIR: %[[TMP_LO:.+]] = cir.get_member %[[REG_TMP]][0] {{.*}} ->
!cir.ptr<!s64i>
+// CIR: cir.store %[[LO_VAL]], %[[TMP_LO]] : !s64i, !cir.ptr<!s64i>
+// CIR: %[[HI_ADDR:.+]] = cir.ptr_stride %[[REG_SAVE_B]], %[[FP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[HI_ADDR_V:.+]] = cir.cast bitcast %[[HI_ADDR]] : !cir.ptr<!u8i>
-> !cir.ptr<!cir.double>
+// CIR: %[[HI_VAL:.+]] = cir.load %[[HI_ADDR_V]] : !cir.ptr<!cir.double>,
!cir.double
+// CIR: %[[TMP_HI:.+]] = cir.get_member %[[REG_TMP]][1] {{.*}} ->
!cir.ptr<!cir.double>
+// CIR: cir.store %[[HI_VAL]], %[[TMP_HI]] : !cir.double,
!cir.ptr<!cir.double>
+// CIR: %[[REG_TMP_B:.+]] = cir.cast bitcast %[[REG_TMP]] :
!cir.ptr<!rec_anon_struct{{[0-9]*}}> -> !cir.ptr<!u8i>
+// CIR: cir.yield %[[REG_TMP_B]] : !cir.ptr<!u8i>
+
+// LLVM-LABEL: define dso_local { i64, double }
@varargs_aggregate_mixed_pair_rev(i32 noundef %{{.*}}, ...)
+// LLVM: %[[GP_OFFSET:.+]] = load i32, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: icmp ule i32 %[[GP_OFFSET]], 40
+// LLVM: %[[FP_OFFSET:.+]] = load i32, ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: icmp ule i32 %[[FP_OFFSET]], 160
+// LLVM: %[[RSA:.+]] = load ptr, ptr %{{.+}}, align {{[0-9]+}}
+// Both halves index the same save area, each by its own cursor.
+// LLVMCIR: %[[GP64:.+]] = zext i32 %[[GP_OFFSET]] to i64
+// LLVMCIR: %[[LO_ADDR:.+]] = getelementptr i8, ptr %[[RSA]], i64 %[[GP64]]
+// LLVMCIR: %[[LO_VAL:.+]] = load i64, ptr %[[LO_ADDR]], align {{[0-9]+}}
+// LLVMCIR: store i64 %[[LO_VAL]], ptr %{{.+}}, align {{[0-9]+}}
+// LLVMCIR: %[[FP64:.+]] = zext i32 %[[FP_OFFSET]] to i64
+// LLVMCIR: %[[HI_ADDR:.+]] = getelementptr i8, ptr %[[RSA]], i64 %[[FP64]]
+// LLVMCIR: %[[HI_VAL:.+]] = load double, ptr %[[HI_ADDR]], align {{[0-9]+}}
+// LLVMCIR: store double %[[HI_VAL]], ptr %{{.+}}, align {{[0-9]+}}
+// OGCG: %[[LO_ADDR:.+]] = getelementptr i8, ptr %[[RSA]], i32
%[[GP_OFFSET]]
+// OGCG: %[[HI_ADDR:.+]] = getelementptr i8, ptr %[[RSA]], i32
%[[FP_OFFSET]]
+// OGCG: %[[LO_VAL:.+]] = load i64, ptr %[[LO_ADDR]], align {{[0-9]+}}
+// OGCG: store i64 %[[LO_VAL]], ptr %{{.+}}, align {{[0-9]+}}
+// OGCG: %[[HI_VAL:.+]] = load double, ptr %[[HI_ADDR]], align {{[0-9]+}}
+// OGCG: store double %[[HI_VAL]], ptr %{{.+}}, align {{[0-9]+}}
+// LLVM: add i32 %[[GP_OFFSET]], 8
+// LLVM: add i32 %[[FP_OFFSET]], 16
+
+struct __attribute__((aligned(16))) OverAligned {
+ long a;
+ long b;
+};
+
+struct OverAligned varargs_aggregate_overaligned(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ struct OverAligned res = __builtin_va_arg(args, struct OverAligned);
+ __builtin_va_end(args);
+ return res;
+}
+
+// An alignment attribute raises the record above what its two long members
+// imply, and only the record layout carries that. Both arms have to honor it:
+// the register arm copies through a 16-byte-aligned temp, and the overflow arm
+// rounds the cursor up to 16 before reading.
+// CIR-LABEL: cir.func {{.*}} @varargs_aggregate_overaligned(
+// CIR: %[[GP_OFFSET:.+]] = cir.load %{{.+}} : !cir.ptr<!u32i>, !u32i
+// CIR: %[[GP_LIMIT:.+]] = cir.const #cir.int<32> : !u32i
+// CIR: %[[FITS:.+]] = cir.cmp le %[[GP_OFFSET]], %[[GP_LIMIT]] : !u32i
+// CIR: %[[TEMP:.+]] = cir.alloca "vaarg.reg" align(16) :
!cir.ptr<!rec_OverAligned>
+// CIR: %[[ADDR:.+]] = cir.ternary(%[[FITS]], true {
+// CIR: %[[REG_ADDR:.+]] = cir.ptr_stride %{{.+}}, %[[GP_OFFSET]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[REG_ADDR_V:.+]] = cir.cast bitcast %[[REG_ADDR]] :
!cir.ptr<!u8i> -> !cir.ptr<!rec_OverAligned>
+// CIR: %[[VAL:.+]] = cir.load align(8) %[[REG_ADDR_V]] :
!cir.ptr<!rec_OverAligned>, !rec_OverAligned
+// CIR: cir.store %[[VAL]], %[[TEMP]] : !rec_OverAligned,
!cir.ptr<!rec_OverAligned>
+// CIR: %[[TEMP_B:.+]] = cir.cast bitcast %[[TEMP]] :
!cir.ptr<!rec_OverAligned> -> !cir.ptr<!u8i>
+// CIR: cir.yield %[[TEMP_B]] : !cir.ptr<!u8i>
+// CIR: %[[MASK:.+]] = cir.const #cir.int<-16> : !s64i
+// CIR: %[[ALIGNED:.+]] = cir.ptr_mask %{{.+}}, %[[MASK]] :
(!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR: cir.yield %[[ALIGNED]] : !cir.ptr<!u8i>
----------------
andykaylor wrote:
This cuts off rather abruptly. What happened to the other half of the ternary?
And where is the value read?
https://github.com/llvm/llvm-project/pull/222420
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits