This addresses two issues:
- LLVM errors if the sizes of the two sides don't match, e.g.
`simd_shr(#simd[4]i32, #simd[4]u8)`
- Negative values on the LHS becoming 0 if a shr overflows.
LLVM's va_arg instruction only implements the Darwin char* va_list on
arm64. On Linux and the BSDs the va_list is the AAPCS64 struct, and the
instruction walked its first field, __stack, as if it were the Darwin
list. Arguments passed in registers were never read from the register
save areas, so c_va_arg returned whatever lay at the incoming stack
pointer.
c_va_arg now lowers the struct form the way clang does: take the value
from the general or floating-point register save area while the class
offset is negative, otherwise from __stack, with 16-byte alignment and
indirect composites handled.
The new test passes more arguments than any target keeps in registers,
so both the register and the stack path are covered.
LLVM 19 made llvm.va_start, llvm.va_end and llvm.va_copy overloaded on
the pointer type, so the backend asked for the "llvm.va_start.p0"
declaration. On LLVM 18, which the FreeBSD and riscv64 CI jobs use, that
name is not an intrinsic, so it became an undefined symbol at link time
for any procedure that uses c_va_start.
The overload type is only passed on LLVM 19 and later.
Callers pass C variadic arguments with the default argument promotions
applied (f16/f32 as f64, small integers and booleans as i32, see
lb_emit_c_vararg), but c_va_arg read the slot as the requested type.
For f32 and f16 that read part of a double, which gave garbage such as
0 instead of 2.25 on arm64 Darwin, x86-64 SysV and Win64 alike. Small
integers happened to work on these little-endian targets because the
low bytes of the slot hold the value.
c_va_arg now reads the promoted type and converts it back, as C code
must do, which also makes the integer case explicit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LLVM only adds evex512 by itself for the generic x86 CPUs, so with the
default x86-64-v2 microarch, -target-features:avx512f left 512-bit
vectors split into ymm pairs and C got garbage lanes back from Odin
(and the other way round). Clang adds it whenever an AVX-512 feature is
on and evex512 isn't named, do the same for the global features and for
@(enable_target_feature).