Files
clang-p2996/llvm/test/CodeGen/AMDGPU/expand-variadic-call.ll
Jon Chesterfield 8516f54e6a [AMDGPU] Implement variadic functions by IR lowering (#93362)
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.
2024-06-06 10:44:53 +01:00

546 lines
29 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: amdgpu-registered-target
target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
target triple = "amdgcn-amd-amdhsa"
; 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, double }>
; CHECK: %double_i32.vararg = type <{ double, i32 }>
; CHECK: %i32_libcS.vararg = type <{ i32, %struct.libcS }>
; CHECK: %libcS_i32.vararg = type <{ %struct.libcS, i32 }>
; CHECK: %i32_v4f32.vararg = type <{ i32, <4 x float> }>
; CHECK: %v4f32_i32.vararg = type <{ <4 x float>, i32 }>
; CHECK: %i32_v8f32.vararg = type <{ i32, <8 x float> }>
; CHECK: %v8f32_i32.vararg = type <{ <8 x float>, i32 }>
; CHECK: %i32_v16f32.vararg = type <{ i32, <16 x float> }>
; CHECK: %v16f32_i32.vararg = type <{ <16 x float>, i32 }>
; CHECK: %i32_v32f32.vararg = type <{ i32, <32 x float> }>
; CHECK: %v32f32_i32.vararg = type <{ <32 x float>, i32 }>
; CHECK: %fptr_single_i32.vararg = type <{ i32 }>
; CHECK: %fptr_libcS.vararg = type <{ %struct.libcS }>
%struct.libcS = type { i8, i16, i32, i64, float, double }
@vararg_ptr = hidden addrspace(1) global ptr @vararg, align 8
define hidden void @copy(ptr noundef %va) {
; CHECK-LABEL: define {{[^@]+}}@copy(ptr noundef %va) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %va.addr = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: %cp = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: %va.addr.ascast = addrspacecast ptr addrspace(5) %va.addr to ptr
; CHECK-NEXT: %cp.ascast = addrspacecast ptr addrspace(5) %cp to ptr
; CHECK-NEXT: store ptr %va, ptr addrspace(5) %va.addr, align 8
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %cp)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %cp.ascast, ptr %va.addr.ascast, i32 8, i1 false)
; CHECK-NEXT: %0 = load ptr, ptr addrspace(5) %cp, align 8
; CHECK-NEXT: call void @valist(ptr noundef %0)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %cp)
; CHECK-NEXT: ret void
;
entry:
%va.addr = alloca ptr, align 8, addrspace(5)
%cp = alloca ptr, align 8, addrspace(5)
%va.addr.ascast = addrspacecast ptr addrspace(5) %va.addr to ptr
%cp.ascast = addrspacecast ptr addrspace(5) %cp to ptr
store ptr %va, ptr addrspace(5) %va.addr, align 8
call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %cp)
call void @llvm.va_copy.p0(ptr %cp.ascast, ptr nonnull %va.addr.ascast)
%0 = load ptr, ptr addrspace(5) %cp, align 8
call void @valist(ptr noundef %0)
call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %cp)
ret void
}
declare void @llvm.lifetime.start.p5(i64 immarg, ptr addrspace(5) nocapture)
declare void @llvm.va_copy.p0(ptr, ptr)
declare hidden void @valist(ptr noundef)
declare void @llvm.lifetime.end.p5(i64 immarg, ptr addrspace(5) nocapture)
define hidden void @start_once(...) {
; CHECK-LABEL: define {{[^@]+}}@start_once(ptr %varargs) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %s = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: %s.ascast = addrspacecast ptr addrspace(5) %s to ptr
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s)
; CHECK-NEXT: store ptr %varargs, ptr %s.ascast, align 8
; CHECK-NEXT: %0 = load ptr, ptr addrspace(5) %s, align 8
; CHECK-NEXT: call void @valist(ptr noundef %0)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %s)
; CHECK-NEXT: ret void
;
entry:
%s = alloca ptr, align 8, addrspace(5)
%s.ascast = addrspacecast ptr addrspace(5) %s to ptr
call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s)
call void @llvm.va_start.p0(ptr %s.ascast)
%0 = load ptr, ptr addrspace(5) %s, align 8
call void @valist(ptr noundef %0)
call void @llvm.va_end.p0(ptr %s.ascast)
call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %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 8, addrspace(5)
; CHECK-NEXT: %s1 = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: %s0.ascast = addrspacecast ptr addrspace(5) %s0 to ptr
; CHECK-NEXT: %s1.ascast = addrspacecast ptr addrspace(5) %s1 to ptr
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s0)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s1)
; CHECK-NEXT: store ptr %varargs, ptr %s0.ascast, align 8
; CHECK-NEXT: %0 = load ptr, ptr addrspace(5) %s0, align 8
; CHECK-NEXT: call void @valist(ptr noundef %0)
; CHECK-NEXT: store ptr %varargs, ptr %s1.ascast, align 8
; CHECK-NEXT: %1 = load ptr, ptr addrspace(5) %s1, align 8
; CHECK-NEXT: call void @valist(ptr noundef %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %s1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %s0)
; CHECK-NEXT: ret void
;
entry:
%s0 = alloca ptr, align 8, addrspace(5)
%s1 = alloca ptr, align 8, addrspace(5)
%s0.ascast = addrspacecast ptr addrspace(5) %s0 to ptr
%s1.ascast = addrspacecast ptr addrspace(5) %s1 to ptr
call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s0)
call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %s1)
call void @llvm.va_start.p0(ptr %s0.ascast)
%0 = load ptr, ptr addrspace(5) %s0, align 8
call void @valist(ptr noundef %0)
call void @llvm.va_end.p0(ptr %s0.ascast)
call void @llvm.va_start.p0(ptr %s1.ascast)
%1 = load ptr, ptr addrspace(5) %s1, align 8
call void @valist(ptr noundef %1)
call void @llvm.va_end.p0(ptr %s1.ascast)
call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %s1)
call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: ret void
;
entry:
tail call void (...) @vararg(i32 noundef %x)
ret void
}
declare hidden 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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 8, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_double.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store double %x, ptr addrspace(5) %0, align 8
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 8, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 16, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_v4f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <4 x float> %x, ptr addrspace(5) %0, align 16
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 16, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 32, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_v8f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <8 x float> %x, ptr addrspace(5) %0, align 32
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 32, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 64, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_v16f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <16 x float> %x, ptr addrspace(5) %0, align 64
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 64, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 128, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %single_v32f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <32 x float> %x, ptr addrspace(5) %0, align 128
; CHECK-NEXT: %1 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %1)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 128, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 12, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_double.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_double.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store double %y, ptr addrspace(5) %1, align 8
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 12, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 12, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %double_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store double %x, ptr addrspace(5) %0, align 8
; CHECK-NEXT: %1 = getelementptr inbounds %double_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 12, ptr addrspace(5) %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, i8 %y.coerce0, i16 %y.coerce1, i32 %y.coerce2, i64 %y.coerce3, float %y.coerce4, double %y.coerce5) {
; CHECK-LABEL: define {{[^@]+}}@i32_libcS(i32 noundef %x, i8 %y.coerce0, i16 %y.coerce1, i32 %y.coerce2, i64 %y.coerce3, float %y.coerce4, double %y.coerce5) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %vararg_buffer = alloca %i32_libcS.vararg, align 4, addrspace(5)
; CHECK-NEXT: %.fca.0.insert = insertvalue %struct.libcS poison, i8 %y.coerce0, 0
; CHECK-NEXT: %.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %y.coerce1, 1
; CHECK-NEXT: %.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %y.coerce2, 2
; CHECK-NEXT: %.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %y.coerce3, 3
; CHECK-NEXT: %.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %y.coerce4, 4
; CHECK-NEXT: %.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %y.coerce5, 5
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_libcS.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_libcS.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store %struct.libcS %.fca.5.insert, ptr addrspace(5) %1, align 8
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: ret void
;
entry:
%.fca.0.insert = insertvalue %struct.libcS poison, i8 %y.coerce0, 0
%.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %y.coerce1, 1
%.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %y.coerce2, 2
%.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %y.coerce3, 3
%.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %y.coerce4, 4
%.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %y.coerce5, 5
tail call void (...) @vararg(i32 noundef %x, %struct.libcS %.fca.5.insert)
ret void
}
define hidden void @libcS_i32(i8 %x.coerce0, i16 %x.coerce1, i32 %x.coerce2, i64 %x.coerce3, float %x.coerce4, double %x.coerce5, i32 noundef %y) {
; CHECK-LABEL: define {{[^@]+}}@libcS_i32(i8 %x.coerce0, i16 %x.coerce1, i32 %x.coerce2, i64 %x.coerce3, float %x.coerce4, double %x.coerce5, i32 noundef %y) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %vararg_buffer = alloca %libcS_i32.vararg, align 4, addrspace(5)
; CHECK-NEXT: %.fca.0.insert = insertvalue %struct.libcS poison, i8 %x.coerce0, 0
; CHECK-NEXT: %.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %x.coerce1, 1
; CHECK-NEXT: %.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %x.coerce2, 2
; CHECK-NEXT: %.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %x.coerce3, 3
; CHECK-NEXT: %.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %x.coerce4, 4
; CHECK-NEXT: %.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %x.coerce5, 5
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %libcS_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store %struct.libcS %.fca.5.insert, ptr addrspace(5) %0, align 8
; CHECK-NEXT: %1 = getelementptr inbounds %libcS_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: ret void
;
entry:
%.fca.0.insert = insertvalue %struct.libcS poison, i8 %x.coerce0, 0
%.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %x.coerce1, 1
%.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %x.coerce2, 2
%.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %x.coerce3, 3
%.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %x.coerce4, 4
%.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %x.coerce5, 5
tail call void (...) @vararg(%struct.libcS %.fca.5.insert, 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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 20, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v4f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v4f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store <4 x float> %y, ptr addrspace(5) %1, align 16
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 20, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 20, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %v4f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <4 x float> %x, ptr addrspace(5) %0, align 16
; CHECK-NEXT: %1 = getelementptr inbounds %v4f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 20, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v8f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v8f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store <8 x float> %y, ptr addrspace(5) %1, align 32
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 36, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 36, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %v8f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <8 x float> %x, ptr addrspace(5) %0, align 32
; CHECK-NEXT: %1 = getelementptr inbounds %v8f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 36, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 68, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v16f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v16f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store <16 x float> %y, ptr addrspace(5) %1, align 64
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 68, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 68, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %v16f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <16 x float> %x, ptr addrspace(5) %0, align 64
; CHECK-NEXT: %1 = getelementptr inbounds %v16f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 68, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 132, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %i32_v32f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %0, align 4
; CHECK-NEXT: %1 = getelementptr inbounds %i32_v32f32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store <32 x float> %y, ptr addrspace(5) %1, align 128
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 132, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 132, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %0 = getelementptr inbounds %v32f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store <32 x float> %x, ptr addrspace(5) %0, align 128
; CHECK-NEXT: %1 = getelementptr inbounds %v32f32_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 %y, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void @vararg(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 132, ptr addrspace(5) %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 4, addrspace(5)
; CHECK-NEXT: %0 = load volatile ptr, ptr addrspacecast (ptr addrspace(1) @vararg_ptr to ptr), align 8
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %1 = getelementptr inbounds %fptr_single_i32.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i32 %x, ptr addrspace(5) %1, align 4
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void %0(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 4, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: ret void
;
entry:
%0 = load volatile ptr, ptr addrspacecast (ptr addrspace(1) @vararg_ptr to ptr), align 8
tail call void (...) %0(i32 noundef %x)
ret void
}
define hidden void @fptr_libcS(i8 %x.coerce0, i16 %x.coerce1, i32 %x.coerce2, i64 %x.coerce3, float %x.coerce4, double %x.coerce5) {
; CHECK-LABEL: define {{[^@]+}}@fptr_libcS(i8 %x.coerce0, i16 %x.coerce1, i32 %x.coerce2, i64 %x.coerce3, float %x.coerce4, double %x.coerce5) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %vararg_buffer = alloca %fptr_libcS.vararg, align 4, addrspace(5)
; CHECK-NEXT: %0 = load volatile ptr, ptr addrspacecast (ptr addrspace(1) @vararg_ptr to ptr), align 8
; CHECK-NEXT: %.fca.0.insert = insertvalue %struct.libcS poison, i8 %x.coerce0, 0
; CHECK-NEXT: %.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %x.coerce1, 1
; CHECK-NEXT: %.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %x.coerce2, 2
; CHECK-NEXT: %.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %x.coerce3, 3
; CHECK-NEXT: %.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %x.coerce4, 4
; CHECK-NEXT: %.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %x.coerce5, 5
; CHECK-NEXT: call void @llvm.lifetime.start.p5(i64 32, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: %1 = getelementptr inbounds %fptr_libcS.vararg, ptr addrspace(5) %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store %struct.libcS %.fca.5.insert, ptr addrspace(5) %1, align 8
; CHECK-NEXT: %2 = addrspacecast ptr addrspace(5) %vararg_buffer to ptr
; CHECK-NEXT: call void %0(ptr %2)
; CHECK-NEXT: call void @llvm.lifetime.end.p5(i64 32, ptr addrspace(5) %vararg_buffer)
; CHECK-NEXT: ret void
;
entry:
%0 = load volatile ptr, ptr addrspacecast (ptr addrspace(1) @vararg_ptr to ptr), align 8
%.fca.0.insert = insertvalue %struct.libcS poison, i8 %x.coerce0, 0
%.fca.1.insert = insertvalue %struct.libcS %.fca.0.insert, i16 %x.coerce1, 1
%.fca.2.insert = insertvalue %struct.libcS %.fca.1.insert, i32 %x.coerce2, 2
%.fca.3.insert = insertvalue %struct.libcS %.fca.2.insert, i64 %x.coerce3, 3
%.fca.4.insert = insertvalue %struct.libcS %.fca.3.insert, float %x.coerce4, 4
%.fca.5.insert = insertvalue %struct.libcS %.fca.4.insert, double %x.coerce5, 5
tail call void (...) %0(%struct.libcS %.fca.5.insert)
ret void
}