mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-23 21:41:34 +00:00
i386: pass and return bare vectors directly, coercing only 8-byte integer vectors,
i386: treat a bit_field as the aggregate it is in C, fixing a segfault on return, abi test: document the i386 microarch baseline; do not read a signal exit as a type index
This commit is contained in:
@@ -479,11 +479,50 @@ namespace lbAbi386 {
|
||||
ft->ctx = c;
|
||||
ft->args = compute_arg_types(c, arg_types, arg_count, original_type);
|
||||
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
|
||||
|
||||
// `bit_field` is treated as a struct.
|
||||
Type *return_source = lb_abi_single_result_type(original_type);
|
||||
if (return_is_defined && !return_is_tuple &&
|
||||
return_source != nullptr && is_type_bit_field(return_source) &&
|
||||
!lb_is_type_kind(return_type, LLVMStructTypeKind) &&
|
||||
!lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
|
||||
// Windows and the BSDs return a small struct in registers, same as the scalar
|
||||
// path so only the psABI targets need moving to the hidden pointer.
|
||||
bool small_in_registers = build_context.metrics.os == TargetOs_windows ||
|
||||
build_context.metrics.os == TargetOs_freebsd ||
|
||||
build_context.metrics.os == TargetOs_openbsd;
|
||||
i64 sz = lb_sizeof(return_type);
|
||||
bool returned_in_registers = small_in_registers && (sz == 1 || sz == 2 || sz == 4 || sz == 8);
|
||||
if (!returned_in_registers) {
|
||||
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
|
||||
ft->ret = lb_arg_type_indirect(return_type, attr);
|
||||
}
|
||||
}
|
||||
|
||||
ft->calling_convention = calling_convention;
|
||||
return ft;
|
||||
}
|
||||
|
||||
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return, Type *source_type) {
|
||||
// A bare vector is passed and returned as itself; only aggregates
|
||||
// take the indirect path below, ergo the vector check has to be first.
|
||||
//
|
||||
// Exception is an 8-byte vector Arg whose element is an integer: its an MMX type;
|
||||
// clang coerces it to `i64` to keep it out of the MMX registers. An 8-byte
|
||||
// vector of floats is an SSE type and stays itself, so the rule turns on the element and
|
||||
// not on the width alone:
|
||||
//
|
||||
// <8 x i8> <4 x i16> <2 x i32> -> i64
|
||||
// <2 x float> <4 x half> -> unchanged
|
||||
//
|
||||
// The RETURN is never coerced -- `<8 x i8>` comes back as itself.
|
||||
if (LLVMGetTypeKind(type) == LLVMVectorTypeKind) {
|
||||
if (!is_return && lb_sizeof(type) == 8 &&
|
||||
LLVMGetTypeKind(LLVMGetElementType(type)) == LLVMIntegerTypeKind) {
|
||||
return lb_arg_type_direct(type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
|
||||
}
|
||||
return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
|
||||
}
|
||||
if (!is_return && lb_sizeof(type) > 8) {
|
||||
return lb_arg_type_indirect(type, nullptr);
|
||||
}
|
||||
@@ -514,7 +553,11 @@ namespace lbAbi386 {
|
||||
LLVMTypeRef t = arg_types[i];
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(t);
|
||||
i64 sz = lb_sizeof(t);
|
||||
if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) {
|
||||
// `bit_field` lowers to a bare integer; C represents it as a struct with bit-field
|
||||
// members, and i386 passes every struct by value on the stack. Use Src Type to match
|
||||
bool is_aggregate = kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind ||
|
||||
(srcs[i] != nullptr && is_type_bit_field(srcs[i]));
|
||||
if (is_aggregate) {
|
||||
if (sz == 0) {
|
||||
args[i] = lb_arg_type_ignore(t);
|
||||
} else {
|
||||
|
||||
@@ -11,9 +11,19 @@ set -eu
|
||||
# clang (which targets everything) and qemu-user.
|
||||
#
|
||||
# ./cross.sh linux_arm64 aarch64-linux-gnu qemu-aarch64
|
||||
# ./cross.sh linux_i386 i386-linux-gnu qemu-i386
|
||||
# ./cross.sh linux_i386 i386-linux-gnu qemu-i386 -microarch:pentium4
|
||||
# ./cross.sh linux_arm32 arm-linux-gnueabihf qemu-arm
|
||||
# ./cross.sh linux_riscv64 riscv64-linux-gnu qemu-riscv64
|
||||
#
|
||||
# i386 needs a microarch: below SSE2 the x86 backend cannot legalise a
|
||||
# sub-16-byte `f16` vector, and merely declaring a `proc "c"` that takes a
|
||||
# `#simd[2]f16` aborts the compiler with "LLVM ERROR: Do not know how to split
|
||||
# the result of this operator!".
|
||||
#
|
||||
# `pentium4` is what clang's own default for `i386-linux-gnu` is, `haswell`
|
||||
# additionally has F16C, which isolates one non-ABI failure: `hfa4_f16`
|
||||
# diverges at pentium4 and agrees at haswell. It is `_Float16` conversion
|
||||
# codegen rather than a calling convention
|
||||
|
||||
TARGET=${1:?odin target, e.g. linux_arm64}
|
||||
TRIPLE=${2:?clang triple, e.g. aarch64-linux-gnu}
|
||||
@@ -115,6 +125,16 @@ set +e
|
||||
rc=$?
|
||||
set -e
|
||||
|
||||
# A driver that DIES reports 128+signal, and that collides with the type indices: 139 is both
|
||||
# SIGSEGV and a perfectly good index, so reading it as an index names an innocent type. There is no
|
||||
# cheap way to tell them apart here. ABI_SKIP is a compile-time `-define`
|
||||
if [ "$rc" -gt 128 ] && [ "$rc" -lt 165 ]; then
|
||||
echo "$TARGET: exit $rc is AMBIGUOUS." >&2
|
||||
echo " Either type index $rc, or the driver died of signal $((rc-128)) (11 = SIGSEGV)." >&2
|
||||
echo " Re-run with -define:ABI_SKIP=$((rc+1)): if the result moves it was the type," >&2
|
||||
echo " and if it does not, a wrong-ABI call is corrupting the process." >&2
|
||||
fi
|
||||
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
echo "$TARGET: every type agrees with clang"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user