| ... | @@ -35,22 +35,28 @@ pub const FnData = struct { | ... | @@ -35,22 +35,28 @@ pub const FnData = struct { |
| 35 | /// where the offset is calculated using the previous segments and the content length | 35 | /// where the offset is calculated using the previous segments and the content length |
| 36 | /// of the data | 36 | /// of the data |
| 37 | pub const DataSection = struct { | 37 | pub const DataSection = struct { |
| 38 | segments: std.AutoArrayHashMapUnmanaged(*Module.Decl, struct { data: [*]const u8, len: u32 }) = .{}, | 38 | /// Every data object will be appended to this list, |
| | 39 | /// containing its `Decl`, the data in bytes, and its length. |
| | 40 | segments: std.ArrayListUnmanaged(struct { |
| | 41 | decl: *Module.Decl, |
| | 42 | data: [*]const u8, |
| | 43 | len: u32, |
| | 44 | }) = .{}, |
| 39 | | 45 | |
| 40 | /// Returns the offset into the data segment based on a given `Decl` | 46 | /// Returns the offset into the data segment based on a given `Decl` |
| 41 | pub fn offset(self: DataSection, decl: *const Module.Decl) u32 { | 47 | pub fn offset(self: DataSection, decl: *const Module.Decl) u32 { |
| 42 | var cur_offset: u32 = 0; | 48 | var cur_offset: u32 = 0; |
| 43 | return for (self.segments.items()) |entry| { | 49 | return for (self.segments.items) |entry| { |
| 44 | if (entry.key == decl) break cur_offset; | 50 | if (entry.decl == decl) break cur_offset; |
| 45 | cur_offset += entry.value.len; | 51 | cur_offset += entry.len; |
| 46 | } else unreachable; // offset() called on declaration that does not live inside 'data' section | 52 | } else unreachable; // offset() called on declaration that does not live inside 'data' section |
| 47 | } | 53 | } |
| 48 | | 54 | |
| 49 | /// Returns the total payload size of the data section | 55 | /// Returns the total payload size of the data section |
| 50 | pub fn size(self: DataSection) u32 { | 56 | pub fn size(self: DataSection) u32 { |
| 51 | var total: u32 = 0; | 57 | var total: u32 = 0; |
| 52 | for (self.segments.items()) |entry| { | 58 | for (self.segments.items) |entry| { |
| 53 | total += entry.value.len; | 59 | total += entry.len; |
| 54 | } | 60 | } |
| 55 | return total; | 61 | return total; |
| 56 | } | 62 | } |
| ... | @@ -59,8 +65,17 @@ pub const DataSection = struct { | ... | @@ -59,8 +65,17 @@ pub const DataSection = struct { |
| 59 | /// It's illegal behaviour to call this before allocateDeclIndexes was called | 65 | /// It's illegal behaviour to call this before allocateDeclIndexes was called |
| 60 | /// `data` must be managed externally with a lifetime that last as long as codegen does. | 66 | /// `data` must be managed externally with a lifetime that last as long as codegen does. |
| 61 | pub fn updateData(self: DataSection, decl: *Module.Decl, data: []const u8) void { | 67 | pub fn updateData(self: DataSection, decl: *Module.Decl, data: []const u8) void { |
| 62 | const entry = self.segments.getEntry(decl).?; // called updateData before the declaration was added to data segments | 68 | const entry = for (self.segments.items) |*item| { |
| 63 | entry.value.data = data.ptr; | 69 | if (item.decl == decl) break item; |
| | 70 | } else unreachable; // called updateData before the declaration was added to data segments |
| | 71 | entry.data = data.ptr; |
| | 72 | } |
| | 73 | |
| | 74 | /// Returns the index of a declaration and `null` when not found |
| | 75 | pub fn idx(self: DataSection, decl: *Module.Decl) ?usize { |
| | 76 | return for (self.segments.items) |entry, i| { |
| | 77 | if (entry.decl == decl) break i; |
| | 78 | } else null; |
| 64 | } | 79 | } |
| 65 | }; | 80 | }; |
| 66 | | 81 | |
| ... | @@ -129,9 +144,10 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -129,9 +144,10 @@ pub fn deinit(self: *Wasm) void { |
| 129 | decl.fn_link.wasm.code.deinit(self.base.allocator); | 144 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| 130 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | 145 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| 131 | } | 146 | } |
| 132 | for (self.data.segments.items()) |entry| { | 147 | for (self.data.segments.items) |entry| { |
| 133 | // data segments only use the code section | 148 | entry.decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 134 | entry.key.fn_link.wasm.code.deinit(self.base.allocator); | 149 | entry.decl.fn_link.wasm.code.deinit(self.base.allocator); |
| | 150 | entry.decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| 135 | } | 151 | } |
| 136 | self.funcs.deinit(self.base.allocator); | 152 | self.funcs.deinit(self.base.allocator); |
| 137 | self.ext_funcs.deinit(self.base.allocator); | 153 | self.ext_funcs.deinit(self.base.allocator); |
| ... | @@ -139,7 +155,6 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -139,7 +155,6 @@ pub fn deinit(self: *Wasm) void { |
| 139 | } | 155 | } |
| 140 | | 156 | |
| 141 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | 157 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 142 | std.debug.print("INIT: '{s}'\n", .{decl.name}); | | |
| 143 | const tv = decl.typed_value.most_recent.typed_value; | 158 | const tv = decl.typed_value.most_recent.typed_value; |
| 144 | decl.fn_link.wasm = .{}; | 159 | decl.fn_link.wasm = .{}; |
| 145 | | 160 | |
| ... | @@ -149,7 +164,22 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | ... | @@ -149,7 +164,22 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 149 | // we must calculate its data length now so that the data offsets are available | 164 | // we must calculate its data length now so that the data offsets are available |
| 150 | // to other decls when called | 165 | // to other decls when called |
| 151 | const data_len = calcDataLen(self, tv) catch return error.AnalysisFail; | 166 | const data_len = calcDataLen(self, tv) catch return error.AnalysisFail; |
| 152 | try self.data.segments.putNoClobber(self.base.allocator, decl, .{ .data = undefined, .len = data_len }); | 167 | try self.data.segments.append(self.base.allocator, .{ |
| | 168 | .decl = decl, |
| | 169 | .data = undefined, |
| | 170 | .len = data_len, |
| | 171 | }); |
| | 172 | |
| | 173 | // detect if we can replace it into a to-be-deleted decl's spot to ensure no gaps are |
| | 174 | // made in our data segment |
| | 175 | const idx: ?usize = for (self.data.segments.items) |entry, i| { |
| | 176 | if (entry.decl.deletion_flag) break i; |
| | 177 | } else null; |
| | 178 | if (idx) |id| { |
| | 179 | const old_decl = self.data.segments.swapRemove(id); // current decl is now in to-be-deleted decl's spot |
| | 180 | // re-append to end of list so it can be cleaned up by `freeDecl` |
| | 181 | try self.data.segments.append(self.base.allocator, old_decl); |
| | 182 | } |
| 153 | }, | 183 | }, |
| 154 | .Fn => if (self.getFuncidx(decl) == null) switch (tv.val.tag()) { | 184 | .Fn => if (self.getFuncidx(decl) == null) switch (tv.val.tag()) { |
| 155 | // dependent on function type, appends it to the correct list | 185 | // dependent on function type, appends it to the correct list |
| ... | @@ -193,7 +223,6 @@ fn calcDataLen(bin_file: *Wasm, typed_value: TypedValue) DataLenError!u32 { | ... | @@ -193,7 +223,6 @@ fn calcDataLen(bin_file: *Wasm, typed_value: TypedValue) DataLenError!u32 { |
| 193 | // Generate code for the Decl, storing it in memory to be later written to | 223 | // Generate code for the Decl, storing it in memory to be later written to |
| 194 | // the file on flush(). | 224 | // the file on flush(). |
| 195 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | 225 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 196 | std.debug.print("Updating '{s}'\n", .{decl.name}); | | |
| 197 | const typed_value = decl.typed_value.most_recent.typed_value; | 226 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 198 | | 227 | |
| 199 | const fn_data = &decl.fn_link.wasm; | 228 | const fn_data = &decl.fn_link.wasm; |
| ... | @@ -262,10 +291,12 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { | ... | @@ -262,10 +291,12 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 262 | else => unreachable, | 291 | else => unreachable, |
| 263 | } | 292 | } |
| 264 | } | 293 | } |
| | 294 | if (self.data.idx(decl)) |idx| { |
| | 295 | _ = self.data.segments.swapRemove(idx); |
| | 296 | } |
| 265 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | 297 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 266 | decl.fn_link.wasm.code.deinit(self.base.allocator); | 298 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| 267 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | 299 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| 268 | _ = self.data.segments.orderedRemove(decl); | | |
| 269 | decl.fn_link.wasm = undefined; | 300 | decl.fn_link.wasm = undefined; |
| 270 | } | 301 | } |
| 271 | | 302 | |
| ... | @@ -468,7 +499,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -468,7 +499,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 468 | try leb.writeULEB128(writer, self.data.size()); | 499 | try leb.writeULEB128(writer, self.data.size()); |
| 469 | | 500 | |
| 470 | // write payload | 501 | // write payload |
| 471 | for (self.data.segments.items()) |entry| try writer.writeAll(entry.value.data[0..entry.value.len]); | 502 | for (self.data.segments.items) |entry| try writer.writeAll(entry.data[0..entry.len]); |
| 472 | | 503 | |
| 473 | try writeVecSectionHeader( | 504 | try writeVecSectionHeader( |
| 474 | file, | 505 | file, |
| ... | @@ -742,7 +773,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -742,7 +773,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 742 | /// Get the current index of a given Decl in the function list | 773 | /// Get the current index of a given Decl in the function list |
| 743 | /// This will correctly provide the index, regardless whether the function is extern or not | 774 | /// This will correctly provide the index, regardless whether the function is extern or not |
| 744 | /// TODO: we could maintain a hash map to potentially make this simpler | 775 | /// TODO: we could maintain a hash map to potentially make this simpler |
| 745 | fn getFuncidx(self: Wasm, decl: *const Module.Decl) ?u32 { | 776 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { |
| 746 | var offset: u32 = 0; | 777 | var offset: u32 = 0; |
| 747 | const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) { | 778 | const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) { |
| 748 | .function => blk: { | 779 | .function => blk: { |