authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-04-03 20:59:41+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-04-08 22:47:08+02:00
log9fd1dab58230edae11e2798f30ec43704a0c2178
treec08e9f88d40401aa026d47fb9593747e75ce557f
parent1bd5552fc1a8fd2ddcb8f0c17f35662e4eb1cbcf
signature Commit is signed but in an unrecognized format.

Handle incremental compilation correctly


1 files changed, 48 insertions(+), 17 deletions(-)

src/link/Wasm.zig+48-17
...@@ -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 length35/// where the offset is calculated using the previous segments and the content length
36/// of the data36/// of the data
37pub const DataSection = struct {37pub 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 }) = .{},
3945
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' section52 } else unreachable; // offset() called on declaration that does not live inside 'data' section
47 }53 }
4854
49 /// Returns the total payload size of the data section55 /// 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 called65 /// 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 segments68 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};
6681
...@@ -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 section148 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}
140156
141pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {157pub 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 = .{};
145160
...@@ -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 available164 // we must calculate its data length now so that the data offsets are available
150 // to other decls when called165 // 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 list185 // 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 to223// Generate code for the Decl, storing it in memory to be later written to
194// the file on flush().224// the file on flush().
195pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {225pub 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;
198227
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}
271302
...@@ -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());
469500
470 // write payload501 // 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]);
472503
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 list773/// 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 not774/// 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 simpler775/// TODO: we could maintain a hash map to potentially make this simpler
745fn getFuncidx(self: Wasm, decl: *const Module.Decl) ?u32 {776fn 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: {