This is a mostly-target-independent variadic function optimisation and lowering pass. It is only enabled for AMDGPU in this initial commit. The purpose is to make C style variadic functions a zero cost abstraction. They are lowered to equivalent IR which is then amenable to other optimisations. This is inherently slightly target specific but much less so than one might expect - the C varargs interface heavily constrains the ABI design divergence. The pass is primarily tested from webassembly. This is because wasm has a straightforward variadic lowering strategy which coincides exactly with what this pass transforms code into and a struct passing convention with few cases to check. Adding further targets conventions is straightforward and elided from this patch primarily to simplify the review. Implemented in other branches are Linux X86, AMD64, AArch64 and NVPTX. Testing for targets that have existing lowering for va_arg from clang is most efficiently done by checking that clang | opt completely elides the variadic syntax from test cases. The lowering produces a struct for each call site which can be inspected to check the various alignment and indirections are correct. AMDGPU presently has no variadic support other than some ad hoc printf handling. Combined with the pass being inactive on all other targets landing this represents strict increase in capability with zero risk. Testing and refining will continue post commit. In addition to the compiler tests included here, a self contained x64 clang/musl toolchain was constructed using the "lowering" instead of the systemv ABI and used to build various C programs like lua and libxml2.
485 lines
22 KiB
LLVM
485 lines
22 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p --function-signature
|
|
; RUN: opt -S --passes=expand-variadics --expand-variadics-override=lowering < %s | FileCheck %s
|
|
; REQUIRES: webassembly-registered-target
|
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
; Check the variables are lowered to the locations this target expects
|
|
|
|
; The types show the call frames
|
|
; CHECK: %single_i32.vararg = type <{ i32 }>
|
|
; CHECK: %single_double.vararg = type <{ double }>
|
|
; CHECK: %single_v4f32.vararg = type <{ <4 x float> }>
|
|
; CHECK: %single_v8f32.vararg = type <{ <8 x float> }>
|
|
; CHECK: %single_v16f32.vararg = type <{ <16 x float> }>
|
|
; CHECK: %single_v32f32.vararg = type <{ <32 x float> }>
|
|
; CHECK: %i32_double.vararg = type <{ i32, [4 x i8], double }>
|
|
; CHECK: %double_i32.vararg = type <{ double, i32 }>
|
|
; CHECK: %i32_libcS.vararg = type <{ i32, ptr }>
|
|
; CHECK: %libcS_i32.vararg = type <{ ptr, i32 }>
|
|
; CHECK: %i32_v4f32.vararg = type <{ i32, [12 x i8], <4 x float> }>
|
|
; CHECK: %v4f32_i32.vararg = type <{ <4 x float>, i32 }>
|
|
; CHECK: %i32_v8f32.vararg = type <{ i32, [28 x i8], <8 x float> }>
|
|
; CHECK: %v8f32_i32.vararg = type <{ <8 x float>, i32 }>
|
|
; CHECK: %i32_v16f32.vararg = type <{ i32, [60 x i8], <16 x float> }>
|
|
; CHECK: %v16f32_i32.vararg = type <{ <16 x float>, i32 }>
|
|
; CHECK: %i32_v32f32.vararg = type <{ i32, [124 x i8], <32 x float> }>
|
|
; CHECK: %v32f32_i32.vararg = type <{ <32 x float>, i32 }>
|
|
; CHECK: %fptr_single_i32.vararg = type <{ i32 }>
|
|
; CHECK: %fptr_libcS.vararg = type <{ ptr }>
|
|
|
|
%struct.libcS = type { i8, i16, i32, i32, float, double }
|
|
|
|
@vararg_ptr = hidden global ptr @vararg, align 4
|
|
|
|
define hidden void @copy(ptr noundef %va) {
|
|
; CHECK-LABEL: define {{[^@]+}}@copy(ptr noundef %va) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %va.addr = alloca ptr, align 4
|
|
; CHECK-NEXT: %cp = alloca ptr, align 4
|
|
; CHECK-NEXT: store ptr %va, ptr %va.addr, align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %cp)
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %cp, ptr %va.addr, i32 4, i1 false)
|
|
; CHECK-NEXT: %0 = load ptr, ptr %cp, align 4
|
|
; CHECK-NEXT: call void @valist(ptr noundef %0)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %cp)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%va.addr = alloca ptr, align 4
|
|
%cp = alloca ptr, align 4
|
|
store ptr %va, ptr %va.addr, align 4
|
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %cp)
|
|
call void @llvm.va_copy.p0(ptr nonnull %cp, ptr nonnull %va.addr)
|
|
%0 = load ptr, ptr %cp, align 4
|
|
call void @valist(ptr noundef %0)
|
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %cp)
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
|
|
|
|
declare void @llvm.va_copy.p0(ptr, ptr)
|
|
|
|
declare void @valist(ptr noundef)
|
|
|
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
|
|
|
|
define hidden void @start_once(...) {
|
|
; CHECK-LABEL: define {{[^@]+}}@start_once(ptr %varargs) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %s = alloca ptr, align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s)
|
|
; CHECK-NEXT: store ptr %varargs, ptr %s, align 4
|
|
; CHECK-NEXT: %0 = load ptr, ptr %s, align 4
|
|
; CHECK-NEXT: call void @valist(ptr noundef %0)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%s = alloca ptr, align 4
|
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s)
|
|
call void @llvm.va_start.p0(ptr nonnull %s)
|
|
%0 = load ptr, ptr %s, align 4
|
|
call void @valist(ptr noundef %0)
|
|
call void @llvm.va_end.p0(ptr %s)
|
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s)
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.va_start.p0(ptr)
|
|
|
|
declare void @llvm.va_end.p0(ptr)
|
|
|
|
define hidden void @start_twice(...) {
|
|
; CHECK-LABEL: define {{[^@]+}}@start_twice(ptr %varargs) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %s0 = alloca ptr, align 4
|
|
; CHECK-NEXT: %s1 = alloca ptr, align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s0)
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s1)
|
|
; CHECK-NEXT: store ptr %varargs, ptr %s0, align 4
|
|
; CHECK-NEXT: %0 = load ptr, ptr %s0, align 4
|
|
; CHECK-NEXT: call void @valist(ptr noundef %0)
|
|
; CHECK-NEXT: store ptr %varargs, ptr %s1, align 4
|
|
; CHECK-NEXT: %1 = load ptr, ptr %s1, align 4
|
|
; CHECK-NEXT: call void @valist(ptr noundef %1)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s1)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s0)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%s0 = alloca ptr, align 4
|
|
%s1 = alloca ptr, align 4
|
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s0)
|
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %s1)
|
|
call void @llvm.va_start.p0(ptr nonnull %s0)
|
|
%0 = load ptr, ptr %s0, align 4
|
|
call void @valist(ptr noundef %0)
|
|
call void @llvm.va_end.p0(ptr %s0)
|
|
call void @llvm.va_start.p0(ptr nonnull %s1)
|
|
%1 = load ptr, ptr %s1, align 4
|
|
call void @valist(ptr noundef %1)
|
|
call void @llvm.va_end.p0(ptr %s1)
|
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s1)
|
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %s0)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @single_i32(i32 noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_i32(i32 noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_i32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x)
|
|
ret void
|
|
}
|
|
|
|
declare void @vararg(...)
|
|
|
|
define hidden void @single_double(double noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_double(double noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_double.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_double.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store double %x, ptr %0, align 8
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(double noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @single_v4f32(<4 x float> noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_v4f32(<4 x float> noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_v4f32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_v4f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <4 x float> %x, ptr %0, align 16
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<4 x float> noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @single_v8f32(<8 x float> noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_v8f32(<8 x float> noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_v8f32.vararg, align 32
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_v8f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <8 x float> %x, ptr %0, align 32
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<8 x float> noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @single_v16f32(<16 x float> noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_v16f32(<16 x float> noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_v16f32.vararg, align 64
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 64, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_v16f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <16 x float> %x, ptr %0, align 64
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 64, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<16 x float> noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @single_v32f32(<32 x float> noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@single_v32f32(<32 x float> noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %single_v32f32.vararg, align 128
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 128, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %single_v32f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <32 x float> %x, ptr %0, align 128
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 128, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<32 x float> noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_double(i32 noundef %x, double noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_double(i32 noundef %x, double noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_double.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_double.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_double.vararg, ptr %vararg_buffer, i32 0, i32 2
|
|
; CHECK-NEXT: store double %y, ptr %1, align 8
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, double noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @double_i32(double noundef %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@double_i32(double noundef %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %double_i32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 12, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %double_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store double %x, ptr %0, align 8
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %double_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 12, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(double noundef %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_libcS(i32 noundef %x, ptr noundef byval(%struct.libcS) align 8 %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_libcS(i32 noundef %x, ptr noundef byval(%struct.libcS) align 8 %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %IndirectAlloca = alloca %struct.libcS, align 8
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_libcS.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %IndirectAlloca, ptr %y, i64 24, i1 false)
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_libcS.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_libcS.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store ptr %IndirectAlloca, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, ptr noundef nonnull byval(%struct.libcS) align 8 %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @libcS_i32(ptr noundef byval(%struct.libcS) align 8 %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@libcS_i32(ptr noundef byval(%struct.libcS) align 8 %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %IndirectAlloca = alloca %struct.libcS, align 8
|
|
; CHECK-NEXT: %vararg_buffer = alloca %libcS_i32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %IndirectAlloca, ptr %x, i64 24, i1 false)
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %libcS_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store ptr %IndirectAlloca, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %libcS_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(ptr noundef nonnull byval(%struct.libcS) align 8 %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_v4f32(i32 noundef %x, <4 x float> noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_v4f32(i32 noundef %x, <4 x float> noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_v4f32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v4f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v4f32.vararg, ptr %vararg_buffer, i32 0, i32 2
|
|
; CHECK-NEXT: store <4 x float> %y, ptr %1, align 16
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, <4 x float> noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @v4f32_i32(<4 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@v4f32_i32(<4 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %v4f32_i32.vararg, align 16
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %v4f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <4 x float> %x, ptr %0, align 16
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %v4f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<4 x float> noundef %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_v8f32(i32 noundef %x, <8 x float> noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_v8f32(i32 noundef %x, <8 x float> noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_v8f32.vararg, align 32
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 64, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v8f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v8f32.vararg, ptr %vararg_buffer, i32 0, i32 2
|
|
; CHECK-NEXT: store <8 x float> %y, ptr %1, align 32
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 64, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, <8 x float> noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @v8f32_i32(<8 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@v8f32_i32(<8 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %v8f32_i32.vararg, align 32
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 36, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %v8f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <8 x float> %x, ptr %0, align 32
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %v8f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 36, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<8 x float> noundef %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_v16f32(i32 noundef %x, <16 x float> noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_v16f32(i32 noundef %x, <16 x float> noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_v16f32.vararg, align 64
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 128, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v16f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v16f32.vararg, ptr %vararg_buffer, i32 0, i32 2
|
|
; CHECK-NEXT: store <16 x float> %y, ptr %1, align 64
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 128, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, <16 x float> noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @v16f32_i32(<16 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@v16f32_i32(<16 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %v16f32_i32.vararg, align 64
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 68, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %v16f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <16 x float> %x, ptr %0, align 64
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %v16f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 68, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<16 x float> noundef %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @i32_v32f32(i32 noundef %x, <32 x float> noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@i32_v32f32(i32 noundef %x, <32 x float> noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %i32_v32f32.vararg, align 128
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 256, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v32f32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %0, align 4
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v32f32.vararg, ptr %vararg_buffer, i32 0, i32 2
|
|
; CHECK-NEXT: store <32 x float> %y, ptr %1, align 128
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 256, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(i32 noundef %x, <32 x float> noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @v32f32_i32(<32 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-LABEL: define {{[^@]+}}@v32f32_i32(<32 x float> noundef %x, i32 noundef %y) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %v32f32_i32.vararg, align 128
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 132, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %0 = getelementptr inbounds %v32f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store <32 x float> %x, ptr %0, align 128
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %v32f32_i32.vararg, ptr %vararg_buffer, i32 0, i32 1
|
|
; CHECK-NEXT: store i32 %y, ptr %1, align 4
|
|
; CHECK-NEXT: call void @vararg(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 132, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
tail call void (...) @vararg(<32 x float> noundef %x, i32 noundef %y)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @fptr_single_i32(i32 noundef %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@fptr_single_i32(i32 noundef %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %vararg_buffer = alloca %fptr_single_i32.vararg, align 16
|
|
; CHECK-NEXT: %0 = load volatile ptr, ptr @vararg_ptr, align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %fptr_single_i32.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store i32 %x, ptr %1, align 4
|
|
; CHECK-NEXT: call void %0(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%0 = load volatile ptr, ptr @vararg_ptr, align 4
|
|
tail call void (...) %0(i32 noundef %x)
|
|
ret void
|
|
}
|
|
|
|
define hidden void @fptr_libcS(ptr noundef byval(%struct.libcS) align 8 %x) {
|
|
; CHECK-LABEL: define {{[^@]+}}@fptr_libcS(ptr noundef byval(%struct.libcS) align 8 %x) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %IndirectAlloca = alloca %struct.libcS, align 8
|
|
; CHECK-NEXT: %vararg_buffer = alloca %fptr_libcS.vararg, align 16
|
|
; CHECK-NEXT: %0 = load volatile ptr, ptr @vararg_ptr, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %IndirectAlloca, ptr %x, i64 24, i1 false)
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: %1 = getelementptr inbounds %fptr_libcS.vararg, ptr %vararg_buffer, i32 0, i32 0
|
|
; CHECK-NEXT: store ptr %IndirectAlloca, ptr %1, align 4
|
|
; CHECK-NEXT: call void %0(ptr %vararg_buffer)
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %vararg_buffer)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%0 = load volatile ptr, ptr @vararg_ptr, align 4
|
|
tail call void (...) %0(ptr noundef nonnull byval(%struct.libcS) align 8 %x)
|
|
ret void
|
|
}
|
|
|