
Generate nuw GEPs for struct member accesses, as inbounds + non-negative implies nuw. Regression tests are updated using update scripts where possible, and by find + replace where not.
480 lines
24 KiB
C
480 lines
24 KiB
C
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
|
|
// REQUIRES: webassembly-registered-target
|
|
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -o - %s | FileCheck %s
|
|
|
|
// Multiple targets use emitVoidPtrVAArg to lower va_arg instructions in clang
|
|
// PPC is complicated, excluding from this case analysis
|
|
// ForceRightAdjust is false for all non-PPC targets
|
|
// AllowHigherAlign is only false for two Microsoft targets, both of which
|
|
// pass most things by reference.
|
|
//
|
|
// Address emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
|
// QualType ValueTy, bool IsIndirect,
|
|
// TypeInfoChars ValueInfo, CharUnits SlotSizeAndAlign,
|
|
// bool AllowHigherAlign, bool ForceRightAdjust =
|
|
// false);
|
|
//
|
|
// Target IsIndirect SlotSize AllowHigher ForceRightAdjust
|
|
// ARC false four true false
|
|
// ARM varies four true false
|
|
// Mips false 4 or 8 true false
|
|
// RISCV varies register true false
|
|
// PPC elided
|
|
// LoongArch varies register true false
|
|
// NVPTX WIP
|
|
// AMDGPU WIP
|
|
// X86_32 false four true false
|
|
// X86_64 MS varies eight false false
|
|
// CSKY false four true false
|
|
// Webassembly varies four true false
|
|
// AArch64 false eight true false
|
|
// AArch64 MS false eight false false
|
|
//
|
|
// Webassembly passes indirectly iff it's an aggregate of multiple values
|
|
// Choosing this as a representative architecture to check IR generation
|
|
// partly because it has a relatively simple variadic calling convention.
|
|
|
|
// Int, by itself and packed in structs
|
|
// CHECK-LABEL: @raw_int(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: ret i32 [[TMP0]]
|
|
//
|
|
int raw_int(__builtin_va_list list) { return __builtin_va_arg(list, int); }
|
|
|
|
typedef struct {
|
|
int x;
|
|
} one_int_t;
|
|
|
|
// CHECK-LABEL: @one_int(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_INT_T:%.*]], align 4
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_INT_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
|
|
// CHECK-NEXT: ret i32 [[TMP0]]
|
|
//
|
|
one_int_t one_int(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, one_int_t);
|
|
}
|
|
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
} two_int_t;
|
|
|
|
// CHECK-LABEL: @two_int(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT:%.*]], ptr align 4 [[TMP0]], i32 8, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
two_int_t two_int(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, two_int_t);
|
|
}
|
|
|
|
// Double, by itself and packed in structs
|
|
// CHECK-LABEL: @raw_double(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
|
|
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8
|
|
// CHECK-NEXT: ret double [[TMP1]]
|
|
//
|
|
double raw_double(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, double);
|
|
}
|
|
|
|
typedef struct {
|
|
double x;
|
|
} one_double_t;
|
|
|
|
// CHECK-LABEL: @one_double(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_DOUBLE_T:%.*]], align 8
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
|
|
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[RETVAL]], ptr align 8 [[ARGP_CUR_ALIGNED]], i32 8, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_DOUBLE_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[COERCE_DIVE]], align 8
|
|
// CHECK-NEXT: ret double [[TMP1]]
|
|
//
|
|
one_double_t one_double(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, one_double_t);
|
|
}
|
|
|
|
typedef struct {
|
|
double x;
|
|
double y;
|
|
} two_double_t;
|
|
|
|
// CHECK-LABEL: @two_double(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[TMP0]], i32 16, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
two_double_t two_double(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, two_double_t);
|
|
}
|
|
|
|
// Scalar smaller than the slot size (C would promote a short to int)
|
|
typedef struct {
|
|
char x;
|
|
} one_char_t;
|
|
|
|
// CHECK-LABEL: @one_char(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_CHAR_T:%.*]], align 1
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_CHAR_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[COERCE_DIVE]], align 1
|
|
// CHECK-NEXT: ret i8 [[TMP0]]
|
|
//
|
|
one_char_t one_char(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, one_char_t);
|
|
}
|
|
|
|
typedef struct {
|
|
short x;
|
|
} one_short_t;
|
|
|
|
// CHECK-LABEL: @one_short(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_SHORT_T:%.*]], align 2
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 2, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_SHORT_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[COERCE_DIVE]], align 2
|
|
// CHECK-NEXT: ret i16 [[TMP0]]
|
|
//
|
|
one_short_t one_short(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, one_short_t);
|
|
}
|
|
|
|
// Composite smaller than the slot size
|
|
typedef struct {
|
|
_Alignas(2) char x;
|
|
char y;
|
|
} char_pair_t;
|
|
|
|
// CHECK-LABEL: @char_pair(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[AGG_RESULT:%.*]], ptr align 2 [[TMP0]], i32 2, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
char_pair_t char_pair(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, char_pair_t);
|
|
}
|
|
|
|
// Empty struct
|
|
typedef struct {
|
|
} empty_t;
|
|
|
|
// CHECK-LABEL: @empty(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_T:%.*]], align 1
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 0
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 0, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
empty_t empty(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, empty_t);
|
|
}
|
|
|
|
typedef struct {
|
|
empty_t x;
|
|
int y;
|
|
} empty_int_t;
|
|
|
|
// CHECK-LABEL: @empty_int(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_INT_T:%.*]], align 4
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_EMPTY_INT_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
|
|
// CHECK-NEXT: ret i32 [[TMP0]]
|
|
//
|
|
empty_int_t empty_int(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, empty_int_t);
|
|
}
|
|
|
|
typedef struct {
|
|
int x;
|
|
empty_t y;
|
|
} int_empty_t;
|
|
|
|
// CHECK-LABEL: @int_empty(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_INT_EMPTY_T:%.*]], align 4
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_INT_EMPTY_T]], ptr [[RETVAL]], i32 0, i32 0
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
|
|
// CHECK-NEXT: ret i32 [[TMP0]]
|
|
//
|
|
int_empty_t int_empty(__builtin_va_list list) {
|
|
return __builtin_va_arg(list, int_empty_t);
|
|
}
|
|
|
|
// Need multiple va_arg instructions to check the postincrement
|
|
// Using types that are passed directly as the indirect handling
|
|
// is independent of the alignment handling in emitVoidPtrDirectVAArg.
|
|
|
|
// CHECK-LABEL: @multiple_int(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP0]], ptr [[TMP1]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4
|
|
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP4]], ptr [[TMP5]], align 4
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void multiple_int(__builtin_va_list list, int *out0, int *out1, int *out2) {
|
|
*out0 = __builtin_va_arg(list, int);
|
|
*out1 = __builtin_va_arg(list, int);
|
|
*out2 = __builtin_va_arg(list, int);
|
|
}
|
|
|
|
// Scalars in structs are an easy way of specifying alignment from C
|
|
// CHECK-LABEL: @increasing_alignment(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP0]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false)
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP1]], ptr align 4 [[ARGP_CUR1]], i32 2, i1 false)
|
|
// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 7
|
|
// CHECK-NEXT: [[ARGP_CUR5_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP4]], i32 -8)
|
|
// CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5_ALIGNED]], i32 8
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[ARGP_CUR5_ALIGNED]], align 8
|
|
// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4
|
|
// CHECK-NEXT: store double [[TMP5]], ptr [[TMP6]], align 8
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void increasing_alignment(__builtin_va_list list, one_char_t *out0,
|
|
one_short_t *out1, int *out2, double *out3) {
|
|
*out0 = __builtin_va_arg(list, one_char_t);
|
|
*out1 = __builtin_va_arg(list, one_short_t);
|
|
*out2 = __builtin_va_arg(list, int);
|
|
*out3 = __builtin_va_arg(list, double);
|
|
}
|
|
|
|
// CHECK-LABEL: @decreasing_alignment(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
|
|
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
|
|
// CHECK-NEXT: store double [[TMP1]], ptr [[TMP2]], align 8
|
|
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4
|
|
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP3]], ptr [[TMP4]], align 4
|
|
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP5]], ptr align 4 [[ARGP_CUR3]], i32 2, i1 false)
|
|
// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP6]], ptr align 4 [[ARGP_CUR5]], i32 1, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void decreasing_alignment(__builtin_va_list list, double *out0, int *out1,
|
|
one_short_t *out2, one_char_t *out3) {
|
|
*out0 = __builtin_va_arg(list, double);
|
|
*out1 = __builtin_va_arg(list, int);
|
|
*out2 = __builtin_va_arg(list, one_short_t);
|
|
*out3 = __builtin_va_arg(list, one_char_t);
|
|
}
|
|
|
|
// Typical edge cases, none hit special handling in VAArg lowering.
|
|
typedef struct {
|
|
int x[16];
|
|
double y[8];
|
|
} large_value_t;
|
|
|
|
// CHECK-LABEL: @large_value(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[TMP0]], ptr align 8 [[TMP1]], i32 128, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void large_value(__builtin_va_list list, large_value_t *out) {
|
|
*out = __builtin_va_arg(list, large_value_t);
|
|
}
|
|
|
|
typedef int v128_t __attribute__((__vector_size__(16), __aligned__(16)));
|
|
// CHECK-LABEL: @vector(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 15
|
|
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16)
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 16
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[ARGP_CUR_ALIGNED]], align 16
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: store <4 x i32> [[TMP1]], ptr [[TMP2]], align 16
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void vector(__builtin_va_list list, v128_t *out) {
|
|
*out = __builtin_va_arg(list, v128_t);
|
|
}
|
|
|
|
typedef struct BF {
|
|
float not_an_i32[2];
|
|
int A : 1;
|
|
char B;
|
|
int C : 13;
|
|
} BF;
|
|
|
|
// CHECK-LABEL: @bitfield(
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[TMP0]], ptr align 4 [[TMP1]], i32 12, i1 false)
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
void bitfield(__builtin_va_list list, BF *out) {
|
|
*out = __builtin_va_arg(list, BF);
|
|
}
|