| ... | @@ -26,6 +26,8 @@ base: link.File, | ... | @@ -26,6 +26,8 @@ base: link.File, |
| 26 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | 26 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 27 | /// Corresponds to `mir_instructions`. | 27 | /// Corresponds to `mir_instructions`. |
| 28 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | 28 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, |
| | 29 | /// When the key is an enum type, this represents a `@tagName` function. |
| | 30 | zcu_funcs: std.array_hash_map.Auto(InternPool.Index, ZcuFunc) = .empty, |
| 29 | | 31 | |
| 30 | pub fn open( | 32 | pub fn open( |
| 31 | arena: Allocator, | 33 | arena: Allocator, |
| ... | @@ -88,18 +90,74 @@ pub fn updateFunc( | ... | @@ -88,18 +90,74 @@ pub fn updateFunc( |
| 88 | any_mir: *const codegen.AnyMir, | 90 | any_mir: *const codegen.AnyMir, |
| 89 | ) !void { | 91 | ) !void { |
| 90 | dev.check(.spork8_backend); | 92 | dev.check(.spork8_backend); |
| 91 | // This linker implementation only works with codegen backend `.stage2_wasm`. | 93 | // This linker implementation only works with codegen backend `.stage2_spork8`. |
| 92 | const mir = &any_mir.spork8; | 94 | const mir = &any_mir.spork8; |
| 93 | const zcu = pt.zcu; | 95 | const zcu = pt.zcu; |
| 94 | const gpa = zcu.gpa; | 96 | const gpa = zcu.gpa; |
| 95 | const ip = &zcu.intern_pool; | 97 | const ip = &zcu.intern_pool; |
| 96 | const owner_nav = zcu.funcInfo(func_index).owner_nav; | 98 | const owner_nav = zcu.funcInfo(func_index).owner_nav; |
| 97 | _ = gpa; | 99 | |
| 98 | _ = mir; | | |
| 99 | _ = spork8; | | |
| 100 | log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); | 100 | log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); |
| | 101 | |
| | 102 | // For Spork8, we do not lower the MIR to code just yet. That lowering happens during `flush`, |
| | 103 | // after garbage collection, which can affect function and global indexes, which affects the |
| | 104 | // LEB integer encoding, which affects the output binary size. |
| | 105 | |
| | 106 | // However, we do move the MIR into a more efficient in-memory representation, where the arrays |
| | 107 | // for all functions are packed together rather than keeping them each in their own `Mir`. |
| | 108 | const mir_instructions_off: u32 = @intCast(spork8.mir_instructions.len); |
| | 109 | const mir_extra_off: u32 = @intCast(spork8.mir_extra.items.len); |
| | 110 | { |
| | 111 | // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices. |
| | 112 | const old_len = spork8.mir_instructions.len; |
| | 113 | try spork8.mir_instructions.resize(gpa, old_len + mir.instructions.len); |
| | 114 | const dest_slice = spork8.mir_instructions.slice().subslice(old_len, mir.instructions.len); |
| | 115 | const src_slice = mir.instructions; |
| | 116 | @memcpy(dest_slice.items(.tag), src_slice.items(.tag)); |
| | 117 | @memcpy(dest_slice.items(.data), src_slice.items(.data)); |
| | 118 | } |
| | 119 | try spork8.mir_extra.appendSlice(gpa, mir.extra); |
| | 120 | |
| | 121 | try spork8.zcu_funcs.ensureUnusedCapacity(gpa, 1); |
| | 122 | |
| | 123 | // This converts AIR to MIR but does not yet lower to Spork8 code. |
| | 124 | spork8.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{ |
| | 125 | .instructions_off = mir_instructions_off, |
| | 126 | .instructions_len = @intCast(mir.instructions.len), |
| | 127 | .extra_off = mir_extra_off, |
| | 128 | .extra_len = @intCast(mir.extra.len), |
| | 129 | } }); |
| 101 | } | 130 | } |
| 102 | | 131 | |
| | 132 | pub const ZcuFunc = union { |
| | 133 | function: Function, |
| | 134 | |
| | 135 | pub const Function = extern struct { |
| | 136 | /// Index into `Spork8.mir_instructions`. |
| | 137 | instructions_off: u32, |
| | 138 | /// This is unused except for as a safety slice bound and could be removed. |
| | 139 | instructions_len: u32, |
| | 140 | /// Index into `Spork8.mir_extra`. |
| | 141 | extra_off: u32, |
| | 142 | /// This is unused except for as a safety slice bound and could be removed. |
| | 143 | extra_len: u32, |
| | 144 | }; |
| | 145 | |
| | 146 | /// Index into `Spork8.zcu_funcs`. |
| | 147 | /// Note that swapRemove is sometimes performed on `zcu_funcs`. |
| | 148 | pub const Index = enum(u32) { |
| | 149 | _, |
| | 150 | |
| | 151 | pub fn key(i: @This(), spork8: *const Spork8) *InternPool.Index { |
| | 152 | return &spork8.zcu_funcs.keys()[@backingInt(i)]; |
| | 153 | } |
| | 154 | |
| | 155 | pub fn value(i: @This(), spork8: *const Spork8) *ZcuFunc { |
| | 156 | return &spork8.zcu_funcs.values()[@backingInt(i)]; |
| | 157 | } |
| | 158 | }; |
| | 159 | }; |
| | 160 | |
| 103 | // Generate code for the "Nav", storing it in memory to be later written to | 161 | // Generate code for the "Nav", storing it in memory to be later written to |
| 104 | // the file on flush(). | 162 | // the file on flush(). |
| 105 | pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 163 | pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| ... | @@ -172,11 +230,34 @@ pub fn flush( | ... | @@ -172,11 +230,34 @@ pub fn flush( |
| 172 | ) link.Error!void { | 230 | ) link.Error!void { |
| 173 | const sub_prog_node = prog_node.start("Spork8 Flush", 0); | 231 | const sub_prog_node = prog_node.start("Spork8 Flush", 0); |
| 174 | defer sub_prog_node.end(); | 232 | defer sub_prog_node.end(); |
| | 233 | const io = spork8.base.comp.io; |
| | 234 | const diags = &spork8.base.comp.link_diags; |
| 175 | | 235 | |
| 176 | _ = spork8; | | |
| 177 | _ = arena; | 236 | _ = arena; |
| 178 | _ = tid; | 237 | _ = tid; |
| 179 | log.debug("TODO implement flush", .{}); | 238 | |
| | 239 | // Finally, write the entire binary into the file. |
| | 240 | var buffer: [1000]u8 = undefined; |
| | 241 | var file_writer = spork8.base.file.?.writer(io, &buffer); |
| | 242 | mirToMC(spork8, &file_writer.interface) catch |err| switch (err) { |
| | 243 | error.WriteFailed => return diags.fail("failed writing to file: {t}", .{file_writer.err.?}), |
| | 244 | }; |
| | 245 | file_writer.end() catch |err| switch (err) { |
| | 246 | error.WriteFailed => return diags.fail("failed writing to file: {t}", .{file_writer.err.?}), |
| | 247 | else => |e| return diags.fail("failed writing to file: {t}", .{e}), |
| | 248 | }; |
| | 249 | } |
| | 250 | |
| | 251 | fn mirToMC(spork8: *Spork8, w: *Io.Writer) !void { |
| | 252 | for (spork8.mir_instructions.items(.tag)) |tag| { |
| | 253 | switch (tag) { |
| | 254 | .set_page_i => @panic("TODO"), |
| | 255 | .set_addr_i => @panic("TODO"), |
| | 256 | .load_i => @panic("TODO"), |
| | 257 | .jump => @panic("TODO"), |
| | 258 | .halt => try w.writeByte(@backingInt(tag)), |
| | 259 | } |
| | 260 | } |
| 180 | } | 261 | } |
| 181 | | 262 | |
| 182 | pub fn prelink(spork8: *Spork8, prog_node: std.Progress.Node) link.Error!void { | 263 | pub fn prelink(spork8: *Spork8, prog_node: std.Progress.Node) link.Error!void { |