================
@@ -0,0 +1,168 @@
+// 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 --check-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+struct Empty {};
+
+// No field with data lies in the low eightbyte, so the register carries bytes
+// 8 through 15 and the classification reports that byte offset.
+struct EmptyLow {
+ Empty e;
+ long x;
+};
+
+struct EmptyLowSse {
+ Empty e;
+ double d;
+};
+
+// One eightbyte holds the only field with data and the rest of the record
+// carries none, so the record is wider than the register that carries it.
+// Reading the record straight from the slot would also read the next one.
+struct TailPad {
+ long x;
+ Empty e;
+};
+
+EmptyLow varargs_empty_low(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ EmptyLow res = __builtin_va_arg(args, EmptyLow);
+ __builtin_va_end(args);
+ return res;
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z17varargs_empty_lowiz(
+// CIR: %[[GP_P:.+]] = cir.get_member %{{.+}}[0] {name = "gp_offset"} :
!cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[GP:.+]] = cir.load %[[GP_P]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[LIMIT:.+]] = cir.const #cir.int<40> : !u32i
+// CIR: %[[FITS:.+]] = cir.cmp le %[[GP]], %[[LIMIT]] : !u32i
+// CIR: %[[TEMP:.+]] = cir.alloca "vaarg.reg" align(8) :
!cir.ptr<!rec_EmptyLow>
+// CIR: %[[ADDR:.+]] = cir.ternary(%[[FITS]], true {
+// CIR: %[[RSA_B:.+]] = cir.cast bitcast %{{.+}} : !cir.ptr<!void> ->
!cir.ptr<!u8i>
+// CIR: %[[SLOT:.+]] = cir.ptr_stride %[[RSA_B]], %[[GP]] :
(!cir.ptr<!u8i>, !u32i) -> !cir.ptr<!u8i>
+// CIR: %[[SLOT_I:.+]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!u8i> ->
!cir.ptr<!s64i>
+// CIR: %[[VAL:.+]] = cir.load align(8) %[[SLOT_I]] : !cir.ptr<!s64i>,
!s64i
+// CIR: %[[TEMP_B:.+]] = cir.cast bitcast %[[TEMP]] :
!cir.ptr<!rec_EmptyLow> -> !cir.ptr<!u8i>
+// CIR: %[[OFF:.+]] = cir.const #cir.int<8> : !s32i
+// CIR: %[[DST:.+]] = cir.ptr_stride %[[TEMP_B]], %[[OFF]] :
(!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR: %[[DST_I:.+]] = cir.cast bitcast %[[DST]] : !cir.ptr<!u8i> ->
!cir.ptr<!s64i>
+// CIR: cir.store %[[VAL]], %[[DST_I]] : !s64i, !cir.ptr<!s64i>
+// CIR: %[[YIELDED:.+]] = cir.cast bitcast %[[TEMP]] :
!cir.ptr<!rec_EmptyLow> -> !cir.ptr<!u8i>
+// CIR: cir.yield %[[YIELDED]] : !cir.ptr<!u8i>
+// CIR: %[[RESULT_P:.+]] = cir.cast bitcast %[[ADDR]] : !cir.ptr<!u8i> ->
!cir.ptr<!rec_EmptyLow>
+// CIR: cir.load %[[RESULT_P]] : !cir.ptr<!rec_EmptyLow>, !rec_EmptyLow
+
+// LLVM-LABEL: define dso_local i64 @_Z17varargs_empty_lowiz(i32 noundef
%{{.*}}, ...)
+// LLVM: %[[GP_P:.+]] = getelementptr inbounds nuw %struct.__va_list_tag,
ptr %{{.+}}, i32 0, i32 0
+// LLVM: %[[GP:.+]] = load i32, ptr %[[GP_P]], align {{4|16}}
+// LLVM: %[[FITS:.+]] = icmp ule i32 %[[GP]], 40
+// LLVM: %[[RSA:.+]] = load ptr, ptr %{{.+}}, align {{8|16}}
+// LLVMCIR: %[[GP64:.+]] = zext i32 %[[GP]] to i64
+// LLVMCIR: %[[SLOT:.+]] = getelementptr i8, ptr %[[RSA]], i64 %[[GP64]]
+// OGCG: %[[SLOT:.+]] = getelementptr i8, ptr %[[RSA]], i32 %[[GP]]
+// LLVM: %[[VAL:.+]] = load i64, ptr %[[SLOT]], align 8
+// LLVM: %[[DST:.+]] = getelementptr i8, ptr %[[TEMP:.+]], i{{32|64}} 8
+// LLVM: store i64 %[[VAL]], ptr %[[DST]], align 8
+// LLVM: %[[BUMPED:.+]] = add i32 %[[GP]], 8
+// LLVM: store i32 %[[BUMPED]], ptr %[[GP_P]], align {{4|16}}
+
+// The overflow area holds the whole record laid out normally, so the memory
+// path reads it from its base and the byte offset does not apply.
+// 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: %[[NEXT:.+]] = getelementptr i8, ptr %[[OVERFLOW]], i{{32|64}} 16
+// LLVM: store ptr %[[NEXT]], ptr %[[OVERFLOW_P]], align 8
+
+// LLVMCIR: %[[ADDR:.+]] = phi ptr [ %[[OVERFLOW]], %{{.+}} ], [ %[[TEMP]],
%{{.+}} ]
+// OGCG: %[[ADDR:.+]] = phi ptr [ %[[TEMP]], %{{.+}} ], [ %[[OVERFLOW]],
%{{.+}} ]
+// LLVMCIR: load %struct.EmptyLow, ptr %[[ADDR]], align 8
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %{{.+}}, ptr align 8
%[[ADDR]], i64 16, i1 false)
+
+double varargs_empty_low_sse(int count, ...) {
+ __builtin_va_list args;
+ __builtin_va_start(args, count);
+ EmptyLowSse res = __builtin_va_arg(args, EmptyLowSse);
+ __builtin_va_end(args);
+ return res.d;
+}
+
+// The same offset applies when the carrying register is SSE, so the fetch
+// gates on fp_offset and still lands the value at byte 8.
+// CIR-LABEL: cir.func {{.*}} @_Z21varargs_empty_low_sseiz(
+// CIR: %[[FP_P:.+]] = cir.get_member %{{.+}}[1] {name = "fp_offset"} :
!cir.ptr<!rec___va_list_tag> -> !cir.ptr<!u32i>
+// CIR: %[[FP:.+]] = cir.load %[[FP_P]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[LIMIT:.+]] = cir.const #cir.int<160> : !u32i
+// CIR: %[[FITS:.+]] = cir.cmp le %[[FP]], %[[LIMIT]] : !u32i
+// CIR: %[[TEMP:.+]] = cir.alloca "vaarg.reg" align(8) :
!cir.ptr<!rec_EmptyLowSse>
+// CIR: %[[ADDR:.+]] = cir.ternary(%[[FITS]], true {
+// CIR: %[[SLOT:.+]] = cir.ptr_stride %{{.+}}, %[[FP]] : (!cir.ptr<!u8i>,
!u32i) -> !cir.ptr<!u8i>
+// CIR: %[[SLOT_D:.+]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!u8i> ->
!cir.ptr<!cir.double>
+// CIR: %[[VAL:.+]] = cir.load align(8) %[[SLOT_D]] :
!cir.ptr<!cir.double>, !cir.double
+// CIR: %[[TEMP_B:.+]] = cir.cast bitcast %[[TEMP]] :
!cir.ptr<!rec_EmptyLowSse> -> !cir.ptr<!u8i>
+// CIR: %[[OFF:.+]] = cir.const #cir.int<8> : !s32i
+// CIR: %[[DST:.+]] = cir.ptr_stride %[[TEMP_B]], %[[OFF]] :
(!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR: %[[DST_D:.+]] = cir.cast bitcast %[[DST]] : !cir.ptr<!u8i> ->
!cir.ptr<!cir.double>
+// CIR: cir.store %[[VAL]], %[[DST_D]] : !cir.double, !cir.ptr<!cir.double>
+// CIR: %[[YIELDED:.+]] = cir.cast bitcast %[[TEMP]] :
!cir.ptr<!rec_EmptyLowSse> -> !cir.ptr<!u8i>
+// An SSE slot is 16 bytes, so the cursor advances by 16 rather than by 8.
+// CIR: %[[STEP:.+]] = cir.const #cir.int<16> : !u32i
+// CIR: cir.store %{{.+}}, %[[FP_P]] : !u32i, !cir.ptr<!u32i>
+// CIR: cir.yield %[[YIELDED]] : !cir.ptr<!u8i>
+
+// LLVM-LABEL: define dso_local noundef double
@_Z21varargs_empty_low_sseiz(i32 noundef %{{.*}}, ...)
+// LLVM: %[[FP_P:.+]] = getelementptr inbounds nuw %struct.__va_list_tag,
ptr %{{.+}}, i32 0, i32 1
+// LLVM: %[[FP:.+]] = load i32, ptr %[[FP_P]], align {{4|8|16}}
----------------
andykaylor wrote:
Why are there three alignment possibilities here? I had almost convinced myself
that my concerns about alignment were unfounded, but I wanted to check that we
were match OGCG. The `{{4|8|16}}` match here reignited my doubts.
https://github.com/llvm/llvm-project/pull/222420
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits