diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index f0337507418a420489755e6eebf4607bbe19054c..6eea9c9fffd24b817989fcaa2045ac1b39982501 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -2341,6 +2341,7 @@ pub const TargetQuery = struct { sheb, sparc, sparc64, + spork8, spirv32, spirv64, thumb, @@ -2442,6 +2443,7 @@ pub const TargetQuery = struct { elf, hex, macho, + spork8, plan9, raw, spirv, diff --git a/lib/std/Target.zig b/lib/std/Target.zig index b28ebd50ecaf35a117a5a84c5fc2a4cc4a81e086..659caa75b5ed7f0cbd473f5f995cad5386d75c1d 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -104,6 +104,7 @@ pub const Os = struct { .plan9 => arch.plan9Ext(), else => switch (arch) { .wasm32, .wasm64 => ".wasm", + .spork8 => ".spork8", else => "", }, }; @@ -795,6 +796,24 @@ pub const xcore = @import("Target/xcore.zig"); pub const xtensa = @import("Target/xtensa.zig"); pub const z80 = @import("Target/generic.zig"); +pub const spork8 = struct { + pub const Feature = enum {}; + pub const featureSet = Cpu.Feature.FeatureSetFns(Feature).featureSet; + pub const featureSetHas = Cpu.Feature.FeatureSetFns(Feature).featureSetHas; + pub const featureSetHasAny = Cpu.Feature.FeatureSetFns(Feature).featureSetHasAny; + pub const featureSetHasAll = Cpu.Feature.FeatureSetFns(Feature).featureSetHasAll; + + pub const cpu = struct { + pub const generic: Cpu.Model = .{ + .name = "generic", + .llvm_name = null, + .features = .empty, + }; + }; + + pub const all_features: [0]Cpu.Feature = .{}; +}; + pub const Abi = enum { none, gnu, @@ -1046,6 +1065,7 @@ pub const ObjectFormat = enum { hex, /// The Mach object format used by macOS and other Apple platforms. macho, + spork8, /// The a.out format used by Plan 9 from Bell Labs. plan9, /// Machine code with no metadata. @@ -1061,6 +1081,7 @@ pub const ObjectFormat = enum { .coff => ".obj", .elf, .macho, .wasm => ".o", .hex => ".ihex", + .spork8 => ".spork8", .plan9 => arch.plan9Ext(), .raw => ".bin", .spirv => ".spv", @@ -1075,6 +1096,7 @@ pub const ObjectFormat = enum { else => switch (arch) { .spirv32, .spirv64 => .spirv, .wasm32, .wasm64 => .wasm, + .spork8 => .spork8, else => .elf, }, }; @@ -1124,6 +1146,7 @@ pub fn toElfMachine(target: *const Target) std.elf.EM { .spirv64, .wasm32, .wasm64, + .spork8, => .NONE, }; } @@ -1191,6 +1214,7 @@ pub fn toCoffMachine(target: *const Target) std.coff.IMAGE.FILE.MACHINE { .xcore, .xtensa, .xtensaeb, + .spork8, => .UNKNOWN, }; } @@ -1395,6 +1419,7 @@ pub const Cpu = struct { sheb, sparc, sparc64, + spork8, spirv32, spirv64, thumb, @@ -1447,6 +1472,7 @@ pub const Cpu = struct { xcore, xtensa, z80, + spork8, }; pub inline fn family(arch: Arch) Family { @@ -1485,6 +1511,7 @@ pub const Cpu = struct { .x86_16, .x86, .x86_64 => .x86, .xcore => .xcore, .xtensa, .xtensaeb => .xtensa, + .spork8 => .spork8, }; } @@ -1749,6 +1776,7 @@ pub const Cpu = struct { .sparc, .sparc64, .xtensaeb, + .spork8, => .big, // GPU endianness is opaque. For now, assume little endian. @@ -2971,6 +2999,7 @@ pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 { .avr, .msp430, .x86_16, + .spork8, => 16, .ez80, @@ -3536,6 +3565,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 { switch (target.cpu.arch) { .avr, .ez80, + .spork8, => return 1, .x86 => switch (target.os.tag) { .windows, .uefi => switch (c_type) { @@ -3634,6 +3664,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 { .avr, .ez80, + .spork8, => unreachable, // Handled above. }), ); @@ -3643,6 +3674,7 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { return switch (target.cpu.arch) { .avr, .ez80, + .spork8, => 1, .msp430, @@ -3788,6 +3820,7 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention .nvptx, .nvptx64 => .nvptx_device, .spirv32, .spirv64 => .spirv_device, .ez80 => .ez80_cet, + .spork8 => .naked, }; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ca1bf3f6edf8a79fb2459fe481f311ec7f50c4b1..768b6bca673d1fb22415b5de126e006ed2adbe13 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -75,7 +75,7 @@ pub fn TargetInfo(os: std.Target.Os.Tag, arch: std.Target.Cpu.Arch) type { else => @import("debug/SelfInfo/Elf.zig"), }, .macho => @import("debug/SelfInfo/MachO.zig"), - .plan9, .spirv, .wasm => void, + .plan9, .spirv, .wasm, .spork8 => void, .c, .hex, .raw => unreachable, }; } diff --git a/lib/std/lang.zig b/lib/std/lang.zig index 1c47d2d8919dd40b950d389c98a88d26c3e4dcb4..471b627ea661d00a86705760a82abccebd7a1d20 100644 --- a/lib/std/lang.zig +++ b/lib/std/lang.zig @@ -1319,6 +1319,9 @@ pub const CompilerBackend = enum(u64) { /// The reference implementation self-hosted compiler of Zig, using the /// loongarch backend. stage2_loongarch = 13, + /// The Zig Software Foundation self-hosted implementation of Zig. Backend + /// originally contributed by Ben Anderman in 2026. + zsf_spork8 = 14, _, }; diff --git a/lib/std/start.zig b/lib/std/start.zig index 228b51e08ef9cd2b0eaaa8d8f5e974e1613076d7..470f60e1a198ade629f0a55a68207feac9ab6c65 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -19,7 +19,9 @@ comptime { // decls there get run. _ = root; - if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { + if (builtin.cpu.arch == .spork8) { + // always freestanding, even exes + } else if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup"; if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) { @export(&DllMainCRTStartup, .{ .name = dll_main_crt_startup }); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index e18a6414c6e9ba130640e061307c68b16ff02456..edf19d1848c27eb51e703d7a632e8e8cdc50cdc9 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -259,6 +259,7 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), + .spork8 => return std.fmt.allocPrint(allocator, "{s}.spork8", .{root_name}), .plan9 => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 34e1bf7e6fe3c2644cb2b79449ba276aae9c6a58..0c827c0868b865a5a92c136ffeadd3f290a6d7ab 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -162,6 +162,7 @@ pub fn tripleForTarget(allocator: Allocator, target: *const std.Target) ![]const .propeller, .sh, .sheb, + .spork8, .x86_16, .xtensaeb, => unreachable, // Gated by hasLlvmSupport(). @@ -576,6 +577,7 @@ pub const DataLayout = struct { .sheb, .x86_16, .xtensaeb, + .spork8, => unreachable, }; } diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 85915ded1b97d4e324a1cebbdb1385758ebea6f0..9b97d4a2cbafbc2fc8c964ea4ba8aa0159483625 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -501,7 +501,7 @@ pub fn resolve(options: Options) ResolveError!Config { .windows, .uefi => .code_view, else => .{ .dwarf = .@"32" }, }, - .spirv, .hex, .raw, .plan9 => .strip, + .spirv, .hex, .raw, .plan9, .spork8 => .strip, }; }; diff --git a/src/Zcu.zig b/src/Zcu.zig index 1d1eadffc5545c5de94e84fe6258f28773b3193b..f77f59a478c419b6517a69becac310f9fe444ad8 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -4042,6 +4042,7 @@ pub fn atomicPtrAlignment( const target = zcu.getTarget(); const max_atomic_bits: u16 = switch (target.cpu.arch) { .ez80, + .spork8, => 8, .aarch64, @@ -4697,6 +4698,10 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) .loongarch64_lp64, .loongarch32_ilp32, .naked => true, else => false, }, + .zsf_spork8 => switch (cc) { + .naked => true, + else => false, + }, }; if (!backend_ok) return .{ .bad_backend = backend }; return .ok; diff --git a/src/codegen.zig b/src/codegen.zig index a505721eccea3794c4ff9df5a7018d10e8639527..d54901cbd4253bacb0423ec09204b4d2fcfadb95 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -38,6 +38,7 @@ fn devFeatureForBackend(backend: std.lang.CompilerBackend) dev.Feature { .stage2_powerpc => unreachable, .stage2_riscv64 => .riscv64_backend, .stage2_sparc64 => .sparc64_backend, + .zsf_spork8 => .spork8_backend, .stage2_spirv => .spirv_backend, .stage2_wasm => .wasm_backend, .stage2_x86 => .x86_backend, @@ -58,6 +59,7 @@ fn importBackend(comptime backend: std.lang.CompilerBackend) type { .stage2_riscv64 => @import("codegen/riscv64/CodeGen.zig"), .stage2_sparc64 => @import("codegen/sparc64/CodeGen.zig"), .stage2_spirv => @import("codegen/spirv/CodeGen.zig"), + .zsf_spork8 => @import("codegen/spork8/CodeGen.zig"), .stage2_wasm => @import("codegen/wasm/CodeGen.zig"), .stage2_x86, .stage2_x86_64 => @import("codegen/x86_64/CodeGen.zig"), _ => unreachable, @@ -107,6 +109,7 @@ pub const AnyMir = union { wasm: if (dev.env.supports(.wasm_backend)) @import("codegen/wasm/Mir.zig") else noreturn, c: if (dev.env.supports(.c_backend)) @import("codegen/c.zig").Mir else noreturn, spirv: if (dev.env.supports(.spirv_backend)) @import("codegen/spirv/Mir.zig") else noreturn, + spork8: if (dev.env.supports(.spork8_backend)) @import("codegen/spork8/Mir.zig") else noreturn, pub inline fn tag(comptime backend: std.lang.CompilerBackend) []const u8 { return switch (backend) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a0cb2ac891f08409e40c780c1ec4049057dc887c..0de67cfaaccf5f63aec1140a8f7c4fdbce8f49e3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4720,6 +4720,7 @@ pub fn initializeLLVMTarget(io: Io, arch: std.Target.Cpu.Arch) void { .propeller, .sh, .sheb, + .spork8, .x86_16, .xtensaeb, => unreachable, diff --git a/src/codegen/spork8/CodeGen.zig b/src/codegen/spork8/CodeGen.zig new file mode 100644 index 0000000000000000000000000000000000000000..9c579365db817acf783885d824663c867bdb9520 --- /dev/null +++ b/src/codegen/spork8/CodeGen.zig @@ -0,0 +1,367 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const CodeGen = @This(); +const link = @import("../../link.zig"); +const Spork8 = link.File.Spork8; +const Zcu = @import("../../Zcu.zig"); +const InternPool = @import("../../InternPool.zig"); +const Air = @import("../../Air.zig"); +const Liveness = Air.Liveness; +const Mir = @import("Mir.zig"); + +air: Air, +liveness: Liveness, +gpa: Allocator, +spork8: *Spork8, +pt: Zcu.PerThread, +owner_nav: InternPool.Nav.Index, +func_index: InternPool.Index, +mir_instructions: *std.MultiArrayList(Mir.Inst), +/// Contains extra data for MIR +mir_extra: *std.ArrayListUnmanaged(u32), +start_mir_extra_off: u32, + +pub const Error = error{ + OutOfMemory, + /// Compiler was asked to operate on a number larger than supported. + Overflow, + /// Indicates the error is already stored in Zcu `failed_codegen`. + CodegenFail, +}; + +pub const Function = extern struct { + /// Index into `Spork8.mir_instructions`. + mir_off: u32, + /// This is unused except for as a safety slice bound and could be removed. + mir_len: u32, + /// Index into `Spork8.mir_extra`. + mir_extra_off: u32, + /// This is unused except for as a safety slice bound and could be removed. + mir_extra_len: u32, +}; + +pub fn function( + spork8: *Spork8, + pt: Zcu.PerThread, + func_index: InternPool.Index, + air: Air, + liveness: Liveness, +) Error!Function { + const zcu = pt.zcu; + const gpa = zcu.gpa; + const func_info = zcu.funcInfo(func_index); + + var code_gen: CodeGen = .{ + .gpa = gpa, + .pt = pt, + .air = air, + .liveness = liveness, + .owner_nav = func_info.owner_nav, + .spork8 = spork8, + .func_index = func_index, + .mir_instructions = &spork8.mir_instructions, + .mir_extra = &spork8.mir_extra, + .start_mir_extra_off = @intCast(spork8.mir_extra.items.len), + }; + defer code_gen.deinit(); + + return functionInner(&code_gen) catch |err| switch (err) { + error.CodegenFail => return error.CodegenFail, + else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), + }; +} + +fn deinit(cg: *CodeGen) void { + cg.* = undefined; +} + +const InnerError = error{ + CodegenFail, + OutOfMemory, +}; + +fn functionInner(cg: *CodeGen) InnerError!Function { + const spork8 = cg.spork8; + + const start_mir_off: u32 = @intCast(spork8.mir_instructions.len); + + // Generate MIR for function body + try cg.genBody(cg.air.getMainBody()); + + return .{ + .mir_off = start_mir_off, + .mir_len = @intCast(spork8.mir_instructions.len - start_mir_off), + .mir_extra_off = cg.start_mir_extra_off, + .mir_extra_len = cg.extraLen(), + }; +} + +fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { + const zcu = cg.pt.zcu; + const ip = &zcu.intern_pool; + + for (body) |inst| { + if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) continue; + try cg.genInst(inst); + } +} + +fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const air_tags = cg.air.instructions.items(.tag); + return switch (air_tags[@intFromEnum(inst)]) { + .inferred_alloc, .inferred_alloc_comptime => unreachable, + + .unreach => cg.airUnreachable(inst), + + .add, + .add_sat, + .add_wrap, + .sub, + .sub_sat, + .sub_wrap, + .mul, + .mul_sat, + .mul_wrap, + .div_float, + .div_exact, + .div_trunc, + .div_floor, + .bit_and, + .bit_or, + .bool_and, + .bool_or, + .rem, + .mod, + .shl, + .shl_exact, + .shl_sat, + .shr, + .shr_exact, + .xor, + .max, + .min, + .mul_add, + + .sqrt, + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .floor, + .ceil, + .round, + .trunc_float, + .neg, + + .abs, + + .add_with_overflow, + .sub_with_overflow, + .shl_with_overflow, + .mul_with_overflow, + + .clz, + .ctz, + + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_lte, + .cmp_lt, + .cmp_neq, + + .cmp_vector, + .cmp_lt_errors_len, + + .array_elem_val, + .array_to_slice, + .alloc, + .arg, + .bitcast, + .block, + .trap, + .breakpoint, + .br, + .repeat, + .switch_dispatch, + .cond_br, + .intcast, + .fptrunc, + .fpext, + .int_from_float, + .float_from_int, + .get_union_tag, + + .@"try", + .try_cold, + .try_ptr, + .try_ptr_cold, + + .dbg_stmt, + .dbg_empty_stmt, + .dbg_inline_block, + .dbg_var_ptr, + .dbg_var_val, + .dbg_arg_inline, + + .call, + .call_always_tail, + .call_never_tail, + .call_never_inline, + + .is_err, + .is_non_err, + + .is_null, + .is_non_null, + .is_null_ptr, + .is_non_null_ptr, + + .load, + .loop, + .memset, + .memset_safe, + .not, + .optional_payload, + .optional_payload_ptr, + .optional_payload_ptr_set, + .ptr_add, + .ptr_sub, + .ptr_elem_ptr, + .ptr_elem_val, + .ret, + .ret_safe, + .ret_ptr, + .ret_load, + .splat, + .select, + .shuffle, + .reduce, + .aggregate_init, + .union_init, + .prefetch, + .popcount, + .byte_swap, + .bit_reverse, + + .slice, + .slice_len, + .slice_elem_val, + .slice_elem_ptr, + .slice_ptr, + .ptr_slice_len_ptr, + .ptr_slice_ptr_ptr, + .store, + .store_safe, + + .set_union_tag, + .struct_field_ptr, + .struct_field_ptr_index_0, + .struct_field_ptr_index_1, + .struct_field_ptr_index_2, + .struct_field_ptr_index_3, + .struct_field_val, + .field_parent_ptr, + + .switch_br, + .loop_switch_br, + .trunc, + + .wrap_optional, + .unwrap_errunion_payload, + .unwrap_errunion_payload_ptr, + .unwrap_errunion_err, + .unwrap_errunion_err_ptr, + .wrap_errunion_payload, + .wrap_errunion_err, + .errunion_payload_ptr_set, + .error_name, + + .wasm_memory_size, + .wasm_memory_grow, + + .memcpy, + + .ret_addr, + .tag_name, + + .error_set_has_value, + .frame_addr, + + .assembly, + .is_err_ptr, + .is_non_err_ptr, + + .err_return_trace, + .set_err_return_trace, + .save_err_return_trace_index, + .is_named_enum_value, + .addrspace_cast, + .vector_store_elem, + .c_va_arg, + .c_va_copy, + .c_va_end, + .c_va_start, + .memmove, + + .atomic_load, + .atomic_store_unordered, + .atomic_store_monotonic, + .atomic_store_release, + .atomic_store_seq_cst, + .atomic_rmw, + .cmpxchg_weak, + .cmpxchg_strong, + + .add_optimized, + .sub_optimized, + .mul_optimized, + .div_float_optimized, + .div_trunc_optimized, + .div_floor_optimized, + .div_exact_optimized, + .rem_optimized, + .mod_optimized, + .neg_optimized, + .cmp_lt_optimized, + .cmp_lte_optimized, + .cmp_eq_optimized, + .cmp_gte_optimized, + .cmp_gt_optimized, + .cmp_neq_optimized, + .cmp_vector_optimized, + .reduce_optimized, + .int_from_float_optimized, + .add_safe, + .sub_safe, + .mul_safe, + .intcast_safe, + => |tag| return cg.fail("TODO: implement spork8 inst: {s}", .{@tagName(tag)}), + + .work_item_id, + .work_group_size, + .work_group_id, + => unreachable, + }; +} + +fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { + _ = cg; + _ = inst; +} + +fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { + const zcu = cg.pt.zcu; + const func = zcu.funcInfo(cg.func_index); + return zcu.codegenFail(func.owner_nav, fmt, args); +} + +fn extraLen(cg: *const CodeGen) u32 { + return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); +} diff --git a/src/codegen/spork8/Mir.zig b/src/codegen/spork8/Mir.zig new file mode 100644 index 0000000000000000000000000000000000000000..c049dd20894af18734752eb9e721cf2f3ce3292a --- /dev/null +++ b/src/codegen/spork8/Mir.zig @@ -0,0 +1,46 @@ +const Mir = @This(); +const InternPool = @import("../../InternPool.zig"); + +const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; + +instruction_tags: []const Inst.Tag, +instruction_datas: []const Inst.Data, +extra: []const u32, + +pub const Inst = struct { + tag: Tag, + data: Data, + + /// The position of a given MIR isntruction with the instruction list. + pub const Index = enum(u32) { + _, + }; + + pub const Tag = enum(u8) { + /// imm8 + set_page_i = 0x04, + /// imm8 + set_addr_i = 0x09, + /// imm8 + load_i = 0x10, + /// index + jump, + }; + + /// All instructions contain a 4-byte payload, which is contained within + /// this union. `Tag` determines which union tag is active, as well as + /// how to interpret the data within. + pub const Data = union { + imm8: u8, + index: Index, + + comptime { + switch (builtin.mode) { + .Debug, .ReleaseSafe => {}, + .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4), + } + } + }; +}; diff --git a/src/dev.zig b/src/dev.zig index 1de9810eb00f5e754fea549b3f0f9fa3d5484d8f..249566889df0efec1b588e4ff5622fbe766f6c27 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -60,6 +60,10 @@ pub const Env = enum { /// - `zig build-* -fincremental -fno-llvm -fno-lld -target loongarch(32/64)-linux --listen=-` @"loongarch-linux", + /// - sema + /// - `zig build-* -fno-llvm -fno-lld -target spork8-* --listen=-` + spork8, + pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool { return switch (dev_env) { .full => true, @@ -102,6 +106,7 @@ pub const Env = enum { .sparc64_backend, .spirv_backend, .loongarch_backend, + .spork8_backend, .lld_linker, .coff_linker, .coff2_linker, @@ -112,6 +117,7 @@ pub const Env = enum { .wasm_linker, .spirv_linker, .plan9_linker, + .spork8_linker, .jit_command, => true, .cc_command, @@ -239,6 +245,14 @@ pub const Env = enum { => true, else => Env.sema.supports(feature), }, + .spork8 => switch (feature) { + .stdio_listen, + .incremental, + .spork8_backend, + .spork8_linker, + => true, + else => Env.sema.supports(feature), + }, }; } @@ -303,6 +317,7 @@ pub const Feature = enum { sparc64_backend, spirv_backend, loongarch_backend, + spork8_backend, lld_linker, coff_linker, @@ -314,6 +329,7 @@ pub const Feature = enum { wasm_linker, spirv_linker, plan9_linker, + spork8_linker, }; /// Makes the code following the call to this function unreachable if `feature` is disabled. diff --git a/src/link.zig b/src/link.zig index eeab7e4f56f441070fc8817c7d8969c0558a4ee0..1e9cbb18c059511eba1a631f21ed18068d3fdf35 100644 --- a/src/link.zig +++ b/src/link.zig @@ -673,6 +673,7 @@ pub const File = struct { }); }, .plan9 => unreachable, + .spork8 => dev.check(.spork8_linker), } } @@ -750,6 +751,7 @@ pub const File = struct { }, .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }), .plan9 => unreachable, + .spork8 => dev.check(.spork8_linker), } } @@ -1025,6 +1027,7 @@ pub const File = struct { .spirv => unreachable, .wasm => unreachable, .plan9 => unreachable, + .spork8 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); @@ -1047,6 +1050,7 @@ pub const File = struct { .spirv => unreachable, .wasm => unreachable, .plan9 => unreachable, + .spork8 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align); @@ -1064,6 +1068,7 @@ pub const File = struct { .spirv => unreachable, .wasm => unreachable, .plan9 => unreachable, + .spork8 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); @@ -1088,6 +1093,7 @@ pub const File = struct { .spirv, .plan9, .lld, + .spork8, => return .unimplemented, inline else => |tag| { dev.check(tag.devFeature()); @@ -1266,6 +1272,7 @@ pub const File = struct { c, wasm, spirv, + spork8, plan9, lld, @@ -1280,6 +1287,7 @@ pub const File = struct { .spirv => SpirV, .lld => Lld, .plan9 => comptime unreachable, + .spork8 => Spork8, }; } @@ -1292,6 +1300,7 @@ pub const File = struct { .plan9 => .plan9, .c => .c, .spirv => .spirv, + .spork8 => .spork8, .hex => @panic("TODO implement hex object format"), .raw => @panic("TODO implement raw object format"), }; @@ -1373,6 +1382,7 @@ pub const File = struct { pub const Lld = @import("link/Lld.zig"); pub const C = @import("link/C.zig"); pub const Coff2 = @import("link/Coff.zig"); + pub const Spork8 = @import("link/Spork8.zig"); pub const Elf = @import("link/Elf.zig"); pub const Elf2 = @import("link/Elf2.zig"); pub const MachO = @import("link/MachO.zig"); diff --git a/src/link/Spork8.zig b/src/link/Spork8.zig new file mode 100644 index 0000000000000000000000000000000000000000..bd2d3000d5b5848ae2cdac45573faddb937716a5 --- /dev/null +++ b/src/link/Spork8.zig @@ -0,0 +1,188 @@ +const Spork8 = @This(); +const builtin = @import("builtin"); +const build_options = @import("build_options"); + +const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const Path = std.Build.Cache.Path; +const log = std.log.scoped(.link); + +const Air = @import("../Air.zig"); +const InternPool = @import("../InternPool.zig"); +const Zcu = @import("../Zcu.zig"); +const CodeGen = @import("../codegen/spork8/CodeGen.zig"); +const codegen = @import("../codegen.zig"); +const Mir = @import("../codegen/spork8/Mir.zig"); +const link = @import("../link.zig"); +const Compilation = @import("../Compilation.zig"); +const Liveness = @import("../Air/Liveness.zig"); +const dev = @import("../dev.zig"); +const Value = @import("../Value.zig"); + +base: link.File, +funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, CodeGen.Function) = .empty, +/// All MIR instructions for all Zcu functions. +mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, +/// Corresponds to `mir_instructions`. +mir_extra: std.ArrayListUnmanaged(u32) = .empty, + +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Path, + options: link.File.OpenOptions, +) !*Spork8 { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); +} + +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Path, + options: link.File.OpenOptions, +) !*Spork8 { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .spork8); + assert(comp.config.output_mode == .Exe); + const io = comp.io; + + const spork8 = try arena.create(Spork8); + spork8.* = .{ + .base = .{ + .tag = .spork8, + .comp = comp, + .emit = emit, + .gc_sections = options.gc_sections orelse true, + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse switch (target.os.tag) { + .freestanding => 1 * 1024 * 1024, // 1 MiB + else => 16 * 1024 * 1024, // 16 MiB + }, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, + .file = null, + .build_id = options.build_id, + }, + }; + errdefer spork8.base.destroy(); + + spork8.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ + .truncate = true, + .read = true, + }); + + return spork8; +} + +pub fn deinit(spork8: *Spork8) void { + const gpa = spork8.base.comp.gpa; + _ = gpa; +} + +pub fn updateFunc( + spork8: *Spork8, + pt: Zcu.PerThread, + func_index: InternPool.Index, + any_mir: *const codegen.AnyMir, +) !void { + dev.check(.spork8_backend); + // This linker implementation only works with codegen backend `.stage2_wasm`. + const mir = &any_mir.spork8; + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + const owner_nav = zcu.funcInfo(func_index).owner_nav; + _ = gpa; + _ = mir; + _ = spork8; + log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); +} + +// Generate code for the "Nav", storing it in memory to be later written to +// the file on flush(). +pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { + _ = spork8; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + log.debug("updateNav {f}", .{nav.fqn.fmt(ip)}); +} + +pub fn updateLineNumber(spork8: *Spork8, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { + _ = spork8; + _ = pt; + _ = ti_id; +} + +pub fn deleteExport( + spork8: *Spork8, + exported: Zcu.Exported, + name: InternPool.NullTerminatedString, +) void { + const zcu = spork8.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const name_slice = name.toSlice(ip); + switch (exported) { + .nav => |nav_index| { + log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) }); + }, + .uav => |uav_index| { + log.debug("deleteExport '{s}' uav={d}", .{ name_slice, @backingInt(uav_index) }); + }, + } +} + +pub fn updateExports( + spork8: *Spork8, + pt: Zcu.PerThread, + export_indices: []const Zcu.Export.Index, +) !void { + _ = spork8; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + for (export_indices) |export_idx| { + const exp = export_idx.ptr(zcu); + const name_slice = exp.opts.name.toSlice(ip); + switch (exp.exported) { + .nav => |nav_index| { + log.debug("updateExports {q} nav={d}", .{ name_slice, @backingInt(nav_index) }); + }, + .uav => |uav_index| { + log.debug("updateExports {q} uav={d}", .{ name_slice, @backingInt(uav_index) }); + }, + } + } +} + +pub fn loadInput(spork8: *Spork8, input: link.Input) !void { + _ = input; + const comp = spork8.base.comp; + const diags = &comp.link_diags; + return diags.failParse("spork8 does not support linking files together", .{}); +} + +pub fn flush( + spork8: *Spork8, + arena: Allocator, + tid: Zcu.PerThread.Id, + prog_node: std.Progress.Node, +) link.Error!void { + const sub_prog_node = prog_node.start("Spork8 Flush", 0); + defer sub_prog_node.end(); + + _ = spork8; + _ = arena; + _ = tid; + log.debug("TODO implement flush", .{}); +} + +pub fn prelink(spork8: *Spork8, prog_node: std.Progress.Node) link.Error!void { + const sub_prog_node = prog_node.start("Spork8 Prelink", 0); + defer sub_prog_node.end(); + + _ = spork8; +} diff --git a/src/target.zig b/src/target.zig index fda6a4cbf6e45b6b398320d237293cb3965beab5..02cd9c2f93561df3b191aa6e47be1e8b97eda5aa 100644 --- a/src/target.zig +++ b/src/target.zig @@ -189,6 +189,7 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) // LLVM does not support these object formats: .c, .plan9, + .spork8, => return false, .coff, @@ -271,6 +272,7 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) .sheb, .x86_16, .xtensaeb, + .spork8, => false, }; } @@ -433,6 +435,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_onl } switch (target.cpu.arch) { .spirv32, .spirv64 => return .no, + .spork8 => return .no, // Remove this once https://github.com/ziglang/zig/issues/23714 is fixed .amdgcn => return .no, else => {}, @@ -445,6 +448,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_onl pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only, llvm_lld_only } { switch (target.cpu.arch) { + .spork8 => return .no, .spirv32, .spirv64 => return .no, // Remove this once https://github.com/ziglang/zig/issues/23715 is fixed .nvptx, .nvptx64 => return .no, @@ -929,6 +933,7 @@ pub fn zigBackend(target: *const std.Target, use_llvm: bool) std.lang.CompilerBa .wasm32, .wasm64 => .stage2_wasm, .x86 => .stage2_x86, .x86_64 => .stage2_x86_64, + .spork8 => .zsf_spork8, else => .other, }; }