; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -mtriple=wasm32-unknown-unknown -S --passes=expand-variadics --expand-variadics-override=optimize < %s | FileCheck %s -check-prefixes=OPT
; RUN: opt -mtriple=wasm32-unknown-unknown -S --passes=expand-variadics --expand-variadics-override=lowering < %s | FileCheck %s -check-prefixes=ABI
; REQUIRES: webassembly-registered-target
; Wasm passes struct {char} as an i8 so can check the varargs passing works on integers smaller than the slot size
declare void @sink(...)
define void @pass_nothing() {
; OPT-LABEL: @pass_nothing(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink()
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_nothing(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_NOTHING_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 1, ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 1, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink()
ret void
}
define void @pass_s1(i8 %x) {
; OPT-LABEL: @pass_s1(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i8 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_s1(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_S1_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 1, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_S1_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i8 [[X:%.*]], ptr [[TMP0]], align 1
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 1, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i8 %x)
ret void
}
define void @pass_s2(i16 %x) {
; OPT-LABEL: @pass_s2(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i16 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_s2(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_S2_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 2, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_S2_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i16 [[X:%.*]], ptr [[TMP0]], align 2
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 2, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i16 %x)
ret void
}
define void @pass_s3(i32 %x) {
; OPT-LABEL: @pass_s3(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_s3(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_S3_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_S3_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[X:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %x)
ret void
}
define void @pass_s4(i64 %x) {
; OPT-LABEL: @pass_s4(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i64 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_s4(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_S4_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_S4_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i64 [[X:%.*]], ptr [[TMP0]], align 8
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i64 %x)
ret void
}
define void @pass_s5(<4 x i32> %x) {
; OPT-LABEL: @pass_s5(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(<4 x i32> [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_s5(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_S5_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_S5_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store <4 x i32> [[X:%.*]], ptr [[TMP0]], align 16
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(<4 x i32> %x)
ret void
}
define void @pass_int_s1(i32 %i, i8 %x) {
; OPT-LABEL: @pass_int_s1(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], i8 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_int_s1(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_INT_S1_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 5, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_INT_S1_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_INT_S1_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
; ABI-NEXT: store i8 [[X:%.*]], ptr [[TMP1]], align 1
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 5, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, i8 %x)
ret void
}
define void @pass_int_s2(i32 %i, i16 %x) {
; OPT-LABEL: @pass_int_s2(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], i16 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_int_s2(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_INT_S2_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 6, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_INT_S2_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_INT_S2_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
; ABI-NEXT: store i16 [[X:%.*]], ptr [[TMP1]], align 2
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 6, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, i16 %x)
ret void
}
define void @pass_int_s3(i32 %i, i32 %x) {
; OPT-LABEL: @pass_int_s3(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], i32 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_int_s3(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_INT_S3_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_INT_S3_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_INT_S3_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
; ABI-NEXT: store i32 [[X:%.*]], ptr [[TMP1]], align 4
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, i32 %x)
ret void
}
define void @pass_int_s4(i32 %i, i64 %x) {
; OPT-LABEL: @pass_int_s4(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], i64 [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_int_s4(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_INT_S4_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_INT_S4_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_INT_S4_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 2
; ABI-NEXT: store i64 [[X:%.*]], ptr [[TMP1]], align 8
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, i64 %x)
ret void
}
define void @pass_int_s5(i32 %i, <4 x i32> %x) {
; OPT-LABEL: @pass_int_s5(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], <4 x i32> [[X:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_int_s5(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_INT_S5_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_INT_S5_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_INT_S5_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 2
; ABI-NEXT: store <4 x i32> [[X:%.*]], ptr [[TMP1]], align 16
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, <4 x i32> %x)
ret void
}
define void @pass_asc(i8 %x1, i16 %x2, i32 %x3, i64 %x4, <4 x i32> %x5) {
; OPT-LABEL: @pass_asc(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i8 [[X1:%.*]], i16 [[X2:%.*]], i32 [[X3:%.*]], i64 [[X4:%.*]], <4 x i32> [[X5:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_asc(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_ASC_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 48, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_ASC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i8 [[X1:%.*]], ptr [[TMP0]], align 1
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_ASC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 2
; ABI-NEXT: store i16 [[X2:%.*]], ptr [[TMP1]], align 2
; ABI-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw [[PASS_ASC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 4
; ABI-NEXT: store i32 [[X3:%.*]], ptr [[TMP2]], align 4
; ABI-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[PASS_ASC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 6
; ABI-NEXT: store i64 [[X4:%.*]], ptr [[TMP3]], align 8
; ABI-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw [[PASS_ASC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 8
; ABI-NEXT: store <4 x i32> [[X5:%.*]], ptr [[TMP4]], align 16
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 48, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i8 %x1, i16 %x2, i32 %x3, i64 %x4, <4 x i32> %x5)
ret void
}
define void @pass_dsc(<4 x i32> %x0, i64 %x1, i32 %x2, i16 %x3, i8 %x4) {
; OPT-LABEL: @pass_dsc(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(<4 x i32> [[X0:%.*]], i64 [[X1:%.*]], i32 [[X2:%.*]], i16 [[X3:%.*]], i8 [[X4:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_dsc(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_DSC_VARARG:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 33, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_DSC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store <4 x i32> [[X0:%.*]], ptr [[TMP0]], align 16
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_DSC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
; ABI-NEXT: store i64 [[X1:%.*]], ptr [[TMP1]], align 8
; ABI-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw [[PASS_DSC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 2
; ABI-NEXT: store i32 [[X2:%.*]], ptr [[TMP2]], align 4
; ABI-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[PASS_DSC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 3
; ABI-NEXT: store i16 [[X3:%.*]], ptr [[TMP3]], align 2
; ABI-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw [[PASS_DSC_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 5
; ABI-NEXT: store i8 [[X4:%.*]], ptr [[TMP4]], align 1
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 33, ptr [[VARARG_BUFFER]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(<4 x i32> %x0, i64 %x1, i32 %x2, i16 %x3, i8 %x4)
ret void
}
define void @pass_multiple(i32 %i, i8 %x1, i16 %x2, i32 %x3, i64 %x4, <4 x i32> %x5) {
; OPT-LABEL: @pass_multiple(
; OPT-NEXT: entry:
; OPT-NEXT: tail call void (...) @sink(i32 [[I:%.*]], i16 [[X2:%.*]], i64 [[X4:%.*]])
; OPT-NEXT: tail call void (...) @sink(i32 [[I]], i8 [[X1:%.*]], i32 [[X3:%.*]], <4 x i32> [[X5:%.*]])
; OPT-NEXT: ret void
;
; ABI-LABEL: @pass_multiple(
; ABI-NEXT: entry:
; ABI-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[PASS_MULTIPLE_VARARG:%.*]], align 16
; ABI-NEXT: [[VARARG_BUFFER1:%.*]] = alloca [[PASS_MULTIPLE_VARARG_0:%.*]], align 16
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
; ABI-NEXT: store i32 [[I:%.*]], ptr [[TMP0]], align 4
; ABI-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
; ABI-NEXT: store i16 [[X2:%.*]], ptr [[TMP1]], align 2
; ABI-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 3
; ABI-NEXT: store i64 [[X4:%.*]], ptr [[TMP2]], align 8
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr [[VARARG_BUFFER]])
; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr [[VARARG_BUFFER1]])
; ABI-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG_0]], ptr [[VARARG_BUFFER1]], i32 0, i32 0
; ABI-NEXT: store i32 [[I]], ptr [[TMP3]], align 4
; ABI-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG_0]], ptr [[VARARG_BUFFER1]], i32 0, i32 1
; ABI-NEXT: store i8 [[X1:%.*]], ptr [[TMP4]], align 1
; ABI-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG_0]], ptr [[VARARG_BUFFER1]], i32 0, i32 3
; ABI-NEXT: store i32 [[X3:%.*]], ptr [[TMP5]], align 4
; ABI-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw [[PASS_MULTIPLE_VARARG_0]], ptr [[VARARG_BUFFER1]], i32 0, i32 5
; ABI-NEXT: store <4 x i32> [[X5:%.*]], ptr [[TMP6]], align 16
; ABI-NEXT: call void @sink(ptr [[VARARG_BUFFER1]])
; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr [[VARARG_BUFFER1]])
; ABI-NEXT: ret void
;
entry:
tail call void (...) @sink(i32 %i, i16 %x2, i64 %x4)
tail call void (...) @sink(i32 %i, i8 %x1, i32 %x3, <4 x i32> %x5)
ret void
}