Summary: We support variadic functions in AMDGPU / NVPTX via an LLVM-IR pass. This patch applies the same handling here to support them on this target. I am unsure what the ABI should look like here, I have mostly copied the one we use for NVPTX where it's basically a struct layout with natural alignment. This wastes some space, which is why AMDGPU does not pad them. Additionally, this required allowing the SPIRV_FUNC calling convention. I'm assuming this is compatible with the C calling convention in IR, but I will need someone to confirm that for me.
219 lines
11 KiB
LLVM
219 lines
11 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
|
|
; RUN: opt -S -mtriple=spirv64-- --passes=expand-variadics --expand-variadics-override=lowering < %s | FileCheck %s
|
|
|
|
%struct.agg = type { i32, double }
|
|
|
|
define spir_func void @variadic_sink(i32 noundef %tag, ...) {
|
|
; CHECK-LABEL: define spir_func void @variadic_sink(
|
|
; CHECK-SAME: i32 noundef [[TAG:%.*]], ptr [[VARARGS:%.*]]) {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[AP:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: store ptr [[VARARGS]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: switch i32 [[TAG]], label %[[SW_DEFAULT:.*]] [
|
|
; CHECK-NEXT: i32 0, label %[[SW_BB:.*]]
|
|
; CHECK-NEXT: i32 1, label %[[SW_BB1:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[SW_BB4:.*]]
|
|
; CHECK-NEXT: i32 3, label %[[SW_BB7:.*]]
|
|
; CHECK-NEXT: i32 4, label %[[SW_BB10:.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: [[SW_BB]]:
|
|
; CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 3
|
|
; CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP0]], i64 -4)
|
|
; CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 4
|
|
; CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR_ALIGNED]], align 4
|
|
; CHECK-NEXT: br label %[[SW_EPILOG:.*]]
|
|
; CHECK: [[SW_BB1]]:
|
|
; CHECK-NEXT: [[ARGP_CUR2:%.*]] = load ptr, ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2]], i32 7
|
|
; CHECK-NEXT: [[ARGP_CUR2_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP2]], i64 -8)
|
|
; CHECK-NEXT: [[ARGP_NEXT3:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR2_ALIGNED]], i64 8
|
|
; CHECK-NEXT: store ptr [[ARGP_NEXT3]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[ARGP_CUR2_ALIGNED]], align 8
|
|
; CHECK-NEXT: br label %[[SW_EPILOG]]
|
|
; CHECK: [[SW_BB4]]:
|
|
; CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 7
|
|
; CHECK-NEXT: [[ARGP_CUR5_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP4]], i64 -8)
|
|
; CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5_ALIGNED]], i64 8
|
|
; CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[ARGP_CUR5_ALIGNED]], align 8
|
|
; CHECK-NEXT: br label %[[SW_EPILOG]]
|
|
; CHECK: [[SW_BB7]]:
|
|
; CHECK-NEXT: [[ARGP_CUR8:%.*]] = load ptr, ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR8]], i32 7
|
|
; CHECK-NEXT: [[ARGP_CUR8_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP6]], i64 -8)
|
|
; CHECK-NEXT: [[ARGP_NEXT9:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR8_ALIGNED]], i64 16
|
|
; CHECK-NEXT: store ptr [[ARGP_NEXT9]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: br label %[[SW_EPILOG]]
|
|
; CHECK: [[SW_BB10]]:
|
|
; CHECK-NEXT: [[ARGP_CUR11:%.*]] = load ptr, ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR11]], i32 15
|
|
; CHECK-NEXT: [[ARGP_CUR11_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP7]], i64 -16)
|
|
; CHECK-NEXT: [[ARGP_NEXT12:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR11_ALIGNED]], i64 16
|
|
; CHECK-NEXT: store ptr [[ARGP_NEXT12]], ptr [[AP]], align 8
|
|
; CHECK-NEXT: [[TMP8:%.*]] = load <4 x float>, ptr [[ARGP_CUR11_ALIGNED]], align 16
|
|
; CHECK-NEXT: br label %[[SW_EPILOG]]
|
|
; CHECK: [[SW_DEFAULT]]:
|
|
; CHECK-NEXT: br label %[[SW_EPILOG]]
|
|
; CHECK: [[SW_EPILOG]]:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%ap = alloca ptr, align 8
|
|
call void @llvm.va_start.p0(ptr %ap)
|
|
switch i32 %tag, label %sw.default [
|
|
i32 0, label %sw.bb
|
|
i32 1, label %sw.bb1
|
|
i32 2, label %sw.bb4
|
|
i32 3, label %sw.bb7
|
|
i32 4, label %sw.bb10
|
|
]
|
|
|
|
sw.bb: ; preds = %entry
|
|
%argp.cur = load ptr, ptr %ap, align 8
|
|
%0 = getelementptr inbounds i8, ptr %argp.cur, i32 3
|
|
%argp.cur.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %0, i64 -4)
|
|
%argp.next = getelementptr inbounds i8, ptr %argp.cur.aligned, i64 4
|
|
store ptr %argp.next, ptr %ap, align 8
|
|
%1 = load i32, ptr %argp.cur.aligned, align 4
|
|
br label %sw.epilog
|
|
|
|
sw.bb1: ; preds = %entry
|
|
%argp.cur2 = load ptr, ptr %ap, align 8
|
|
%2 = getelementptr inbounds i8, ptr %argp.cur2, i32 7
|
|
%argp.cur2.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %2, i64 -8)
|
|
%argp.next3 = getelementptr inbounds i8, ptr %argp.cur2.aligned, i64 8
|
|
store ptr %argp.next3, ptr %ap, align 8
|
|
%3 = load i64, ptr %argp.cur2.aligned, align 8
|
|
br label %sw.epilog
|
|
|
|
sw.bb4: ; preds = %entry
|
|
%argp.cur5 = load ptr, ptr %ap, align 8
|
|
%4 = getelementptr inbounds i8, ptr %argp.cur5, i32 7
|
|
%argp.cur5.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %4, i64 -8)
|
|
%argp.next6 = getelementptr inbounds i8, ptr %argp.cur5.aligned, i64 8
|
|
store ptr %argp.next6, ptr %ap, align 8
|
|
%5 = load double, ptr %argp.cur5.aligned, align 8
|
|
br label %sw.epilog
|
|
|
|
sw.bb7: ; preds = %entry
|
|
%argp.cur8 = load ptr, ptr %ap, align 8
|
|
%6 = getelementptr inbounds i8, ptr %argp.cur8, i32 7
|
|
%argp.cur8.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %6, i64 -8)
|
|
%argp.next9 = getelementptr inbounds i8, ptr %argp.cur8.aligned, i64 16
|
|
store ptr %argp.next9, ptr %ap, align 8
|
|
br label %sw.epilog
|
|
|
|
sw.bb10: ; preds = %entry
|
|
%argp.cur11 = load ptr, ptr %ap, align 8
|
|
%7 = getelementptr inbounds i8, ptr %argp.cur11, i32 15
|
|
%argp.cur11.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %7, i64 -16)
|
|
%argp.next12 = getelementptr inbounds i8, ptr %argp.cur11.aligned, i64 16
|
|
store ptr %argp.next12, ptr %ap, align 8
|
|
%8 = load <4 x float>, ptr %argp.cur11.aligned, align 16
|
|
br label %sw.epilog
|
|
|
|
sw.default: ; preds = %entry
|
|
br label %sw.epilog
|
|
|
|
sw.epilog: ; preds = %sw.default, %sw.bb10, %sw.bb7, %sw.bb4, %sw.bb1, %sw.bb
|
|
call void @llvm.va_end.p0(ptr %ap)
|
|
ret void
|
|
}
|
|
|
|
define spir_func void @call_i32() {
|
|
; CHECK-LABEL: define spir_func void @call_i32() {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[CALL_I32_VARARG:%.*]], align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[CALL_I32_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
|
|
; CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4
|
|
; CHECK-NEXT: call spir_func void @variadic_sink(i32 noundef 0, ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
call spir_func void (i32, ...) @variadic_sink(i32 noundef 0, i32 noundef 1)
|
|
ret void
|
|
}
|
|
|
|
define spir_func void @call_i64() {
|
|
; CHECK-LABEL: define spir_func void @call_i64() {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[CALL_I64_VARARG:%.*]], align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[CALL_I64_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
|
|
; CHECK-NEXT: store i64 1, ptr [[TMP0]], align 8
|
|
; CHECK-NEXT: call spir_func void @variadic_sink(i32 noundef 1, ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
call spir_func void (i32, ...) @variadic_sink(i32 noundef 1, i64 noundef 1)
|
|
ret void
|
|
}
|
|
|
|
define spir_func void @call_f64() {
|
|
; CHECK-LABEL: define spir_func void @call_f64() {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[CALL_F64_VARARG:%.*]], align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[CALL_F64_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
|
|
; CHECK-NEXT: store double 1.000000e+00, ptr [[TMP0]], align 8
|
|
; CHECK-NEXT: call spir_func void @variadic_sink(i32 noundef 2, ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
call spir_func void (i32, ...) @variadic_sink(i32 noundef 2, double noundef 1.000000e+00)
|
|
ret void
|
|
}
|
|
|
|
define spir_func void @call_struct() {
|
|
; CHECK-LABEL: define spir_func void @call_struct() {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_AGG:%.*]], align 8
|
|
; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[CALL_STRUCT_VARARG:%.*]], align 8
|
|
; CHECK-NEXT: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_AGG]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0
|
|
; CHECK-NEXT: store i32 1, ptr [[A]], align 8
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP0]], i8 0, i64 4, i1 false)
|
|
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds nuw [[STRUCT_AGG]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1
|
|
; CHECK-NEXT: store double 2.000000e+00, ptr [[B]], align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[CALL_STRUCT_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[TMP1]], ptr [[DOTCOMPOUNDLITERAL]], i64 16, i1 false)
|
|
; CHECK-NEXT: call spir_func void @variadic_sink(i32 noundef 3, ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%.compoundliteral = alloca %struct.agg, align 8
|
|
%a = getelementptr inbounds nuw %struct.agg, ptr %.compoundliteral, i32 0, i32 0
|
|
store i32 1, ptr %a, align 8
|
|
%0 = getelementptr i8, ptr %.compoundliteral, i64 4
|
|
call void @llvm.memset.p0.i64(ptr align 4 %0, i8 0, i64 4, i1 false)
|
|
%b = getelementptr inbounds nuw %struct.agg, ptr %.compoundliteral, i32 0, i32 1
|
|
store double 2.000000e+00, ptr %b, align 8
|
|
call spir_func void (i32, ...) @variadic_sink(i32 noundef 3, ptr noundef byval(%struct.agg) align 8 %.compoundliteral)
|
|
ret void
|
|
}
|
|
|
|
define spir_func void @call_vector() {
|
|
; CHECK-LABEL: define spir_func void @call_vector() {
|
|
; CHECK-NEXT: [[ENTRY:.*:]]
|
|
; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[CALL_VECTOR_VARARG:%.*]], align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[CALL_VECTOR_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
|
|
; CHECK-NEXT: store <4 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00>, ptr [[TMP0]], align 16
|
|
; CHECK-NEXT: call spir_func void @variadic_sink(i32 noundef 4, ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
call spir_func void (i32, ...) @variadic_sink(i32 noundef 4, <4 x float> noundef <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00>)
|
|
ret void
|
|
}
|