aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-07-03 04:57:25 +0200
committerGitHub <noreply@github.com>2025-07-03 04:57:25 +0200
commit31bc6d5a9ddaf09511d8e5dc6017957adec0564b (patch)
treef0de91fd97b736c2e069abaeec58a35cb9a0fc37 /src/codegen
parentedf785db0f30842b958f540a3aaf7205b8b82493 (diff)
parente126e5592d205c2e7e8e4946af22f033ee1db79a (diff)
downloadzig-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.zig150
-rw-r--r--src/codegen/arm/abi.zig163
-rw-r--r--src/codegen/llvm.zig8
-rw-r--r--src/codegen/mips/abi.zig84
-rw-r--r--src/codegen/wasm/abi.zig87
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;
+}