diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-07-03 04:57:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-03 04:57:25 +0200 |
| commit | 31bc6d5a9ddaf09511d8e5dc6017957adec0564b (patch) | |
| tree | f0de91fd97b736c2e069abaeec58a35cb9a0fc37 /src/codegen | |
| parent | edf785db0f30842b958f540a3aaf7205b8b82493 (diff) | |
| parent | e126e5592d205c2e7e8e4946af22f033ee1db79a (diff) | |
| download | zig-31bc6d5a9ddaf09511d8e5dc6017957adec0564b.tar.gz zig-31bc6d5a9ddaf09511d8e5dc6017957adec0564b.zip | |
Merge pull request #24322 from ziglang/delete-dead-backends
delete abandoned backends
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/aarch64/abi.zig | 150 | ||||
| -rw-r--r-- | src/codegen/arm/abi.zig | 163 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 8 | ||||
| -rw-r--r-- | src/codegen/mips/abi.zig | 84 | ||||
| -rw-r--r-- | src/codegen/wasm/abi.zig | 87 |
5 files changed, 488 insertions, 4 deletions
diff --git a/src/codegen/aarch64/abi.zig b/src/codegen/aarch64/abi.zig new file mode 100644 index 0000000000..0cd0b389b1 --- /dev/null +++ b/src/codegen/aarch64/abi.zig @@ -0,0 +1,150 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const bits = @import("../../arch/aarch64/bits.zig"); +const Register = bits.Register; +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +pub const Class = union(enum) { + memory, + byval, + integer, + double_integer, + float_array: u8, +}; + +/// For `float_array` the second element will be the amount of floats. +pub fn classifyType(ty: Type, zcu: *Zcu) Class { + std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + + var maybe_float_bits: ?u16 = null; + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + if (ty.containerLayout(zcu) == .@"packed") return .byval; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= sret_float_count) return .{ .float_array = float_count }; + + const bit_size = ty.bitSize(zcu); + if (bit_size > 128) return .memory; + if (bit_size > 64) return .double_integer; + return .integer; + }, + .@"union" => { + if (ty.containerLayout(zcu) == .@"packed") return .byval; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= sret_float_count) return .{ .float_array = float_count }; + + const bit_size = ty.bitSize(zcu); + if (bit_size > 128) return .memory; + if (bit_size > 64) return .double_integer; + return .integer; + }, + .int, .@"enum", .error_set, .float, .bool => return .byval, + .vector => { + const bit_size = ty.bitSize(zcu); + // TODO is this controlled by a cpu feature? + if (bit_size > 128) return .memory; + return .byval; + }, + .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, + } +} + +const sret_float_count = 4; +fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 { + const ip = &zcu.intern_pool; + const target = zcu.getTarget(); + const invalid = std.math.maxInt(u8); + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + var max_count: u8 = 0; + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + if (field_count > max_count) max_count = field_count; + if (max_count > sret_float_count) return invalid; + } + return max_count; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var count: u8 = 0; + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_count = countFloats(field_ty, zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + count += field_count; + if (count > sret_float_count) return invalid; + } + return count; + }, + .float => { + const float_bits = maybe_float_bits.* orelse { + maybe_float_bits.* = ty.floatBits(target); + return 1; + }; + if (ty.floatBits(target) == float_bits) return 1; + return invalid; + }, + .void => return 0, + else => return invalid, + } +} + +pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type { + const ip = &zcu.intern_pool; + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + for (union_obj.field_types.get(ip)) |field_ty| { + if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some; + } + return null; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + if (getFloatArrayType(field_ty, zcu)) |some| return some; + } + return null; + }, + .float => return ty, + else => return null, + } +} + +pub const callee_preserved_regs = [_]Register{ + .x19, .x20, .x21, .x22, .x23, + .x24, .x25, .x26, .x27, .x28, +}; + +pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; +pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; + +const allocatable_registers = callee_preserved_regs; diff --git a/src/codegen/arm/abi.zig b/src/codegen/arm/abi.zig new file mode 100644 index 0000000000..23606d6145 --- /dev/null +++ b/src/codegen/arm/abi.zig @@ -0,0 +1,163 @@ +const std = @import("std"); +const assert = std.debug.assert; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +pub const Class = union(enum) { + memory, + byval, + i32_array: u8, + i64_array: u8, + + fn arrSize(total_size: u64, arr_size: u64) Class { + const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size)); + if (arr_size == 32) { + return .{ .i32_array = count }; + } else { + return .{ .i64_array = count }; + } + } +}; + +pub const Context = enum { ret, arg }; + +pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { + assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + + var maybe_float_bits: ?u16 = null; + const max_byval_size = 512; + const ip = &zcu.intern_pool; + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + const bit_size = ty.bitSize(zcu); + if (ty.containerLayout(zcu) == .@"packed") { + if (bit_size > 64) return .memory; + return .byval; + } + if (bit_size > max_byval_size) return .memory; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= byval_float_count) return .byval; + + const fields = ty.structFieldCount(zcu); + var i: u32 = 0; + while (i < fields) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_alignment = ty.fieldAlignment(i, zcu); + const field_size = field_ty.bitSize(zcu); + if (field_size > 32 or field_alignment.compare(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + } + return Class.arrSize(bit_size, 32); + }, + .@"union" => { + const bit_size = ty.bitSize(zcu); + const union_obj = zcu.typeToUnion(ty).?; + if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (bit_size > 64) return .memory; + return .byval; + } + if (bit_size > max_byval_size) return .memory; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= byval_float_count) return .byval; + + for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { + if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or + ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32")) + { + return Class.arrSize(bit_size, 64); + } + } + return Class.arrSize(bit_size, 32); + }, + .bool, .float => return .byval, + .int => { + // TODO this is incorrect for _BitInt(128) but implementing + // this correctly makes implementing compiler-rt impossible. + // const bit_size = ty.bitSize(zcu); + // if (bit_size > 64) return .memory; + return .byval; + }, + .@"enum", .error_set => { + const bit_size = ty.bitSize(zcu); + if (bit_size > 64) return .memory; + return .byval; + }, + .vector => { + const bit_size = ty.bitSize(zcu); + // TODO is this controlled by a cpu feature? + if (ctx == .ret and bit_size > 128) return .memory; + if (bit_size > 512) return .memory; + return .byval; + }, + .optional => { + assert(ty.isPtrLikeOptional(zcu)); + return .byval; + }, + .pointer => { + 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, + } +} + +const byval_float_count = 4; +fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 { + const ip = &zcu.intern_pool; + const target = zcu.getTarget(); + const invalid = std.math.maxInt(u32); + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + var max_count: u32 = 0; + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + if (field_count > max_count) max_count = field_count; + if (max_count > byval_float_count) return invalid; + } + return max_count; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var count: u32 = 0; + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_count = countFloats(field_ty, zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + count += field_count; + if (count > byval_float_count) return invalid; + } + return count; + }, + .float => { + const float_bits = maybe_float_bits.* orelse { + const float_bits = ty.floatBits(target); + if (float_bits != 32 and float_bits != 64) return invalid; + maybe_float_bits.* = float_bits; + return 1; + }; + if (ty.floatBits(target) == float_bits) return 1; + return invalid; + }, + .void => return 0, + else => return invalid, + } +} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a5e6d35fd6..27092a1de8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -21,11 +21,11 @@ const Air = @import("../Air.zig"); const Value = @import("../Value.zig"); const Type = @import("../Type.zig"); const x86_64_abi = @import("../arch/x86_64/abi.zig"); -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 wasm_c_abi = @import("wasm/abi.zig"); +const aarch64_c_abi = @import("aarch64/abi.zig"); +const arm_c_abi = @import("arm/abi.zig"); const riscv_c_abi = @import("../arch/riscv64/abi.zig"); -const mips_c_abi = @import("../arch/mips/abi.zig"); +const mips_c_abi = @import("mips/abi.zig"); const dev = @import("../dev.zig"); const target_util = @import("../target.zig"); diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig new file mode 100644 index 0000000000..02c4c637a4 --- /dev/null +++ b/src/codegen/mips/abi.zig @@ -0,0 +1,84 @@ +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 => { + 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, + } +} diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig new file mode 100644 index 0000000000..a1fa812649 --- /dev/null +++ b/src/codegen/wasm/abi.zig @@ -0,0 +1,87 @@ +//! Classifies Zig types to follow the C-ABI for Wasm. +//! The convention for Wasm's C-ABI can be found at the tool-conventions repo: +//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md +//! When not targeting the C-ABI, Zig is allowed to do derail from this convention. +//! Note: Above mentioned document is not an official specification, therefore called a convention. + +const std = @import("std"); +const Target = std.Target; +const assert = std.debug.assert; + +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +/// Defines how to pass a type as part of a function signature, +/// both for parameters as well as return values. +pub const Class = union(enum) { + direct: Type, + indirect, +}; + +/// Classifies a given Zig type to determine how they must be passed +/// or returned as value within a wasm function. +pub fn classifyType(ty: Type, zcu: *const Zcu) Class { + const ip = &zcu.intern_pool; + assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + switch (ty.zigTypeTag(zcu)) { + .int, .@"enum", .error_set => return .{ .direct = ty }, + .float => return .{ .direct = ty }, + .bool => return .{ .direct = ty }, + .vector => return .{ .direct = ty }, + .array => return .indirect, + .optional => { + assert(ty.isPtrLikeOptional(zcu)); + return .{ .direct = ty }; + }, + .pointer => { + assert(!ty.isSlice(zcu)); + return .{ .direct = ty }; + }, + .@"struct" => { + const struct_type = zcu.typeToStruct(ty).?; + if (struct_type.layout == .@"packed") { + return .{ .direct = ty }; + } + if (struct_type.field_types.len > 1) { + // The struct type is non-scalar. + return .indirect; + } + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); + const explicit_align = struct_type.fieldAlign(ip, 0); + if (explicit_align != .none) { + if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) + return .indirect; + } + return classifyType(field_ty, zcu); + }, + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + if (union_obj.flagsUnordered(ip).layout == .@"packed") { + return .{ .direct = ty }; + } + const layout = ty.unionGetLayout(zcu); + assert(layout.tag_size == 0); + if (union_obj.field_types.len > 1) return .indirect; + const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); + return classifyType(first_field_ty, zcu); + }, + .error_union, + .frame, + .@"anyframe", + .noreturn, + .void, + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .@"fn", + .@"opaque", + .enum_literal, + => unreachable, + } +} + +pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool { + return scalar_ty.bitSize(zcu) > 64; +} |
