| ... | @@ -16,21 +16,11 @@ const link = @import("../link.zig"); | ... | @@ -16,21 +16,11 @@ const link = @import("../link.zig"); |
| 16 | const trace = @import("../tracy.zig").trace; | 16 | const trace = @import("../tracy.zig").trace; |
| 17 | const build_options = @import("build_options"); | 17 | const build_options = @import("build_options"); |
| 18 | const Cache = @import("../Cache.zig"); | 18 | const Cache = @import("../Cache.zig"); |
| | 19 | const TypedValue = @import("../TypedValue.zig"); |
| 19 | | 20 | |
| 20 | pub const base_tag = link.File.Tag.wasm; | 21 | pub const base_tag = link.File.Tag.wasm; |
| 21 | | 22 | |
| 22 | pub const FnData = struct { | | |
| 23 | /// Generated code for the type of the function | | |
| 24 | functype: std.ArrayListUnmanaged(u8) = .{}, | | |
| 25 | /// Generated code for the body of the function | | |
| 26 | code: std.ArrayListUnmanaged(u8) = .{}, | | |
| 27 | /// Locations in the generated code where function indexes must be filled in. | | |
| 28 | /// This must be kept ordered by offset. | | |
| 29 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, | | |
| 30 | }; | | |
| 31 | | | |
| 32 | base: link.File, | 23 | base: link.File, |
| 33 | | | |
| 34 | /// 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 |
| 35 | /// 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 |
| 36 | /// 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 |
| ... | @@ -45,6 +35,77 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, | ... | @@ -45,6 +35,77 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 45 | /// to support existing code. | 35 | /// to support existing code. |
| 46 | /// TODO: Allow setting this through a flag? | 36 | /// TODO: Allow setting this through a flag? |
| 47 | host_name: []const u8 = "env", | 37 | host_name: []const u8 = "env", |
| | 38 | /// The last `DeclBlock` that was initialized will be saved here. |
| | 39 | last_block: ?*DeclBlock = null, |
| | 40 | /// Table with offsets, each element represents an offset with the value being |
| | 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 | |
| | 51 | pub const FnData = struct { |
| | 52 | /// Generated code for the type of the function |
| | 53 | functype: std.ArrayListUnmanaged(u8), |
| | 54 | /// Generated code for the body of the function |
| | 55 | code: std.ArrayListUnmanaged(u8), |
| | 56 | /// Locations in the generated code where function indexes must be filled in. |
| | 57 | /// This must be kept ordered by offset. |
| | 58 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }), |
| | 59 | |
| | 60 | pub const empty: FnData = .{ |
| | 61 | .functype = .{}, |
| | 62 | .code = .{}, |
| | 63 | .idx_refs = .{}, |
| | 64 | }; |
| | 65 | }; |
| | 66 | |
| | 67 | pub const DeclBlock = struct { |
| | 68 | /// Determines whether the `DeclBlock` has been initialized for codegen. |
| | 69 | init: bool, |
| | 70 | /// Index into the `symbols` list. |
| | 71 | symbol_index: u32, |
| | 72 | /// Index into the offset table |
| | 73 | offset_index: u32, |
| | 74 | /// The size of the block and how large part of the data section it occupies. |
| | 75 | /// Will be 0 when the Decl will not live inside the data section and `data` will be undefined. |
| | 76 | size: u32, |
| | 77 | /// Points to the previous and next blocks. |
| | 78 | /// Can be used to find the total size, and used to calculate the `offset` based on the previous block. |
| | 79 | prev: ?*DeclBlock, |
| | 80 | next: ?*DeclBlock, |
| | 81 | /// Pointer to data that will be written to the 'data' section. |
| | 82 | /// This data either lives in `FnData.code` or is externally managed. |
| | 83 | /// For data that does not live inside the 'data' section, this field will be undefined. (size == 0). |
| | 84 | data: [*]const u8, |
| | 85 | |
| | 86 | pub const empty: DeclBlock = .{ |
| | 87 | .init = false, |
| | 88 | .symbol_index = 0, |
| | 89 | .offset_index = 0, |
| | 90 | .size = 0, |
| | 91 | .prev = null, |
| | 92 | .next = null, |
| | 93 | .data = undefined, |
| | 94 | }; |
| | 95 | |
| | 96 | /// Unplugs the `DeclBlock` from the chain |
| | 97 | fn unplug(self: *DeclBlock) void { |
| | 98 | if (self.prev) |prev| { |
| | 99 | prev.next = self.next; |
| | 100 | } |
| | 101 | |
| | 102 | if (self.next) |next| { |
| | 103 | next.prev = self.prev; |
| | 104 | } |
| | 105 | self.next = null; |
| | 106 | self.prev = null; |
| | 107 | } |
| | 108 | }; |
| 48 | | 109 | |
| 49 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { | 110 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 50 | assert(options.object_format == .wasm); | 111 | assert(options.object_format == .wasm); |
| ... | @@ -52,7 +113,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -52,7 +113,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 52 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO | 113 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO |
| 53 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO | 114 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO |
| 54 | | 115 | |
| 55 | // TODO: read the file and keep vaild parts instead of truncating | 116 | // TODO: read the file and keep valid parts instead of truncating |
| 56 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); | 117 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 57 | errdefer file.close(); | 118 | errdefer file.close(); |
| 58 | | 119 | |
| ... | @@ -80,50 +141,67 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { | ... | @@ -80,50 +141,67 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { |
| 80 | } | 141 | } |
| 81 | | 142 | |
| 82 | pub fn deinit(self: *Wasm) void { | 143 | pub fn deinit(self: *Wasm) void { |
| 83 | for (self.funcs.items) |decl| { | 144 | for (self.symbols.items) |decl| { |
| 84 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | 145 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 85 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | 146 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| 86 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | 147 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| 87 | } | | |
| 88 | for (self.ext_funcs.items) |decl| { | | |
| 89 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | | |
| 90 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | | |
| 91 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | | |
| 92 | } | 148 | } |
| | 149 | |
| 93 | self.funcs.deinit(self.base.allocator); | 150 | self.funcs.deinit(self.base.allocator); |
| 94 | self.ext_funcs.deinit(self.base.allocator); | 151 | self.ext_funcs.deinit(self.base.allocator); |
| | 152 | self.offset_table.deinit(self.base.allocator); |
| | 153 | self.offset_table_free_list.deinit(self.base.allocator); |
| | 154 | self.symbols.deinit(self.base.allocator); |
| 95 | } | 155 | } |
| 96 | | 156 | |
| 97 | // Generate code for the Decl, storing it in memory to be later written to | 157 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 98 | // the file on flush(). | 158 | if (decl.link.wasm.init) return; |
| 99 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | 159 | |
| 100 | const typed_value = decl.typed_value.most_recent.typed_value; | 160 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 101 | if (typed_value.ty.zigTypeTag() != .Fn) | 161 | try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1); |
| 102 | return error.TODOImplementNonFnDeclsForWasm; | | |
| 103 | | 162 | |
| 104 | if (decl.fn_link.wasm) |*fn_data| { | 163 | const block = &decl.link.wasm; |
| 105 | fn_data.functype.items.len = 0; | 164 | block.init = true; |
| 106 | fn_data.code.items.len = 0; | 165 | |
| 107 | fn_data.idx_refs.items.len = 0; | 166 | block.symbol_index = @intCast(u32, self.symbols.items.len); |
| | 167 | self.symbols.appendAssumeCapacity(decl); |
| | 168 | |
| | 169 | if (self.offset_table_free_list.popOrNull()) |index| { |
| | 170 | block.offset_index = index; |
| 108 | } else { | 171 | } else { |
| 109 | decl.fn_link.wasm = .{}; | 172 | block.offset_index = @intCast(u32, self.offset_table.items.len); |
| 110 | // dependent on function type, appends it to the correct list | 173 | _ = self.offset_table.addOneAssumeCapacity(); |
| 111 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | 174 | } |
| | 175 | |
| | 176 | self.offset_table.items[block.offset_index] = 0; |
| | 177 | |
| | 178 | const typed_value = decl.typed_value.most_recent.typed_value; |
| | 179 | if (typed_value.ty.zigTypeTag() == .Fn) { |
| | 180 | switch (typed_value.val.tag()) { |
| | 181 | // dependent on function type, appends it to the correct list |
| 112 | .function => try self.funcs.append(self.base.allocator, decl), | 182 | .function => try self.funcs.append(self.base.allocator, decl), |
| 113 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), | 183 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), |
| 114 | else => return error.TODOImplementNonFnDeclsForWasm, | 184 | else => unreachable, |
| 115 | } | 185 | } |
| 116 | } | 186 | } |
| 117 | const fn_data = &decl.fn_link.wasm.?; | 187 | } |
| 118 | | 188 | |
| 119 | var managed_functype = fn_data.functype.toManaged(self.base.allocator); | 189 | // Generate code for the Decl, storing it in memory to be later written to |
| 120 | var managed_code = fn_data.code.toManaged(self.base.allocator); | 190 | // the file on flush(). |
| | 191 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| | 192 | std.debug.assert(decl.link.wasm.init); // Must call allocateDeclIndexes() |
| | 193 | |
| | 194 | const typed_value = decl.typed_value.most_recent.typed_value; |
| | 195 | const fn_data = &decl.fn_link.wasm; |
| | 196 | fn_data.functype.items.len = 0; |
| | 197 | fn_data.code.items.len = 0; |
| | 198 | fn_data.idx_refs.items.len = 0; |
| 121 | | 199 | |
| 122 | var context = codegen.Context{ | 200 | var context = codegen.Context{ |
| 123 | .gpa = self.base.allocator, | 201 | .gpa = self.base.allocator, |
| 124 | .values = .{}, | 202 | .values = .{}, |
| 125 | .code = managed_code, | 203 | .code = fn_data.code.toManaged(self.base.allocator), |
| 126 | .func_type_data = managed_functype, | 204 | .func_type_data = fn_data.functype.toManaged(self.base.allocator), |
| 127 | .decl = decl, | 205 | .decl = decl, |
| 128 | .err_msg = undefined, | 206 | .err_msg = undefined, |
| 129 | .locals = .{}, | 207 | .locals = .{}, |
| ... | @@ -132,7 +210,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | ... | @@ -132,7 +210,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 132 | defer context.deinit(); | 210 | defer context.deinit(); |
| 133 | | 211 | |
| 134 | // generate the 'code' section for the function declaration | 212 | // generate the 'code' section for the function declaration |
| 135 | context.gen() catch |err| switch (err) { | 213 | const result = context.gen(typed_value) catch |err| switch (err) { |
| 136 | error.CodegenFail => { | 214 | error.CodegenFail => { |
| 137 | decl.analysis = .codegen_failure; | 215 | decl.analysis = .codegen_failure; |
| 138 | try module.failed_decls.put(module.gpa, decl, context.err_msg); | 216 | try module.failed_decls.put(module.gpa, decl, context.err_msg); |
| ... | @@ -141,15 +219,38 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | ... | @@ -141,15 +219,38 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 141 | else => |e| return err, | 219 | else => |e| return err, |
| 142 | }; | 220 | }; |
| 143 | | 221 | |
| 144 | // as locals are patched afterwards, the offsets of funcidx's are off, | 222 | const code: []const u8 = switch (result) { |
| 145 | // here we update them to correct them | 223 | .appended => @as([]const u8, context.code.items), |
| 146 | for (decl.fn_link.wasm.?.idx_refs.items) |*func| { | 224 | .externally_managed => |payload| payload, |
| 147 | // For each local, add 6 bytes (count + type) | 225 | }; |
| 148 | func.offset += @intCast(u32, context.locals.items.len * 6); | | |
| 149 | } | | |
| 150 | | 226 | |
| 151 | fn_data.functype = context.func_type_data.toUnmanaged(); | | |
| 152 | fn_data.code = context.code.toUnmanaged(); | 227 | fn_data.code = context.code.toUnmanaged(); |
| | 228 | fn_data.functype = context.func_type_data.toUnmanaged(); |
| | 229 | |
| | 230 | const block = &decl.link.wasm; |
| | 231 | if (typed_value.ty.zigTypeTag() == .Fn) { |
| | 232 | // as locals are patched afterwards, the offsets of funcidx's are off, |
| | 233 | // here we update them to correct them |
| | 234 | for (fn_data.idx_refs.items) |*func| { |
| | 235 | // For each local, add 6 bytes (count + type) |
| | 236 | func.offset += @intCast(u32, context.locals.items.len * 6); |
| | 237 | } |
| | 238 | } else { |
| | 239 | block.size = @intCast(u32, code.len); |
| | 240 | block.data = code.ptr; |
| | 241 | } |
| | 242 | |
| | 243 | // If we're updating an existing decl, unplug it first |
| | 244 | // to avoid infinite loops due to earlier links |
| | 245 | block.unplug(); |
| | 246 | |
| | 247 | if (self.last_block) |last| { |
| | 248 | if (last != block) { |
| | 249 | last.next = block; |
| | 250 | block.prev = last; |
| | 251 | } |
| | 252 | } |
| | 253 | self.last_block = block; |
| 153 | } | 254 | } |
| 154 | | 255 | |
| 155 | pub fn updateDeclExports( | 256 | pub fn updateDeclExports( |
| ... | @@ -160,18 +261,34 @@ pub fn updateDeclExports( | ... | @@ -160,18 +261,34 @@ pub fn updateDeclExports( |
| 160 | ) !void {} | 261 | ) !void {} |
| 161 | | 262 | |
| 162 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { | 263 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 163 | // TODO: remove this assert when non-function Decls are implemented | 264 | if (self.getFuncidx(decl)) |func_idx| { |
| 164 | assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); | 265 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { |
| 165 | const func_idx = self.getFuncidx(decl).?; | 266 | .function => _ = self.funcs.swapRemove(func_idx), |
| 166 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | 267 | .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), |
| 167 | .function => _ = self.funcs.swapRemove(func_idx), | 268 | else => unreachable, |
| 168 | .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), | 269 | } |
| 169 | else => unreachable, | 270 | } |
| | 271 | const block = &decl.link.wasm; |
| | 272 | |
| | 273 | if (self.last_block == block) { |
| | 274 | self.last_block = block.prev; |
| 170 | } | 275 | } |
| 171 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | 276 | |
| 172 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | 277 | block.unplug(); |
| 173 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | 278 | |
| 174 | decl.fn_link.wasm = null; | 279 | self.offset_table_free_list.append(self.base.allocator, decl.link.wasm.offset_index) catch {}; |
| | 280 | _ = self.symbols.swapRemove(block.symbol_index); |
| | 281 | |
| | 282 | // update symbol_index as we swap removed the last symbol into the removed's position |
| | 283 | if (block.symbol_index < self.symbols.items.len) |
| | 284 | self.symbols.items[block.symbol_index].link.wasm.symbol_index = block.symbol_index; |
| | 285 | |
| | 286 | block.init = false; |
| | 287 | |
| | 288 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| | 289 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
| | 290 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); |
| | 291 | decl.fn_link.wasm = undefined; |
| 175 | } | 292 | } |
| 176 | | 293 | |
| 177 | pub fn flush(self: *Wasm, comp: *Compilation) !void { | 294 | pub fn flush(self: *Wasm, comp: *Compilation) !void { |
| ... | @@ -188,6 +305,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -188,6 +305,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 188 | | 305 | |
| 189 | const file = self.base.file.?; | 306 | const file = self.base.file.?; |
| 190 | const header_size = 5 + 1; | 307 | const header_size = 5 + 1; |
| | 308 | // ptr_width in bytes |
| | 309 | const ptr_width = self.base.options.target.cpu.arch.ptrBitWidth() / 8; |
| | 310 | // The size of the offset table in bytes |
| | 311 | // The table contains all decl's with its corresponding offset into |
| | 312 | // the 'data' section |
| | 313 | const offset_table_size = @intCast(u32, self.offset_table.items.len * ptr_width); |
| | 314 | |
| | 315 | // The size of the data, this together with `offset_table_size` amounts to the |
| | 316 | // total size of the 'data' section |
| | 317 | var first_decl: ?*DeclBlock = null; |
| | 318 | const data_size: u32 = if (self.last_block) |last| blk: { |
| | 319 | var size = last.size; |
| | 320 | var cur = last; |
| | 321 | while (cur.prev) |prev| : (cur = prev) { |
| | 322 | size += prev.size; |
| | 323 | } |
| | 324 | first_decl = cur; |
| | 325 | break :blk size; |
| | 326 | } else 0; |
| 191 | | 327 | |
| 192 | // No need to rewrite the magic/version header | 328 | // No need to rewrite the magic/version header |
| 193 | try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); | 329 | try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); |
| ... | @@ -199,8 +335,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -199,8 +335,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 199 | | 335 | |
| 200 | // extern functions are defined in the wasm binary first through the `import` | 336 | // extern functions are defined in the wasm binary first through the `import` |
| 201 | // section, so define their func types first | 337 | // section, so define their func types first |
| 202 | for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | 338 | for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.functype.items); |
| 203 | for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | 339 | for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.functype.items); |
| 204 | | 340 | |
| 205 | try writeVecSectionHeader( | 341 | try writeVecSectionHeader( |
| 206 | file, | 342 | file, |
| ... | @@ -257,6 +393,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -257,6 +393,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 257 | ); | 393 | ); |
| 258 | } | 394 | } |
| 259 | | 395 | |
| | 396 | // Memory section |
| | 397 | if (data_size != 0) { |
| | 398 | const header_offset = try reserveVecSectionHeader(file); |
| | 399 | const writer = file.writer(); |
| | 400 | |
| | 401 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 402 | // Calculate the amount of memory pages are required and write them. |
| | 403 | // Wasm uses 64kB page sizes. Round up to ensure the data segments fit into the memory |
| | 404 | try leb.writeULEB128( |
| | 405 | writer, |
| | 406 | try std.math.divCeil( |
| | 407 | u32, |
| | 408 | offset_table_size + data_size, |
| | 409 | std.wasm.page_size, |
| | 410 | ), |
| | 411 | ); |
| | 412 | try writeVecSectionHeader( |
| | 413 | file, |
| | 414 | header_offset, |
| | 415 | .memory, |
| | 416 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| | 417 | @as(u32, 1), // wasm currently only supports 1 linear memory segment |
| | 418 | ); |
| | 419 | } |
| | 420 | |
| 260 | // Export section | 421 | // Export section |
| 261 | if (self.base.options.module) |module| { | 422 | if (self.base.options.module) |module| { |
| 262 | const header_offset = try reserveVecSectionHeader(file); | 423 | const header_offset = try reserveVecSectionHeader(file); |
| ... | @@ -281,6 +442,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -281,6 +442,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 281 | count += 1; | 442 | count += 1; |
| 282 | } | 443 | } |
| 283 | } | 444 | } |
| | 445 | |
| | 446 | // export memory if size is not 0 |
| | 447 | if (data_size != 0) { |
| | 448 | try leb.writeULEB128(writer, @intCast(u32, "memory".len)); |
| | 449 | try writer.writeAll("memory"); |
| | 450 | try writer.writeByte(wasm.externalKind(.memory)); |
| | 451 | try leb.writeULEB128(writer, @as(u32, 0)); // only 1 memory 'object' can exist |
| | 452 | count += 1; |
| | 453 | } |
| | 454 | |
| 284 | try writeVecSectionHeader( | 455 | try writeVecSectionHeader( |
| 285 | file, | 456 | file, |
| 286 | header_offset, | 457 | header_offset, |
| ... | @@ -295,7 +466,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -295,7 +466,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 295 | const header_offset = try reserveVecSectionHeader(file); | 466 | const header_offset = try reserveVecSectionHeader(file); |
| 296 | const writer = file.writer(); | 467 | const writer = file.writer(); |
| 297 | for (self.funcs.items) |decl| { | 468 | for (self.funcs.items) |decl| { |
| 298 | const fn_data = &decl.fn_link.wasm.?; | 469 | const fn_data = &decl.fn_link.wasm; |
| 299 | | 470 | |
| 300 | // Write the already generated code to the file, inserting | 471 | // Write the already generated code to the file, inserting |
| 301 | // function indexes where required. | 472 | // function indexes where required. |
| ... | @@ -320,6 +491,51 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -320,6 +491,51 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 320 | @intCast(u32, self.funcs.items.len), | 491 | @intCast(u32, self.funcs.items.len), |
| 321 | ); | 492 | ); |
| 322 | } | 493 | } |
| | 494 | |
| | 495 | // Data section |
| | 496 | if (data_size != 0) { |
| | 497 | const header_offset = try reserveVecSectionHeader(file); |
| | 498 | const writer = file.writer(); |
| | 499 | var len: u32 = 0; |
| | 500 | // index to memory section (currently, there can only be 1 memory section in wasm) |
| | 501 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 502 | |
| | 503 | // offset into data section |
| | 504 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 505 | try leb.writeILEB128(writer, @as(i32, 0)); |
| | 506 | try writer.writeByte(wasm.opcode(.end)); |
| | 507 | |
| | 508 | const total_size = offset_table_size + data_size; |
| | 509 | |
| | 510 | // offset table + data size |
| | 511 | try leb.writeULEB128(writer, total_size); |
| | 512 | |
| | 513 | // fill in the offset table and the data segments |
| | 514 | const file_offset = try file.getPos(); |
| | 515 | var cur = first_decl; |
| | 516 | var data_offset = offset_table_size; |
| | 517 | while (cur) |cur_block| : (cur = cur_block.next) { |
| | 518 | if (cur_block.size == 0) continue; |
| | 519 | std.debug.assert(cur_block.init); |
| | 520 | |
| | 521 | const offset = (cur_block.offset_index) * ptr_width; |
| | 522 | var buf: [4]u8 = undefined; |
| | 523 | std.mem.writeIntLittle(u32, &buf, data_offset); |
| | 524 | |
| | 525 | try file.pwriteAll(&buf, file_offset + offset); |
| | 526 | try file.pwriteAll(cur_block.data[0..cur_block.size], file_offset + data_offset); |
| | 527 | data_offset += cur_block.size; |
| | 528 | } |
| | 529 | |
| | 530 | try file.seekTo(file_offset + data_offset); |
| | 531 | try writeVecSectionHeader( |
| | 532 | file, |
| | 533 | header_offset, |
| | 534 | .data, |
| | 535 | @intCast(u32, (file_offset + data_offset) - header_offset - header_size), |
| | 536 | @intCast(u32, 1), // only 1 data section |
| | 537 | ); |
| | 538 | } |
| 323 | } | 539 | } |
| 324 | | 540 | |
| 325 | fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | 541 | fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |