| ... | @@ -20,70 +20,7 @@ const TypedValue = @import("../TypedValue.zig"); | ... | @@ -20,70 +20,7 @@ const TypedValue = @import("../TypedValue.zig"); |
| 20 | | 20 | |
| 21 | pub const base_tag = link.File.Tag.wasm; | 21 | pub const base_tag = link.File.Tag.wasm; |
| 22 | | 22 | |
| 23 | pub const FnData = struct { | | |
| 24 | /// Generated code for the type of the function | | |
| 25 | functype: std.ArrayListUnmanaged(u8) = .{}, | | |
| 26 | /// Generated code for the body of the function | | |
| 27 | code: std.ArrayListUnmanaged(u8) = .{}, | | |
| 28 | /// Locations in the generated code where function indexes must be filled in. | | |
| 29 | /// This must be kept ordered by offset. | | |
| 30 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, | | |
| 31 | }; | | |
| 32 | | | |
| 33 | /// Data section of the wasm binary | | |
| 34 | /// Each declaration will have its own 'data_segment' within the section | | |
| 35 | /// where the offset is calculated using the previous segments and the content length | | |
| 36 | /// of the data | | |
| 37 | pub const DataSection = struct { | | |
| 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 | /// The decl that lives inside the 'data' section such as an array | | |
| 42 | decl: *Module.Decl, | | |
| 43 | /// The contents of the data in bytes | | |
| 44 | data: [*]const u8, | | |
| 45 | /// The length of the contents inside the 'data' section | | |
| 46 | len: u32, | | |
| 47 | }) = .{}, | | |
| 48 | | | |
| 49 | /// Returns the offset into the data segment based on a given `Decl` | | |
| 50 | pub fn offset(self: DataSection, decl: *const Module.Decl) u32 { | | |
| 51 | var cur_offset: u32 = 0; | | |
| 52 | return for (self.segments.items) |entry| { | | |
| 53 | if (entry.decl == decl) break cur_offset; | | |
| 54 | cur_offset += entry.len; | | |
| 55 | } else unreachable; // offset() called on declaration that does not live inside 'data' section | | |
| 56 | } | | |
| 57 | | | |
| 58 | /// Returns the total payload size of the data section | | |
| 59 | pub fn size(self: DataSection) u32 { | | |
| 60 | var total: u32 = 0; | | |
| 61 | for (self.segments.items) |entry| { | | |
| 62 | total += entry.len; | | |
| 63 | } | | |
| 64 | return total; | | |
| 65 | } | | |
| 66 | | | |
| 67 | /// Updates the data in the data segment belonging to the given decl. | | |
| 68 | /// It's illegal behaviour to call this before allocateDeclIndexes was called | | |
| 69 | /// `data` must be managed externally with a lifetime that last as long as codegen does. | | |
| 70 | pub fn updateData(self: DataSection, decl: *Module.Decl, data: []const u8) void { | | |
| 71 | const entry = for (self.segments.items) |*item| { | | |
| 72 | if (item.decl == decl) break item; | | |
| 73 | } else unreachable; // called updateData before the declaration was added to data segments | | |
| 74 | entry.data = data.ptr; | | |
| 75 | } | | |
| 76 | | | |
| 77 | /// Returns the index of a declaration and `null` when not found | | |
| 78 | pub fn getIdx(self: DataSection, decl: *Module.Decl) ?usize { | | |
| 79 | return for (self.segments.items) |entry, i| { | | |
| 80 | if (entry.decl == decl) break i; | | |
| 81 | } else null; | | |
| 82 | } | | |
| 83 | }; | | |
| 84 | | | |
| 85 | base: link.File, | 23 | base: link.File, |
| 86 | | | |
| 87 | /// List of all function Decls to be written to the output file. The index of | 24 | /// List of all function Decls to be written to the output file. The index of |
| 88 | /// each Decl in this list at the time of writing the binary is used as the | 25 | /// each Decl in this list at the time of writing the binary is used as the |
| 89 | /// function index. In the event where ext_funcs' size is not 0, the index of | 26 | /// function index. In the event where ext_funcs' size is not 0, the index of |
| ... | @@ -98,10 +35,67 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, | ... | @@ -98,10 +35,67 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 98 | /// to support existing code. | 35 | /// to support existing code. |
| 99 | /// TODO: Allow setting this through a flag? | 36 | /// TODO: Allow setting this through a flag? |
| 100 | host_name: []const u8 = "env", | 37 | host_name: []const u8 = "env", |
| 101 | /// Map of declarations with its bytes payload, used to keep track of all data segments | 38 | /// The last `DeclBlock` that was initialized will be saved here. |
| 102 | /// that needs to be emit when creating the wasm binary. | 39 | last_block: ?*DeclBlock = null, |
| 103 | /// The `DataSection`'s lifetime must be kept alive until the linking stage. | 40 | /// Table with offsets, each element represents an offset with the value being |
| 104 | data: DataSection = .{}, | 41 | /// the offset into the 'data' section where the data lives |
| | 42 | offset_table: std.ArrayListUnmanaged(u32) = .{}, |
| | 43 | /// List of offset indexes which are free to be used for new decl's. |
| | 44 | /// Each element's value points to an index into the offset_table. |
| | 45 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| | 46 | /// List of all `Decl` that are currently alive. |
| | 47 | /// This is ment for bookkeeping so we can safely cleanup all codegen memory |
| | 48 | /// when calling `deinit` |
| | 49 | symbols: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| | 50 | /// Contains indexes into `symbols` that are no longer used and can be populated instead, |
| | 51 | /// removing the need to search for a symbol and remove it when it's dereferenced. |
| | 52 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| | 53 | |
| | 54 | pub const FnData = struct { |
| | 55 | /// Generated code for the type of the function |
| | 56 | functype: std.ArrayListUnmanaged(u8), |
| | 57 | /// Generated code for the body of the function |
| | 58 | code: std.ArrayListUnmanaged(u8), |
| | 59 | /// Locations in the generated code where function indexes must be filled in. |
| | 60 | /// This must be kept ordered by offset. |
| | 61 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }), |
| | 62 | |
| | 63 | pub const empty: FnData = .{ |
| | 64 | .functype = .{}, |
| | 65 | .code = .{}, |
| | 66 | .idx_refs = .{}, |
| | 67 | }; |
| | 68 | }; |
| | 69 | |
| | 70 | pub const DeclBlock = struct { |
| | 71 | /// Determines whether the `DeclBlock` has been initialized for codegen. |
| | 72 | init: bool, |
| | 73 | /// Index into the `symbols` list. |
| | 74 | symbol_index: u32, |
| | 75 | /// Index into the offset table |
| | 76 | offset_index: u32, |
| | 77 | /// The size of the block and how large part of the data section it occupies. |
| | 78 | /// Will be 0 when the Decl will not live inside the data section and `data` will be undefined. |
| | 79 | size: u32, |
| | 80 | /// Points to the previous and next blocks. |
| | 81 | /// Can be used to find the total size, and used to calculate the `offset` based on the previous block. |
| | 82 | prev: ?*DeclBlock, |
| | 83 | next: ?*DeclBlock, |
| | 84 | /// Pointer to data that will be written to the 'data' section. |
| | 85 | /// This data either lives in `FnData.code` or is externally managed. |
| | 86 | /// For data that does not live inside the 'data' section, this field will be undefined. (size == 0). |
| | 87 | data: [*]const u8, |
| | 88 | |
| | 89 | pub const empty: DeclBlock = .{ |
| | 90 | .init = false, |
| | 91 | .symbol_index = 0, |
| | 92 | .offset_index = 0, |
| | 93 | .size = 0, |
| | 94 | .prev = null, |
| | 95 | .next = null, |
| | 96 | .data = undefined, |
| | 97 | }; |
| | 98 | }; |
| 105 | | 99 | |
| 106 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { | 100 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 107 | assert(options.object_format == .wasm); | 101 | assert(options.object_format == .wasm); |
| ... | @@ -137,94 +131,66 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { | ... | @@ -137,94 +131,66 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { |
| 137 | } | 131 | } |
| 138 | | 132 | |
| 139 | pub fn deinit(self: *Wasm) void { | 133 | pub fn deinit(self: *Wasm) void { |
| 140 | for (self.funcs.items) |decl| { | 134 | while (self.symbols_free_list.popOrNull()) |idx| { |
| 141 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | 135 | //dead decl's so remove them from symbol list before trying to clean them up |
| 142 | decl.fn_link.wasm.code.deinit(self.base.allocator); | 136 | _ = self.symbols.swapRemove(idx); |
| 143 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | | |
| 144 | } | 137 | } |
| 145 | for (self.ext_funcs.items) |decl| { | 138 | for (self.symbols.items) |decl| { |
| 146 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | 139 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 147 | decl.fn_link.wasm.code.deinit(self.base.allocator); | 140 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| 148 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | 141 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| 149 | } | 142 | } |
| 150 | for (self.data.segments.items) |entry| { | 143 | |
| 151 | // decl's that live in data section do not generate idx_refs or func types | | |
| 152 | entry.decl.fn_link.wasm.code.deinit(self.base.allocator); | | |
| 153 | } | | |
| 154 | self.funcs.deinit(self.base.allocator); | 144 | self.funcs.deinit(self.base.allocator); |
| 155 | self.ext_funcs.deinit(self.base.allocator); | 145 | self.ext_funcs.deinit(self.base.allocator); |
| 156 | self.data.segments.deinit(self.base.allocator); | 146 | self.offset_table.deinit(self.base.allocator); |
| | 147 | self.offset_table_free_list.deinit(self.base.allocator); |
| | 148 | self.symbols.deinit(self.base.allocator); |
| | 149 | self.symbols_free_list.deinit(self.base.allocator); |
| 157 | } | 150 | } |
| 158 | | 151 | |
| 159 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | 152 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 160 | const typed_value = decl.typed_value.most_recent.typed_value; | 153 | if (decl.link.wasm.init) return; |
| 161 | | 154 | |
| 162 | switch (typed_value.ty.zigTypeTag()) { | 155 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 163 | .Array => { | 156 | try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1); |
| 164 | // if the codegen of the given decl contributes to the data segment | | |
| 165 | // we must calculate its data length now so that the data offsets are available | | |
| 166 | // to other decls when called | | |
| 167 | const data_len = self.calcDataLen(typed_value); | | |
| 168 | try self.data.segments.append(self.base.allocator, .{ | | |
| 169 | .decl = decl, | | |
| 170 | .data = undefined, | | |
| 171 | .len = data_len, | | |
| 172 | }); | | |
| 173 | | 157 | |
| 174 | // detect if we can replace it into a to-be-deleted decl's spot to ensure no gaps are | 158 | const block = &decl.link.wasm; |
| 175 | // made in our data segment | 159 | block.init = true; |
| 176 | const idx: ?usize = for (self.data.segments.items) |entry, i| { | 160 | |
| 177 | if (entry.decl.deletion_flag) break i; | 161 | if (self.symbols_free_list.popOrNull()) |index| { |
| 178 | } else null; | 162 | block.symbol_index = index; |
| 179 | | 163 | } else { |
| 180 | if (idx) |id| { | 164 | block.symbol_index = @intCast(u32, self.symbols.items.len); |
| 181 | // swap to-be-removed decl with newly added to create a contigious valid data segment | 165 | _ = self.symbols.addOneAssumeCapacity(); |
| 182 | const items = self.data.segments.items; | 166 | } |
| 183 | std.mem.swap( | 167 | |
| 184 | std.meta.Child(@TypeOf(items)), | 168 | if (self.offset_table_free_list.popOrNull()) |index| { |
| 185 | &items[id], | 169 | block.offset_index = index; |
| 186 | &items[items.len - 1], | 170 | } else { |
| 187 | ); | 171 | block.offset_index = @intCast(u32, self.offset_table.items.len); |
| 188 | } | 172 | _ = self.offset_table.addOneAssumeCapacity(); |
| 189 | }, | 173 | } |
| 190 | .Fn => if (self.getFuncidx(decl) == null) switch (typed_value.val.tag()) { | 174 | |
| | 175 | self.offset_table.items[block.offset_index] = 0; |
| | 176 | |
| | 177 | const typed_value = decl.typed_value.most_recent.typed_value; |
| | 178 | if (typed_value.ty.zigTypeTag() == .Fn) { |
| | 179 | switch (typed_value.val.tag()) { |
| 191 | // dependent on function type, appends it to the correct list | 180 | // dependent on function type, appends it to the correct list |
| 192 | .function => try self.funcs.append(self.base.allocator, decl), | 181 | .function => try self.funcs.append(self.base.allocator, decl), |
| 193 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), | 182 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), |
| 194 | else => unreachable, | 183 | else => unreachable, |
| 195 | }, | 184 | } |
| 196 | else => {}, | | |
| 197 | } | | |
| 198 | } | | |
| 199 | | | |
| 200 | /// Calculates the length of the data segment that will be occupied by the given `TypedValue` | | |
| 201 | fn calcDataLen(self: *Wasm, typed_value: TypedValue) u32 { | | |
| 202 | switch (typed_value.ty.zigTypeTag()) { | | |
| 203 | .Array => { | | |
| 204 | if (typed_value.val.castTag(.bytes)) |payload| { | | |
| 205 | if (typed_value.ty.sentinel()) |sentinel| { | | |
| 206 | return @intCast(u32, payload.data.len) + self.calcDataLen(.{ | | |
| 207 | .ty = typed_value.ty.elemType(), | | |
| 208 | .val = sentinel, | | |
| 209 | }); | | |
| 210 | } | | |
| 211 | } | | |
| 212 | return @intCast(u32, typed_value.ty.arrayLen()); | | |
| 213 | }, | | |
| 214 | .Int => { | | |
| 215 | const info = typed_value.ty.intInfo(self.base.options.target); | | |
| 216 | return std.math.divCeil(u32, info.bits, 8) catch unreachable; | | |
| 217 | }, | | |
| 218 | .Pointer => return 4, | | |
| 219 | else => unreachable, | | |
| 220 | } | 185 | } |
| 221 | } | 186 | } |
| 222 | | 187 | |
| 223 | // Generate code for the Decl, storing it in memory to be later written to | 188 | // Generate code for the Decl, storing it in memory to be later written to |
| 224 | // the file on flush(). | 189 | // the file on flush(). |
| 225 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | 190 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 226 | const typed_value = decl.typed_value.most_recent.typed_value; | 191 | std.debug.assert(decl.link.wasm.init); // Must call allocateDeclIndexes() |
| 227 | | 192 | |
| | 193 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 228 | const fn_data = &decl.fn_link.wasm; | 194 | const fn_data = &decl.fn_link.wasm; |
| 229 | fn_data.functype.items.len = 0; | 195 | fn_data.functype.items.len = 0; |
| 230 | fn_data.code.items.len = 0; | 196 | fn_data.code.items.len = 0; |
| ... | @@ -252,28 +218,43 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | ... | @@ -252,28 +218,43 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 252 | else => |e| return err, | 218 | else => |e| return err, |
| 253 | }; | 219 | }; |
| 254 | | 220 | |
| 255 | switch (typed_value.ty.zigTypeTag()) { | 221 | const code: []const u8 = switch (result) { |
| 256 | .Fn => { | 222 | .appended => @as([]const u8, context.code.items), |
| 257 | // as locals are patched afterwards, the offsets of funcidx's are off, | 223 | .externally_managed => |payload| payload, |
| 258 | // here we update them to correct them | 224 | }; |
| 259 | for (fn_data.idx_refs.items) |*func| { | | |
| 260 | // For each local, add 6 bytes (count + type) | | |
| 261 | func.offset += @intCast(u32, context.locals.items.len * 6); | | |
| 262 | } | | |
| 263 | | 225 | |
| 264 | fn_data.functype = context.func_type_data.toUnmanaged(); | 226 | fn_data.code = context.code.toUnmanaged(); |
| 265 | fn_data.code = context.code.toUnmanaged(); | 227 | fn_data.functype = context.func_type_data.toUnmanaged(); |
| 266 | }, | 228 | |
| 267 | .Array => switch (result) { | 229 | const block = &decl.link.wasm; |
| 268 | .appended => { | 230 | if (typed_value.ty.zigTypeTag() == .Fn) { |
| 269 | fn_data.functype = context.func_type_data.toUnmanaged(); | 231 | // as locals are patched afterwards, the offsets of funcidx's are off, |
| 270 | fn_data.code = context.code.toUnmanaged(); | 232 | // here we update them to correct them |
| 271 | self.data.updateData(decl, fn_data.code.items); | 233 | for (fn_data.idx_refs.items) |*func| { |
| 272 | }, | 234 | // For each local, add 6 bytes (count + type) |
| 273 | .externally_managed => |payload| self.data.updateData(decl, payload), | 235 | func.offset += @intCast(u32, context.locals.items.len * 6); |
| 274 | }, | 236 | } |
| 275 | else => return error.TODO, | 237 | } else { |
| | 238 | block.size = @intCast(u32, code.len); |
| | 239 | block.data = code.ptr; |
| | 240 | } |
| | 241 | |
| | 242 | // If we're updating an existing decl, unplug it first |
| | 243 | // to avoid infinite loops due to earlier links |
| | 244 | if (block.prev) |prev| { |
| | 245 | prev.next = block.next; |
| | 246 | } |
| | 247 | if (block.next) |next| { |
| | 248 | next.prev = block.prev; |
| 276 | } | 249 | } |
| | 250 | |
| | 251 | if (self.last_block) |last| { |
| | 252 | if (last != block) { |
| | 253 | last.next = block; |
| | 254 | block.prev = last; |
| | 255 | } |
| | 256 | } |
| | 257 | self.last_block = block; |
| 277 | } | 258 | } |
| 278 | | 259 | |
| 279 | pub fn updateDeclExports( | 260 | pub fn updateDeclExports( |
| ... | @@ -291,9 +272,24 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { | ... | @@ -291,9 +272,24 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 291 | else => unreachable, | 272 | else => unreachable, |
| 292 | } | 273 | } |
| 293 | } | 274 | } |
| 294 | if (self.data.getIdx(decl)) |idx| { | 275 | const block = &decl.link.wasm; |
| 295 | _ = self.data.segments.swapRemove(idx); | 276 | |
| | 277 | if (self.last_block == block) { |
| | 278 | self.last_block = block.prev; |
| | 279 | } |
| | 280 | |
| | 281 | if (block.prev) |prev| { |
| | 282 | prev.next = block.next; |
| 296 | } | 283 | } |
| | 284 | |
| | 285 | if (block.next) |next| { |
| | 286 | next.prev = block.prev; |
| | 287 | } |
| | 288 | |
| | 289 | self.offset_table_free_list.append(self.base.allocator, decl.link.wasm.offset_index) catch {}; |
| | 290 | self.symbols_free_list.append(self.base.allocator, decl.link.wasm.symbol_index) catch {}; |
| | 291 | block.init = false; |
| | 292 | |
| 297 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | 293 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 298 | decl.fn_link.wasm.code.deinit(self.base.allocator); | 294 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| 299 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | 295 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| ... | @@ -314,7 +310,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -314,7 +310,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 314 | | 310 | |
| 315 | const file = self.base.file.?; | 311 | const file = self.base.file.?; |
| 316 | const header_size = 5 + 1; | 312 | const header_size = 5 + 1; |
| 317 | const data_size = self.data.size(); | 313 | // ptr_width in bytes |
| | 314 | const ptr_width = self.base.options.target.cpu.arch.ptrBitWidth() / 8; |
| | 315 | // The size of the offset table in bytes |
| | 316 | // The table contains all decl's with its corresponding offset into |
| | 317 | // the 'data' section |
| | 318 | const offset_table_size = @intCast(u32, self.offset_table.items.len * ptr_width); |
| | 319 | |
| | 320 | // The size of the data, this together with `offset_table_size` amounts to the |
| | 321 | // total size of the 'data' section |
| | 322 | var first_decl: ?*DeclBlock = null; |
| | 323 | const data_size: u32 = if (self.last_block) |last| blk: { |
| | 324 | var size = last.size; |
| | 325 | var cur = last; |
| | 326 | while (cur.prev) |prev| : (cur = prev) { |
| | 327 | size += prev.size; |
| | 328 | } |
| | 329 | first_decl = cur; |
| | 330 | break :blk size; |
| | 331 | } else 0; |
| 318 | | 332 | |
| 319 | // No need to rewrite the magic/version header | 333 | // No need to rewrite the magic/version header |
| 320 | try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); | 334 | try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); |
| ... | @@ -396,8 +410,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -396,8 +410,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 396 | writer, | 410 | writer, |
| 397 | try std.math.divCeil( | 411 | try std.math.divCeil( |
| 398 | u32, | 412 | u32, |
| 399 | self.data.size(), | 413 | offset_table_size + data_size, |
| 400 | std.mem.page_size, | 414 | std.wasm.page_size, |
| 401 | ), | 415 | ), |
| 402 | ); | 416 | ); |
| 403 | try writeVecSectionHeader( | 417 | try writeVecSectionHeader( |
| ... | @@ -496,18 +510,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -496,18 +510,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 496 | try leb.writeILEB128(writer, @as(i32, 0)); | 510 | try leb.writeILEB128(writer, @as(i32, 0)); |
| 497 | try writer.writeByte(wasm.opcode(.end)); | 511 | try writer.writeByte(wasm.opcode(.end)); |
| 498 | | 512 | |
| 499 | // payload size | 513 | const total_size = offset_table_size + data_size; |
| 500 | try leb.writeULEB128(writer, data_size); | | |
| 501 | | 514 | |
| 502 | // write payload | 515 | // offset table + data size |
| 503 | for (self.data.segments.items) |entry| try writer.writeAll(entry.data[0..entry.len]); | 516 | try leb.writeULEB128(writer, total_size); |
| 504 | | 517 | |
| | 518 | // fill in the offset table and the data segments |
| | 519 | const file_offset = try file.getPos(); |
| | 520 | var cur = first_decl; |
| | 521 | var data_offset = offset_table_size; |
| | 522 | while (cur) |cur_block| : (cur = cur_block.next) { |
| | 523 | if (cur_block.size == 0) continue; |
| | 524 | std.debug.assert(cur_block.init); |
| | 525 | |
| | 526 | const offset = (cur_block.offset_index) * ptr_width; |
| | 527 | var buf: [4]u8 = undefined; |
| | 528 | std.mem.writeIntLittle(u32, &buf, data_offset); |
| | 529 | |
| | 530 | try file.pwriteAll(&buf, file_offset + offset); |
| | 531 | try file.pwriteAll(cur_block.data[0..cur_block.size], file_offset + data_offset); |
| | 532 | data_offset += cur_block.size; |
| | 533 | } |
| | 534 | |
| | 535 | try file.seekTo(file_offset + data_offset); |
| 505 | try writeVecSectionHeader( | 536 | try writeVecSectionHeader( |
| 506 | file, | 537 | file, |
| 507 | header_offset, | 538 | header_offset, |
| 508 | .data, | 539 | .data, |
| 509 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 540 | @intCast(u32, (file_offset + data_offset) - header_offset - header_size), |
| 510 | @intCast(u32, 1), | 541 | @intCast(u32, 1), // only 1 data section |
| 511 | ); | 542 | ); |
| 512 | } | 543 | } |
| 513 | } | 544 | } |