| ... | ... | @@ -26,6 +26,8 @@ base: link.File, |
| 26 | 26 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 27 | 27 | /// Corresponds to `mir_instructions`. |
| 28 | 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 | 32 | pub fn open( |
| 31 | 33 | arena: Allocator, |
| ... | ... | @@ -88,18 +90,74 @@ pub fn updateFunc( |
| 88 | 90 | any_mir: *const codegen.AnyMir, |
| 89 | 91 | ) !void { |
| 90 | 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 | 94 | const mir = &any_mir.spork8; |
| 93 | 95 | const zcu = pt.zcu; |
| 94 | 96 | const gpa = zcu.gpa; |
| 95 | 97 | const ip = &zcu.intern_pool; |
| 96 | 98 | const owner_nav = zcu.funcInfo(func_index).owner_nav; |
| 97 | | _ = gpa; |
| 98 | | _ = mir; |
| 99 | | _ = spork8; |
| 99 | |
| 100 | 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 | 161 | // Generate code for the "Nav", storing it in memory to be later written to |
| 104 | 162 | // the file on flush(). |
| 105 | 163 | pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| ... | ... | @@ -172,11 +230,34 @@ pub fn flush( |
| 172 | 230 | ) link.Error!void { |
| 173 | 231 | const sub_prog_node = prog_node.start("Spork8 Flush", 0); |
| 174 | 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 | 236 | _ = arena; |
| 178 | 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 | 263 | pub fn prelink(spork8: *Spork8, prog_node: std.Progress.Node) link.Error!void { |