mips: fix C ABI compatibility

This commit is contained in:
Maciej 'vesim' Kuliński 2024-09-07 17:29:43 +02:00 committed by Andrew Kelley
parent fb81522e0b
commit fb0028a0d7
4 changed files with 145 additions and 43 deletions

86
src/arch/mips/abi.zig Normal file
View File

@ -0,0 +1,86 @@
const std = @import("std");
const Type = @import("../../Type.zig");
const Zcu = @import("../../Zcu.zig");
const assert = std.debug.assert;
pub const Class = union(enum) {
memory,
byval,
i32_array: u8,
};
pub const Context = enum { ret, arg };
pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
const target = zcu.getTarget();
std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
const max_direct_size = target.ptrBitWidth() * 2;
switch (ty.zigTypeTag(zcu)) {
.@"struct" => {
const bit_size = ty.bitSize(zcu);
if (ty.containerLayout(zcu) == .@"packed") {
if (bit_size > max_direct_size) return .memory;
return .byval;
}
if (bit_size > max_direct_size) return .memory;
// TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute
const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32));
return .{ .i32_array = count };
},
.@"union" => {
const bit_size = ty.bitSize(zcu);
if (ty.containerLayout(zcu) == .@"packed") {
if (bit_size > max_direct_size) return .memory;
return .byval;
}
if (bit_size > max_direct_size) return .memory;
return .byval;
},
.bool => return .byval,
.float => return .byval,
.int, .@"enum", .error_set => {
const bit_size = ty.bitSize(zcu);
if (bit_size > max_direct_size) return .memory;
return .byval;
},
.vector => {
const elem_type = ty.elemType2(zcu);
switch (elem_type.zigTypeTag(zcu)) {
.bool, .int => {
const bit_size = ty.bitSize(zcu);
if (ctx == .ret and bit_size > 128) return .memory;
if (bit_size > 512) return .memory;
// TODO: byval vector arguments with non power of 2 size need inreg attribute
return .byval;
},
.float => return .memory,
else => unreachable,
}
},
.optional => {
std.debug.assert(ty.isPtrLikeOptional(zcu));
return .byval;
},
.pointer => {
std.debug.assert(!ty.isSlice(zcu));
return .byval;
},
.error_union,
.frame,
.@"anyframe",
.noreturn,
.void,
.type,
.comptime_float,
.comptime_int,
.undefined,
.null,
.@"fn",
.@"opaque",
.enum_literal,
.array,
=> unreachable,
}
}

View File

@ -26,6 +26,7 @@ const wasm_c_abi = @import("../arch/wasm/abi.zig");
const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
const arm_c_abi = @import("../arch/arm/abi.zig");
const riscv_c_abi = @import("../arch/riscv64/abi.zig");
const mips_c_abi = @import("../arch/mips/abi.zig");
const dev = @import("../dev.zig");
const target_util = @import("../target.zig");
@ -11681,7 +11682,10 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe
return switch (fn_info.cc) {
.Unspecified, .Inline => returnTypeByRef(zcu, target, return_type),
.C => switch (target.cpu.arch) {
.mips, .mipsel => false,
.mips, .mipsel => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
.memory, .i32_array => true,
.byval => false,
},
.x86 => isByRef(return_type, zcu),
.x86_64 => switch (target.os.tag) {
.windows => x86_64_abi.classifyWindows(return_type, zcu) == .memory,
@ -11732,7 +11736,12 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
.C => {
switch (target.cpu.arch) {
.mips, .mipsel => return o.lowerType(return_type),
.mips, .mipsel => {
switch (mips_c_abi.classifyType(return_type, zcu, .ret)) {
.memory, .i32_array => return .void,
.byval => return o.lowerType(return_type),
}
},
.x86 => return if (isByRef(return_type, zcu)) .void else o.lowerType(return_type),
.x86_64 => switch (target.os.tag) {
.windows => return lowerWin64FnRetTy(o, fn_info),
@ -11978,7 +11987,14 @@ const ParamTypeIterator = struct {
.mips, .mipsel => {
it.zig_index += 1;
it.llvm_index += 1;
return .byval;
switch (mips_c_abi.classifyType(ty, zcu, .arg)) {
.memory => {
it.byval_attr = true;
return .byref;
},
.byval => return .byval,
.i32_array => |size| return Lowering{ .i32_array = size },
}
},
.x86_64 => switch (target.os.tag) {
.windows => return it.nextWin64(ty),

View File

@ -2657,7 +2657,7 @@ void run_c_tests(void) {
}
#endif
#if !defined(__mips__) && !defined(ZIG_PPC32)
#if !defined(ZIG_PPC32)
{
struct Struct_u64_u64 s = zig_ret_struct_u64_u64();
assert_or_panic(s.a == 1);
@ -2708,7 +2708,7 @@ void run_c_tests(void) {
#endif
#if !defined __i386__ && !defined __arm__ && !defined __aarch64__ && \
!defined __mips__ && !defined __powerpc__ && !defined ZIG_RISCV64
!defined __powerpc__ && !defined ZIG_RISCV64
{
struct SmallStructInts s = {1, 2, 3, 4};
zig_small_struct_ints(s);
@ -2716,7 +2716,7 @@ void run_c_tests(void) {
#endif
#if !defined __arm__ && !defined __aarch64__ && \
!defined __mips__ && !defined __powerpc__ && !defined ZIG_RISCV64
!defined __powerpc__ && !defined ZIG_RISCV64
{
struct MedStructInts s = {1, 2, 3};
zig_med_struct_ints(s);
@ -2741,7 +2741,7 @@ void run_c_tests(void) {
zig_small_packed_struct(s);
}
#if !defined __i386__ && !defined __arm__ && !defined __mips__ && \
#if !defined __i386__ && !defined __arm__ && \
!defined ZIG_PPC32 && !defined _ARCH_PPC64
{
struct SplitStructInts s = {1234, 100, 1337};
@ -2756,7 +2756,7 @@ void run_c_tests(void) {
}
#endif
#if !defined __i386__ && !defined __arm__ && !defined __mips__ && \
#if !defined __i386__ && !defined __arm__ && \
!defined ZIG_PPC32 && !defined _ARCH_PPC64
{
struct SplitStructMixed s = {1234, 100, 1337.0f};
@ -2764,7 +2764,7 @@ void run_c_tests(void) {
}
#endif
#if !defined __mips__ && !defined ZIG_PPC32
#if !defined ZIG_PPC32
{
struct BigStruct s = {30, 31, 32, 33, 34};
struct BigStruct res = zig_big_struct_both(s);
@ -2784,7 +2784,7 @@ void run_c_tests(void) {
}
#endif
#if !defined __mips__ && !defined ZIG_PPC32
#if !defined ZIG_PPC32
{
struct FloatRect r1 = {1, 21, 16, 4};
struct FloatRect r2 = {178, 189, 21, 15};

View File

@ -322,7 +322,7 @@ extern fn c_struct_u64_u64_7(usize, usize, usize, usize, usize, usize, usize, St
extern fn c_struct_u64_u64_8(usize, usize, usize, usize, usize, usize, usize, usize, Struct_u64_u64) void;
test "C ABI struct u64 u64" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const s = c_ret_struct_u64_u64();
@ -359,7 +359,7 @@ extern fn c_ret_struct_f32f32_f32() Struct_f32f32_f32;
extern fn c_struct_f32f32_f32(Struct_f32f32_f32) void;
test "C ABI struct {f32,f32} f32" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const s = c_ret_struct_f32f32_f32();
@ -389,7 +389,7 @@ extern fn c_ret_struct_f32_f32f32() Struct_f32_f32f32;
extern fn c_struct_f32_f32f32(Struct_f32_f32f32) void;
test "C ABI struct f32 {f32,f32}" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const s = c_ret_struct_f32_f32f32();
@ -424,7 +424,7 @@ extern fn c_ret_struct_u32_union_u32_u32u32() Struct_u32_Union_u32_u32u32;
extern fn c_struct_u32_union_u32_u32u32(Struct_u32_Union_u32_u32u32) void;
test "C ABI struct{u32,union{u32,struct{u32,u32}}}" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = c_ret_struct_u32_union_u32_u32u32();
@ -444,7 +444,7 @@ const BigStruct = extern struct {
extern fn c_big_struct(BigStruct) void;
test "C ABI big struct" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const s = BigStruct{
@ -503,7 +503,7 @@ extern fn c_med_struct_mixed(MedStructMixed) void;
extern fn c_ret_med_struct_mixed() MedStructMixed;
test "C ABI medium struct of ints and floats" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = MedStructMixed{
@ -535,7 +535,7 @@ extern fn c_ret_small_struct_ints() SmallStructInts;
test "C ABI small struct of ints" {
if (builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = SmallStructInts{
@ -568,7 +568,7 @@ extern fn c_med_struct_ints(MedStructInts) void;
extern fn c_ret_med_struct_ints() MedStructInts;
test "C ABI medium struct of ints" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = MedStructInts{
@ -646,7 +646,7 @@ extern fn c_split_struct_ints(SplitStructInt) void;
test "C ABI split struct of ints" {
if (builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = SplitStructInt{
@ -673,7 +673,7 @@ extern fn c_ret_split_struct_mixed() SplitStructMixed;
test "C ABI split struct of ints and floats" {
if (builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const s = SplitStructMixed{
@ -700,7 +700,7 @@ extern fn c_multiple_struct_ints(Rect, Rect) void;
extern fn c_multiple_struct_floats(FloatRect, FloatRect) void;
test "C ABI sret and byval together" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const s = BigStruct{
@ -752,7 +752,7 @@ const Vector5 = extern struct {
extern fn c_big_struct_floats(Vector5) void;
test "C ABI structs of floats as parameter" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const v3 = Vector3{
@ -828,7 +828,7 @@ export fn zig_multiple_struct_floats(x: FloatRect, y: FloatRect) void {
}
test "C ABI structs of floats as multiple parameters" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
const r1 = FloatRect{
@ -941,7 +941,7 @@ extern fn c_ret_struct_with_array() StructWithArray;
test "Struct with array as padding." {
if (builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
c_struct_with_array(.{ .a = 1, .padding = undefined, .b = 2 });
@ -966,7 +966,7 @@ extern fn c_float_array_struct(FloatArrayStruct) void;
extern fn c_ret_float_array_struct() FloatArrayStruct;
test "Float array like struct" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
c_float_array_struct(.{
@ -1031,7 +1031,7 @@ extern fn c_ret_big_vec() BigVec;
test "big simd vector" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .macos and builtin.mode != .Debug) return error.SkipZigTest;
@ -5340,7 +5340,7 @@ extern fn c_ptr_size_float_struct(Vector2) void;
extern fn c_ret_ptr_size_float_struct() Vector2;
test "C ABI pointer sized float struct" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
@ -5362,25 +5362,25 @@ pub inline fn expectOk(c_err: c_int) !void {
/// Tests for Double + Char struct
const DC = extern struct { v1: f64, v2: u8 };
test "DC: Zig passes to C" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_DC(.{ .v1 = -0.25, .v2 = 15 }));
}
test "DC: Zig returns to C" {
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_ret_DC());
}
test "DC: C passes to Zig" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_send_DC());
}
test "DC: C returns to Zig" {
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectEqual(DC{ .v1 = -0.25, .v2 = 15 }, c_ret_DC());
@ -5406,12 +5406,12 @@ const CFF = extern struct { v1: u8, v2: f32, v3: f32 };
test "CFF: Zig passes to C" {
if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_CFF(.{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }));
}
test "CFF: Zig returns to C" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_ret_CFF());
}
@ -5419,7 +5419,7 @@ test "CFF: C passes to Zig" {
if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch == .aarch64 and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_send_CFF());
@ -5427,7 +5427,7 @@ test "CFF: C passes to Zig" {
test "CFF: C returns to Zig" {
if (builtin.cpu.arch == .aarch64 and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isRISCV() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectEqual(CFF{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }, c_ret_CFF());
}
@ -5451,22 +5451,22 @@ pub export fn zig_ret_CFF() CFF {
const PD = extern struct { v1: ?*anyopaque, v2: f64 };
test "PD: Zig passes to C" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_PD(.{ .v1 = null, .v2 = 0.5 }));
}
test "PD: Zig returns to C" {
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_assert_ret_PD());
}
test "PD: C passes to Zig" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectOk(c_send_PD());
}
test "PD: C returns to Zig" {
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
try expectEqual(PD{ .v1 = null, .v2 = 0.5 }, c_ret_PD());
}
@ -5520,7 +5520,7 @@ const ByVal = extern struct {
extern fn c_func_ptr_byval(*anyopaque, *anyopaque, ByVal, c_ulong, *anyopaque, c_ulong) void;
test "C function that takes byval struct called via function pointer" {
if (builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64() and builtin.mode != .Debug) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
var fn_ptr = &c_func_ptr_byval;
@ -5551,7 +5551,7 @@ const f16_struct = extern struct {
};
extern fn c_f16_struct(f16_struct) f16_struct;
test "f16 struct" {
if (builtin.target.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.target.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.target.cpu.arch.isPowerPC32()) return error.SkipZigTest;
if (builtin.cpu.arch.isARM() and builtin.mode != .Debug) return error.SkipZigTest;
@ -5666,7 +5666,7 @@ const Coord2 = extern struct {
extern fn stdcall_coord2(Coord2, Coord2, Coord2) callconv(stdcall_callconv) Coord2;
test "Stdcall ABI structs" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
const res = stdcall_coord2(
@ -5751,7 +5751,7 @@ const byval_tail_callsite_attr = struct {
};
test "byval tail callsite attribute" {
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
// Originally reported at https://github.com/ziglang/zig/issues/16290