authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-29 20:51:08+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-29 20:51:08+01:00
log791e28bb68a6e71e64aa0dd43766246483316017
tree9adb9da32b589602067a633d29aa8378f2a0ef27
parentaf06584241ca3623b3d2495a10b6c90f18846a5a
parent202ed7330fdc55cce22bfa9d9b5da03776e871b4
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19121 from Luukdegram/wasm-linker-zigobject

wasm-linker: encapsulate Zig module in ZigObject

15 files changed, 2252 insertions(+), 2017 deletions(-)

src/arch/wasm/CodeGen.zig+18-17
......@@ -1286,8 +1286,9 @@ fn genFunc(func: *CodeGen) InnerError!void {
12861286 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
12871287 defer prologue.deinit();
12881288
1289 const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym);
12891290 // load stack pointer
1290 try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
1291 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
12911292 // store stack pointer so we can restore it when we return from the function
12921293 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
12931294 // get the total stack size
......@@ -1303,7 +1304,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
13031304 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
13041305 // Store the current stack pointer value into the global stack pointer so other function calls will
13051306 // start from this value instead and not overwrite the current stack.
1306 try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
1307 try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } });
13071308
13081309 // reserve space and insert all prologue instructions at the front of the instruction list
13091310 // We insert them in reserve order as there is no insertSlice in multiArrayList.
......@@ -1502,7 +1503,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
15021503 try func.emitWValue(func.initial_stack_value);
15031504
15041505 // save its value in the global stack pointer
1505 try func.addLabel(.global_set, 0);
1506 try func.addLabel(.global_set, @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym));
15061507}
15071508
15081509/// From a given type, will create space on the virtual stack to store the value of such type.
......@@ -2205,7 +2206,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
22052206 const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type);
22062207 try func.bin_file.addOrUpdateImport(
22072208 mod.intern_pool.stringToSlice(ext_decl.name),
2208 atom.getSymbolIndex().?,
2209 atom.sym_index,
22092210 mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name),
22102211 type_index,
22112212 );
......@@ -2239,8 +2240,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
22392240 }
22402241
22412242 if (callee) |direct| {
2242 const atom_index = func.bin_file.decls.get(direct).?;
2243 try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);
2243 const atom_index = func.bin_file.zigObjectPtr().?.decls_map.get(direct).?.atom;
2244 try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
22442245 } else {
22452246 // in this case we call a function pointer
22462247 // so load its value onto the stack
......@@ -2251,7 +2252,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
22512252 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), mod);
22522253 defer fn_type.deinit(func.gpa);
22532254
2254 const fn_type_index = try func.bin_file.putOrGetFuncType(fn_type);
2255 const fn_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, fn_type);
22552256 try func.addLabel(.call_indirect, fn_type_index);
22562257 }
22572258
......@@ -3157,8 +3158,8 @@ fn lowerAnonDeclRef(
31573158 return error.CodegenFail;
31583159 },
31593160 }
3160 const target_atom_index = func.bin_file.anon_decls.get(decl_val).?;
3161 const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?;
3161 const target_atom_index = func.bin_file.zigObjectPtr().?.anon_decls.get(decl_val).?;
3162 const target_sym_index = @intFromEnum(func.bin_file.getAtom(target_atom_index).sym_index);
31623163 if (is_fn_body) {
31633164 return WValue{ .function_index = target_sym_index };
31643165 } else if (offset == 0) {
......@@ -3189,9 +3190,8 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl
31893190 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
31903191 const atom = func.bin_file.getAtom(atom_index);
31913192
3192 const target_sym_index = atom.sym_index;
3193 const target_sym_index = @intFromEnum(atom.sym_index);
31933194 if (decl.ty.zigTypeTag(mod) == .Fn) {
3194 try func.bin_file.addTableFunction(target_sym_index);
31953195 return WValue{ .function_index = target_sym_index };
31963196 } else if (offset == 0) {
31973197 return WValue{ .memory = target_sym_index };
......@@ -3712,7 +3712,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37123712 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
37133713 const operand = try func.resolveInst(un_op);
37143714 const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
3715 const errors_len = WValue{ .memory = sym_index };
3715 const errors_len = WValue{ .memory = @intFromEnum(sym_index) };
37163716
37173717 try func.emitWValue(operand);
37183718 const mod = func.bin_file.base.comp.module.?;
......@@ -7154,7 +7154,7 @@ fn callIntrinsic(
71547154 args: []const WValue,
71557155) InnerError!WValue {
71567156 assert(param_types.len == args.len);
7157 const symbol_index = func.bin_file.base.getGlobalSymbol(name, null) catch |err| {
7157 const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {
71587158 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
71597159 };
71607160
......@@ -7162,7 +7162,7 @@ fn callIntrinsic(
71627162 const mod = func.bin_file.base.comp.module.?;
71637163 var func_type = try genFunctype(func.gpa, .C, param_types, return_type, mod);
71647164 defer func_type.deinit(func.gpa);
7165 const func_type_index = try func.bin_file.putOrGetFuncType(func_type);
7165 const func_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, func_type);
71667166 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
71677167
71687168 const want_sret_param = firstParamSRet(.C, return_type, mod);
......@@ -7182,7 +7182,7 @@ fn callIntrinsic(
71827182 }
71837183
71847184 // Actually call our intrinsic
7185 try func.addLabel(.call, symbol_index);
7185 try func.addLabel(.call, @intFromEnum(symbol_index));
71867186
71877187 if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) {
71887188 return WValue.none;
......@@ -7225,7 +7225,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
72257225
72267226 // check if we already generated code for this.
72277227 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
7228 return loc.index;
7228 return @intFromEnum(loc.index);
72297229 }
72307230
72317231 const int_tag_ty = enum_ty.intTagType(mod);
......@@ -7365,7 +7365,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
73657365
73667366 const slice_ty = Type.slice_const_u8_sentinel_0;
73677367 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, mod);
7368 return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
7368 const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
7369 return @intFromEnum(sym_index);
73697370}
73707371
73717372fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/arch/wasm/Emit.zig+17-5
......@@ -310,7 +310,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
310310 const global_offset = emit.offset();
311311 try emit.code.appendSlice(&buf);
312312
313 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
313 const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
314314 const atom = emit.bin_file.getAtomPtr(atom_index);
315315 try atom.relocs.append(gpa, .{
316316 .index = label,
......@@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
370370 try emit.code.appendSlice(&buf);
371371
372372 if (label != 0) {
373 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
373 const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
374374 const atom = emit.bin_file.getAtomPtr(atom_index);
375375 try atom.relocs.append(gpa, .{
376376 .offset = call_offset,
......@@ -385,7 +385,19 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
385385 try emit.code.append(std.wasm.opcode(.call_indirect));
386386 // NOTE: If we remove unused function types in the future for incremental
387387 // linking, we must also emit a relocation for this `type_index`
388 try leb128.writeULEB128(emit.code.writer(), type_index);
388 const call_offset = emit.offset();
389 var buf: [5]u8 = undefined;
390 leb128.writeUnsignedFixed(5, &buf, type_index);
391 try emit.code.appendSlice(&buf);
392 if (type_index != 0) {
393 const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
394 const atom = emit.bin_file.getAtomPtr(atom_index);
395 try atom.relocs.append(emit.bin_file.base.comp.gpa, .{
396 .offset = call_offset,
397 .index = type_index,
398 .relocation_type = .R_WASM_TYPE_INDEX_LEB,
399 });
400 }
389401 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
390402}
391403
......@@ -400,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
400412 try emit.code.appendSlice(&buf);
401413
402414 if (symbol_index != 0) {
403 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
415 const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
404416 const atom = emit.bin_file.getAtomPtr(atom_index);
405417 try atom.relocs.append(gpa, .{
406418 .offset = index_offset,
......@@ -431,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
431443 }
432444
433445 if (mem.pointer != 0) {
434 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
446 const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
435447 const atom = emit.bin_file.getAtomPtr(atom_index);
436448 try atom.relocs.append(gpa, .{
437449 .offset = mem_offset,
src/link/Dwarf.zig+82-84
......@@ -1297,9 +1297,9 @@ pub fn commitDeclState(
12971297 }
12981298 },
12991299 .wasm => {
1300 const wasm_file = self.bin_file.cast(File.Wasm).?;
1301 const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
1302 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
1300 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1301 // const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
1302 // writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
13031303 },
13041304 else => unreachable,
13051305 }
......@@ -1390,26 +1390,26 @@ pub fn commitDeclState(
13901390 },
13911391
13921392 .wasm => {
1393 const wasm_file = self.bin_file.cast(File.Wasm).?;
1394 const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
1395 const debug_line = &atom.code;
1396 const segment_size = debug_line.items.len;
1397 if (needed_size != segment_size) {
1398 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1399 if (needed_size > segment_size) {
1400 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
1401 try debug_line.resize(self.allocator, needed_size);
1402 @memset(debug_line.items[segment_size..], 0);
1403 }
1404 debug_line.items.len = needed_size;
1405 }
1406 writeDbgLineNopsBuffered(
1407 debug_line.items,
1408 src_fn.off,
1409 prev_padding_size,
1410 dbg_line_buffer.items,
1411 next_padding_size,
1412 );
1393 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1394 // const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
1395 // const debug_line = &atom.code;
1396 // const segment_size = debug_line.items.len;
1397 // if (needed_size != segment_size) {
1398 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1399 // if (needed_size > segment_size) {
1400 // log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
1401 // try debug_line.resize(self.allocator, needed_size);
1402 // @memset(debug_line.items[segment_size..], 0);
1403 // }
1404 // debug_line.items.len = needed_size;
1405 // }
1406 // writeDbgLineNopsBuffered(
1407 // debug_line.items,
1408 // src_fn.off,
1409 // prev_padding_size,
1410 // dbg_line_buffer.items,
1411 // next_padding_size,
1412 // );
14131413 },
14141414 else => unreachable,
14151415 }
......@@ -1553,10 +1553,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)
15531553 }
15541554 },
15551555 .wasm => {
1556 const wasm_file = self.bin_file.cast(File.Wasm).?;
1557 const debug_info_index = wasm_file.debug_info_atom.?;
1558 const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
1559 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
1556 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1557 // const debug_info_index = wasm_file.debug_info_atom.?;
1558 // const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
1559 // try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
15601560 },
15611561 else => unreachable,
15621562 }
......@@ -1594,7 +1594,6 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
15941594 // This logic is nearly identical to the logic above in `updateDecl` for
15951595 // `SrcFn` and the line number programs. If you are editing this logic, you
15961596 // probably need to edit that logic too.
1597 const gpa = self.allocator;
15981597
15991598 const atom = self.getAtom(.di_atom, atom_index);
16001599 const last_decl_index = self.di_atom_last_index.?;
......@@ -1665,31 +1664,31 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
16651664 },
16661665
16671666 .wasm => {
1668 const wasm_file = self.bin_file.cast(File.Wasm).?;
1669 const info_atom = wasm_file.debug_info_atom.?;
1670 const debug_info = &wasm_file.getAtomPtr(info_atom).code;
1671 const segment_size = debug_info.items.len;
1672 if (needed_size != segment_size) {
1673 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1674 if (needed_size > segment_size) {
1675 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
1676 try debug_info.resize(self.allocator, needed_size);
1677 @memset(debug_info.items[segment_size..], 0);
1678 }
1679 debug_info.items.len = needed_size;
1680 }
1681 log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
1682 debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
1683 });
1684 try writeDbgInfoNopsToArrayList(
1685 gpa,
1686 debug_info,
1687 atom.off,
1688 prev_padding_size,
1689 dbg_info_buf,
1690 next_padding_size,
1691 trailing_zero,
1692 );
1667 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1668 // const info_atom = wasm_file.debug_info_atom.?;
1669 // const debug_info = &wasm_file.getAtomPtr(info_atom).code;
1670 // const segment_size = debug_info.items.len;
1671 // if (needed_size != segment_size) {
1672 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1673 // if (needed_size > segment_size) {
1674 // log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
1675 // try debug_info.resize(self.allocator, needed_size);
1676 // @memset(debug_info.items[segment_size..], 0);
1677 // }
1678 // debug_info.items.len = needed_size;
1679 // }
1680 // log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
1681 // debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
1682 // });
1683 // try writeDbgInfoNopsToArrayList(
1684 // gpa,
1685 // debug_info,
1686 // atom.off,
1687 // prev_padding_size,
1688 // dbg_info_buf,
1689 // next_padding_size,
1690 // trailing_zero,
1691 // );
16931692 },
16941693 else => unreachable,
16951694 }
......@@ -1735,10 +1734,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D
17351734 }
17361735 },
17371736 .wasm => {
1738 const wasm_file = self.bin_file.cast(File.Wasm).?;
1739 const offset = atom.off + self.getRelocDbgLineOff();
1740 const line_atom_index = wasm_file.debug_line_atom.?;
1741 wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
1737 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1738 // const offset = atom.off + self.getRelocDbgLineOff();
1739 // const line_atom_index = wasm_file.debug_line_atom.?;
1740 // wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
17421741 },
17431742 else => unreachable,
17441743 }
......@@ -1803,7 +1802,6 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void {
18031802}
18041803
18051804pub fn writeDbgAbbrev(self: *Dwarf) !void {
1806 const gpa = self.allocator;
18071805 // These are LEB encoded but since the values are all less than 127
18081806 // we can simply append these bytes.
18091807 // zig fmt: off
......@@ -1960,10 +1958,10 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
19601958 }
19611959 },
19621960 .wasm => {
1963 const wasm_file = self.bin_file.cast(File.Wasm).?;
1964 const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
1965 try debug_abbrev.resize(gpa, needed_size);
1966 debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
1961 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1962 // const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
1963 // try debug_abbrev.resize(gpa, needed_size);
1964 // debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
19671965 },
19681966 else => unreachable,
19691967 }
......@@ -2055,9 +2053,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64)
20552053 }
20562054 },
20572055 .wasm => {
2058 const wasm_file = self.bin_file.cast(File.Wasm).?;
2059 const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2060 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
2056 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2057 // const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2058 // try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
20612059 },
20622060 else => unreachable,
20632061 }
......@@ -2318,7 +2316,6 @@ fn writeDbgInfoNopsToArrayList(
23182316
23192317pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
23202318 const comp = self.bin_file.comp;
2321 const gpa = comp.gpa;
23222319 const target = comp.root_mod.resolved_target.result;
23232320 const target_endian = target.cpu.arch.endian();
23242321 const ptr_width_bytes = self.ptrWidthBytes();
......@@ -2391,10 +2388,10 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
23912388 }
23922389 },
23932390 .wasm => {
2394 const wasm_file = self.bin_file.cast(File.Wasm).?;
2395 const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
2396 try debug_ranges.resize(gpa, needed_size);
2397 @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
2391 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2392 // const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
2393 // try debug_ranges.resize(gpa, needed_size);
2394 // @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
23982395 },
23992396 else => unreachable,
24002397 }
......@@ -2548,14 +2545,15 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
25482545 }
25492546 },
25502547 .wasm => {
2551 const wasm_file = self.bin_file.cast(File.Wasm).?;
2552 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2553 {
2554 const src = debug_line.items[first_fn.off..];
2555 @memcpy(buffer[0..src.len], src);
2556 }
2557 try debug_line.resize(self.allocator, debug_line.items.len + delta);
2558 @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);
2548 _ = &buffer;
2549 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2550 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2551 // {
2552 // const src = debug_line.items[first_fn.off..];
2553 // @memcpy(buffer[0..src.len], src);
2554 // }
2555 // try debug_line.resize(self.allocator, debug_line.items.len + delta);
2556 // @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);
25592557 },
25602558 else => unreachable,
25612559 }
......@@ -2604,9 +2602,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
26042602 }
26052603 },
26062604 .wasm => {
2607 const wasm_file = self.bin_file.cast(File.Wasm).?;
2608 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2609 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
2605 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2606 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2607 // writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
26102608 },
26112609 else => unreachable,
26122610 }
......@@ -2754,9 +2752,9 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
27542752 }
27552753 },
27562754 .wasm => {
2757 const wasm_file = self.bin_file.cast(File.Wasm).?;
2758 const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2759 debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
2755 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2756 // const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2757 // debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
27602758 },
27612759 else => unreachable,
27622760 }
src/link/Wasm.zig+543-1695
......@@ -1,67 +1,78 @@
11const Wasm = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
5const mem = std.mem;
6const Allocator = std.mem.Allocator;
4
75const assert = std.debug.assert;
6const build_options = @import("build_options");
7const builtin = @import("builtin");
8const codegen = @import("../codegen.zig");
89const fs = std.fs;
910const leb = std.leb;
10const log = std.log.scoped(.link);
11
12pub const Atom = @import("Wasm/Atom.zig");
13const Dwarf = @import("Dwarf.zig");
14const Module = @import("../Module.zig");
15const InternPool = @import("../InternPool.zig");
16const Compilation = @import("../Compilation.zig");
17const CodeGen = @import("../arch/wasm/CodeGen.zig");
18const codegen = @import("../codegen.zig");
1911const link = @import("../link.zig");
2012const lldMain = @import("../main.zig").lldMain;
13const log = std.log.scoped(.link);
14const gc_log = std.log.scoped(.gc);
15const mem = std.mem;
2116const trace = @import("../tracy.zig").trace;
22const build_options = @import("build_options");
17const types = @import("Wasm/types.zig");
2318const wasi_libc = @import("../wasi_libc.zig");
24const Cache = std.Build.Cache;
25const Type = @import("../type.zig").Type;
26const Value = @import("../Value.zig");
27const TypedValue = @import("../TypedValue.zig");
28const LlvmObject = @import("../codegen/llvm.zig").Object;
19
2920const Air = @import("../Air.zig");
21const Allocator = std.mem.Allocator;
22const Archive = @import("Wasm/Archive.zig");
23const Cache = std.Build.Cache;
24const CodeGen = @import("../arch/wasm/CodeGen.zig");
25const Compilation = @import("../Compilation.zig");
26const Dwarf = @import("Dwarf.zig");
27const File = @import("Wasm/file.zig").File;
28const InternPool = @import("../InternPool.zig");
3029const Liveness = @import("../Liveness.zig");
31const Symbol = @import("Wasm/Symbol.zig");
30const LlvmObject = @import("../codegen/llvm.zig").Object;
31const Module = @import("../Module.zig");
3232const Object = @import("Wasm/Object.zig");
33const Archive = @import("Wasm/Archive.zig");
34const types = @import("Wasm/types.zig");
33const Symbol = @import("Wasm/Symbol.zig");
34const Type = @import("../type.zig").Type;
35const TypedValue = @import("../TypedValue.zig");
36const ZigObject = @import("Wasm/ZigObject.zig");
37
38pub const Atom = @import("Wasm/Atom.zig");
3539pub const Relocation = types.Relocation;
3640
3741pub const base_tag: link.File.Tag = .wasm;
3842
3943base: link.File,
44/// Symbol name of the entry function to export
4045entry_name: ?[]const u8,
46/// When true, will allow undefined symbols
4147import_symbols: bool,
48/// List of *global* symbol names to export to the host environment.
4249export_symbol_names: []const []const u8,
50/// When defined, sets the start of the data section.
4351global_base: ?u64,
52/// When defined, sets the initial memory size of the memory.
4453initial_memory: ?u64,
54/// When defined, sets the maximum memory size of the memory.
4555max_memory: ?u64,
56/// When true, will import the function table from the host environment.
57import_table: bool,
58/// When true, will export the function table to the host environment.
59export_table: bool,
4660/// Output name of the file
4761name: []const u8,
4862/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
4963llvm_object: ?*LlvmObject = null,
64/// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists,
65/// and the chosen backend is the Wasm backend.
66zig_object_index: File.Index = .null,
67/// List of relocatable files to be linked into the final binary.
68files: std.MultiArrayList(File.Entry) = .{},
5069/// When importing objects from the host environment, a name must be supplied.
5170/// LLVM uses "env" by default when none is given. This would be a good default for Zig
5271/// to support existing code.
5372/// TODO: Allow setting this through a flag?
5473host_name: []const u8 = "env",
55/// List of all `Decl` that are currently alive.
56/// Each index maps to the corresponding `Atom.Index`.
57decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{},
58/// Mapping between an `Atom` and its type index representing the Wasm
59/// type of the function signature.
60atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
61/// List of all symbols generated by Zig code.
62symbols: std.ArrayListUnmanaged(Symbol) = .{},
63/// List of symbol indexes which are free to be used.
64symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
74/// List of symbols generated by the linker.
75synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .{},
6576/// Maps atoms to their segment index
6677atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
6778/// List of all atoms.
......@@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
107118segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},
108119/// Deduplicated string table for strings used by symbols, imports and exports.
109120string_table: StringTable = .{},
110/// Debug information for wasm
111dwarf: ?Dwarf = null,
112121
113122// Output sections
114123/// Output type section
......@@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
116125/// Output function section where the key is the original
117126/// function index and the value is function.
118127/// This allows us to map multiple symbols to the same function.
119functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, struct { func: std.wasm.Func, sym_index: u32 }) = .{},
128functions: std.AutoArrayHashMapUnmanaged(
129 struct { file: File.Index, index: u32 },
130 struct { func: std.wasm.Func, sym_index: Symbol.Index },
131) = .{},
120132/// Output global section
121133wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
122134/// Memory section
......@@ -143,7 +155,7 @@ entry: ?u32 = null,
143155function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
144156
145157/// All object files and their data which are linked into the final binary
146objects: std.ArrayListUnmanaged(Object) = .{},
158objects: std.ArrayListUnmanaged(File.Index) = .{},
147159/// All archive files that are lazy loaded.
148160/// e.g. when an undefined symbol references a symbol from the archive.
149161archives: std.ArrayListUnmanaged(Archive) = .{},
......@@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{},
165177/// data of a symbol, such as its size, or its offset to perform a relocation.
166178/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
167179symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
168/// Maps a symbol's location to its export name, which may differ from the decl's name
169/// which does the exporting.
170/// Note: The value represents the offset into the string table, rather than the actual string.
171export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
172
173/// Represents the symbol index of the error name table
174/// When this is `null`, no code references an error using runtime `@errorName`.
175/// During initializion, a symbol with corresponding atom will be created that is
176/// used to perform relocations to the pointer of this table.
177/// The actual table is populated during `flush`.
178error_table_symbol: ?u32 = null,
179
180// Debug section atoms. These are only set when the current compilation
181// unit contains Zig code. The lifetime of these atoms are extended
182// until the end of the compiler's lifetime. Meaning they're not freed
183// during `flush()` in incremental-mode.
184debug_info_atom: ?Atom.Index = null,
185debug_line_atom: ?Atom.Index = null,
186debug_loc_atom: ?Atom.Index = null,
187debug_ranges_atom: ?Atom.Index = null,
188debug_abbrev_atom: ?Atom.Index = null,
189debug_str_atom: ?Atom.Index = null,
190debug_pubnames_atom: ?Atom.Index = null,
191debug_pubtypes_atom: ?Atom.Index = null,
192
193/// List of atom indexes of functions that are generated by the backend,
194/// rather than by the linker.
195synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{},
196
197/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
198anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
199
200import_table: bool,
201export_table: bool,
202180
203181pub const Alignment = types.Alignment;
204182
......@@ -226,39 +204,33 @@ pub const Segment = struct {
226204 }
227205};
228206
229pub const Export = struct {
230 sym_index: ?u32 = null,
231};
232
233207pub const SymbolLoc = struct {
234208 /// The index of the symbol within the specified file
235 index: u32,
209 index: Symbol.Index,
236210 /// The index of the object file where the symbol resides.
237 /// When this is `null` the symbol comes from a non-object file.
238 file: ?u16,
211 file: File.Index,
239212
240213 /// From a given location, returns the corresponding symbol in the wasm binary
241 pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol {
242 if (wasm_bin.discarded.get(loc)) |new_loc| {
243 return new_loc.getSymbol(wasm_bin);
214 pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol {
215 if (wasm_file.discarded.get(loc)) |new_loc| {
216 return new_loc.getSymbol(wasm_file);
244217 }
245 if (loc.file) |object_index| {
246 const object = wasm_bin.objects.items[object_index];
247 return &object.symtable[loc.index];
218 if (wasm_file.file(loc.file)) |obj_file| {
219 return obj_file.symbol(loc.index);
248220 }
249 return &wasm_bin.symbols.items[loc.index];
221 return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
250222 }
251223
252224 /// From a given location, returns the name of the symbol.
253 pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 {
254 if (wasm_bin.discarded.get(loc)) |new_loc| {
255 return new_loc.getName(wasm_bin);
225 pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 {
226 if (wasm_file.discarded.get(loc)) |new_loc| {
227 return new_loc.getName(wasm_file);
256228 }
257 if (loc.file) |object_index| {
258 const object = wasm_bin.objects.items[object_index];
259 return object.string_table.get(object.symtable[loc.index].name);
229 if (wasm_file.file(loc.file)) |obj_file| {
230 return obj_file.symbolName(loc.index);
260231 }
261 return wasm_bin.string_table.get(wasm_bin.symbols.items[loc.index].name);
232 const sym = wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
233 return wasm_file.string_table.get(sym.name);
262234 }
263235
264236 /// From a given symbol location, returns the final location.
......@@ -266,9 +238,9 @@ pub const SymbolLoc = struct {
266238 /// in a different file, this will return said location.
267239 /// If the symbol wasn't replaced by another, this will return
268240 /// the given location itwasm.
269 pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc {
270 if (wasm_bin.discarded.get(loc)) |new_loc| {
271 return new_loc.finalLoc(wasm_bin);
241 pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc {
242 if (wasm_file.discarded.get(loc)) |new_loc| {
243 return new_loc.finalLoc(wasm_file);
272244 }
273245 return loc;
274246 }
......@@ -280,9 +252,9 @@ pub const InitFuncLoc = struct {
280252 /// object file index in the list of objects.
281253 /// Unlike `SymbolLoc` this cannot be `null` as we never define
282254 /// our own ctors.
283 file: u16,
255 file: File.Index,
284256 /// Symbol index within the corresponding object file.
285 index: u32,
257 index: Symbol.Index,
286258 /// The priority in which the constructor must be called.
287259 priority: u32,
288260
......@@ -459,7 +431,7 @@ pub fn createEmpty(
459431 // can be passed to LLD.
460432 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
461433
462 const file = try emit.directory.handle.createFile(sub_path, .{
434 wasm.base.file = try emit.directory.handle.createFile(sub_path, .{
463435 .truncate = true,
464436 .read = true,
465437 .mode = if (fs.has_executable_bit)
......@@ -470,7 +442,6 @@ pub fn createEmpty(
470442 else
471443 0,
472444 });
473 wasm.base.file = file;
474445 wasm.name = sub_path;
475446
476447 // create stack pointer symbol
......@@ -550,6 +521,7 @@ pub fn createEmpty(
550521 const symbol = loc.getSymbol(wasm);
551522 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
552523 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
524 symbol.mark();
553525 try wasm.wasm_globals.append(gpa, .{
554526 .global_type = .{ .valtype = .i32, .mutable = true },
555527 .init = .{ .i32_const = undefined },
......@@ -560,6 +532,7 @@ pub fn createEmpty(
560532 const symbol = loc.getSymbol(wasm);
561533 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
562534 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
535 symbol.mark();
563536 try wasm.wasm_globals.append(gpa, .{
564537 .global_type = .{ .valtype = .i32, .mutable = false },
565538 .init = .{ .i32_const = undefined },
......@@ -570,6 +543,7 @@ pub fn createEmpty(
570543 const symbol = loc.getSymbol(wasm);
571544 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
572545 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
546 symbol.mark();
573547 try wasm.wasm_globals.append(gpa, .{
574548 .global_type = .{ .valtype = .i32, .mutable = false },
575549 .init = .{ .i32_const = undefined },
......@@ -582,9 +556,64 @@ pub fn createEmpty(
582556 }
583557 }
584558
559 if (comp.module) |zcu| {
560 if (!use_llvm) {
561 const index: File.Index = @enumFromInt(wasm.files.len);
562 var zig_object: ZigObject = .{
563 .index = index,
564 .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}),
565 .stack_pointer_sym = .null,
566 };
567 try zig_object.init(wasm);
568 try wasm.files.append(gpa, .{ .zig_object = zig_object });
569 wasm.zig_object_index = index;
570 }
571 }
572
585573 return wasm;
586574}
587575
576pub fn file(wasm: *const Wasm, index: File.Index) ?File {
577 if (index == .null) return null;
578 const tag = wasm.files.items(.tags)[@intFromEnum(index)];
579 return switch (tag) {
580 .zig_object => .{ .zig_object = &wasm.files.items(.data)[@intFromEnum(index)].zig_object },
581 .object => .{ .object = &wasm.files.items(.data)[@intFromEnum(index)].object },
582 };
583}
584
585pub fn zigObjectPtr(wasm: *Wasm) ?*ZigObject {
586 if (wasm.zig_object_index == .null) return null;
587 return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object;
588}
589
590pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
591 var index: u32 = 0;
592 while (index < wasm.func_types.items.len) : (index += 1) {
593 if (wasm.func_types.items[index].eql(func_type)) return index;
594 }
595 return null;
596}
597
598/// Either creates a new import, or updates one if existing.
599/// When `type_index` is non-null, we assume an external function.
600/// In all other cases, a data-symbol will be created instead.
601pub fn addOrUpdateImport(
602 wasm: *Wasm,
603 /// Name of the import
604 name: []const u8,
605 /// Symbol index that is external
606 symbol_index: Symbol.Index,
607 /// Optional library name (i.e. `extern "c" fn foo() void`
608 lib_name: ?[:0]const u8,
609 /// The index of the type that represents the function signature
610 /// when the extern is a function. When this is null, a data-symbol
611 /// is asserted instead.
612 type_index: ?u32,
613) !void {
614 return wasm.zigObjectPtr().?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index);
615}
616
588617/// For a given name, creates a new global synthetic symbol.
589618/// Leaves index undefined and the default flags (0).
590619fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc {
......@@ -594,10 +623,10 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
594623}
595624
596625fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {
597 const sym_index = @as(u32, @intCast(wasm.symbols.items.len));
598 const loc: SymbolLoc = .{ .index = sym_index, .file = null };
626 const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
627 const loc: SymbolLoc = .{ .index = sym_index, .file = .null };
599628 const gpa = wasm.base.comp.gpa;
600 try wasm.symbols.append(gpa, .{
629 try wasm.synthetic_symbols.append(gpa, .{
601630 .name = name_offset,
602631 .flags = 0,
603632 .tag = tag,
......@@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !
609638 return loc;
610639}
611640
612/// Initializes symbols and atoms for the debug sections
613/// Initialization is only done when compiling Zig code.
614/// When Zig is invoked as a linker instead, the atoms
615/// and symbols come from the object files instead.
616pub fn initDebugSections(wasm: *Wasm) !void {
617 if (wasm.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
618 assert(wasm.debug_info_index == null);
619 // this will create an Atom and set the index for us.
620 wasm.debug_info_atom = try wasm.createDebugSectionForIndex(&wasm.debug_info_index, ".debug_info");
621 wasm.debug_line_atom = try wasm.createDebugSectionForIndex(&wasm.debug_line_index, ".debug_line");
622 wasm.debug_loc_atom = try wasm.createDebugSectionForIndex(&wasm.debug_loc_index, ".debug_loc");
623 wasm.debug_abbrev_atom = try wasm.createDebugSectionForIndex(&wasm.debug_abbrev_index, ".debug_abbrev");
624 wasm.debug_ranges_atom = try wasm.createDebugSectionForIndex(&wasm.debug_ranges_index, ".debug_ranges");
625 wasm.debug_str_atom = try wasm.createDebugSectionForIndex(&wasm.debug_str_index, ".debug_str");
626 wasm.debug_pubnames_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubnames_index, ".debug_pubnames");
627 wasm.debug_pubtypes_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubtypes_index, ".debug_pubtypes");
628}
629
630641fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
631642 for (files) |path| {
632643 if (try wasm.parseObjectFile(path)) continue;
......@@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
639650/// file and parsed successfully. Returns false when file is not an object file.
640651/// May return an error instead when parsing failed.
641652fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
642 const file = try fs.cwd().openFile(path, .{});
643 errdefer file.close();
653 const obj_file = try fs.cwd().openFile(path, .{});
654 errdefer obj_file.close();
644655
645656 const gpa = wasm.base.comp.gpa;
646 var object = Object.create(gpa, file, path, null) catch |err| switch (err) {
657 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
647658 error.InvalidMagicByte, error.NotObjectFile => return false,
648 else => |e| return e,
659 else => |e| {
660 var err_note = try wasm.addErrorWithNotes(1);
661 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
662 try err_note.addNote(wasm, "while parsing '{s}'", .{path});
663 return error.FlushFailure;
664 },
649665 };
650666 errdefer object.deinit(gpa);
651 try wasm.objects.append(gpa, object);
667 object.index = @enumFromInt(wasm.files.len);
668 try wasm.files.append(gpa, .{ .object = object });
669 try wasm.objects.append(gpa, object.index);
652670 return true;
653671}
654672
655/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
656/// When the index was not found, a new `Atom` will be created, and its index will be returned.
657/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
658pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index {
659 const gpa = wasm.base.comp.gpa;
660 const gop = try wasm.decls.getOrPut(gpa, decl_index);
661 if (!gop.found_existing) {
662 const atom_index = try wasm.createAtom();
663 gop.value_ptr.* = atom_index;
664 const atom = wasm.getAtom(atom_index);
665 const symbol = atom.symbolLoc().getSymbol(wasm);
666 const mod = wasm.base.comp.module.?;
667 const decl = mod.declPtr(decl_index);
668 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
669 symbol.name = try wasm.string_table.put(gpa, full_name);
670 }
671 return gop.value_ptr.*;
672}
673
674673/// Creates a new empty `Atom` and returns its `Atom.Index`
675fn createAtom(wasm: *Wasm) !Atom.Index {
674pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, file_index: File.Index) !Atom.Index {
676675 const gpa = wasm.base.comp.gpa;
677 const index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
676 const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len);
678677 const atom = try wasm.managed_atoms.addOne(gpa);
679 atom.* = Atom.empty;
680 atom.sym_index = try wasm.allocateSymbol();
681 try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index);
678 atom.* = .{ .file = file_index, .sym_index = sym_index };
679 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
682680
683681 return index;
684682}
685683
686684pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
687 return wasm.managed_atoms.items[index];
685 return wasm.managed_atoms.items[@intFromEnum(index)];
688686}
689687
690688pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
691 return &wasm.managed_atoms.items[index];
689 return &wasm.managed_atoms.items[@intFromEnum(index)];
692690}
693691
694692/// Parses an archive file and will then parse each object file
......@@ -702,11 +700,11 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
702700fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
703701 const gpa = wasm.base.comp.gpa;
704702
705 const file = try fs.cwd().openFile(path, .{});
706 errdefer file.close();
703 const archive_file = try fs.cwd().openFile(path, .{});
704 errdefer archive_file.close();
707705
708706 var archive: Archive = .{
709 .file = file,
707 .file = archive_file,
710708 .name = path,
711709 };
712710 archive.parse(gpa) catch |err| switch (err) {
......@@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714712 archive.deinit(gpa);
715713 return false;
716714 },
717 else => |e| return e,
715 else => |e| {
716 var err_note = try wasm.addErrorWithNotes(1);
717 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
718 try err_note.addNote(wasm, "while parsing archive {s}", .{path});
719 return error.FlushFailure;
720 },
718721 };
719722
720723 if (!force_load) {
......@@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
736739 }
737740
738741 for (offsets.keys()) |file_offset| {
739 const object = try wasm.objects.addOne(gpa);
740 object.* = try archive.parseObject(gpa, file_offset);
742 var object = archive.parseObject(wasm, file_offset) catch |e| {
743 var err_note = try wasm.addErrorWithNotes(1);
744 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
745 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
746 return error.FlushFailure;
747 };
748 object.index = @enumFromInt(wasm.files.len);
749 try wasm.files.append(gpa, .{ .object = object });
750 try wasm.objects.append(gpa, object.index);
741751 }
742752
743753 return true;
......@@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool {
752762 return false;
753763}
754764
755fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
765fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
756766 const gpa = wasm.base.comp.gpa;
757 const object: Object = wasm.objects.items[object_index];
758 log.debug("Resolving symbols in object: '{s}'", .{object.name});
759
760 for (object.symtable, 0..) |symbol, i| {
761 const sym_index = @as(u32, @intCast(i));
762 const location: SymbolLoc = .{
763 .file = object_index,
764 .index = sym_index,
765 };
766 const sym_name = object.string_table.get(symbol.name);
767 const obj_file = wasm.file(file_index).?;
768 log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()});
769
770 for (obj_file.symbols(), 0..) |symbol, i| {
771 const sym_index: Symbol.Index = @enumFromInt(i);
772 const location: SymbolLoc = .{ .file = file_index, .index = sym_index };
773 const sym_name = obj_file.string(symbol.name);
767774 if (mem.eql(u8, sym_name, "__indirect_function_table")) {
768775 continue;
769776 }
......@@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
771778
772779 if (symbol.isLocal()) {
773780 if (symbol.isUndefined()) {
774 log.err("Local symbols are not allowed to reference imports", .{});
775 log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name });
776 return error.UndefinedLocal;
781 var err = try wasm.addErrorWithNotes(1);
782 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
783 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
777784 }
778785 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
779786 continue;
......@@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
792799
793800 const existing_loc = maybe_existing.value_ptr.*;
794801 const existing_sym: *Symbol = existing_loc.getSymbol(wasm);
802 const existing_file = wasm.file(existing_loc.file);
795803
796 const existing_file_path = if (existing_loc.file) |file| blk: {
797 break :blk wasm.objects.items[file].name;
798 } else wasm.name;
804 const existing_file_path = if (existing_file) |existing_obj_file|
805 existing_obj_file.path()
806 else
807 wasm.name;
799808
800809 if (!existing_sym.isUndefined()) outer: {
801810 if (!symbol.isUndefined()) inner: {
......@@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
806815 break :outer; // existing is weak, while new one isn't. Replace it.
807816 }
808817 // both are defined and weak, we have a symbol collision.
809 log.err("symbol '{s}' defined multiple times", .{sym_name});
810 log.err(" first definition in '{s}'", .{existing_file_path});
811 log.err(" next definition in '{s}'", .{object.name});
812 return error.SymbolCollision;
818 var err = try wasm.addErrorWithNotes(2);
819 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
820 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
821 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
813822 }
814823
815824 try wasm.discarded.put(gpa, location, existing_loc);
......@@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
817826 }
818827
819828 if (symbol.tag != existing_sym.tag) {
820 log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) });
821 log.err(" first definition in '{s}'", .{existing_file_path});
822 log.err(" next definition in '{s}'", .{object.name});
823 return error.SymbolMismatchingType;
829 var err = try wasm.addErrorWithNotes(2);
830 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
831 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
832 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
824833 }
825834
826835 if (existing_sym.isUndefined() and symbol.isUndefined()) {
827836 // only verify module/import name for function symbols
828837 if (symbol.tag == .function) {
829 const existing_name = if (existing_loc.file) |file_index| blk: {
830 const obj = wasm.objects.items[file_index];
831 const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;
832 break :blk obj.string_table.get(name_index);
838 const existing_name = if (existing_file) |existing_obj| blk: {
839 const imp = existing_obj.import(existing_loc.index);
840 break :blk existing_obj.string(imp.module_name);
833841 } else blk: {
834842 const name_index = wasm.imports.get(existing_loc).?.module_name;
835843 break :blk wasm.string_table.get(name_index);
836844 };
837845
838 const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;
839 const module_name = object.string_table.get(module_index);
846 const imp = obj_file.import(sym_index);
847 const module_name = obj_file.string(imp.module_name);
840848 if (!mem.eql(u8, existing_name, module_name)) {
841 log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
849 var err = try wasm.addErrorWithNotes(2);
850 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
842851 sym_name,
843852 existing_name,
844853 module_name,
845854 });
846 log.err(" first definition in '{s}'", .{existing_file_path});
847 log.err(" next definition in '{s}'", .{object.name});
848 return error.ModuleNameMismatch;
855 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
856 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
849857 }
850858 }
851859
......@@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
858866 const existing_ty = wasm.getGlobalType(existing_loc);
859867 const new_ty = wasm.getGlobalType(location);
860868 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
861 log.err("symbol '{s}' mismatching global types", .{sym_name});
862 log.err(" first definition in '{s}'", .{existing_file_path});
863 log.err(" next definition in '{s}'", .{object.name});
864 return error.GlobalTypeMismatch;
869 var err = try wasm.addErrorWithNotes(2);
870 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
871 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
872 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
865873 }
866874 }
867875
......@@ -869,11 +877,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
869877 const existing_ty = wasm.getFunctionSignature(existing_loc);
870878 const new_ty = wasm.getFunctionSignature(location);
871879 if (!existing_ty.eql(new_ty)) {
872 log.err("symbol '{s}' mismatching function signatures.", .{sym_name});
873 log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty });
874 log.err(" first definition in '{s}'", .{existing_file_path});
875 log.err(" next definition in '{s}'", .{object.name});
876 return error.FunctionSignatureMismatch;
880 var err = try wasm.addErrorWithNotes(3);
881 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
882 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
883 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
884 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
877885 }
878886 }
879887
......@@ -888,7 +896,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
888896 // simply overwrite with the new symbol
889897 log.debug("Overwriting symbol '{s}'", .{sym_name});
890898 log.debug(" old definition in '{s}'", .{existing_file_path});
891 log.debug(" new definition in '{s}'", .{object.name});
899 log.debug(" new definition in '{s}'", .{obj_file.path()});
892900 try wasm.discarded.putNoClobber(gpa, existing_loc, location);
893901 maybe_existing.value_ptr.* = location;
894902 try wasm.globals.put(gpa, sym_name_index, location);
......@@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
920928 // Symbol is found in unparsed object file within current archive.
921929 // Parse object and and resolve symbols again before we check remaining
922930 // undefined symbols.
923 const object_file_index: u16 = @intCast(wasm.objects.items.len);
924 const object = try archive.parseObject(gpa, offset.items[0]);
925 try wasm.objects.append(gpa, object);
926 try wasm.resolveSymbolsInObject(object_file_index);
931 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
932 var err_note = try wasm.addErrorWithNotes(1);
933 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
934 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
935 return error.FlushFailure;
936 };
937 object.index = @enumFromInt(wasm.files.len);
938 try wasm.files.append(gpa, .{ .object = object });
939 try wasm.objects.append(gpa, object.index);
940 try wasm.resolveSymbolsInObject(object.index);
927941
928942 // continue loop for any remaining undefined symbols that still exist
929943 // after resolving last object file
......@@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
953967 if (!wasm.hasPassiveInitializationSegments()) {
954968 return;
955969 }
970 const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function);
971 sym_loc.getSymbol(wasm).mark();
956972
957973 const flag_address: u32 = if (shared_memory) address: {
958974 // when we have passive initialization segments and shared memory
......@@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
11151131 return;
11161132 }
11171133
1118 // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs");
1134 const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs", .function);
1135 loc.getSymbol(wasm).mark();
11191136 var function_body = std.ArrayList(u8).init(gpa);
11201137 defer function_body.deinit();
11211138 const writer = function_body.writer();
......@@ -1185,9 +1202,10 @@ fn validateFeatures(
11851202
11861203 // extract all the used, disallowed and required features from each
11871204 // linked object file so we can test them.
1188 for (wasm.objects.items, 0..) |object, object_index| {
1205 for (wasm.objects.items) |file_index| {
1206 const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
11891207 for (object.features) |feature| {
1190 const value = @as(u16, @intCast(object_index)) << 1 | @as(u1, 1);
1208 const value = @as(u16, @intFromEnum(file_index)) << 1 | @as(u1, 1);
11911209 switch (feature.prefix) {
11921210 .used => {
11931211 used[@intFromEnum(feature.tag)] = value;
......@@ -1218,29 +1236,30 @@ fn validateFeatures(
12181236 allowed[used_index] = is_enabled;
12191237 emit_features_count.* += @intFromBool(is_enabled);
12201238 } else if (is_enabled and !allowed[used_index]) {
1221 log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1222 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});
1239 var err = try wasm.addErrorWithNotes(1);
1240 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1241 try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
12231242 valid_feature_set = false;
12241243 }
12251244 }
12261245
12271246 if (!valid_feature_set) {
1228 return error.InvalidFeatureSet;
1247 return error.FlushFailure;
12291248 }
12301249
12311250 if (shared_memory) {
12321251 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
12331252 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1234 log.err(
1253 try wasm.addErrorWithoutNotes(
12351254 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1236 .{wasm.objects.items[disallowed_feature >> 1].name},
1255 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
12371256 );
12381257 valid_feature_set = false;
12391258 }
12401259
12411260 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12421261 if (!allowed[@intFromEnum(feature)]) {
1243 log.err("feature '{}' is not used but is required for shared-memory", .{feature});
1262 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
12441263 }
12451264 }
12461265 }
......@@ -1248,21 +1267,23 @@ fn validateFeatures(
12481267 if (has_tls) {
12491268 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12501269 if (!allowed[@intFromEnum(feature)]) {
1251 log.err("feature '{}' is not used but is required for thread-local storage", .{feature});
1270 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
12521271 }
12531272 }
12541273 }
12551274 // For each linked object, validate the required and disallowed features
1256 for (wasm.objects.items) |object| {
1275 for (wasm.objects.items) |file_index| {
12571276 var object_used_features = [_]bool{false} ** known_features_count;
1277 const object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
12581278 for (object.features) |feature| {
12591279 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
12601280 // from here a feature is always used
12611281 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
12621282 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1263 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1264 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});
1265 log.err(" used in '{s}'", .{object.name});
1283 var err = try wasm.addErrorWithNotes(2);
1284 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
1285 try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1286 try err.addNote(wasm, "used in '{s}'", .{object.path});
12661287 valid_feature_set = false;
12671288 }
12681289
......@@ -1273,16 +1294,17 @@ fn validateFeatures(
12731294 for (required, 0..) |required_feature, feature_index| {
12741295 const is_required = @as(u1, @truncate(required_feature)) != 0;
12751296 if (is_required and !object_used_features[feature_index]) {
1276 log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1277 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});
1278 log.err(" missing in '{s}'", .{object.name});
1297 var err = try wasm.addErrorWithNotes(2);
1298 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1299 try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1300 try err.addNote(wasm, "missing in '{s}'", .{object.path});
12791301 valid_feature_set = false;
12801302 }
12811303 }
12821304 }
12831305
12841306 if (!valid_feature_set) {
1285 return error.InvalidFeatureSet;
1307 return error.FlushFailure;
12861308 }
12871309
12881310 to_emit.* = allowed;
......@@ -1329,13 +1351,6 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
13291351 }
13301352 }
13311353 }
1332 if (wasm.string_table.getOffset("__zig_errors_len")) |name_offset| {
1333 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
1334 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
1335 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1336 _ = wasm.resolved_symbols.swapRemove(kv.value);
1337 }
1338 }
13391354}
13401355
13411356// Tries to find a global symbol by its name. Returns null when not found,
......@@ -1355,16 +1370,18 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13551370 const symbol = undef.getSymbol(wasm);
13561371 if (symbol.tag == .data) {
13571372 found_undefined_symbols = true;
1358 const file_name = if (undef.file) |file_index| name: {
1359 break :name wasm.objects.items[file_index].name;
1360 } else wasm.name;
1373 const file_name = if (wasm.file(undef.file)) |obj_file|
1374 obj_file.path()
1375 else
1376 wasm.name;
13611377 const symbol_name = undef.getName(wasm);
1362 log.err("could not resolve undefined symbol '{s}'", .{symbol_name});
1363 log.err(" defined in '{s}'", .{file_name});
1378 var err = try wasm.addErrorWithNotes(1);
1379 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
1380 try err.addNote(wasm, "defined in '{s}'", .{file_name});
13641381 }
13651382 }
13661383 if (found_undefined_symbols) {
1367 return error.UndefinedSymbol;
1384 return error.FlushFailure;
13681385 }
13691386}
13701387
......@@ -1378,54 +1395,28 @@ pub fn deinit(wasm: *Wasm) void {
13781395 for (wasm.segment_info.values()) |segment_info| {
13791396 gpa.free(segment_info.name);
13801397 }
1381 for (wasm.objects.items) |*object| {
1382 object.deinit(gpa);
1398 if (wasm.zigObjectPtr()) |zig_obj| {
1399 zig_obj.deinit(wasm);
1400 }
1401 for (wasm.objects.items) |obj_index| {
1402 wasm.file(obj_index).?.object.deinit(gpa);
13831403 }
13841404
13851405 for (wasm.archives.items) |*archive| {
13861406 archive.deinit(gpa);
13871407 }
13881408
1389 // For decls and anon decls we free the memory of its atoms.
1390 // The memory of atoms parsed from object files is managed by
1391 // the object file itself, and therefore we can skip those.
1392 {
1393 var it = wasm.decls.valueIterator();
1394 while (it.next()) |atom_index_ptr| {
1395 const atom = wasm.getAtomPtr(atom_index_ptr.*);
1396 for (atom.locals.items) |local_index| {
1397 const local_atom = wasm.getAtomPtr(local_index);
1398 local_atom.deinit(gpa);
1399 }
1400 atom.deinit(gpa);
1401 }
1402 }
1403 {
1404 for (wasm.anon_decls.values()) |atom_index| {
1405 const atom = wasm.getAtomPtr(atom_index);
1406 for (atom.locals.items) |local_index| {
1407 const local_atom = wasm.getAtomPtr(local_index);
1408 local_atom.deinit(gpa);
1409 }
1410 atom.deinit(gpa);
1411 }
1412 }
1413 for (wasm.synthetic_functions.items) |atom_index| {
1414 const atom = wasm.getAtomPtr(atom_index);
1415 atom.deinit(gpa);
1409 if (wasm.findGlobalSymbol("__wasm_init_tls")) |loc| {
1410 const atom = wasm.symbol_atom.get(loc).?;
1411 wasm.getAtomPtr(atom).deinit(gpa);
14161412 }
14171413
1418 wasm.decls.deinit(gpa);
1419 wasm.anon_decls.deinit(gpa);
1420 wasm.atom_types.deinit(gpa);
1421 wasm.symbols.deinit(gpa);
1422 wasm.symbols_free_list.deinit(gpa);
1414 wasm.synthetic_symbols.deinit(gpa);
14231415 wasm.globals.deinit(gpa);
14241416 wasm.resolved_symbols.deinit(gpa);
14251417 wasm.undefs.deinit(gpa);
14261418 wasm.discarded.deinit(gpa);
14271419 wasm.symbol_atom.deinit(gpa);
1428 wasm.export_names.deinit(gpa);
14291420 wasm.atoms.deinit(gpa);
14301421 wasm.managed_atoms.deinit(gpa);
14311422 wasm.segments.deinit(gpa);
......@@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void {
14451436 wasm.exports.deinit(gpa);
14461437
14471438 wasm.string_table.deinit(gpa);
1448 wasm.synthetic_functions.deinit(gpa);
1449
1450 if (wasm.dwarf) |*dwarf| {
1451 dwarf.deinit();
1452 }
1453}
1454
1455/// Allocates a new symbol and returns its index.
1456/// Will re-use slots when a symbol was freed at an earlier stage.
1457pub fn allocateSymbol(wasm: *Wasm) !u32 {
1458 const gpa = wasm.base.comp.gpa;
1459
1460 try wasm.symbols.ensureUnusedCapacity(gpa, 1);
1461 const symbol: Symbol = .{
1462 .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls
1463 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1464 .tag = .undefined, // will be set after updateDecl
1465 .index = std.math.maxInt(u32), // will be set during atom parsing
1466 .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
1467 };
1468 if (wasm.symbols_free_list.popOrNull()) |index| {
1469 wasm.symbols.items[index] = symbol;
1470 return index;
1471 }
1472 const index = @as(u32, @intCast(wasm.symbols.items.len));
1473 wasm.symbols.appendAssumeCapacity(symbol);
1474 return index;
1439 wasm.files.deinit(gpa);
14751440}
14761441
14771442pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
......@@ -1479,64 +1444,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air:
14791444 @panic("Attempted to compile for object format that was disabled by build configuration");
14801445 }
14811446 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
1482
1483 const tracy = trace(@src());
1484 defer tracy.end();
1485
1486 const gpa = wasm.base.comp.gpa;
1487 const func = mod.funcInfo(func_index);
1488 const decl_index = func.owner_decl;
1489 const decl = mod.declPtr(decl_index);
1490 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1491 const atom = wasm.getAtomPtr(atom_index);
1492 atom.clear();
1493
1494 // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1495 // defer if (decl_state) |*ds| ds.deinit();
1496
1497 var code_writer = std.ArrayList(u8).init(gpa);
1498 defer code_writer.deinit();
1499 // const result = try codegen.generateFunction(
1500 // &wasm.base,
1501 // decl.srcLoc(mod),
1502 // func,
1503 // air,
1504 // liveness,
1505 // &code_writer,
1506 // if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1507 // );
1508 const result = try codegen.generateFunction(
1509 &wasm.base,
1510 decl.srcLoc(mod),
1511 func_index,
1512 air,
1513 liveness,
1514 &code_writer,
1515 .none,
1516 );
1517
1518 const code = switch (result) {
1519 .ok => code_writer.items,
1520 .fail => |em| {
1521 func.analysis(&mod.intern_pool).state = .codegen_failure;
1522 try mod.failed_decls.put(mod.gpa, decl_index, em);
1523 return;
1524 },
1525 };
1526
1527 // if (wasm.dwarf) |*dwarf| {
1528 // try dwarf.commitDeclState(
1529 // mod,
1530 // decl_index,
1531 // // Actual value will be written after relocation.
1532 // // For Wasm, this is the offset relative to the code section
1533 // // which isn't known until flush().
1534 // 0,
1535 // code.len,
1536 // &decl_state.?,
1537 // );
1538 // }
1539 return wasm.finishUpdateDecl(decl_index, code, .function);
1447 try wasm.zigObjectPtr().?.updateFunc(wasm, mod, func_index, air, liveness);
15401448}
15411449
15421450// Generate code for the Decl, storing it in memory to be later written to
......@@ -1546,84 +1454,12 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !
15461454 @panic("Attempted to compile for object format that was disabled by build configuration");
15471455 }
15481456 if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
1549
1550 const tracy = trace(@src());
1551 defer tracy.end();
1552
1553 const decl = mod.declPtr(decl_index);
1554 if (decl.val.getFunction(mod)) |_| {
1555 return;
1556 } else if (decl.val.getExternFunc(mod)) |_| {
1557 return;
1558 }
1559
1560 const gpa = wasm.base.comp.gpa;
1561 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1562 const atom = wasm.getAtomPtr(atom_index);
1563 atom.clear();
1564
1565 if (decl.isExtern(mod)) {
1566 const variable = decl.getOwnedVariable(mod).?;
1567 const name = mod.intern_pool.stringToSlice(decl.name);
1568 const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
1569 return wasm.addOrUpdateImport(name, atom.sym_index, lib_name, null);
1570 }
1571 const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
1572
1573 var code_writer = std.ArrayList(u8).init(gpa);
1574 defer code_writer.deinit();
1575
1576 const res = try codegen.generateSymbol(
1577 &wasm.base,
1578 decl.srcLoc(mod),
1579 .{ .ty = decl.ty, .val = val },
1580 &code_writer,
1581 .none,
1582 .{ .parent_atom_index = atom.sym_index },
1583 );
1584
1585 const code = switch (res) {
1586 .ok => code_writer.items,
1587 .fail => |em| {
1588 decl.analysis = .codegen_failure;
1589 try mod.failed_decls.put(mod.gpa, decl_index, em);
1590 return;
1591 },
1592 };
1593
1594 return wasm.finishUpdateDecl(decl_index, code, .data);
1457 try wasm.zigObjectPtr().?.updateDecl(wasm, mod, decl_index);
15951458}
15961459
15971460pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void {
15981461 if (wasm.llvm_object) |_| return;
1599 if (wasm.dwarf) |*dw| {
1600 const tracy = trace(@src());
1601 defer tracy.end();
1602
1603 const decl = mod.declPtr(decl_index);
1604 const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
1605
1606 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1607 try dw.updateDeclLineNumber(mod, decl_index);
1608 }
1609}
1610
1611fn finishUpdateDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex, code: []const u8, symbol_tag: Symbol.Tag) !void {
1612 const gpa = wasm.base.comp.gpa;
1613 const mod = wasm.base.comp.module.?;
1614 const decl = mod.declPtr(decl_index);
1615 const atom_index = wasm.decls.get(decl_index).?;
1616 const atom = wasm.getAtomPtr(atom_index);
1617 const symbol = &wasm.symbols.items[atom.sym_index];
1618 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
1619 symbol.name = try wasm.string_table.put(gpa, full_name);
1620 symbol.tag = symbol_tag;
1621 try atom.code.appendSlice(gpa, code);
1622 try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {});
1623
1624 atom.size = @intCast(code.len);
1625 if (code.len == 0) return;
1626 atom.alignment = decl.getAlignment(mod);
1462 try wasm.zigObjectPtr().?.updateDeclLineNumber(mod, decl_index);
16271463}
16281464
16291465/// From a given symbol location, returns its `wasm.GlobalType`.
......@@ -1632,13 +1468,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
16321468 const symbol = loc.getSymbol(wasm);
16331469 assert(symbol.tag == .global);
16341470 const is_undefined = symbol.isUndefined();
1635 if (loc.file) |file_index| {
1636 const obj: Object = wasm.objects.items[file_index];
1471 if (wasm.file(loc.file)) |obj_file| {
16371472 if (is_undefined) {
1638 return obj.findImport(.global, symbol.index).kind.global;
1473 return obj_file.import(loc.index).kind.global;
16391474 }
1640 const import_global_count = obj.importedCountByKind(.global);
1641 return obj.globals[symbol.index - import_global_count].global_type;
1475 return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type;
16421476 }
16431477 if (is_undefined) {
16441478 return wasm.imports.get(loc).?.kind.global;
......@@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
16521486 const symbol = loc.getSymbol(wasm);
16531487 assert(symbol.tag == .function);
16541488 const is_undefined = symbol.isUndefined();
1655 if (loc.file) |file_index| {
1656 const obj: Object = wasm.objects.items[file_index];
1489 if (wasm.file(loc.file)) |obj_file| {
16571490 if (is_undefined) {
1658 const ty_index = obj.findImport(.function, symbol.index).kind.function;
1659 return obj.func_types[ty_index];
1491 const ty_index = obj_file.import(loc.index).kind.function;
1492 return obj_file.funcTypes()[ty_index];
16601493 }
1661 const import_function_count = obj.importedCountByKind(.function);
1662 const type_index = obj.functions[symbol.index - import_function_count].type_index;
1663 return obj.func_types[type_index];
1494 const type_index = obj_file.function(loc.index).type_index;
1495 return obj_file.funcTypes()[type_index];
16641496 }
16651497 if (is_undefined) {
16661498 const ty_index = wasm.imports.get(loc).?.kind.function;
......@@ -1673,118 +1505,16 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
16731505/// Returns the symbol index of the local
16741506/// The given `decl` is the parent decl whom owns the constant.
16751507pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
1676 const gpa = wasm.base.comp.gpa;
1677 const mod = wasm.base.comp.module.?;
1678 assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions
1679 const decl = mod.declPtr(decl_index);
1680
1681 const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1682 const parent_atom = wasm.getAtom(parent_atom_index);
1683 const local_index = parent_atom.locals.items.len;
1684 const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
1685 const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{
1686 fqn, local_index,
1687 });
1688 defer gpa.free(name);
1689
1690 switch (try wasm.lowerConst(name, tv, decl.srcLoc(mod))) {
1691 .ok => |atom_index| {
1692 try wasm.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index);
1693 return wasm.getAtom(atom_index).getSymbolIndex().?;
1694 },
1695 .fail => |em| {
1696 decl.analysis = .codegen_failure;
1697 try mod.failed_decls.put(mod.gpa, decl_index, em);
1698 return error.CodegenFail;
1699 },
1700 }
1701}
1702
1703const LowerConstResult = union(enum) {
1704 ok: Atom.Index,
1705 fail: *Module.ErrorMsg,
1706};
1707
1708fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult {
1709 const gpa = wasm.base.comp.gpa;
1710 const mod = wasm.base.comp.module.?;
1711
1712 // Create and initialize a new local symbol and atom
1713 const atom_index = try wasm.createAtom();
1714 var value_bytes = std.ArrayList(u8).init(gpa);
1715 defer value_bytes.deinit();
1716
1717 const code = code: {
1718 const atom = wasm.getAtomPtr(atom_index);
1719 atom.alignment = tv.ty.abiAlignment(mod);
1720 wasm.symbols.items[atom.sym_index] = .{
1721 .name = try wasm.string_table.put(gpa, name),
1722 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1723 .tag = .data,
1724 .index = undefined,
1725 .virtual_address = undefined,
1726 };
1727 try wasm.resolved_symbols.putNoClobber(gpa, atom.symbolLoc(), {});
1728
1729 const result = try codegen.generateSymbol(
1730 &wasm.base,
1731 src_loc,
1732 tv,
1733 &value_bytes,
1734 .none,
1735 .{
1736 .parent_atom_index = atom.sym_index,
1737 .addend = null,
1738 },
1739 );
1740 break :code switch (result) {
1741 .ok => value_bytes.items,
1742 .fail => |em| {
1743 return .{ .fail = em };
1744 },
1745 };
1746 };
1747
1748 const atom = wasm.getAtomPtr(atom_index);
1749 atom.size = @intCast(code.len);
1750 try atom.code.appendSlice(gpa, code);
1751 return .{ .ok = atom_index };
1508 return wasm.zigObjectPtr().?.lowerUnnamedConst(wasm, tv, decl_index);
17521509}
17531510
17541511/// Returns the symbol index from a symbol of which its flag is set global,
17551512/// such as an exported or imported symbol.
17561513/// If the symbol does not yet exist, creates a new one symbol instead
17571514/// and then returns the index to it.
1758pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u32 {
1515pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
17591516 _ = lib_name;
1760 const gpa = wasm.base.comp.gpa;
1761 const name_index = try wasm.string_table.put(gpa, name);
1762 const gop = try wasm.globals.getOrPut(gpa, name_index);
1763 if (gop.found_existing) {
1764 return gop.value_ptr.*.index;
1765 }
1766
1767 var symbol: Symbol = .{
1768 .name = name_index,
1769 .flags = 0,
1770 .index = undefined, // index to type will be set after merging function symbols
1771 .tag = .function,
1772 .virtual_address = undefined,
1773 };
1774 symbol.setGlobal(true);
1775 symbol.setUndefined(true);
1776
1777 const sym_index = if (wasm.symbols_free_list.popOrNull()) |index| index else blk: {
1778 const index: u32 = @intCast(wasm.symbols.items.len);
1779 try wasm.symbols.ensureUnusedCapacity(gpa, 1);
1780 wasm.symbols.items.len += 1;
1781 break :blk index;
1782 };
1783 wasm.symbols.items[sym_index] = symbol;
1784 gop.value_ptr.* = .{ .index = sym_index, .file = null };
1785 try wasm.resolved_symbols.put(gpa, gop.value_ptr.*, {});
1786 try wasm.undefs.putNoClobber(gpa, name_index, gop.value_ptr.*);
1787 return sym_index;
1517 return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name);
17881518}
17891519
17901520/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
......@@ -1794,42 +1524,7 @@ pub fn getDeclVAddr(
17941524 decl_index: InternPool.DeclIndex,
17951525 reloc_info: link.File.RelocInfo,
17961526) !u64 {
1797 const target = wasm.base.comp.root_mod.resolved_target.result;
1798 const gpa = wasm.base.comp.gpa;
1799 const mod = wasm.base.comp.module.?;
1800 const decl = mod.declPtr(decl_index);
1801
1802 const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1803 const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
1804
1805 assert(reloc_info.parent_atom_index != 0);
1806 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1807 const atom = wasm.getAtomPtr(atom_index);
1808 const is_wasm32 = target.cpu.arch == .wasm32;
1809 if (decl.ty.zigTypeTag(mod) == .Fn) {
1810 assert(reloc_info.addend == 0); // addend not allowed for function relocations
1811 // We found a function pointer, so add it to our table,
1812 // as function pointers are not allowed to be stored inside the data section.
1813 // They are instead stored in a function table which are called by index.
1814 try wasm.addTableFunction(target_symbol_index);
1815 try atom.relocs.append(gpa, .{
1816 .index = target_symbol_index,
1817 .offset = @intCast(reloc_info.offset),
1818 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
1819 });
1820 } else {
1821 try atom.relocs.append(gpa, .{
1822 .index = target_symbol_index,
1823 .offset = @intCast(reloc_info.offset),
1824 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
1825 .addend = @intCast(reloc_info.addend),
1826 });
1827 }
1828 // we do not know the final address at this point,
1829 // as atom allocation will determine the address and relocations
1830 // will calculate and rewrite this. Therefore, we simply return the symbol index
1831 // that was targeted.
1832 return target_symbol_index;
1527 return wasm.zigObjectPtr().?.getDeclVAddr(wasm, decl_index, reloc_info);
18331528}
18341529
18351530pub fn lowerAnonDecl(
......@@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl(
18381533 explicit_alignment: Alignment,
18391534 src_loc: Module.SrcLoc,
18401535) !codegen.Result {
1841 const gpa = wasm.base.comp.gpa;
1842 const gop = try wasm.anon_decls.getOrPut(gpa, decl_val);
1843 if (!gop.found_existing) {
1844 const mod = wasm.base.comp.module.?;
1845 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
1846 const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) };
1847 var name_buf: [32]u8 = undefined;
1848 const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
1849 @intFromEnum(decl_val),
1850 }) catch unreachable;
1851
1852 switch (try wasm.lowerConst(name, tv, src_loc)) {
1853 .ok => |atom_index| wasm.anon_decls.values()[gop.index] = atom_index,
1854 .fail => |em| return .{ .fail = em },
1855 }
1856 }
1857
1858 const atom = wasm.getAtomPtr(wasm.anon_decls.values()[gop.index]);
1859 atom.alignment = switch (atom.alignment) {
1860 .none => explicit_alignment,
1861 else => switch (explicit_alignment) {
1862 .none => atom.alignment,
1863 else => atom.alignment.maxStrict(explicit_alignment),
1864 },
1865 };
1866 return .ok;
1536 return wasm.zigObjectPtr().?.lowerAnonDecl(wasm, decl_val, explicit_alignment, src_loc);
18671537}
18681538
18691539pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
1870 const gpa = wasm.base.comp.gpa;
1871 const target = wasm.base.comp.root_mod.resolved_target.result;
1872 const atom_index = wasm.anon_decls.get(decl_val).?;
1873 const target_symbol_index = wasm.getAtom(atom_index).getSymbolIndex().?;
1874
1875 const parent_atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1876 const parent_atom = wasm.getAtomPtr(parent_atom_index);
1877 const is_wasm32 = target.cpu.arch == .wasm32;
1878 const mod = wasm.base.comp.module.?;
1879 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
1880 if (ty.zigTypeTag(mod) == .Fn) {
1881 assert(reloc_info.addend == 0); // addend not allowed for function relocations
1882 // We found a function pointer, so add it to our table,
1883 // as function pointers are not allowed to be stored inside the data section.
1884 // They are instead stored in a function table which are called by index.
1885 try wasm.addTableFunction(target_symbol_index);
1886 try parent_atom.relocs.append(gpa, .{
1887 .index = target_symbol_index,
1888 .offset = @intCast(reloc_info.offset),
1889 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
1890 });
1891 } else {
1892 try parent_atom.relocs.append(gpa, .{
1893 .index = target_symbol_index,
1894 .offset = @intCast(reloc_info.offset),
1895 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
1896 .addend = @intCast(reloc_info.addend),
1897 });
1898 }
1899
1900 // we do not know the final address at this point,
1901 // as atom allocation will determine the address and relocations
1902 // will calculate and rewrite this. Therefore, we simply return the symbol index
1903 // that was targeted.
1904 return target_symbol_index;
1540 return wasm.zigObjectPtr().?.getAnonDeclVAddr(wasm, decl_val, reloc_info);
19051541}
19061542
19071543pub fn deleteDeclExport(
......@@ -1909,19 +1545,8 @@ pub fn deleteDeclExport(
19091545 decl_index: InternPool.DeclIndex,
19101546 name: InternPool.NullTerminatedString,
19111547) void {
1912 _ = name;
19131548 if (wasm.llvm_object) |_| return;
1914 const atom_index = wasm.decls.get(decl_index) orelse return;
1915 const sym_index = wasm.getAtom(atom_index).sym_index;
1916 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
1917 const symbol = loc.getSymbol(wasm);
1918 const symbol_name = wasm.string_table.get(symbol.name);
1919 log.debug("Deleting export for decl '{s}'", .{symbol_name});
1920 if (wasm.export_names.fetchRemove(loc)) |kv| {
1921 assert(wasm.globals.remove(kv.value));
1922 } else {
1923 assert(wasm.globals.remove(symbol.name));
1924 }
1549 return wasm.zigObjectPtr().?.deleteDeclExport(wasm, decl_index, name);
19251550}
19261551
19271552pub fn updateExports(
......@@ -1934,159 +1559,12 @@ pub fn updateExports(
19341559 @panic("Attempted to compile for object format that was disabled by build configuration");
19351560 }
19361561 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
1937
1938 const decl_index = switch (exported) {
1939 .decl_index => |i| i,
1940 .value => |val| {
1941 _ = val;
1942 @panic("TODO: implement Wasm linker code for exporting a constant value");
1943 },
1944 };
1945 const decl = mod.declPtr(decl_index);
1946 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1947 const atom = wasm.getAtom(atom_index);
1948 const atom_sym = atom.symbolLoc().getSymbol(wasm).*;
1949 const gpa = mod.gpa;
1950
1951 for (exports) |exp| {
1952 if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| {
1953 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
1954 gpa,
1955 decl.srcLoc(mod),
1956 "Unimplemented: ExportOptions.section '{s}'",
1957 .{section},
1958 ));
1959 continue;
1960 }
1961
1962 const exported_decl_index = switch (exp.exported) {
1963 .value => {
1964 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
1965 gpa,
1966 decl.srcLoc(mod),
1967 "Unimplemented: exporting a named constant value",
1968 .{},
1969 ));
1970 continue;
1971 },
1972 .decl_index => |i| i,
1973 };
1974 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exported_decl_index);
1975 const exported_atom = wasm.getAtom(exported_atom_index);
1976 const export_name = try wasm.string_table.put(gpa, mod.intern_pool.stringToSlice(exp.opts.name));
1977 const sym_loc = exported_atom.symbolLoc();
1978 const symbol = sym_loc.getSymbol(wasm);
1979 symbol.setGlobal(true);
1980 symbol.setUndefined(false);
1981 symbol.index = atom_sym.index;
1982 symbol.tag = atom_sym.tag;
1983 symbol.name = atom_sym.name;
1984
1985 switch (exp.opts.linkage) {
1986 .Internal => {
1987 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
1988 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
1989 },
1990 .Weak => {
1991 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
1992 },
1993 .Strong => {}, // symbols are strong by default
1994 .LinkOnce => {
1995 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
1996 gpa,
1997 decl.srcLoc(mod),
1998 "Unimplemented: LinkOnce",
1999 .{},
2000 ));
2001 continue;
2002 },
2003 }
2004
2005 if (wasm.globals.get(export_name)) |existing_loc| {
2006 if (existing_loc.index == atom.sym_index) continue;
2007 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
2008
2009 if (!existing_sym.isUndefined()) blk: {
2010 if (symbol.isWeak()) {
2011 try wasm.discarded.put(gpa, existing_loc, sym_loc);
2012 continue; // to-be-exported symbol is weak, so we keep the existing symbol
2013 }
2014
2015 // new symbol is not weak while existing is, replace existing symbol
2016 if (existing_sym.isWeak()) {
2017 break :blk;
2018 }
2019 // When both the to-be-exported symbol and the already existing symbol
2020 // are strong symbols, we have a linker error.
2021 // In the other case we replace one with the other.
2022 try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create(
2023 gpa,
2024 decl.srcLoc(mod),
2025 \\LinkError: symbol '{}' defined multiple times
2026 \\ first definition in '{s}'
2027 \\ next definition in '{s}'
2028 ,
2029 .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name },
2030 ));
2031 continue;
2032 }
2033
2034 // in this case the existing symbol must be replaced either because it's weak or undefined.
2035 try wasm.discarded.put(gpa, existing_loc, sym_loc);
2036 _ = wasm.imports.remove(existing_loc);
2037 _ = wasm.undefs.swapRemove(existing_sym.name);
2038 }
2039
2040 // Ensure the symbol will be exported using the given name
2041 if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) {
2042 try wasm.export_names.put(gpa, sym_loc, export_name);
2043 }
2044
2045 try wasm.globals.put(
2046 gpa,
2047 export_name,
2048 sym_loc,
2049 );
2050 }
1562 return wasm.zigObjectPtr().?.updateExports(wasm, mod, exported, exports);
20511563}
20521564
20531565pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
20541566 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
2055 const gpa = wasm.base.comp.gpa;
2056 const mod = wasm.base.comp.module.?;
2057 const decl = mod.declPtr(decl_index);
2058 const atom_index = wasm.decls.get(decl_index).?;
2059 const atom = wasm.getAtomPtr(atom_index);
2060 atom.prev = null;
2061 wasm.symbols_free_list.append(gpa, atom.sym_index) catch {};
2062 _ = wasm.decls.remove(decl_index);
2063 wasm.symbols.items[atom.sym_index].tag = .dead;
2064 for (atom.locals.items) |local_atom_index| {
2065 const local_atom = wasm.getAtom(local_atom_index);
2066 const local_symbol = &wasm.symbols.items[local_atom.sym_index];
2067 local_symbol.tag = .dead; // also for any local symbol
2068 wasm.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
2069 assert(wasm.resolved_symbols.swapRemove(local_atom.symbolLoc()));
2070 assert(wasm.symbol_atom.remove(local_atom.symbolLoc()));
2071 }
2072
2073 if (decl.isExtern(mod)) {
2074 _ = wasm.imports.remove(atom.symbolLoc());
2075 }
2076 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
2077 _ = wasm.symbol_atom.remove(atom.symbolLoc());
2078
2079 // if (wasm.dwarf) |*dwarf| {
2080 // dwarf.freeDecl(decl_index);
2081 // }
2082
2083}
2084
2085/// Appends a new entry to the indirect function table
2086pub fn addTableFunction(wasm: *Wasm, symbol_index: u32) !void {
2087 const gpa = wasm.base.comp.gpa;
2088 const index: u32 = @intCast(wasm.function_table.count());
2089 try wasm.function_table.put(gpa, .{ .file = null, .index = symbol_index }, index);
1567 return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index);
20901568}
20911569
20921570/// Assigns indexes to all indirect functions.
......@@ -2118,203 +1596,6 @@ fn mapFunctionTable(wasm: *Wasm) void {
21181596 }
21191597}
21201598
2121/// Either creates a new import, or updates one if existing.
2122/// When `type_index` is non-null, we assume an external function.
2123/// In all other cases, a data-symbol will be created instead.
2124pub fn addOrUpdateImport(
2125 wasm: *Wasm,
2126 /// Name of the import
2127 name: []const u8,
2128 /// Symbol index that is external
2129 symbol_index: u32,
2130 /// Optional library name (i.e. `extern "c" fn foo() void`
2131 lib_name: ?[:0]const u8,
2132 /// The index of the type that represents the function signature
2133 /// when the extern is a function. When this is null, a data-symbol
2134 /// is asserted instead.
2135 type_index: ?u32,
2136) !void {
2137 const gpa = wasm.base.comp.gpa;
2138 assert(symbol_index != 0);
2139 // For the import name, we use the decl's name, rather than the fully qualified name
2140 // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
2141 // name but different module can be resolved correctly.
2142 const mangle_name = lib_name != null and
2143 !std.mem.eql(u8, lib_name.?, "c");
2144 const full_name = if (mangle_name) full_name: {
2145 break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? });
2146 } else name;
2147 defer if (mangle_name) gpa.free(full_name);
2148
2149 const decl_name_index = try wasm.string_table.put(gpa, full_name);
2150 const symbol: *Symbol = &wasm.symbols.items[symbol_index];
2151 symbol.setUndefined(true);
2152 symbol.setGlobal(true);
2153 symbol.name = decl_name_index;
2154 if (mangle_name) {
2155 // we specified a specific name for the symbol that does not match the import name
2156 symbol.setFlag(.WASM_SYM_EXPLICIT_NAME);
2157 }
2158 const global_gop = try wasm.globals.getOrPut(gpa, decl_name_index);
2159 if (!global_gop.found_existing) {
2160 const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
2161 global_gop.value_ptr.* = loc;
2162 try wasm.resolved_symbols.put(gpa, loc, {});
2163 try wasm.undefs.putNoClobber(gpa, decl_name_index, loc);
2164 } else if (global_gop.value_ptr.*.index != symbol_index) {
2165 // We are not updating a symbol, but found an existing global
2166 // symbol with the same name. This means we always favor the
2167 // existing symbol, regardless whether it's defined or not.
2168 // We can also skip storing the import as we will not output
2169 // this symbol.
2170 return wasm.discarded.put(
2171 gpa,
2172 .{ .file = null, .index = symbol_index },
2173 global_gop.value_ptr.*,
2174 );
2175 }
2176
2177 if (type_index) |ty_index| {
2178 const gop = try wasm.imports.getOrPut(gpa, .{ .index = symbol_index, .file = null });
2179 const module_name = if (lib_name) |l_name| blk: {
2180 break :blk l_name;
2181 } else wasm.host_name;
2182 if (!gop.found_existing) {
2183 gop.value_ptr.* = .{
2184 .module_name = try wasm.string_table.put(gpa, module_name),
2185 .name = try wasm.string_table.put(gpa, name),
2186 .kind = .{ .function = ty_index },
2187 };
2188 }
2189 } else {
2190 // non-functions will not be imported from the runtime, but only resolved during link-time
2191 symbol.tag = .data;
2192 }
2193}
2194
2195/// Kind represents the type of an Atom, which is only
2196/// used to parse a decl into an Atom to define in which section
2197/// or segment it should be placed.
2198const Kind = union(enum) {
2199 /// Represents the segment the data symbol should
2200 /// be inserted into.
2201 /// TODO: Add TLS segments
2202 data: enum {
2203 read_only,
2204 uninitialized,
2205 initialized,
2206 },
2207 function: void,
2208
2209 /// Returns the segment name the data kind represents.
2210 /// Asserts `kind` has its active tag set to `data`.
2211 fn segmentName(kind: Kind) []const u8 {
2212 switch (kind.data) {
2213 .read_only => return ".rodata.",
2214 .uninitialized => return ".bss.",
2215 .initialized => return ".data.",
2216 }
2217 }
2218};
2219
2220/// Parses an Atom and inserts its metadata into the corresponding sections.
2221fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2222 const comp = wasm.base.comp;
2223 const gpa = comp.gpa;
2224 const shared_memory = comp.config.shared_memory;
2225 const import_memory = comp.config.import_memory;
2226 const atom = wasm.getAtomPtr(atom_index);
2227 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
2228 const do_garbage_collect = wasm.base.gc_sections;
2229
2230 if (symbol.isDead() and do_garbage_collect) {
2231 // Prevent unreferenced symbols from being parsed.
2232 return;
2233 }
2234
2235 const final_index: u32 = switch (kind) {
2236 .function => result: {
2237 const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
2238 const type_index = wasm.atom_types.get(atom_index).?;
2239 try wasm.functions.putNoClobber(
2240 gpa,
2241 .{ .file = null, .index = index },
2242 .{ .func = .{ .type_index = type_index }, .sym_index = atom.sym_index },
2243 );
2244 symbol.tag = .function;
2245 symbol.index = index;
2246
2247 if (wasm.code_section_index == null) {
2248 wasm.code_section_index = @intCast(wasm.segments.items.len);
2249 try wasm.segments.append(gpa, .{
2250 .alignment = atom.alignment,
2251 .size = atom.size,
2252 .offset = 0,
2253 .flags = 0,
2254 });
2255 }
2256
2257 break :result wasm.code_section_index.?;
2258 },
2259 .data => result: {
2260 const segment_name = try std.mem.concat(gpa, u8, &.{
2261 kind.segmentName(),
2262 wasm.string_table.get(symbol.name),
2263 });
2264 errdefer gpa.free(segment_name);
2265 const segment_info: types.Segment = .{
2266 .name = segment_name,
2267 .alignment = atom.alignment,
2268 .flags = 0,
2269 };
2270 symbol.tag = .data;
2271
2272 // when creating an object file, or importing memory and the data belongs in the .bss segment
2273 // we set the entire region of it to zeroes.
2274 // We do not have to do this when exporting the memory (the default) because the runtime
2275 // will do it for us, and we do not emit the bss segment at all.
2276 if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) {
2277 @memset(atom.code.items, 0);
2278 }
2279
2280 const should_merge = wasm.base.comp.config.output_mode != .Obj;
2281 const gop = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(should_merge));
2282 if (gop.found_existing) {
2283 const index = gop.value_ptr.*;
2284 wasm.segments.items[index].size += atom.size;
2285
2286 symbol.index = @intCast(wasm.segment_info.getIndex(index).?);
2287 // segment info already exists, so free its memory
2288 gpa.free(segment_name);
2289 break :result index;
2290 } else {
2291 const index: u32 = @intCast(wasm.segments.items.len);
2292 var flags: u32 = 0;
2293 if (shared_memory) {
2294 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
2295 }
2296 try wasm.segments.append(gpa, .{
2297 .alignment = atom.alignment,
2298 .size = 0,
2299 .offset = 0,
2300 .flags = flags,
2301 });
2302 gop.value_ptr.* = index;
2303
2304 const info_index: u32 = @intCast(wasm.segment_info.count());
2305 try wasm.segment_info.put(gpa, index, segment_info);
2306 symbol.index = info_index;
2307 break :result index;
2308 }
2309 },
2310 };
2311
2312 const segment: *Segment = &wasm.segments.items[final_index];
2313 segment.alignment = segment.alignment.max(atom.alignment);
2314
2315 try wasm.appendAtomAtIndex(final_index, atom_index);
2316}
2317
23181599/// From a given index, append the given `Atom` at the back of the linked list.
23191600/// Simply inserts it into the map of atoms when it doesn't exist yet.
23201601pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
......@@ -2328,40 +1609,9 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void
23281609 }
23291610}
23301611
2331/// Allocates debug atoms into their respective debug sections
2332/// to merge them with maybe-existing debug atoms from object files.
2333fn allocateDebugAtoms(wasm: *Wasm) !void {
2334 if (wasm.dwarf == null) return;
2335
2336 const allocAtom = struct {
2337 fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void {
2338 const index = maybe_index.* orelse idx: {
2339 const index = @as(u32, @intCast(bin.segments.items.len));
2340 try bin.appendDummySegment();
2341 maybe_index.* = index;
2342 break :idx index;
2343 };
2344 const atom = bin.getAtomPtr(atom_index);
2345 atom.size = @as(u32, @intCast(atom.code.items.len));
2346 bin.symbols.items[atom.sym_index].index = index;
2347 try bin.appendAtomAtIndex(index, atom_index);
2348 }
2349 }.f;
2350
2351 try allocAtom(wasm, &wasm.debug_info_index, wasm.debug_info_atom.?);
2352 try allocAtom(wasm, &wasm.debug_line_index, wasm.debug_line_atom.?);
2353 try allocAtom(wasm, &wasm.debug_loc_index, wasm.debug_loc_atom.?);
2354 try allocAtom(wasm, &wasm.debug_str_index, wasm.debug_str_atom.?);
2355 try allocAtom(wasm, &wasm.debug_ranges_index, wasm.debug_ranges_atom.?);
2356 try allocAtom(wasm, &wasm.debug_abbrev_index, wasm.debug_abbrev_atom.?);
2357 try allocAtom(wasm, &wasm.debug_pubnames_index, wasm.debug_pubnames_atom.?);
2358 try allocAtom(wasm, &wasm.debug_pubtypes_index, wasm.debug_pubtypes_atom.?);
2359}
2360
23611612fn allocateAtoms(wasm: *Wasm) !void {
23621613 // first sort the data segments
23631614 try sortDataSegments(wasm);
2364 try allocateDebugAtoms(wasm);
23651615
23661616 var it = wasm.atoms.iterator();
23671617 while (it.next()) |entry| {
......@@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void {
23791629 // Ensure we get the original symbol, so we verify the correct symbol on whether
23801630 // it is dead or not and ensure an atom is removed when dead.
23811631 // This is required as we may have parsed aliases into atoms.
2382 const sym = if (symbol_loc.file) |object_index| sym: {
2383 const object = wasm.objects.items[object_index];
2384 break :sym object.symtable[symbol_loc.index];
2385 } else wasm.symbols.items[symbol_loc.index];
1632 const sym = if (wasm.file(symbol_loc.file)) |obj_file|
1633 obj_file.symbol(symbol_loc.index).*
1634 else
1635 wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)];
23861636
23871637 // Dead symbols must be unlinked from the linked-list to prevent them
23881638 // from being emit into the binary.
23891639 if (sym.isDead()) {
2390 if (entry.value_ptr.* == atom_index and atom.prev != null) {
1640 if (entry.value_ptr.* == atom_index and atom.prev != .null) {
23911641 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
23921642 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
23931643 // was removed and therefore do not emit any code at all.
2394 entry.value_ptr.* = atom.prev.?;
1644 entry.value_ptr.* = atom.prev;
23951645 }
2396 atom_index = atom.prev orelse break;
2397 atom.prev = null;
1646 if (atom.prev == .null) break;
1647 atom_index = atom.prev;
1648 atom.prev = .null;
23981649 continue;
23991650 }
24001651 offset = @intCast(atom.alignment.forward(offset));
......@@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void {
24061657 atom.size,
24071658 });
24081659 offset += atom.size;
2409 atom_index = atom.prev orelse break;
1660 if (atom.prev == .null) break;
1661 atom_index = atom.prev;
24101662 }
24111663 segment.size = @intCast(segment.alignment.forward(offset));
24121664 }
......@@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {
24281680
24291681 const atom = wasm.getAtom(atom_index);
24301682 const merge_segment = wasm.base.comp.config.output_mode != .Obj;
2431 const segment_info = if (atom.file) |object_index| blk: {
2432 break :blk wasm.objects.items[object_index].segment_info;
2433 } else wasm.segment_info.values();
1683 const segment_info = if (atom.file != .null)
1684 wasm.file(atom.file).?.segmentInfo()
1685 else
1686 wasm.segment_info.values();
24341687 const segment_name = segment_info[symbol.index].outputName(merge_segment);
24351688 const segment_index = wasm.data_segments.get(segment_name).?;
24361689 const segment = wasm.segments.items[segment_index];
......@@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void {
24861739/// contain any parameters.
24871740fn setupInitFunctions(wasm: *Wasm) !void {
24881741 const gpa = wasm.base.comp.gpa;
2489 for (wasm.objects.items, 0..) |object, file_index| {
1742 // There's no constructors for Zig so we can simply search through linked object files only.
1743 for (wasm.objects.items) |file_index| {
1744 const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
24901745 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
24911746 for (object.init_funcs) |init_func| {
24921747 const symbol = object.symtable[init_func.symbol_index];
24931748 const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {
2494 const imp: types.Import = object.findImport(.function, symbol.index);
1749 const imp: types.Import = object.findImport(symbol);
24951750 break :ty object.func_types[imp.kind.function];
24961751 } else ty: {
2497 const func_index = symbol.index - object.importedCountByKind(.function);
1752 const func_index = symbol.index - object.imported_functions_count;
24981753 const func = object.functions[func_index];
24991754 break :ty object.func_types[func.type_index];
25001755 };
25011756 if (ty.params.len != 0) {
2502 log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
2503 return error.InvalidInitFunc;
1757 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
25041758 }
25051759 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
25061760 wasm.init_funcs.appendAssumeCapacity(.{
2507 .index = init_func.symbol_index,
2508 .file = @as(u16, @intCast(file_index)),
1761 .index = @enumFromInt(init_func.symbol_index),
1762 .file = file_index,
25091763 .priority = init_func.priority,
25101764 });
2511 try wasm.mark(.{ .index = init_func.symbol_index, .file = @intCast(file_index) });
1765 try wasm.mark(.{ .index = @enumFromInt(init_func.symbol_index), .file = file_index });
25121766 }
25131767 }
25141768
......@@ -2521,34 +1775,6 @@ fn setupInitFunctions(wasm: *Wasm) !void {
25211775 }
25221776}
25231777
2524/// Generates an atom containing the global error set' size.
2525/// This will only be generated if the symbol exists.
2526fn setupErrorsLen(wasm: *Wasm) !void {
2527 const gpa = wasm.base.comp.gpa;
2528 const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return;
2529
2530 const errors_len = wasm.base.comp.module.?.global_error_set.count();
2531 // overwrite existing atom if it already exists (maybe the error set has increased)
2532 // if not, allcoate a new atom.
2533 const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
2534 const atom = wasm.getAtomPtr(index);
2535 atom.deinit(gpa);
2536 break :blk index;
2537 } else new_atom: {
2538 const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
2539 try wasm.symbol_atom.put(gpa, loc, atom_index);
2540 try wasm.managed_atoms.append(gpa, undefined);
2541 break :new_atom atom_index;
2542 };
2543 const atom = wasm.getAtomPtr(atom_index);
2544 atom.* = Atom.empty;
2545 atom.sym_index = loc.index;
2546 atom.size = 2;
2547 try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
2548
2549 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2550}
2551
25521778/// Creates a function body for the `__wasm_call_ctors` symbol.
25531779/// Loops over all constructors found in `init_funcs` and calls them
25541780/// respectively based on their priority which was sorted by `setupInitFunctions`.
......@@ -2609,8 +1835,7 @@ fn createSyntheticFunction(
26091835 function_body: *std.ArrayList(u8),
26101836) !void {
26111837 const gpa = wasm.base.comp.gpa;
2612 const loc = wasm.findGlobalSymbol(symbol_name) orelse
2613 try wasm.createSyntheticSymbol(symbol_name, .function);
1838 const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol?
26141839 const symbol = loc.getSymbol(wasm);
26151840 if (symbol.isDead()) {
26161841 return;
......@@ -2620,32 +1845,21 @@ fn createSyntheticFunction(
26201845 const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
26211846 try wasm.functions.putNoClobber(
26221847 gpa,
2623 .{ .file = null, .index = func_index },
1848 .{ .file = .null, .index = func_index },
26241849 .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
26251850 );
26261851 symbol.index = func_index;
26271852
26281853 // create the atom that will be output into the final binary
2629 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
2630 const atom = try wasm.managed_atoms.addOne(gpa);
2631 atom.* = .{
2632 .size = @as(u32, @intCast(function_body.items.len)),
2633 .offset = 0,
2634 .sym_index = loc.index,
2635 .file = null,
2636 .alignment = .@"1",
2637 .prev = null,
2638 .code = function_body.moveToUnmanaged(),
2639 .original_offset = 0,
2640 };
1854 const atom_index = try wasm.createAtom(loc.index, .null);
1855 const atom = wasm.getAtomPtr(atom_index);
1856 atom.size = @intCast(function_body.items.len);
1857 atom.code = function_body.moveToUnmanaged();
26411858 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
2642 try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
26431859}
26441860
26451861/// Unlike `createSyntheticFunction` this function is to be called by
2646/// the codegeneration backend. This will not allocate the created Atom yet,
2647/// but will instead be appended to `synthetic_functions` list and will be
2648/// parsed at the end of code generation.
1862/// the codegeneration backend. This will not allocate the created Atom yet.
26491863/// Returns the index of the symbol.
26501864pub fn createFunction(
26511865 wasm: *Wasm,
......@@ -2653,37 +1867,8 @@ pub fn createFunction(
26531867 func_ty: std.wasm.Type,
26541868 function_body: *std.ArrayList(u8),
26551869 relocations: *std.ArrayList(Relocation),
2656) !u32 {
2657 const gpa = wasm.base.comp.gpa;
2658 const loc = try wasm.createSyntheticSymbol(symbol_name, .function);
2659
2660 const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
2661 const atom = try wasm.managed_atoms.addOne(gpa);
2662 atom.* = .{
2663 .size = @intCast(function_body.items.len),
2664 .offset = 0,
2665 .sym_index = loc.index,
2666 .file = null,
2667 .alignment = .@"1",
2668 .prev = null,
2669 .code = function_body.moveToUnmanaged(),
2670 .relocs = relocations.moveToUnmanaged(),
2671 .original_offset = 0,
2672 };
2673 const symbol = loc.getSymbol(wasm);
2674 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported
2675
2676 const section_index = wasm.code_section_index orelse idx: {
2677 const index = @as(u32, @intCast(wasm.segments.items.len));
2678 try wasm.appendDummySegment();
2679 break :idx index;
2680 };
2681 try wasm.appendAtomAtIndex(section_index, atom_index);
2682 try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
2683 try wasm.atom_types.put(gpa, atom_index, try wasm.putOrGetFuncType(func_ty));
2684 try wasm.synthetic_functions.append(gpa, atom_index);
2685
2686 return loc.index;
1870) !Symbol.Index {
1871 return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
26871872}
26881873
26891874/// If required, sets the function index in the `start` section.
......@@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
27001885
27011886 if (!shared_memory) return;
27021887
1888 // ensure function is marked as we must emit it
1889 wasm.findGlobalSymbol("__wasm_init_tls").?.getSymbol(wasm).mark();
1890
27031891 var function_body = std.ArrayList(u8).init(gpa);
27041892 defer function_body.deinit();
27051893 const writer = function_body.writer();
......@@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
27481936 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
27491937 try writer.writeByte(std.wasm.opcode(.call));
27501938 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
1939 loc.getSymbol(wasm).mark();
27511940 }
27521941
27531942 try writer.writeByte(std.wasm.opcode(.end));
......@@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
27621951fn setupImports(wasm: *Wasm) !void {
27631952 const gpa = wasm.base.comp.gpa;
27641953 log.debug("Merging imports", .{});
2765 var discarded_it = wasm.discarded.keyIterator();
2766 while (discarded_it.next()) |discarded| {
2767 if (discarded.file == null) {
2768 // remove an import if it was resolved
2769 if (wasm.imports.remove(discarded.*)) {
2770 log.debug("Removed symbol '{s}' as an import", .{
2771 discarded.getName(wasm),
2772 });
2773 }
2774 }
2775 }
2776
27771954 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2778 const file_index = symbol_loc.file orelse {
2779 // imports generated by Zig code are already in the `import` section
1955 const obj_file = wasm.file(symbol_loc.file) orelse {
1956 // Synthetic symbols will already exist in the `import` section
27801957 continue;
27811958 };
27821959
......@@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void {
27891966 }
27901967
27911968 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
2792 const object = wasm.objects.items[file_index];
2793 const import = object.findImport(symbol.tag.externalType(), symbol.index);
1969 const import = obj_file.import(symbol_loc.index);
27941970
27951971 // We copy the import to a new import to ensure the names contain references
27961972 // to the internal string table, rather than of the object file.
27971973 const new_imp: types.Import = .{
2798 .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)),
2799 .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)),
1974 .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)),
1975 .name = try wasm.string_table.put(gpa, obj_file.string(import.name)),
28001976 .kind = import.kind,
28011977 };
28021978 // TODO: De-duplicate imports when they contain the same names and type
......@@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void {
28472023 defer removed_duplicates.deinit();
28482024
28492025 for (wasm.resolved_symbols.keys()) |sym_loc| {
2850 if (sym_loc.file == null) {
2851 // Zig code-generated symbols are already within the sections and do not
2852 // require to be merged
2026 const obj_file = wasm.file(sym_loc.file) orelse {
2027 // Synthetic symbols already live in the corresponding sections.
28532028 continue;
2854 }
2855
2856 const object = &wasm.objects.items[sym_loc.file.?];
2857 const symbol = &object.symtable[sym_loc.index];
2029 };
28582030
2859 if (symbol.isDead() or
2860 symbol.isUndefined() or
2861 (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table))
2862 {
2031 const symbol = obj_file.symbol(sym_loc.index);
2032 if (symbol.isDead() or symbol.isUndefined()) {
28632033 // Skip undefined symbols as they go in the `import` section
2864 // Also skip symbols that do not need to have a section merged.
28652034 continue;
28662035 }
28672036
2868 const offset = object.importedCountByKind(symbol.tag.externalType());
2869 const index = symbol.index - offset;
28702037 switch (symbol.tag) {
28712038 .function => {
28722039 const gop = try wasm.functions.getOrPut(
......@@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void {
28772044 // We found an alias to the same function, discard this symbol in favor of
28782045 // the original symbol and point the discard function to it. This ensures
28792046 // we only emit a single function, instead of duplicates.
2880 symbol.unmark();
2881 try wasm.discarded.putNoClobber(
2882 gpa,
2883 sym_loc,
2884 .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index },
2885 );
2886 try removed_duplicates.append(sym_loc);
2887 continue;
2047 // we favor keeping the global over a local.
2048 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
2049 const original_sym = original_loc.getSymbol(wasm);
2050 if (original_sym.isLocal() and symbol.isGlobal()) {
2051 original_sym.unmark();
2052 try wasm.discarded.put(gpa, original_loc, sym_loc);
2053 try removed_duplicates.append(original_loc);
2054 } else {
2055 symbol.unmark();
2056 try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc);
2057 try removed_duplicates.append(sym_loc);
2058 continue;
2059 }
28882060 }
2889 gop.value_ptr.* = .{ .func = object.functions[index], .sym_index = sym_loc.index };
2061 gop.value_ptr.* = .{ .func = obj_file.function(sym_loc.index), .sym_index = sym_loc.index };
28902062 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
28912063 },
28922064 .global => {
2893 const original_global = object.globals[index];
2065 const index = symbol.index - obj_file.importedFunctions();
2066 const original_global = obj_file.globals()[index];
28942067 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
28952068 try wasm.wasm_globals.append(gpa, original_global);
28962069 },
28972070 .table => {
2898 const original_table = object.tables[index];
2071 const index = symbol.index - obj_file.importedFunctions();
2072 // assert it's a regular relocatable object file as `ZigObject` will never
2073 // contain a table.
2074 const original_table = obj_file.object.tables[index];
28992075 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
29002076 try wasm.tables.append(gpa, original_table);
29012077 },
2902 else => unreachable,
2078 .dead, .undefined => unreachable,
2079 else => {},
29032080 }
29042081 }
29052082
29062083 // For any removed duplicates, remove them from the resolved symbols list
29072084 for (removed_duplicates.items) |sym_loc| {
29082085 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2086 gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)});
29092087 }
29102088
29112089 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
......@@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void {
29262104 defer dirty.deinit();
29272105
29282106 for (wasm.resolved_symbols.keys()) |sym_loc| {
2929 if (sym_loc.file == null) {
2107 const obj_file = wasm.file(sym_loc.file) orelse {
29302108 // zig code-generated symbols are already present in final type section
29312109 continue;
2932 }
2933 const object = wasm.objects.items[sym_loc.file.?];
2934 const symbol = object.symtable[sym_loc.index];
2110 };
2111
2112 const symbol = obj_file.symbol(sym_loc.index);
29352113 if (symbol.tag != .function or symbol.isDead()) {
29362114 // Only functions have types. Only retrieve the type of referenced functions.
29372115 continue;
......@@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void {
29402118 if (symbol.isUndefined()) {
29412119 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
29422120 const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;
2943 const original_type = object.func_types[import.kind.function];
2121 const original_type = obj_file.funcTypes()[import.kind.function];
29442122 import.kind.function = try wasm.putOrGetFuncType(original_type);
29452123 } else if (!dirty.contains(symbol.index)) {
29462124 log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});
29472125 const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
2948 func.type_index = try wasm.putOrGetFuncType(object.func_types[func.type_index]);
2126 func.type_index = try wasm.putOrGetFuncType(obj_file.funcTypes()[func.type_index]);
29492127 dirty.putAssumeCapacityNoClobber(symbol.index, {});
29502128 }
29512129 }
29522130 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
29532131}
29542132
2955fn setupExports(wasm: *Wasm) !void {
2956 const comp = wasm.base.comp;
2957 const gpa = comp.gpa;
2958 if (comp.config.output_mode == .Obj) return;
2959 log.debug("Building exports from symbols", .{});
2960
2133fn checkExportNames(wasm: *Wasm) !void {
29612134 const force_exp_names = wasm.export_symbol_names;
29622135 if (force_exp_names.len > 0) {
29632136 var failed_exports = false;
29642137
29652138 for (force_exp_names) |exp_name| {
29662139 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2967 log.err("could not export '{s}', symbol not found", .{exp_name});
2140 try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
29682141 failed_exports = true;
29692142 continue;
29702143 };
......@@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void {
29742147 }
29752148
29762149 if (failed_exports) {
2977 return error.MissingSymbol;
2150 return error.FlushFailure;
29782151 }
29792152 }
2153}
2154
2155fn setupExports(wasm: *Wasm) !void {
2156 const comp = wasm.base.comp;
2157 const gpa = comp.gpa;
2158 if (comp.config.output_mode == .Obj) return;
2159 log.debug("Building exports from symbols", .{});
29802160
29812161 for (wasm.resolved_symbols.keys()) |sym_loc| {
29822162 const symbol = sym_loc.getSymbol(wasm);
29832163 if (!symbol.isExported(comp.config.rdynamic)) continue;
29842164
29852165 const sym_name = sym_loc.getName(wasm);
2986 const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: {
2987 if (sym_loc.file == null) break :blk symbol.name;
2988 break :blk try wasm.string_table.put(gpa, sym_name);
2989 };
2166 const export_name = if (sym_loc.file == .null)
2167 symbol.name
2168 else
2169 try wasm.string_table.put(gpa, sym_name);
29902170 const exp: types.Export = if (symbol.tag == .data) exp: {
29912171 const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
29922172 try wasm.wasm_globals.append(gpa, .{
......@@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void {
30202200 const entry_name = wasm.entry_name orelse return;
30212201
30222202 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
3023 log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
3024 return error.MissingSymbol;
2203 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
2204 return error.FlushFailure;
30252205 };
30262206
30272207 const symbol = symbol_loc.getSymbol(wasm);
30282208 if (symbol.tag != .function) {
3029 log.err("Entry symbol '{s}' is not a function", .{entry_name});
3030 return error.InvalidEntryKind;
2209 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
2210 return error.FlushFailure;
30312211 }
30322212
30332213 // Ensure the symbol is exported so host environment can access it
......@@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void {
30552235
30562236 const is_obj = comp.config.output_mode == .Obj;
30572237
2238 const stack_ptr = if (wasm.findGlobalSymbol("__stack_pointer")) |loc| index: {
2239 const sym = loc.getSymbol(wasm);
2240 break :index sym.index - wasm.imported_globals_count;
2241 } else null;
2242
30582243 if (place_stack_first and !is_obj) {
30592244 memory_ptr = stack_alignment.forward(memory_ptr);
30602245 memory_ptr += wasm.base.stack_size;
30612246 // We always put the stack pointer global at index 0
3062 wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2247 if (stack_ptr) |index| {
2248 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2249 }
30632250 }
30642251
30652252 var offset: u32 = @as(u32, @intCast(memory_ptr));
......@@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void {
30982285 memory_ptr = mem.alignForward(u64, memory_ptr, 4);
30992286 const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
31002287 const sym = loc.getSymbol(wasm);
2288 sym.mark();
31012289 sym.virtual_address = @as(u32, @intCast(memory_ptr));
31022290 memory_ptr += 4;
31032291 }
......@@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void {
31052293 if (!place_stack_first and !is_obj) {
31062294 memory_ptr = stack_alignment.forward(memory_ptr);
31072295 memory_ptr += wasm.base.stack_size;
3108 wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2296 if (stack_ptr) |index| {
2297 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2298 }
31092299 }
31102300
31112301 // One of the linked object files has a reference to the __heap_base symbol.
......@@ -3121,16 +2311,13 @@ fn setupMemory(wasm: *Wasm) !void {
31212311
31222312 if (wasm.initial_memory) |initial_memory| {
31232313 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
3124 log.err("Initial memory must be {d}-byte aligned", .{page_size});
3125 return error.MissAlignment;
2314 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
31262315 }
31272316 if (memory_ptr > initial_memory) {
3128 log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
3129 return error.MemoryTooSmall;
2317 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
31302318 }
31312319 if (initial_memory > max_memory_allowed) {
3132 log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
3133 return error.MemoryTooBig;
2320 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
31342321 }
31352322 memory_ptr = initial_memory;
31362323 }
......@@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void {
31472334
31482335 if (wasm.max_memory) |max_memory| {
31492336 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
3150 log.err("Maximum memory must be {d}-byte aligned", .{page_size});
3151 return error.MissAlignment;
2337 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
31522338 }
31532339 if (memory_ptr > max_memory) {
3154 log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
3155 return error.MemoryTooSmall;
2340 try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
31562341 }
31572342 if (max_memory > max_memory_allowed) {
3158 log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
3159 return error.MemoryTooBig;
2343 try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
31602344 }
31612345 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
31622346 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
......@@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void {
31702354/// From a given object's index and the index of the segment, returns the corresponding
31712355/// index of the segment within the final data section. When the segment does not yet
31722356/// exist, a new one will be initialized and appended. The new index will be returned in that case.
3173pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 {
2357pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 {
31742358 const comp = wasm.base.comp;
31752359 const gpa = comp.gpa;
3176 const object: Object = wasm.objects.items[object_index];
3177 const symbol = object.symtable[symbol_index];
2360 const obj_file = wasm.file(file_index).?;
2361 const symbol = obj_file.symbols()[@intFromEnum(symbol_index)];
31782362 const index: u32 = @intCast(wasm.segments.items.len);
31792363 const shared_memory = comp.config.shared_memory;
31802364
31812365 switch (symbol.tag) {
31822366 .data => {
3183 const segment_info = object.segment_info[symbol.index];
2367 const segment_info = obj_file.segmentInfo()[symbol.index];
31842368 const merge_segment = comp.config.output_mode != .Obj;
31852369 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
31862370 if (!result.found_existing) {
......@@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
32092393 break :blk index;
32102394 },
32112395 .section => {
3212 const section_name = object.string_table.get(symbol.name);
2396 const section_name = obj_file.symbolName(symbol_index);
32132397 if (mem.eql(u8, section_name, ".debug_info")) {
32142398 return wasm.debug_info_index orelse blk: {
32152399 wasm.debug_info_index = index;
......@@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
32572441 wasm.debug_str_index = index;
32582442 try wasm.appendDummySegment();
32592443 break :blk index;
3260 };
3261 } else {
3262 log.warn("found unknown section '{s}'", .{section_name});
3263 return error.UnexpectedValue;
3264 }
3265 },
3266 else => unreachable,
3267 }
3268}
3269
3270/// Appends a new segment with default field values
3271fn appendDummySegment(wasm: *Wasm) !void {
3272 const gpa = wasm.base.comp.gpa;
3273 try wasm.segments.append(gpa, .{
3274 .alignment = .@"1",
3275 .size = 0,
3276 .offset = 0,
3277 .flags = 0,
3278 });
3279}
3280
3281/// Returns the symbol index of the error name table.
3282///
3283/// When the symbol does not yet exist, it will create a new one instead.
3284pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
3285 if (wasm.error_table_symbol) |symbol| {
3286 return symbol;
3287 }
3288
3289 // no error was referenced yet, so create a new symbol and atom for it
3290 // and then return said symbol's index. The final table will be populated
3291 // during `flush` when we know all possible error names.
3292
3293 const gpa = wasm.base.comp.gpa;
3294 const atom_index = try wasm.createAtom();
3295 const atom = wasm.getAtomPtr(atom_index);
3296 const slice_ty = Type.slice_const_u8_sentinel_0;
3297 const mod = wasm.base.comp.module.?;
3298 atom.alignment = slice_ty.abiAlignment(mod);
3299 const sym_index = atom.sym_index;
3300
3301 const sym_name = try wasm.string_table.put(gpa, "__zig_err_name_table");
3302 const symbol = &wasm.symbols.items[sym_index];
3303 symbol.* = .{
3304 .name = sym_name,
3305 .tag = .data,
3306 .flags = 0,
3307 .index = 0,
3308 .virtual_address = undefined,
3309 };
3310 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3311 symbol.mark();
3312
3313 try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {});
3314
3315 log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
3316 wasm.error_table_symbol = sym_index;
3317 return sym_index;
3318}
3319
3320/// Populates the error name table, when `error_table_symbol` is not null.
3321///
3322/// This creates a table that consists of pointers and length to each error name.
3323/// The table is what is being pointed to within the runtime bodies that are generated.
3324fn populateErrorNameTable(wasm: *Wasm) !void {
3325 const gpa = wasm.base.comp.gpa;
3326 const symbol_index = wasm.error_table_symbol orelse return;
3327 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
3328
3329 // Rather than creating a symbol for each individual error name,
3330 // we create a symbol for the entire region of error names. We then calculate
3331 // the pointers into the list using addends which are appended to the relocation.
3332 const names_atom_index = try wasm.createAtom();
3333 const names_atom = wasm.getAtomPtr(names_atom_index);
3334 names_atom.alignment = .@"1";
3335 const sym_name = try wasm.string_table.put(gpa, "__zig_err_names");
3336 const names_symbol = &wasm.symbols.items[names_atom.sym_index];
3337 names_symbol.* = .{
3338 .name = sym_name,
3339 .tag = .data,
3340 .flags = 0,
3341 .index = 0,
3342 .virtual_address = undefined,
3343 };
3344 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3345 names_symbol.mark();
3346
3347 log.debug("Populating error names", .{});
3348
3349 // Addend for each relocation to the table
3350 var addend: u32 = 0;
3351 const mod = wasm.base.comp.module.?;
3352 for (mod.global_error_set.keys()) |error_name_nts| {
3353 const atom = wasm.getAtomPtr(atom_index);
3354
3355 const error_name = mod.intern_pool.stringToSlice(error_name_nts);
3356 const len = @as(u32, @intCast(error_name.len + 1)); // names are 0-termianted
3357
3358 const slice_ty = Type.slice_const_u8_sentinel_0;
3359 const offset = @as(u32, @intCast(atom.code.items.len));
3360 // first we create the data for the slice of the name
3361 try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
3362 try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
3363 // create relocation to the error name
3364 try atom.relocs.append(gpa, .{
3365 .index = names_atom.sym_index,
3366 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
3367 .offset = offset,
3368 .addend = @as(i32, @intCast(addend)),
3369 });
3370 atom.size += @as(u32, @intCast(slice_ty.abiSize(mod)));
3371 addend += len;
3372
3373 // as we updated the error name table, we now store the actual name within the names atom
3374 try names_atom.code.ensureUnusedCapacity(gpa, len);
3375 names_atom.code.appendSliceAssumeCapacity(error_name);
3376 names_atom.code.appendAssumeCapacity(0);
3377
3378 log.debug("Populated error name: '{s}'", .{error_name});
2444 };
2445 } else {
2446 var err = try wasm.addErrorWithNotes(1);
2447 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
2448 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
2449 return error.UnexpectedValue;
2450 }
2451 },
2452 else => unreachable,
33792453 }
3380 names_atom.size = addend;
3381
3382 const name_loc = names_atom.symbolLoc();
3383 try wasm.resolved_symbols.put(gpa, name_loc, {});
3384 try wasm.symbol_atom.put(gpa, name_loc, names_atom_index);
3385
3386 // link the atoms with the rest of the binary so they can be allocated
3387 // and relocations will be performed.
3388 try wasm.parseAtom(atom_index, .{ .data = .read_only });
3389 try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
33902454}
33912455
3392/// From a given index variable, creates a new debug section.
3393/// This initializes the index, appends a new segment,
3394/// and finally, creates a managed `Atom`.
3395pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
3396 const gpa = wasm.base.comp.gpa;
3397 const new_index: u32 = @intCast(wasm.segments.items.len);
3398 index.* = new_index;
3399 try wasm.appendDummySegment();
3400
3401 const atom_index = try wasm.createAtom();
3402 const atom = wasm.getAtomPtr(atom_index);
3403 wasm.symbols.items[atom.sym_index] = .{
3404 .tag = .section,
3405 .name = try wasm.string_table.put(gpa, name),
3406 .index = 0,
3407 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
3408 };
3409
3410 atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
3411 return atom_index;
3412}
3413
3414fn resetState(wasm: *Wasm) void {
2456/// Appends a new segment with default field values
2457fn appendDummySegment(wasm: *Wasm) !void {
34152458 const gpa = wasm.base.comp.gpa;
3416
3417 for (wasm.segment_info.values()) |segment_info| {
3418 gpa.free(segment_info.name);
3419 }
3420
3421 var atom_it = wasm.decls.valueIterator();
3422 while (atom_it.next()) |atom_index| {
3423 const atom = wasm.getAtomPtr(atom_index.*);
3424 atom.prev = null;
3425
3426 for (atom.locals.items) |local_atom_index| {
3427 const local_atom = wasm.getAtomPtr(local_atom_index);
3428 local_atom.prev = null;
3429 }
3430 }
3431
3432 wasm.functions.clearRetainingCapacity();
3433 wasm.exports.clearRetainingCapacity();
3434 wasm.segments.clearRetainingCapacity();
3435 wasm.segment_info.clearRetainingCapacity();
3436 wasm.data_segments.clearRetainingCapacity();
3437 wasm.atoms.clearRetainingCapacity();
3438 wasm.symbol_atom.clearRetainingCapacity();
3439 wasm.code_section_index = null;
3440 wasm.debug_info_index = null;
3441 wasm.debug_line_index = null;
3442 wasm.debug_loc_index = null;
3443 wasm.debug_str_index = null;
3444 wasm.debug_ranges_index = null;
3445 wasm.debug_abbrev_index = null;
3446 wasm.debug_pubnames_index = null;
3447 wasm.debug_pubtypes_index = null;
2459 try wasm.segments.append(gpa, .{
2460 .alignment = .@"1",
2461 .size = 0,
2462 .offset = 0,
2463 .flags = 0,
2464 });
34482465}
34492466
34502467pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
34512468 const comp = wasm.base.comp;
34522469 const use_lld = build_options.have_llvm and comp.config.use_lld;
3453 const use_llvm = comp.config.use_llvm;
34542470
34552471 if (use_lld) {
34562472 return wasm.linkWithLLD(arena, prog_node);
3457 } else if (use_llvm) {
3458 return wasm.linkWithZld(arena, prog_node);
3459 } else {
3460 return wasm.flushModule(arena, prog_node);
34612473 }
2474 return wasm.flushModule(arena, prog_node);
34622475}
34632476
34642477/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.
3465fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
2478pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
34662479 const tracy = trace(@src());
34672480 defer tracy.end();
34682481
34692482 const comp = wasm.base.comp;
3470 const shared_memory = comp.config.shared_memory;
3471 const import_memory = comp.config.import_memory;
2483 if (wasm.llvm_object) |llvm_object| {
2484 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
2485 const use_lld = build_options.have_llvm and comp.config.use_lld;
2486 if (use_lld) return;
2487 }
2488
2489 var sub_prog_node = prog_node.start("Wasm Flush", 0);
2490 sub_prog_node.activate();
2491 defer sub_prog_node.end();
34722492
34732493 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
34742494 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
3475 const opt_zcu = comp.module;
3476 const use_llvm = comp.config.use_llvm;
3477
3478 // If there is no Zig code to compile, then we should skip flushing the output file because it
3479 // will not be part of the linker line anyway.
3480 const module_obj_path: ?[]const u8 = if (opt_zcu != null) blk: {
3481 assert(use_llvm); // `linkWithZld` should never be called when the Wasm backend is used
3482 try wasm.flushModule(arena, prog_node);
3483
2495 const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: {
34842496 if (fs.path.dirname(full_out_path)) |dirname| {
3485 break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? });
2497 break :blk try fs.path.join(arena, &.{ dirname, path });
34862498 } else {
3487 break :blk wasm.base.zcu_object_sub_path.?;
2499 break :blk path;
34882500 }
34892501 } else null;
34902502
3491 var sub_prog_node = prog_node.start("Wasm Flush", 0);
3492 sub_prog_node.activate();
3493 defer sub_prog_node.end();
3494
3495 const compiler_rt_path: ?[]const u8 = blk: {
3496 if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
3497 if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
3498 break :blk null;
3499 };
3500
3501 const id_symlink_basename = "zld.id";
3502
3503 var man: Cache.Manifest = undefined;
3504 defer if (!wasm.base.disable_lld_caching) man.deinit();
3505 var digest: [Cache.hex_digest_len]u8 = undefined;
3506
3507 const objects = comp.objects;
3508
3509 // NOTE: The following section must be maintained to be equal
3510 // as the section defined in `linkWithLLD`
3511 if (!wasm.base.disable_lld_caching) {
3512 man = comp.cache_parent.obtain();
3513
3514 // We are about to obtain this lock, so here we give other processes a chance first.
3515 wasm.base.releaseLock();
3516
3517 comptime assert(Compilation.link_hash_implementation_version == 12);
3518
3519 for (objects) |obj| {
3520 _ = try man.addFile(obj.path, null);
3521 man.hash.add(obj.must_link);
3522 }
3523 for (comp.c_object_table.keys()) |key| {
3524 _ = try man.addFile(key.status.success.object_path, null);
3525 }
3526 try man.addOptionalFile(module_obj_path);
3527 try man.addOptionalFile(compiler_rt_path);
3528 man.hash.addOptionalBytes(wasm.entry_name);
3529 man.hash.add(wasm.base.stack_size);
3530 man.hash.add(wasm.base.build_id);
3531 man.hash.add(import_memory);
3532 man.hash.add(shared_memory);
3533 man.hash.add(wasm.import_table);
3534 man.hash.add(wasm.export_table);
3535 man.hash.addOptional(wasm.initial_memory);
3536 man.hash.addOptional(wasm.max_memory);
3537 man.hash.addOptional(wasm.global_base);
3538 man.hash.addListOfBytes(wasm.export_symbol_names);
3539 // strip does not need to go into the linker hash because it is part of the hash namespace
3540
3541 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
3542 _ = try man.hit();
3543 digest = man.final();
3544
3545 var prev_digest_buf: [digest.len]u8 = undefined;
3546 const prev_digest: []u8 = Cache.readSmallFile(
3547 directory.handle,
3548 id_symlink_basename,
3549 &prev_digest_buf,
3550 ) catch |err| blk: {
3551 log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
3552 // Handle this as a cache miss.
3553 break :blk prev_digest_buf[0..0];
3554 };
3555 if (mem.eql(u8, prev_digest, &digest)) {
3556 log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
3557 // Hot diggity dog! The output binary is already there.
3558 wasm.base.lock = man.toOwnedLock();
3559 return;
3560 }
3561 log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
3562
3563 // We are about to change the output file to be different, so we invalidate the build hash now.
3564 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
3565 error.FileNotFound => {},
3566 else => |e| return e,
3567 };
3568 }
3569
35702503 // Positional arguments to the linker such as object files and static archives.
35712504 var positionals = std.ArrayList([]const u8).init(arena);
3572 try positionals.ensureUnusedCapacity(objects.len);
2505 try positionals.ensureUnusedCapacity(comp.objects.len);
35732506
35742507 const target = comp.root_mod.resolved_target.result;
35752508 const output_mode = comp.config.output_mode;
......@@ -3578,6 +2511,10 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
35782511 const link_libcpp = comp.config.link_libcpp;
35792512 const wasi_exec_model = comp.config.wasi_exec_model;
35802513
2514 if (wasm.zigObjectPtr()) |zig_object| {
2515 try zig_object.flushModule(wasm);
2516 }
2517
35812518 // When the target os is WASI, we allow linking with WASI-LIBC
35822519 if (target.os.tag == .wasi) {
35832520 const is_exe_or_dyn_lib = output_mode == .Exe or
......@@ -3609,7 +2546,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
36092546 try positionals.append(path);
36102547 }
36112548
3612 for (objects) |object| {
2549 for (comp.objects) |object| {
36132550 try positionals.append(object.path);
36142551 }
36152552
......@@ -3622,171 +2559,35 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
36222559
36232560 try wasm.parseInputFiles(positionals.items);
36242561
3625 for (wasm.objects.items, 0..) |_, object_index| {
3626 try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index)));
3627 }
3628
3629 var emit_features_count: u32 = 0;
3630 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
3631 try wasm.validateFeatures(&enabled_features, &emit_features_count);
3632 try wasm.resolveSymbolsInArchives();
3633 try wasm.resolveLazySymbols();
3634 try wasm.checkUndefinedSymbols();
3635
3636 try wasm.setupInitFunctions();
3637 try wasm.setupStart();
3638
3639 try wasm.markReferences();
3640 try wasm.setupImports();
3641 try wasm.mergeSections();
3642 try wasm.mergeTypes();
3643 try wasm.allocateAtoms();
3644 try wasm.setupMemory();
3645 wasm.allocateVirtualAddresses();
3646 wasm.mapFunctionTable();
3647 try wasm.initializeCallCtorsFunction();
3648 try wasm.setupInitMemoryFunction();
3649 try wasm.setupTLSRelocationsFunction();
3650 try wasm.initializeTLSFunction();
3651 try wasm.setupStartSection();
3652 try wasm.setupExports();
3653 try wasm.writeToFile(enabled_features, emit_features_count, arena);
3654
3655 if (!wasm.base.disable_lld_caching) {
3656 // Update the file with the digest. If it fails we can continue; it only
3657 // means that the next invocation will have an unnecessary cache miss.
3658 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
3659 log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
3660 };
3661 // Again failure here only means an unnecessary cache miss.
3662 man.writeManifest() catch |err| {
3663 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
3664 };
3665 // We hang on to this lock so that the output file path can be used without
3666 // other processes clobbering it.
3667 wasm.base.lock = man.toOwnedLock();
3668 }
3669}
3670
3671pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
3672 const tracy = trace(@src());
3673 defer tracy.end();
3674
3675 const comp = wasm.base.comp;
3676
3677 if (wasm.llvm_object) |llvm_object| {
3678 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
3679 return;
3680 }
3681
3682 var sub_prog_node = prog_node.start("Wasm Flush", 0);
3683 sub_prog_node.activate();
3684 defer sub_prog_node.end();
3685
3686 // ensure the error names table is populated when an error name is referenced
3687 try wasm.populateErrorNameTable();
3688
3689 const objects = comp.objects;
3690
3691 // Positional arguments to the linker such as object files and static archives.
3692 var positionals = std.ArrayList([]const u8).init(arena);
3693 try positionals.ensureUnusedCapacity(objects.len);
3694
3695 for (objects) |object| {
3696 positionals.appendAssumeCapacity(object.path);
3697 }
3698
3699 for (comp.c_object_table.keys()) |c_object| {
3700 try positionals.append(c_object.status.success.object_path);
2562 if (wasm.zig_object_index != .null) {
2563 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
37012564 }
3702
3703 if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path);
3704 if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path);
3705
3706 try wasm.parseInputFiles(positionals.items);
3707
3708 for (wasm.objects.items, 0..) |_, object_index| {
3709 try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index)));
2565 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2566 for (wasm.objects.items) |object_index| {
2567 try wasm.resolveSymbolsInObject(object_index);
37102568 }
2569 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37112570
37122571 var emit_features_count: u32 = 0;
37132572 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
37142573 try wasm.validateFeatures(&enabled_features, &emit_features_count);
37152574 try wasm.resolveSymbolsInArchives();
2575 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37162576 try wasm.resolveLazySymbols();
37172577 try wasm.checkUndefinedSymbols();
2578 try wasm.checkExportNames();
37182579
3719 // When we finish/error we reset the state of the linker
3720 // So we can rebuild the binary file on each incremental update
3721 defer wasm.resetState();
37222580 try wasm.setupInitFunctions();
2581 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37232582 try wasm.setupStart();
2583
37242584 try wasm.markReferences();
3725 try wasm.setupErrorsLen();
37262585 try wasm.setupImports();
3727 if (comp.module) |mod| {
3728 var decl_it = wasm.decls.iterator();
3729 while (decl_it.next()) |entry| {
3730 const decl = mod.declPtr(entry.key_ptr.*);
3731 if (decl.isExtern(mod)) continue;
3732 const atom_index = entry.value_ptr.*;
3733 const atom = wasm.getAtomPtr(atom_index);
3734 if (decl.ty.zigTypeTag(mod) == .Fn) {
3735 try wasm.parseAtom(atom_index, .function);
3736 } else if (decl.getOwnedVariable(mod)) |variable| {
3737 if (variable.is_const) {
3738 try wasm.parseAtom(atom_index, .{ .data = .read_only });
3739 } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
3740 // for safe build modes, we store the atom in the data segment,
3741 // whereas for unsafe build modes we store it in bss.
3742 const decl_namespace = mod.namespacePtr(decl.src_namespace);
3743 const optimize_mode = decl_namespace.file_scope.mod.optimize_mode;
3744 const is_initialized = switch (optimize_mode) {
3745 .Debug, .ReleaseSafe => true,
3746 .ReleaseFast, .ReleaseSmall => false,
3747 };
3748 try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized });
3749 } else {
3750 // when the decl is all zeroes, we store the atom in the bss segment,
3751 // in all other cases it will be in the data segment.
3752 const is_zeroes = for (atom.code.items) |byte| {
3753 if (byte != 0) break false;
3754 } else true;
3755 try wasm.parseAtom(atom_index, .{ .data = if (is_zeroes) .uninitialized else .initialized });
3756 }
3757 } else {
3758 try wasm.parseAtom(atom_index, .{ .data = .read_only });
3759 }
3760
3761 // also parse atoms for a decl's locals
3762 for (atom.locals.items) |local_atom_index| {
3763 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
3764 }
3765 }
3766 // parse anonymous declarations
3767 for (wasm.anon_decls.keys(), wasm.anon_decls.values()) |decl_val, atom_index| {
3768 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
3769 if (ty.zigTypeTag(mod) == .Fn) {
3770 try wasm.parseAtom(atom_index, .function);
3771 } else {
3772 try wasm.parseAtom(atom_index, .{ .data = .read_only });
3773 }
3774 }
3775
3776 // also parse any backend-generated functions
3777 for (wasm.synthetic_functions.items) |atom_index| {
3778 try wasm.parseAtom(atom_index, .function);
3779 }
3780
3781 if (wasm.dwarf) |*dwarf| {
3782 try dwarf.flushModule(comp.module.?);
3783 }
3784 }
3785
37862586 try wasm.mergeSections();
37872587 try wasm.mergeTypes();
37882588 try wasm.allocateAtoms();
37892589 try wasm.setupMemory();
2590 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37902591 wasm.allocateVirtualAddresses();
37912592 wasm.mapFunctionTable();
37922593 try wasm.initializeCallCtorsFunction();
......@@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
37962597 try wasm.setupStartSection();
37972598 try wasm.setupExports();
37982599 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2600 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37992601}
38002602
38012603/// Writes the WebAssembly in-memory module to the file
......@@ -4021,7 +2823,9 @@ fn writeToFile(
40212823 try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
40222824 var symbol_it = wasm.function_table.keyIterator();
40232825 while (symbol_it.next()) |symbol_loc_ptr| {
4024 const sym = symbol_loc_ptr.*.getSymbol(wasm);
2826 const sym = symbol_loc_ptr.getSymbol(wasm);
2827 std.debug.assert(sym.isAlive());
2828 std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
40252829 try leb.writeULEB128(binary_writer, sym.index);
40262830 }
40272831
......@@ -4124,8 +2928,8 @@ fn writeToFile(
41242928 try binary_writer.writeAll(atom.code.items);
41252929
41262930 current_offset += atom.size;
4127 if (atom.prev) |prev| {
4128 atom_index = prev;
2931 if (atom.prev != .null) {
2932 atom_index = atom.prev;
41292933 } else {
41302934 // also pad with zeroes when last atom to ensure
41312935 // segments are aligned.
......@@ -4191,19 +2995,9 @@ fn writeToFile(
41912995 }) catch unreachable;
41922996 try emitBuildIdSection(&binary_bytes, str);
41932997 },
4194 else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),
2998 else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
41952999 }
41963000
4197 // if (wasm.dwarf) |*dwarf| {
4198 // const mod = comp.module.?;
4199 // try dwarf.writeDbgAbbrev();
4200 // // for debug info and ranges, the address is always 0,
4201 // // as locations are always offsets relative to 'code' section.
4202 // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
4203 // try dwarf.writeDbgAranges(0, code_section_size);
4204 // try dwarf.writeDbgLineHeader();
4205 // }
4206
42073001 var debug_bytes = std.ArrayList(u8).init(gpa);
42083002 defer debug_bytes.deinit();
42093003
......@@ -4229,7 +3023,8 @@ fn writeToFile(
42293023 while (true) {
42303024 atom.resolveRelocs(wasm);
42313025 try debug_bytes.appendSlice(atom.code.items);
4232 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3026 if (atom.prev == .null) break;
3027 atom = wasm.getAtomPtr(atom.prev);
42333028 }
42343029 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
42353030 debug_bytes.clearRetainingCapacity();
......@@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
50043799 try leb.writeULEB128(writer, @intFromEnum(symbol.tag));
50053800 try leb.writeULEB128(writer, symbol.flags);
50063801
5007 const sym_name = if (wasm.export_names.get(sym_loc)) |exp_name| wasm.string_table.get(exp_name) else sym_loc.getName(wasm);
3802 const sym_name = sym_loc.getName(wasm);
50083803 switch (symbol.tag) {
50093804 .data => {
50103805 try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len)));
......@@ -5098,7 +3893,7 @@ fn emitCodeRelocations(
50983893 size_offset += getULEB128Size(atom.size);
50993894 for (atom.relocs.items) |relocation| {
51003895 count += 1;
5101 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
3896 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
51023897 const symbol_index = symbol_table.get(sym_loc).?;
51033898 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
51043899 const offset = atom.offset + relocation.offset + size_offset;
......@@ -5109,7 +3904,8 @@ fn emitCodeRelocations(
51093904 }
51103905 log.debug("Emit relocation: {}", .{relocation});
51113906 }
5112 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3907 if (atom.prev == .null) break;
3908 atom = wasm.getAtomPtr(atom.prev);
51133909 }
51143910 if (count == 0) return;
51153911 var buf: [5]u8 = undefined;
......@@ -5145,10 +3941,7 @@ fn emitDataRelocations(
51453941 size_offset += getULEB128Size(atom.size);
51463942 for (atom.relocs.items) |relocation| {
51473943 count += 1;
5148 const sym_loc: SymbolLoc = .{
5149 .file = atom.file,
5150 .index = relocation.index,
5151 };
3944 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
51523945 const symbol_index = symbol_table.get(sym_loc).?;
51533946 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
51543947 const offset = atom.offset + relocation.offset + size_offset;
......@@ -5159,7 +3952,8 @@ fn emitDataRelocations(
51593952 }
51603953 log.debug("Emit relocation: {}", .{relocation});
51613954 }
5162 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3955 if (atom.prev == .null) break;
3956 atom = wasm.getAtomPtr(atom.prev);
51633957 }
51643958 }
51653959 if (count == 0) return;
......@@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
51853979 return false;
51863980}
51873981
5188pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
5189 var index: u32 = 0;
5190 while (index < wasm.func_types.items.len) : (index += 1) {
5191 if (wasm.func_types.items[index].eql(func_type)) return index;
5192 }
5193 return null;
5194}
5195
51963982/// Searches for a matching function signature. When no matching signature is found,
51973983/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
51983984pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
51993985 if (wasm.getTypeIndex(func_type)) |index| {
52003986 return index;
52013987 }
5202 const gpa = wasm.base.comp.gpa;
52033988
52043989 // functype does not exist.
3990 const gpa = wasm.base.comp.gpa;
52053991 const index: u32 = @intCast(wasm.func_types.items.len);
52063992 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
52073993 errdefer gpa.free(params);
......@@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
52184004/// Asserts declaration has an associated `Atom`.
52194005/// Returns the index into the list of types.
52204006pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {
5221 const gpa = wasm.base.comp.gpa;
5222 const atom_index = wasm.decls.get(decl_index).?;
5223 const index = try wasm.putOrGetFuncType(func_type);
5224 try wasm.atom_types.put(gpa, atom_index, index);
5225 return index;
4007 return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, decl_index, func_type);
4008}
4009
4010/// Returns the symbol index of the error name table.
4011///
4012/// When the symbol does not yet exist, it will create a new one instead.
4013pub fn getErrorTableSymbol(wasm_file: *Wasm) !u32 {
4014 const sym_index = try wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file);
4015 return @intFromEnum(sym_index);
4016}
4017
4018/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
4019/// When the index was not found, a new `Atom` will be created, and its index will be returned.
4020/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
4021pub fn getOrCreateAtomForDecl(wasm_file: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index {
4022 return wasm_file.zigObjectPtr().?.getOrCreateAtomForDecl(wasm_file, decl_index);
52264023}
52274024
52284025/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
......@@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void {
52444041 // Debug sections may require to be parsed and marked when it contains
52454042 // relocations to alive symbols.
52464043 if (sym.tag == .section and comp.config.debug_format != .strip) {
5247 const file = sym_loc.file orelse continue; // Incremental debug info is done independently
5248 const object = &wasm.objects.items[file];
5249 const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);
5250 const atom = wasm.getAtom(atom_index);
5251 const atom_sym = atom.symbolLoc().getSymbol(wasm);
5252 atom_sym.mark();
4044 const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently
4045 _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index);
4046 sym.mark();
52534047 }
52544048 }
52554049}
......@@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
52654059 return;
52664060 }
52674061 symbol.mark();
4062 gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)});
52684063 if (symbol.isUndefined()) {
52694064 // undefined symbols do not have an associated `Atom` and therefore also
52704065 // do not contain relocations.
52714066 return;
52724067 }
52734068
5274 const atom_index = if (loc.file) |file_index| idx: {
5275 const object = &wasm.objects.items[file_index];
5276 const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);
5277 break :idx atom_index;
5278 } else wasm.symbol_atom.get(loc) orelse return;
4069 const atom_index = if (wasm.file(loc.file)) |obj_file|
4070 try obj_file.parseSymbolIntoAtom(wasm, loc.index)
4071 else
4072 wasm.symbol_atom.get(loc) orelse return;
52794073
52804074 const atom = wasm.getAtom(atom_index);
52814075 for (atom.relocs.items) |reloc| {
5282 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
4076 const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file };
52834077 try wasm.mark(target_loc.finalLoc(wasm));
52844078 }
52854079}
......@@ -5290,3 +4084,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
52904084 .command => "_start",
52914085 };
52924086}
4087
4088const ErrorWithNotes = struct {
4089 /// Allocated index in comp.link_errors array.
4090 index: usize,
4091
4092 /// Next available note slot.
4093 note_slot: usize = 0,
4094
4095 pub fn addMsg(
4096 err: ErrorWithNotes,
4097 wasm_file: *const Wasm,
4098 comptime format: []const u8,
4099 args: anytype,
4100 ) error{OutOfMemory}!void {
4101 const comp = wasm_file.base.comp;
4102 const gpa = comp.gpa;
4103 const err_msg = &comp.link_errors.items[err.index];
4104 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
4105 }
4106
4107 pub fn addNote(
4108 err: *ErrorWithNotes,
4109 wasm_file: *const Wasm,
4110 comptime format: []const u8,
4111 args: anytype,
4112 ) error{OutOfMemory}!void {
4113 const comp = wasm_file.base.comp;
4114 const gpa = comp.gpa;
4115 const err_msg = &comp.link_errors.items[err.index];
4116 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
4117 err.note_slot += 1;
4118 }
4119};
4120
4121pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4122 const comp = wasm.base.comp;
4123 const gpa = comp.gpa;
4124 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
4125 return wasm.addErrorWithNotesAssumeCapacity(note_count);
4126}
4127
4128pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
4129 const err = try wasm.addErrorWithNotes(0);
4130 try err.addMsg(wasm, fmt, args);
4131}
4132
4133fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4134 const comp = wasm.base.comp;
4135 const gpa = comp.gpa;
4136 const index = comp.link_errors.items.len;
4137 const err = comp.link_errors.addOneAssumeCapacity();
4138 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
4139 return .{ .index = index };
4140}
src/link/Wasm/Archive.zig+21-25
......@@ -1,14 +1,3 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.archive);
7const mem = std.mem;
8
9const Allocator = mem.Allocator;
10const Object = @import("Object.zig");
11
121file: fs.File,
132name: []const u8,
143
......@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
151140 const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));
152141 defer allocator.free(sym_tab);
153142
154 reader.readNoEof(sym_tab) catch {
155 log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len});
156 return error.MalformedArchive;
157 };
143 reader.readNoEof(sym_tab) catch return error.IncompleteSymbolTable;
158144
159145 var i: usize = 0;
160146 var pos: usize = 0;
......@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
178164fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {
179165 const header: ar_hdr = try reader.readStruct(ar_hdr);
180166 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
181 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
182 return error.MalformedArchive;
167 return error.InvalidHeaderDelimiter;
183168 }
184169 if (!mem.eql(u8, header.ar_name[0..2], "//")) {
185 log.err("invalid archive. Long name table missing", .{});
186 return error.MalformedArchive;
170 return error.MissingTableName;
187171 }
188172 const table_size = try header.size();
189173 const long_file_names = try allocator.alloc(u8, table_size);
......@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi
194178
195179/// From a given file offset, starts reading for a file header.
196180/// When found, parses the object file into an `Object` and returns it.
197pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Object {
181pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !Object {
182 const gpa = wasm_file.base.comp.gpa;
198183 try archive.file.seekTo(file_offset);
199184 const reader = archive.file.reader();
200185 const header = try reader.readStruct(ar_hdr);
......@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob
202187 try archive.file.seekTo(0);
203188
204189 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
205 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
206 return error.MalformedArchive;
190 return error.InvalidHeaderDelimiter;
207191 }
208192
209193 const object_name = try archive.parseName(header);
210194 const name = name: {
211195 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
212196 const path = try std.os.realpath(archive.name, &buffer);
213 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
197 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
214198 };
215 defer allocator.free(name);
199 defer gpa.free(name);
216200
217201 const object_file = try std.fs.cwd().openFile(archive.name, .{});
218202 errdefer object_file.close();
219203
220204 const object_file_size = try header.size();
221205 try object_file.seekTo(current_offset);
222 return Object.create(allocator, object_file, name, object_file_size);
206 return Object.create(wasm_file, object_file, name, object_file_size);
223207}
208
209const std = @import("std");
210const assert = std.debug.assert;
211const fs = std.fs;
212const log = std.log.scoped(.archive);
213const mem = std.mem;
214
215const Allocator = mem.Allocator;
216const Object = @import("Object.zig");
217const Wasm = @import("../Wasm.zig");
218
219const Archive = @This();
src/link/Wasm/Atom.zig+32-47
......@@ -1,53 +1,34 @@
1const Atom = @This();
2
3const std = @import("std");
4const types = @import("types.zig");
5const Wasm = @import("../Wasm.zig");
6const Symbol = @import("Symbol.zig");
7
8const leb = std.leb;
9const log = std.log.scoped(.link);
10const mem = std.mem;
11const Allocator = mem.Allocator;
12
1/// Represents the index of the file this atom was generated from.
2/// This is 'null' when the atom was generated by a synthetic linker symbol.
3file: FileIndex,
134/// symbol index of the symbol representing this atom
14sym_index: u32,
5sym_index: Symbol.Index,
156/// Size of the atom, used to calculate section sizes in the final binary
16size: u32,
7size: u32 = 0,
178/// List of relocations belonging to this atom
189relocs: std.ArrayListUnmanaged(types.Relocation) = .{},
1910/// Contains the binary data of an atom, which can be non-relocated
2011code: std.ArrayListUnmanaged(u8) = .{},
2112/// For code this is 1, for data this is set to the highest value of all segments
22alignment: Wasm.Alignment,
13alignment: Wasm.Alignment = .@"1",
2314/// Offset into the section where the atom lives, this already accounts
2415/// for alignment.
25offset: u32,
16offset: u32 = 0,
2617/// The original offset within the object file. This value is substracted from
2718/// relocation offsets to determine where in the `data` to rewrite the value
28original_offset: u32,
29/// Represents the index of the file this atom was generated from.
30/// This is 'null' when the atom was generated by a Decl from Zig code.
31file: ?u16,
19original_offset: u32 = 0,
3220/// Previous atom in relation to this atom.
3321/// is null when this atom is the first in its order
34prev: ?Atom.Index,
22prev: Atom.Index = .null,
3523/// Contains atoms local to a decl, all managed by this `Atom`.
3624/// When the parent atom is being freed, it will also do so for all local atoms.
3725locals: std.ArrayListUnmanaged(Atom.Index) = .{},
3826
39/// Alias to an unsigned 32-bit integer
40pub const Index = u32;
41
42/// Represents a default empty wasm `Atom`
43pub const empty: Atom = .{
44 .alignment = .@"1",
45 .file = null,
46 .offset = 0,
47 .prev = null,
48 .size = 0,
49 .sym_index = 0,
50 .original_offset = 0,
27/// Represents the index of an Atom where `null` is considered
28/// an invalid atom.
29pub const Index = enum(u32) {
30 null = std.math.maxInt(u32),
31 _,
5132};
5233
5334/// Frees all resources owned by this `Atom`.
......@@ -69,7 +50,7 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio
6950 _ = fmt;
7051 _ = options;
7152 try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
72 atom.sym_index,
53 @intFromEnum(atom.sym_index),
7354 atom.alignment,
7455 atom.size,
7556 atom.offset,
......@@ -81,11 +62,6 @@ pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
8162 return .{ .file = atom.file, .index = atom.sym_index };
8263}
8364
84pub fn getSymbolIndex(atom: Atom) ?u32 {
85 if (atom.sym_index == 0) return null;
86 return atom.sym_index;
87}
88
8965/// Resolves the relocations within the atom, writing the new value
9066/// at the calculated offset.
9167pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
......@@ -99,7 +75,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
9975 for (atom.relocs.items) |reloc| {
10076 const value = atom.relocationValue(reloc, wasm_bin);
10177 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
102 (Wasm.SymbolLoc{ .file = atom.file, .index = reloc.index }).getName(wasm_bin),
78 (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(reloc.index) }).getName(wasm_bin),
10379 symbol_name,
10480 reloc.offset,
10581 value,
......@@ -138,7 +114,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
138114/// All values will be represented as a `u64` as all values can fit within it.
139115/// The final value must be casted to the correct size.
140116fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
141 const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = relocation.index }).finalLoc(wasm_bin);
117 const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(relocation.index) }).finalLoc(wasm_bin);
142118 const symbol = target_loc.getSymbol(wasm_bin);
143119 if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
144120 symbol.tag != .section and
......@@ -154,13 +130,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
154130 .R_WASM_TABLE_INDEX_I64,
155131 .R_WASM_TABLE_INDEX_SLEB,
156132 .R_WASM_TABLE_INDEX_SLEB64,
157 => return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0,
133 => return wasm_bin.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
158134 .R_WASM_TYPE_INDEX_LEB => {
159 const file_index = atom.file orelse {
160 return relocation.index;
161 };
162
163 const original_type = wasm_bin.objects.items[file_index].func_types[relocation.index];
135 const obj_file = wasm_bin.file(atom.file) orelse return relocation.index;
136 const original_type = obj_file.funcTypes()[relocation.index];
164137 return wasm_bin.getTypeIndex(original_type).?;
165138 },
166139 .R_WASM_GLOBAL_INDEX_I32,
......@@ -217,3 +190,15 @@ fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
217190 }
218191 return null;
219192}
193
194const leb = std.leb;
195const log = std.log.scoped(.link);
196const mem = std.mem;
197const std = @import("std");
198const types = @import("types.zig");
199
200const Allocator = mem.Allocator;
201const Atom = @This();
202const FileIndex = @import("file.zig").File.Index;
203const Symbol = @import("Symbol.zig");
204const Wasm = @import("../Wasm.zig");
src/link/Wasm/Object.zig+76-66
......@@ -9,19 +9,22 @@ const std = @import("std");
99const Wasm = @import("../Wasm.zig");
1010const Symbol = @import("Symbol.zig");
1111const Alignment = types.Alignment;
12const File = @import("file.zig").File;
1213
1314const Allocator = std.mem.Allocator;
1415const leb = std.leb;
1516const meta = std.meta;
1617
17const log = std.log.scoped(.link);
18const log = std.log.scoped(.object);
1819
20/// Index into the list of relocatable object files within the linker driver.
21index: File.Index = .null,
1922/// Wasm spec version used for this `Object`
2023version: u32 = 0,
2124/// The file descriptor that represents the wasm object file.
2225file: ?std.fs.File = null,
2326/// Name (read path) of the object file.
24name: []const u8,
27path: []const u8,
2528/// Parsed type section
2629func_types: []const std.wasm.Type = &.{},
2730/// A list of all imports for this module
......@@ -64,6 +67,12 @@ relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableDat
6467/// import name, module name and export names. Each string will be deduplicated
6568/// and returns an offset into the table.
6669string_table: Wasm.StringTable = .{},
70/// Amount of functions in the `import` sections.
71imported_functions_count: u32 = 0,
72/// Amount of globals in the `import` section.
73imported_globals_count: u32 = 0,
74/// Amount of tables in the `import` section.
75imported_tables_count: u32 = 0,
6776
6877/// Represents a single item within a section (depending on its `type`)
6978const RelocatableData = struct {
......@@ -118,15 +127,16 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
118127/// This also parses and verifies the object file.
119128/// When a max size is given, will only parse up to the given size,
120129/// else will read until the end of the file.
121pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
130pub fn create(wasm_file: *const Wasm, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
131 const gpa = wasm_file.base.comp.gpa;
122132 var object: Object = .{
123133 .file = file,
124 .name = try gpa.dupe(u8, name),
134 .path = try gpa.dupe(u8, name),
125135 };
126136
127137 var is_object_file: bool = false;
128138 const size = maybe_max_size orelse size: {
129 errdefer gpa.free(object.name);
139 errdefer gpa.free(object.path);
130140 const stat = try file.stat();
131141 break :size @as(usize, @intCast(stat.size));
132142 };
......@@ -142,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
142152 }
143153 var fbs = std.io.fixedBufferStream(file_contents);
144154
145 try object.parse(gpa, fbs.reader(), &is_object_file);
155 try object.parse(gpa, wasm_file, fbs.reader(), &is_object_file);
146156 errdefer object.deinit(gpa);
147157 if (!is_object_file) return error.NotObjectFile;
148158
......@@ -193,68 +203,59 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
193203 }
194204 object.relocatable_data.deinit(gpa);
195205 object.string_table.deinit(gpa);
196 gpa.free(object.name);
206 gpa.free(object.path);
197207 object.* = undefined;
198208}
199209
200210/// Finds the import within the list of imports from a given kind and index of that kind.
201211/// Asserts the import exists
202pub fn findImport(object: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import {
212pub fn findImport(object: *const Object, sym: Symbol) types.Import {
203213 var i: u32 = 0;
204214 return for (object.imports) |import| {
205 if (std.meta.activeTag(import.kind) == import_kind) {
206 if (i == index) return import;
215 if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {
216 if (i == sym.index) return import;
207217 i += 1;
208218 }
209219 } else unreachable; // Only existing imports are allowed to be found
210220}
211221
212/// Counts the entries of imported `kind` and returns the result
213pub fn importedCountByKind(object: *const Object, kind: std.wasm.ExternalKind) u32 {
214 var i: u32 = 0;
215 return for (object.imports) |imp| {
216 if (@as(std.wasm.ExternalKind, imp.kind) == kind) i += 1;
217 } else i;
218}
219
220/// From a given `RelocatableDate`, find the corresponding debug section name
221pub fn getDebugName(object: *const Object, relocatable_data: RelocatableData) []const u8 {
222 return object.string_table.get(relocatable_data.index);
223}
224
225222/// Checks if the object file is an MVP version.
226223/// When that's the case, we check if there's an import table definiton with its name
227224/// set to '__indirect_function_table". When that's also the case,
228225/// we initialize a new table symbol that corresponds to that import and return that symbol.
229226///
230227/// When the object file is *NOT* MVP, we return `null`.
231fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
228fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
232229 var table_count: usize = 0;
233230 for (object.symtable) |sym| {
234231 if (sym.tag == .table) table_count += 1;
235232 }
236233
237 const import_table_count = object.importedCountByKind(.table);
238
239234 // For each import table, we also have a symbol so this is not a legacy object file
240 if (import_table_count == table_count) return null;
235 if (object.imported_tables_count == table_count) return null;
241236
242237 if (table_count != 0) {
243 log.err("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
244 import_table_count,
238 var err = try wasm_file.addErrorWithNotes(1);
239 try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240 object.imported_tables_count,
245241 table_count,
246242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
247244 return error.MissingTableSymbols;
248245 }
249246
250247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
251248 if (object.tables.len > 0) {
252 log.err("Unexpected table definition without representing table symbols.", .{});
249 var err = try wasm_file.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
253252 return error.UnexpectedTable;
254253 }
255254
256 if (import_table_count != 1) {
257 log.err("Found more than one table import, but no representing table symbols", .{});
255 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
258259 return error.MissingTableSymbols;
259260 }
260261
......@@ -265,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
265266 } else unreachable;
266267
267268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
268 log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
269 var err = try wasm_file.addErrorWithNotes(1);
270 try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
269272 return error.MissingTableSymbols;
270273 }
271274
......@@ -318,8 +321,8 @@ pub const ParseError = error{
318321 UnknownFeature,
319322};
320323
321fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
322 var parser = Parser(@TypeOf(reader)).init(object, reader);
324fn parse(object: *Object, gpa: Allocator, wasm_file: *const Wasm, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
325 var parser = Parser(@TypeOf(reader)).init(object, wasm_file, reader);
323326 return parser.parseObject(gpa, is_object_file);
324327}
325328
......@@ -331,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {
331334 reader: std.io.CountingReader(ReaderType),
332335 /// Object file we're building
333336 object: *Object,
337 /// Read-only reference to the WebAssembly linker
338 wasm_file: *const Wasm,
334339
335 fn init(object: *Object, reader: ReaderType) ObjectParser {
336 return .{ .object = object, .reader = std.io.countingReader(reader) };
340 fn init(object: *Object, wasm_file: *const Wasm, reader: ReaderType) ObjectParser {
341 return .{ .object = object, .wasm_file = wasm_file, .reader = std.io.countingReader(reader) };
337342 }
338343
339344 /// Verifies that the first 4 bytes contains \0Asm
......@@ -427,16 +432,25 @@ fn Parser(comptime ReaderType: type) type {
427432
428433 const kind = try readEnum(std.wasm.ExternalKind, reader);
429434 const kind_value: std.wasm.Import.Kind = switch (kind) {
430 .function => .{ .function = try readLeb(u32, reader) },
435 .function => val: {
436 parser.object.imported_functions_count += 1;
437 break :val .{ .function = try readLeb(u32, reader) };
438 },
431439 .memory => .{ .memory = try readLimits(reader) },
432 .global => .{ .global = .{
433 .valtype = try readEnum(std.wasm.Valtype, reader),
434 .mutable = (try reader.readByte()) == 0x01,
435 } },
436 .table => .{ .table = .{
437 .reftype = try readEnum(std.wasm.RefType, reader),
438 .limits = try readLimits(reader),
439 } },
440 .global => val: {
441 parser.object.imported_globals_count += 1;
442 break :val .{ .global = .{
443 .valtype = try readEnum(std.wasm.Valtype, reader),
444 .mutable = (try reader.readByte()) == 0x01,
445 } };
446 },
447 .table => val: {
448 parser.object.imported_tables_count += 1;
449 break :val .{ .table = .{
450 .reftype = try readEnum(std.wasm.RefType, reader),
451 .limits = try readLimits(reader),
452 } };
453 },
440454 };
441455
442456 import.* = .{
......@@ -513,7 +527,7 @@ fn Parser(comptime ReaderType: type) type {
513527 const start = reader.context.bytes_left;
514528 var index: u32 = 0;
515529 const count = try readLeb(u32, reader);
516 const imported_function_count = parser.object.importedCountByKind(.function);
530 const imported_function_count = parser.object.imported_functions_count;
517531 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
518532 defer relocatable_data.deinit();
519533 while (index < count) : (index += 1) {
......@@ -582,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {
582596 try reader.readNoEof(name);
583597
584598 const tag = types.known_features.get(name) orelse {
585 log.err("Object file contains unknown feature: {s}", .{name});
599 var err = try parser.wasm_file.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
586602 return error.UnknownFeature;
587603 };
588604 feature.* = .{
......@@ -751,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {
751767
752768 // we found all symbols, check for indirect function table
753769 // in case of an MVP object file
754 if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| {
770 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm_file)) |symbol| {
755771 try symbols.append(symbol);
756772 log.debug("Found legacy indirect function table. Created symbol", .{});
757773 }
......@@ -830,7 +846,7 @@ fn Parser(comptime ReaderType: type) type {
830846 defer gpa.free(name);
831847 try reader.readNoEof(name);
832848 break :name try parser.object.string_table.put(gpa, name);
833 } else parser.object.findImport(symbol.tag.externalType(), symbol.index).name;
849 } else parser.object.findImport(symbol).name;
834850 },
835851 }
836852 return symbol;
......@@ -904,12 +920,12 @@ fn assertEnd(reader: anytype) !void {
904920}
905921
906922/// Parses an object file into atoms, for code and data sections
907pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index {
923pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: Symbol.Index) !Atom.Index {
908924 const comp = wasm.base.comp;
909925 const gpa = comp.gpa;
910 const symbol = &object.symtable[symbol_index];
926 const symbol = &object.symtable[@intFromEnum(symbol_index)];
911927 const relocatable_data: RelocatableData = switch (symbol.tag) {
912 .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)],
928 .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
913929 .data => object.relocatable_data.get(.data).?[symbol.index],
914930 .section => blk: {
915931 const data = object.relocatable_data.get(.custom).?;
......@@ -922,19 +938,16 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
922938 },
923939 else => unreachable,
924940 };
925 const final_index = try wasm.getMatchingSegment(object_index, symbol_index);
926 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
927 const atom = try wasm.managed_atoms.addOne(gpa);
928 atom.* = Atom.empty;
941 const final_index = try wasm.getMatchingSegment(object.index, symbol_index);
942 const atom_index = try wasm.createAtom(symbol_index, object.index);
929943 try wasm.appendAtomAtIndex(final_index, atom_index);
930944
931 atom.sym_index = symbol_index;
932 atom.file = object_index;
945 const atom = wasm.getAtomPtr(atom_index);
933946 atom.size = relocatable_data.size;
934947 atom.alignment = relocatable_data.getAlignment(object);
935948 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
936949 atom.original_offset = relocatable_data.offset;
937 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);
950
938951 const segment: *Wasm.Segment = &wasm.segments.items[final_index];
939952 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
940953 segment.alignment = segment.alignment.max(atom.alignment);
......@@ -952,8 +965,8 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
952965 .R_WASM_TABLE_INDEX_SLEB64,
953966 => {
954967 try wasm.function_table.put(gpa, .{
955 .file = object_index,
956 .index = reloc.index,
968 .file = object.index,
969 .index = @enumFromInt(reloc.index),
957970 }, 0);
958971 },
959972 .R_WASM_GLOBAL_INDEX_I32,
......@@ -961,10 +974,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
961974 => {
962975 const sym = object.symtable[reloc.index];
963976 if (sym.tag != .global) {
964 try wasm.got_symbols.append(
965 gpa,
966 .{ .file = object_index, .index = reloc.index },
967 );
977 try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) });
968978 }
969979 },
970980 else => {},
src/link/Wasm/Symbol.zig+11-5
......@@ -1,12 +1,8 @@
1//! Represents a wasm symbol. Containing all of its properties,
1//! Represents a WebAssembly symbol. Containing all of its properties,
22//! as well as providing helper methods to determine its functionality
33//! and how it will/must be linked.
44//! The name of the symbol can be found by providing the offset, found
55//! on the `name` field, to a string table in the wasm binary or object file.
6const Symbol = @This();
7
8const std = @import("std");
9const types = @import("types.zig");
106
117/// Bitfield containings flags for a symbol
128/// Can contain any of the flags defined in `Flag`
......@@ -24,6 +20,12 @@ tag: Tag,
2420/// This differs from the offset of an `Atom` which is relative to the start of a segment.
2521virtual_address: u32,
2622
23/// Represents a symbol index where `null` represents an invalid index.
24pub const Index = enum(u32) {
25 null,
26 _,
27};
28
2729pub const Tag = enum {
2830 function,
2931 data,
......@@ -202,3 +204,7 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO
202204 .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
203205 );
204206}
207
208const std = @import("std");
209const types = @import("types.zig");
210const Symbol = @This();
src/link/Wasm/ZigObject.zig created+1248
......@@ -0,0 +1,1248 @@
1//! ZigObject encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4//! Think about this as fake in-memory Object file for the Zig module.
5
6path: []const u8,
7/// Index within the list of relocatable objects of the linker driver.
8index: File.Index,
9/// Map of all `Decl` that are currently alive.
10/// Each index maps to the corresponding `DeclInfo`.
11decls_map: std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclInfo) = .{},
12/// List of function type signatures for this Zig module.
13func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
14/// List of `std.wasm.Func`. Each entry contains the function signature,
15/// rather than the actual body.
16functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
17/// List of indexes pointing to an entry within the `functions` list which has been removed.
18functions_free_list: std.ArrayListUnmanaged(u32) = .{},
19/// Map of symbol locations, represented by its `types.Import`.
20imports: std.AutoHashMapUnmanaged(Symbol.Index, types.Import) = .{},
21/// List of WebAssembly globals.
22globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
23/// Mapping between an `Atom` and its type index representing the Wasm
24/// type of the function signature.
25atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
26/// List of all symbols generated by Zig code.
27symbols: std.ArrayListUnmanaged(Symbol) = .{},
28/// Map from symbol name offset to their index into the `symbols` list.
29global_syms: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
30/// List of symbol indexes which are free to be used.
31symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
32/// Extra metadata about the linking section, such as alignment of segments and their name.
33segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
34/// List of indexes which contain a free slot in the `segment_info` list.
35segment_free_list: std.ArrayListUnmanaged(u32) = .{},
36/// File encapsulated string table, used to deduplicate strings within the generated file.
37string_table: StringTable = .{},
38/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
39anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
40/// List of atom indexes of functions that are generated by the backend.
41synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{},
42/// Represents the symbol index of the error name table
43/// When this is `null`, no code references an error using runtime `@errorName`.
44/// During initializion, a symbol with corresponding atom will be created that is
45/// used to perform relocations to the pointer of this table.
46/// The actual table is populated during `flush`.
47error_table_symbol: Symbol.Index = .null,
48/// Atom index of the table of symbol names. This is stored so we can clean up the atom.
49error_names_atom: Atom.Index = .null,
50/// Amount of functions in the `import` sections.
51imported_functions_count: u32 = 0,
52/// Amount of globals in the `import` section.
53imported_globals_count: u32 = 0,
54/// Symbol index representing the stack pointer. This will be set upon initializion
55/// of a new `ZigObject`. Codegen will make calls into this to create relocations for
56/// this symbol each time the stack pointer is moved.
57stack_pointer_sym: Symbol.Index,
58/// Debug information for the Zig module.
59dwarf: ?Dwarf = null,
60// Debug section atoms. These are only set when the current compilation
61// unit contains Zig code. The lifetime of these atoms are extended
62// until the end of the compiler's lifetime. Meaning they're not freed
63// during `flush()` in incremental-mode.
64debug_info_atom: ?Atom.Index = null,
65debug_line_atom: ?Atom.Index = null,
66debug_loc_atom: ?Atom.Index = null,
67debug_ranges_atom: ?Atom.Index = null,
68debug_abbrev_atom: ?Atom.Index = null,
69debug_str_atom: ?Atom.Index = null,
70debug_pubnames_atom: ?Atom.Index = null,
71debug_pubtypes_atom: ?Atom.Index = null,
72/// The index of the segment representing the custom '.debug_info' section.
73debug_info_index: ?u32 = null,
74/// The index of the segment representing the custom '.debug_line' section.
75debug_line_index: ?u32 = null,
76/// The index of the segment representing the custom '.debug_loc' section.
77debug_loc_index: ?u32 = null,
78/// The index of the segment representing the custom '.debug_ranges' section.
79debug_ranges_index: ?u32 = null,
80/// The index of the segment representing the custom '.debug_pubnames' section.
81debug_pubnames_index: ?u32 = null,
82/// The index of the segment representing the custom '.debug_pubtypes' section.
83debug_pubtypes_index: ?u32 = null,
84/// The index of the segment representing the custom '.debug_pubtypes' section.
85debug_str_index: ?u32 = null,
86/// The index of the segment representing the custom '.debug_pubtypes' section.
87debug_abbrev_index: ?u32 = null,
88
89const DeclInfo = struct {
90 atom: Atom.Index = .null,
91 exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
92
93 fn @"export"(di: DeclInfo, zig_object: *const ZigObject, name: []const u8) ?Symbol.Index {
94 for (di.exports.items) |sym_index| {
95 const sym_name_index = zig_object.symbol(sym_index).name;
96 const sym_name = zig_object.string_table.getAssumeExists(sym_name_index);
97 if (std.mem.eql(u8, name, sym_name)) {
98 return sym_index;
99 }
100 }
101 return null;
102 }
103
104 fn appendExport(di: *DeclInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void {
105 return di.exports.append(gpa, sym_index);
106 }
107
108 fn deleteExport(di: *DeclInfo, sym_index: Symbol.Index) void {
109 for (di.exports.items, 0..) |idx, index| {
110 if (idx == sym_index) {
111 _ = di.exports.swapRemove(index);
112 return;
113 }
114 }
115 unreachable; // invalid sym_index
116 }
117};
118
119/// Initializes the `ZigObject` with initial symbols.
120pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void {
121 // Initialize an undefined global with the name __stack_pointer. Codegen will use
122 // this to generate relocations when moving the stack pointer. This symbol will be
123 // resolved automatically by the final linking stage.
124 try zig_object.createStackPointer(wasm_file);
125
126 // TODO: Initialize debug information when we reimplement Dwarf support.
127}
128
129fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void {
130 const gpa = wasm_file.base.comp.gpa;
131 const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer");
132 const sym = zig_object.symbol(sym_index);
133 sym.index = zig_object.imported_globals_count;
134 sym.tag = .global;
135 const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
136 try zig_object.imports.putNoClobber(gpa, sym_index, .{
137 .name = sym.name,
138 .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name),
139 .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } },
140 });
141 zig_object.imported_globals_count += 1;
142 zig_object.stack_pointer_sym = sym_index;
143}
144
145fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
146 return &zig_object.symbols.items[@intFromEnum(index)];
147}
148
149/// Frees and invalidates all memory of the incrementally compiled Zig module.
150/// It is illegal behavior to access the `ZigObject` after calling `deinit`.
151pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
152 const gpa = wasm_file.base.comp.gpa;
153 for (zig_object.segment_info.items) |segment_info| {
154 gpa.free(segment_info.name);
155 }
156
157 {
158 var it = zig_object.decls_map.valueIterator();
159 while (it.next()) |decl_info| {
160 const atom = wasm_file.getAtomPtr(decl_info.atom);
161 for (atom.locals.items) |local_index| {
162 const local_atom = wasm_file.getAtomPtr(local_index);
163 local_atom.deinit(gpa);
164 }
165 atom.deinit(gpa);
166 decl_info.exports.deinit(gpa);
167 }
168 }
169 {
170 for (zig_object.anon_decls.values()) |atom_index| {
171 const atom = wasm_file.getAtomPtr(atom_index);
172 for (atom.locals.items) |local_index| {
173 const local_atom = wasm_file.getAtomPtr(local_index);
174 local_atom.deinit(gpa);
175 }
176 atom.deinit(gpa);
177 }
178 }
179 if (zig_object.findGlobalSymbol("__zig_errors_len")) |sym_index| {
180 const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index }).?;
181 wasm_file.getAtomPtr(atom_index).deinit(gpa);
182 }
183 if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol })) |atom_index| {
184 const atom = wasm_file.getAtomPtr(atom_index);
185 atom.deinit(gpa);
186 }
187 for (zig_object.synthetic_functions.items) |atom_index| {
188 const atom = wasm_file.getAtomPtr(atom_index);
189 atom.deinit(gpa);
190 }
191 zig_object.synthetic_functions.deinit(gpa);
192 for (zig_object.func_types.items) |*ty| {
193 ty.deinit(gpa);
194 }
195 if (zig_object.error_names_atom != .null) {
196 const atom = wasm_file.getAtomPtr(zig_object.error_names_atom);
197 atom.deinit(gpa);
198 }
199 zig_object.global_syms.deinit(gpa);
200 zig_object.func_types.deinit(gpa);
201 zig_object.atom_types.deinit(gpa);
202 zig_object.functions.deinit(gpa);
203 zig_object.imports.deinit(gpa);
204 zig_object.decls_map.deinit(gpa);
205 zig_object.anon_decls.deinit(gpa);
206 zig_object.symbols.deinit(gpa);
207 zig_object.symbols_free_list.deinit(gpa);
208 zig_object.segment_info.deinit(gpa);
209 zig_object.segment_free_list.deinit(gpa);
210
211 zig_object.string_table.deinit(gpa);
212 if (zig_object.dwarf) |*dwarf| {
213 dwarf.deinit();
214 }
215 gpa.free(zig_object.path);
216 zig_object.* = undefined;
217}
218
219/// Allocates a new symbol and returns its index.
220/// Will re-use slots when a symbol was freed at an earlier stage.
221pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.Index {
222 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
223 const sym: Symbol = .{
224 .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls
225 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
226 .tag = .undefined, // will be set after updateDecl
227 .index = std.math.maxInt(u32), // will be set during atom parsing
228 .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
229 };
230 if (zig_object.symbols_free_list.popOrNull()) |index| {
231 zig_object.symbols.items[@intFromEnum(index)] = sym;
232 return index;
233 }
234 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
235 zig_object.symbols.appendAssumeCapacity(sym);
236 return index;
237}
238
239// Generate code for the Decl, storing it in memory to be later written to
240// the file on flush().
241pub fn updateDecl(
242 zig_object: *ZigObject,
243 wasm_file: *Wasm,
244 mod: *Module,
245 decl_index: InternPool.DeclIndex,
246) !void {
247 const decl = mod.declPtr(decl_index);
248 if (decl.val.getFunction(mod)) |_| {
249 return;
250 } else if (decl.val.getExternFunc(mod)) |_| {
251 return;
252 }
253
254 const gpa = wasm_file.base.comp.gpa;
255 const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
256 const atom = wasm_file.getAtomPtr(atom_index);
257 atom.clear();
258
259 if (decl.isExtern(mod)) {
260 const variable = decl.getOwnedVariable(mod).?;
261 const name = mod.intern_pool.stringToSlice(decl.name);
262 const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
263 return zig_object.addOrUpdateImport(wasm_file, name, atom.sym_index, lib_name, null);
264 }
265 const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
266
267 var code_writer = std.ArrayList(u8).init(gpa);
268 defer code_writer.deinit();
269
270 const res = try codegen.generateSymbol(
271 &wasm_file.base,
272 decl.srcLoc(mod),
273 .{ .ty = decl.ty, .val = val },
274 &code_writer,
275 .none,
276 .{ .parent_atom_index = @intFromEnum(atom.sym_index) },
277 );
278
279 const code = switch (res) {
280 .ok => code_writer.items,
281 .fail => |em| {
282 decl.analysis = .codegen_failure;
283 try mod.failed_decls.put(mod.gpa, decl_index, em);
284 return;
285 },
286 };
287
288 return zig_object.finishUpdateDecl(wasm_file, decl_index, code);
289}
290
291pub fn updateFunc(
292 zig_object: *ZigObject,
293 wasm_file: *Wasm,
294 mod: *Module,
295 func_index: InternPool.Index,
296 air: Air,
297 liveness: Liveness,
298) !void {
299 const gpa = wasm_file.base.comp.gpa;
300 const func = mod.funcInfo(func_index);
301 const decl_index = func.owner_decl;
302 const decl = mod.declPtr(decl_index);
303 const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
304 const atom = wasm_file.getAtomPtr(atom_index);
305 atom.clear();
306
307 var code_writer = std.ArrayList(u8).init(gpa);
308 defer code_writer.deinit();
309 const result = try codegen.generateFunction(
310 &wasm_file.base,
311 decl.srcLoc(mod),
312 func_index,
313 air,
314 liveness,
315 &code_writer,
316 .none,
317 );
318
319 const code = switch (result) {
320 .ok => code_writer.items,
321 .fail => |em| {
322 decl.analysis = .codegen_failure;
323 try mod.failed_decls.put(mod.gpa, decl_index, em);
324 return;
325 },
326 };
327
328 return zig_object.finishUpdateDecl(wasm_file, decl_index, code);
329}
330
331fn finishUpdateDecl(
332 zig_object: *ZigObject,
333 wasm_file: *Wasm,
334 decl_index: InternPool.DeclIndex,
335 code: []const u8,
336) !void {
337 const gpa = wasm_file.base.comp.gpa;
338 const mod = wasm_file.base.comp.module.?;
339 const decl = mod.declPtr(decl_index);
340 const decl_info = zig_object.decls_map.get(decl_index).?;
341 const atom_index = decl_info.atom;
342 const atom = wasm_file.getAtomPtr(atom_index);
343 const sym = zig_object.symbol(atom.sym_index);
344 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
345 sym.name = try zig_object.string_table.insert(gpa, full_name);
346 try atom.code.appendSlice(gpa, code);
347 atom.size = @intCast(code.len);
348
349 switch (decl.ty.zigTypeTag(mod)) {
350 .Fn => {
351 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
352 sym.tag = .function;
353 },
354 else => {
355 const segment_name: []const u8 = if (decl.getOwnedVariable(mod)) |variable| name: {
356 if (variable.is_const) {
357 break :name ".rodata.";
358 } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
359 const decl_namespace = mod.namespacePtr(decl.src_namespace);
360 const optimize_mode = decl_namespace.file_scope.mod.optimize_mode;
361 const is_initialized = switch (optimize_mode) {
362 .Debug, .ReleaseSafe => true,
363 .ReleaseFast, .ReleaseSmall => false,
364 };
365 if (is_initialized) {
366 break :name ".data.";
367 }
368 break :name ".bss.";
369 }
370 // when the decl is all zeroes, we store the atom in the bss segment,
371 // in all other cases it will be in the data segment.
372 for (atom.code.items) |byte| {
373 if (byte != 0) break :name ".data.";
374 }
375 break :name ".bss.";
376 } else ".rodata.";
377 if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and
378 std.mem.startsWith(u8, segment_name, ".bss"))
379 {
380 @memset(atom.code.items, 0);
381 }
382 // Will be freed upon freeing of decl or after cleanup of Wasm binary.
383 const full_segment_name = try std.mem.concat(gpa, u8, &.{
384 segment_name,
385 full_name,
386 });
387 errdefer gpa.free(full_segment_name);
388 sym.tag = .data;
389 sym.index = try zig_object.createDataSegment(gpa, full_segment_name, decl.alignment);
390 },
391 }
392 if (code.len == 0) return;
393 atom.alignment = decl.getAlignment(mod);
394}
395
396/// Creates and initializes a new segment in the 'Data' section.
397/// Reuses free slots in the list of segments and returns the index.
398fn createDataSegment(
399 zig_object: *ZigObject,
400 gpa: std.mem.Allocator,
401 name: []const u8,
402 alignment: InternPool.Alignment,
403) !u32 {
404 const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index|
405 index
406 else index: {
407 const idx: u32 = @intCast(zig_object.segment_info.items.len);
408 _ = try zig_object.segment_info.addOne(gpa);
409 break :index idx;
410 };
411 zig_object.segment_info.items[segment_index] = .{
412 .alignment = alignment,
413 .flags = 0,
414 .name = name,
415 };
416 return segment_index;
417}
418
419/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
420/// When the index was not found, a new `Atom` will be created, and its index will be returned.
421/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
422pub fn getOrCreateAtomForDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index {
423 const gpa = wasm_file.base.comp.gpa;
424 const gop = try zig_object.decls_map.getOrPut(gpa, decl_index);
425 if (!gop.found_existing) {
426 const sym_index = try zig_object.allocateSymbol(gpa);
427 gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) };
428 const mod = wasm_file.base.comp.module.?;
429 const decl = mod.declPtr(decl_index);
430 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
431 const sym = zig_object.symbol(sym_index);
432 sym.name = try zig_object.string_table.insert(gpa, full_name);
433 }
434 return gop.value_ptr.atom;
435}
436
437pub fn lowerAnonDecl(
438 zig_object: *ZigObject,
439 wasm_file: *Wasm,
440 decl_val: InternPool.Index,
441 explicit_alignment: InternPool.Alignment,
442 src_loc: Module.SrcLoc,
443) !codegen.Result {
444 const gpa = wasm_file.base.comp.gpa;
445 const gop = try zig_object.anon_decls.getOrPut(gpa, decl_val);
446 if (!gop.found_existing) {
447 const mod = wasm_file.base.comp.module.?;
448 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
449 const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) };
450 var name_buf: [32]u8 = undefined;
451 const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
452 @intFromEnum(decl_val),
453 }) catch unreachable;
454
455 switch (try zig_object.lowerConst(wasm_file, name, tv, src_loc)) {
456 .ok => |atom_index| zig_object.anon_decls.values()[gop.index] = atom_index,
457 .fail => |em| return .{ .fail = em },
458 }
459 }
460
461 const atom = wasm_file.getAtomPtr(zig_object.anon_decls.values()[gop.index]);
462 atom.alignment = switch (atom.alignment) {
463 .none => explicit_alignment,
464 else => switch (explicit_alignment) {
465 .none => atom.alignment,
466 else => atom.alignment.maxStrict(explicit_alignment),
467 },
468 };
469 return .ok;
470}
471
472/// Lowers a constant typed value to a local symbol and atom.
473/// Returns the symbol index of the local
474/// The given `decl` is the parent decl whom owns the constant.
475pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
476 const gpa = wasm_file.base.comp.gpa;
477 const mod = wasm_file.base.comp.module.?;
478 std.debug.assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions
479 const decl = mod.declPtr(decl_index);
480
481 const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
482 const parent_atom = wasm_file.getAtom(parent_atom_index);
483 const local_index = parent_atom.locals.items.len;
484 const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
485 const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{
486 fqn, local_index,
487 });
488 defer gpa.free(name);
489
490 switch (try zig_object.lowerConst(wasm_file, name, tv, decl.srcLoc(mod))) {
491 .ok => |atom_index| {
492 try wasm_file.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index);
493 return @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
494 },
495 .fail => |em| {
496 decl.analysis = .codegen_failure;
497 try mod.failed_decls.put(mod.gpa, decl_index, em);
498 return error.CodegenFail;
499 },
500 }
501}
502
503const LowerConstResult = union(enum) {
504 ok: Atom.Index,
505 fail: *Module.ErrorMsg,
506};
507
508fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult {
509 const gpa = wasm_file.base.comp.gpa;
510 const mod = wasm_file.base.comp.module.?;
511
512 // Create and initialize a new local symbol and atom
513 const sym_index = try zig_object.allocateSymbol(gpa);
514 const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
515 var value_bytes = std.ArrayList(u8).init(gpa);
516 defer value_bytes.deinit();
517
518 const code = code: {
519 const atom = wasm_file.getAtomPtr(atom_index);
520 atom.alignment = tv.ty.abiAlignment(mod);
521 const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
522 errdefer gpa.free(segment_name);
523 zig_object.symbol(sym_index).* = .{
524 .name = try zig_object.string_table.insert(gpa, name),
525 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
526 .tag = .data,
527 .index = try zig_object.createDataSegment(
528 gpa,
529 segment_name,
530 tv.ty.abiAlignment(mod),
531 ),
532 .virtual_address = undefined,
533 };
534
535 const result = try codegen.generateSymbol(
536 &wasm_file.base,
537 src_loc,
538 tv,
539 &value_bytes,
540 .none,
541 .{
542 .parent_atom_index = @intFromEnum(atom.sym_index),
543 .addend = null,
544 },
545 );
546 break :code switch (result) {
547 .ok => value_bytes.items,
548 .fail => |em| {
549 return .{ .fail = em };
550 },
551 };
552 };
553
554 const atom = wasm_file.getAtomPtr(atom_index);
555 atom.size = @intCast(code.len);
556 try atom.code.appendSlice(gpa, code);
557 return .{ .ok = atom_index };
558}
559
560/// Returns the symbol index of the error name table.
561///
562/// When the symbol does not yet exist, it will create a new one instead.
563pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !Symbol.Index {
564 if (zig_object.error_table_symbol != .null) {
565 return zig_object.error_table_symbol;
566 }
567
568 // no error was referenced yet, so create a new symbol and atom for it
569 // and then return said symbol's index. The final table will be populated
570 // during `flush` when we know all possible error names.
571 const gpa = wasm_file.base.comp.gpa;
572 const sym_index = try zig_object.allocateSymbol(gpa);
573 const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
574 const atom = wasm_file.getAtomPtr(atom_index);
575 const slice_ty = Type.slice_const_u8_sentinel_0;
576 const mod = wasm_file.base.comp.module.?;
577 atom.alignment = slice_ty.abiAlignment(mod);
578
579 const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_name_table");
580 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_name_table");
581 const sym = zig_object.symbol(sym_index);
582 sym.* = .{
583 .name = sym_name,
584 .tag = .data,
585 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
586 .index = try zig_object.createDataSegment(gpa, segment_name, atom.alignment),
587 .virtual_address = undefined,
588 };
589
590 log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(sym_index)});
591 zig_object.error_table_symbol = sym_index;
592 return sym_index;
593}
594
595/// Populates the error name table, when `error_table_symbol` is not null.
596///
597/// This creates a table that consists of pointers and length to each error name.
598/// The table is what is being pointed to within the runtime bodies that are generated.
599fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void {
600 if (zig_object.error_table_symbol == .null) return;
601 const gpa = wasm_file.base.comp.gpa;
602 const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?;
603
604 // Rather than creating a symbol for each individual error name,
605 // we create a symbol for the entire region of error names. We then calculate
606 // the pointers into the list using addends which are appended to the relocation.
607 const names_sym_index = try zig_object.allocateSymbol(gpa);
608 const names_atom_index = try wasm_file.createAtom(names_sym_index, zig_object.index);
609 const names_atom = wasm_file.getAtomPtr(names_atom_index);
610 names_atom.alignment = .@"1";
611 const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names");
612 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
613 const names_symbol = zig_object.symbol(names_sym_index);
614 names_symbol.* = .{
615 .name = sym_name,
616 .tag = .data,
617 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
618 .index = try zig_object.createDataSegment(gpa, segment_name, names_atom.alignment),
619 .virtual_address = undefined,
620 };
621
622 log.debug("Populating error names", .{});
623
624 // Addend for each relocation to the table
625 var addend: u32 = 0;
626 const mod = wasm_file.base.comp.module.?;
627 for (mod.global_error_set.keys()) |error_name_nts| {
628 const atom = wasm_file.getAtomPtr(atom_index);
629
630 const error_name = mod.intern_pool.stringToSlice(error_name_nts);
631 const len: u32 = @intCast(error_name.len + 1); // names are 0-terminated
632
633 const slice_ty = Type.slice_const_u8_sentinel_0;
634 const offset = @as(u32, @intCast(atom.code.items.len));
635 // first we create the data for the slice of the name
636 try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
637 try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
638 // create relocation to the error name
639 try atom.relocs.append(gpa, .{
640 .index = @intFromEnum(names_atom.sym_index),
641 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
642 .offset = offset,
643 .addend = @intCast(addend),
644 });
645 atom.size += @intCast(slice_ty.abiSize(mod));
646 addend += len;
647
648 // as we updated the error name table, we now store the actual name within the names atom
649 try names_atom.code.ensureUnusedCapacity(gpa, len);
650 names_atom.code.appendSliceAssumeCapacity(error_name);
651 names_atom.code.appendAssumeCapacity(0);
652
653 log.debug("Populated error name: '{s}'", .{error_name});
654 }
655 names_atom.size = addend;
656 zig_object.error_names_atom = names_atom_index;
657}
658
659/// Either creates a new import, or updates one if existing.
660/// When `type_index` is non-null, we assume an external function.
661/// In all other cases, a data-symbol will be created instead.
662pub fn addOrUpdateImport(
663 zig_object: *ZigObject,
664 wasm_file: *Wasm,
665 /// Name of the import
666 name: []const u8,
667 /// Symbol index that is external
668 symbol_index: Symbol.Index,
669 /// Optional library name (i.e. `extern "c" fn foo() void`
670 lib_name: ?[:0]const u8,
671 /// The index of the type that represents the function signature
672 /// when the extern is a function. When this is null, a data-symbol
673 /// is asserted instead.
674 type_index: ?u32,
675) !void {
676 const gpa = wasm_file.base.comp.gpa;
677 std.debug.assert(symbol_index != .null);
678 // For the import name, we use the decl's name, rather than the fully qualified name
679 // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
680 // name but different module can be resolved correctly.
681 const mangle_name = lib_name != null and
682 !std.mem.eql(u8, lib_name.?, "c");
683 const full_name = if (mangle_name) full_name: {
684 break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? });
685 } else name;
686 defer if (mangle_name) gpa.free(full_name);
687
688 const decl_name_index = try zig_object.string_table.insert(gpa, full_name);
689 const sym: *Symbol = &zig_object.symbols.items[@intFromEnum(symbol_index)];
690 sym.setUndefined(true);
691 sym.setGlobal(true);
692 sym.name = decl_name_index;
693 if (mangle_name) {
694 // we specified a specific name for the symbol that does not match the import name
695 sym.setFlag(.WASM_SYM_EXPLICIT_NAME);
696 }
697
698 if (type_index) |ty_index| {
699 const gop = try zig_object.imports.getOrPut(gpa, symbol_index);
700 const module_name = if (lib_name) |l_name| l_name else wasm_file.host_name;
701 if (!gop.found_existing) {
702 zig_object.imported_functions_count += 1;
703 }
704 gop.value_ptr.* = .{
705 .module_name = try zig_object.string_table.insert(gpa, module_name),
706 .name = try zig_object.string_table.insert(gpa, name),
707 .kind = .{ .function = ty_index },
708 };
709 sym.tag = .function;
710 } else {
711 sym.tag = .data;
712 }
713}
714
715/// Returns the symbol index from a symbol of which its flag is set global,
716/// such as an exported or imported symbol.
717/// If the symbol does not yet exist, creates a new one symbol instead
718/// and then returns the index to it.
719pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8) !Symbol.Index {
720 const name_index = try zig_object.string_table.insert(gpa, name);
721 const gop = try zig_object.global_syms.getOrPut(gpa, name_index);
722 if (gop.found_existing) {
723 return gop.value_ptr.*;
724 }
725
726 var sym: Symbol = .{
727 .name = name_index,
728 .flags = 0,
729 .index = undefined, // index to type will be set after merging symbols
730 .tag = .function,
731 .virtual_address = std.math.maxInt(u32),
732 };
733 sym.setGlobal(true);
734 sym.setUndefined(true);
735
736 const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: {
737 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
738 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
739 zig_object.symbols.items.len += 1;
740 break :blk index;
741 };
742 zig_object.symbol(sym_index).* = sym;
743 gop.value_ptr.* = sym_index;
744 return sym_index;
745}
746
747/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
748/// Returns the given pointer address
749pub fn getDeclVAddr(
750 zig_object: *ZigObject,
751 wasm_file: *Wasm,
752 decl_index: InternPool.DeclIndex,
753 reloc_info: link.File.RelocInfo,
754) !u64 {
755 const target = wasm_file.base.comp.root_mod.resolved_target.result;
756 const gpa = wasm_file.base.comp.gpa;
757 const mod = wasm_file.base.comp.module.?;
758 const decl = mod.declPtr(decl_index);
759
760 const target_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
761 const target_symbol_index = @intFromEnum(wasm_file.getAtom(target_atom_index).sym_index);
762
763 std.debug.assert(reloc_info.parent_atom_index != 0);
764 const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?;
765 const atom = wasm_file.getAtomPtr(atom_index);
766 const is_wasm32 = target.cpu.arch == .wasm32;
767 if (decl.ty.zigTypeTag(mod) == .Fn) {
768 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
769 try atom.relocs.append(gpa, .{
770 .index = target_symbol_index,
771 .offset = @intCast(reloc_info.offset),
772 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
773 });
774 } else {
775 try atom.relocs.append(gpa, .{
776 .index = target_symbol_index,
777 .offset = @intCast(reloc_info.offset),
778 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
779 .addend = @intCast(reloc_info.addend),
780 });
781 }
782
783 // we do not know the final address at this point,
784 // as atom allocation will determine the address and relocations
785 // will calculate and rewrite this. Therefore, we simply return the symbol index
786 // that was targeted.
787 return target_symbol_index;
788}
789
790pub fn getAnonDeclVAddr(
791 zig_object: *ZigObject,
792 wasm_file: *Wasm,
793 decl_val: InternPool.Index,
794 reloc_info: link.File.RelocInfo,
795) !u64 {
796 const gpa = wasm_file.base.comp.gpa;
797 const target = wasm_file.base.comp.root_mod.resolved_target.result;
798 const atom_index = zig_object.anon_decls.get(decl_val).?;
799 const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
800
801 const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?;
802 const parent_atom = wasm_file.getAtomPtr(parent_atom_index);
803 const is_wasm32 = target.cpu.arch == .wasm32;
804 const mod = wasm_file.base.comp.module.?;
805 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
806 if (ty.zigTypeTag(mod) == .Fn) {
807 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
808 try parent_atom.relocs.append(gpa, .{
809 .index = target_symbol_index,
810 .offset = @intCast(reloc_info.offset),
811 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
812 });
813 } else {
814 try parent_atom.relocs.append(gpa, .{
815 .index = target_symbol_index,
816 .offset = @intCast(reloc_info.offset),
817 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
818 .addend = @intCast(reloc_info.addend),
819 });
820 }
821
822 // we do not know the final address at this point,
823 // as atom allocation will determine the address and relocations
824 // will calculate and rewrite this. Therefore, we simply return the symbol index
825 // that was targeted.
826 return target_symbol_index;
827}
828
829pub fn deleteDeclExport(
830 zig_object: *ZigObject,
831 wasm_file: *Wasm,
832 decl_index: InternPool.DeclIndex,
833 name: InternPool.NullTerminatedString,
834) void {
835 const mod = wasm_file.base.comp.module.?;
836 const decl_info = zig_object.decls_map.getPtr(decl_index) orelse return;
837 const export_name = mod.intern_pool.stringToSlice(name);
838 if (decl_info.@"export"(zig_object, export_name)) |sym_index| {
839 const sym = zig_object.symbol(sym_index);
840 decl_info.deleteExport(sym_index);
841 std.debug.assert(zig_object.global_syms.remove(sym.name));
842 std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index }));
843 zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {};
844 sym.tag = .dead;
845 }
846}
847
848pub fn updateExports(
849 zig_object: *ZigObject,
850 wasm_file: *Wasm,
851 mod: *Module,
852 exported: Module.Exported,
853 exports: []const *Module.Export,
854) !void {
855 const decl_index = switch (exported) {
856 .decl_index => |i| i,
857 .value => |val| {
858 _ = val;
859 @panic("TODO: implement Wasm linker code for exporting a constant value");
860 },
861 };
862 const decl = mod.declPtr(decl_index);
863 const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
864 const decl_info = zig_object.decls_map.getPtr(decl_index).?;
865 const atom = wasm_file.getAtom(atom_index);
866 const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*;
867 const gpa = mod.gpa;
868 log.debug("Updating exports for decl '{s}'", .{mod.intern_pool.stringToSlice(decl.name)});
869
870 for (exports) |exp| {
871 if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| {
872 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
873 gpa,
874 decl.srcLoc(mod),
875 "Unimplemented: ExportOptions.section '{s}'",
876 .{section},
877 ));
878 continue;
879 }
880
881 const export_string = mod.intern_pool.stringToSlice(exp.opts.name);
882 const sym_index = if (decl_info.@"export"(zig_object, export_string)) |idx|
883 idx
884 else index: {
885 const sym_index = try zig_object.allocateSymbol(gpa);
886 try decl_info.appendExport(gpa, sym_index);
887 break :index sym_index;
888 };
889
890 const export_name = try zig_object.string_table.insert(gpa, export_string);
891 const sym = zig_object.symbol(sym_index);
892 sym.setGlobal(true);
893 sym.setUndefined(false);
894 sym.index = atom_sym.index;
895 sym.tag = atom_sym.tag;
896 sym.name = export_name;
897
898 switch (exp.opts.linkage) {
899 .Internal => {
900 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
901 },
902 .Weak => {
903 sym.setFlag(.WASM_SYM_BINDING_WEAK);
904 },
905 .Strong => {}, // symbols are strong by default
906 .LinkOnce => {
907 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
908 gpa,
909 decl.srcLoc(mod),
910 "Unimplemented: LinkOnce",
911 .{},
912 ));
913 continue;
914 },
915 }
916 if (exp.opts.visibility == .hidden) {
917 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
918 }
919 log.debug(" with name '{s}' - {}", .{ export_string, sym });
920 try zig_object.global_syms.put(gpa, export_name, sym_index);
921 try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index);
922 }
923}
924
925pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) void {
926 const gpa = wasm_file.base.comp.gpa;
927 const mod = wasm_file.base.comp.module.?;
928 const decl = mod.declPtr(decl_index);
929 const decl_info = zig_object.decls_map.getPtr(decl_index).?;
930 const atom_index = decl_info.atom;
931 const atom = wasm_file.getAtomPtr(atom_index);
932 zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
933 for (decl_info.exports.items) |exp_sym_index| {
934 const exp_sym = zig_object.symbol(exp_sym_index);
935 exp_sym.tag = .dead;
936 zig_object.symbols_free_list.append(exp_sym_index) catch {};
937 }
938 decl_info.exports.deinit(gpa);
939 std.debug.assert(zig_object.decls_map.remove(decl_index));
940 const sym = &zig_object.symbols.items[atom.sym_index];
941 for (atom.locals.items) |local_atom_index| {
942 const local_atom = wasm_file.getAtom(local_atom_index);
943 const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
944 std.debug.assert(local_symbol.tag == .data);
945 zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
946 std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
947 local_symbol.tag = .dead; // also for any local symbol
948 const segment = &zig_object.segment_info.items[local_atom.sym_index];
949 gpa.free(segment.name);
950 segment.name = &.{}; // Ensure no accidental double free
951 }
952
953 if (decl.isExtern(mod)) {
954 std.debug.assert(zig_object.imports.remove(atom.sym_index));
955 }
956 std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc()));
957
958 // if (wasm.dwarf) |*dwarf| {
959 // dwarf.freeDecl(decl_index);
960 // }
961
962 atom.prev = null;
963 sym.tag = .dead;
964 if (sym.isGlobal()) {
965 std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
966 }
967 switch (decl.ty.zigTypeTag(mod)) {
968 .Fn => {
969 zig_object.functions_free_list.append(gpa, sym.index) catch {};
970 std.debug.assert(zig_object.atom_types.remove(atom_index));
971 },
972 else => {
973 zig_object.segment_free_list.append(gpa, sym.index) catch {};
974 const segment = &zig_object.segment_info.items[sym.index];
975 gpa.free(segment.name);
976 segment.name = &.{}; // Prevent accidental double free
977 },
978 }
979}
980
981fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 {
982 var index: u32 = 0;
983 while (index < zig_object.func_types.items.len) : (index += 1) {
984 if (zig_object.func_types.items[index].eql(func_type)) return index;
985 }
986 return null;
987}
988
989/// Searches for a matching function signature. When no matching signature is found,
990/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
991pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_type: std.wasm.Type) !u32 {
992 if (zig_object.getTypeIndex(func_type)) |index| {
993 return index;
994 }
995
996 // functype does not exist.
997 const index: u32 = @intCast(zig_object.func_types.items.len);
998 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
999 errdefer gpa.free(params);
1000 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
1001 errdefer gpa.free(returns);
1002 try zig_object.func_types.append(gpa, .{
1003 .params = params,
1004 .returns = returns,
1005 });
1006 return index;
1007}
1008
1009/// Generates an atom containing the global error set' size.
1010/// This will only be generated if the symbol exists.
1011fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
1012 const gpa = wasm_file.base.comp.gpa;
1013 const sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return;
1014
1015 const errors_len = wasm_file.base.comp.module.?.global_error_set.count();
1016 // overwrite existing atom if it already exists (maybe the error set has increased)
1017 // if not, allcoate a new atom.
1018 const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: {
1019 const atom = wasm_file.getAtomPtr(index);
1020 atom.prev = .null;
1021 atom.deinit(gpa);
1022 break :blk index;
1023 } else idx: {
1024 // We found a call to __zig_errors_len so make the symbol a local symbol
1025 // and define it, so the final binary or resulting object file will not attempt
1026 // to resolve it.
1027 const sym = zig_object.symbol(sym_index);
1028 sym.setGlobal(false);
1029 sym.setUndefined(false);
1030 sym.tag = .data;
1031 const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len");
1032 sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2");
1033 break :idx try wasm_file.createAtom(sym_index, zig_object.index);
1034 };
1035
1036 const atom = wasm_file.getAtomPtr(atom_index);
1037 atom.code.clearRetainingCapacity();
1038 atom.sym_index = sym_index;
1039 atom.size = 2;
1040 atom.alignment = .@"2";
1041 try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
1042}
1043
1044fn findGlobalSymbol(zig_object: *ZigObject, name: []const u8) ?Symbol.Index {
1045 const offset = zig_object.string_table.getOffset(name) orelse return null;
1046 return zig_object.global_syms.get(offset);
1047}
1048
1049/// Initializes symbols and atoms for the debug sections
1050/// Initialization is only done when compiling Zig code.
1051/// When Zig is invoked as a linker instead, the atoms
1052/// and symbols come from the object files instead.
1053pub fn initDebugSections(zig_object: *ZigObject) !void {
1054 if (zig_object.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
1055 std.debug.assert(zig_object.debug_info_index == null);
1056 // this will create an Atom and set the index for us.
1057 zig_object.debug_info_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_info_index, ".debug_info");
1058 zig_object.debug_line_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_line_index, ".debug_line");
1059 zig_object.debug_loc_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_loc_index, ".debug_loc");
1060 zig_object.debug_abbrev_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_abbrev_index, ".debug_abbrev");
1061 zig_object.debug_ranges_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_ranges_index, ".debug_ranges");
1062 zig_object.debug_str_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_str_index, ".debug_str");
1063 zig_object.debug_pubnames_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubnames_index, ".debug_pubnames");
1064 zig_object.debug_pubtypes_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubtypes_index, ".debug_pubtypes");
1065}
1066
1067/// From a given index variable, creates a new debug section.
1068/// This initializes the index, appends a new segment,
1069/// and finally, creates a managed `Atom`.
1070pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
1071 const gpa = wasm_file.base.comp.gpa;
1072 const new_index: u32 = @intCast(zig_object.segments.items.len);
1073 index.* = new_index;
1074 try zig_object.appendDummySegment();
1075
1076 const sym_index = try zig_object.allocateSymbol(gpa);
1077 const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
1078 const atom = wasm_file.getAtomPtr(atom_index);
1079 zig_object.symbols.items[sym_index] = .{
1080 .tag = .section,
1081 .name = try zig_object.string_table.put(gpa, name),
1082 .index = 0,
1083 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1084 };
1085
1086 atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
1087 return atom_index;
1088}
1089
1090pub fn updateDeclLineNumber(zig_object: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void {
1091 if (zig_object.dwarf) |*dw| {
1092 const decl = mod.declPtr(decl_index);
1093 const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
1094
1095 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1096 try dw.updateDeclLineNumber(mod, decl_index);
1097 }
1098}
1099
1100/// Allocates debug atoms into their respective debug sections
1101/// to merge them with maybe-existing debug atoms from object files.
1102fn allocateDebugAtoms(zig_object: *ZigObject) !void {
1103 if (zig_object.dwarf == null) return;
1104
1105 const allocAtom = struct {
1106 fn f(ctx: *ZigObject, maybe_index: *?u32, atom_index: Atom.Index) !void {
1107 const index = maybe_index.* orelse idx: {
1108 const index = @as(u32, @intCast(ctx.segments.items.len));
1109 try ctx.appendDummySegment();
1110 maybe_index.* = index;
1111 break :idx index;
1112 };
1113 const atom = ctx.getAtomPtr(atom_index);
1114 atom.size = @as(u32, @intCast(atom.code.items.len));
1115 ctx.symbols.items[atom.sym_index].index = index;
1116 try ctx.appendAtomAtIndex(index, atom_index);
1117 }
1118 }.f;
1119
1120 try allocAtom(zig_object, &zig_object.debug_info_index, zig_object.debug_info_atom.?);
1121 try allocAtom(zig_object, &zig_object.debug_line_index, zig_object.debug_line_atom.?);
1122 try allocAtom(zig_object, &zig_object.debug_loc_index, zig_object.debug_loc_atom.?);
1123 try allocAtom(zig_object, &zig_object.debug_str_index, zig_object.debug_str_atom.?);
1124 try allocAtom(zig_object, &zig_object.debug_ranges_index, zig_object.debug_ranges_atom.?);
1125 try allocAtom(zig_object, &zig_object.debug_abbrev_index, zig_object.debug_abbrev_atom.?);
1126 try allocAtom(zig_object, &zig_object.debug_pubnames_index, zig_object.debug_pubnames_atom.?);
1127 try allocAtom(zig_object, &zig_object.debug_pubtypes_index, zig_object.debug_pubtypes_atom.?);
1128}
1129
1130/// For the given `decl_index`, stores the corresponding type representing the function signature.
1131/// Asserts declaration has an associated `Atom`.
1132/// Returns the index into the list of types.
1133pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {
1134 const decl_info = zig_object.decls_map.get(decl_index).?;
1135 const index = try zig_object.putOrGetFuncType(gpa, func_type);
1136 try zig_object.atom_types.put(gpa, decl_info.atom, index);
1137 return index;
1138}
1139
1140/// The symbols in ZigObject are already represented by an atom as we need to store its data.
1141/// So rather than creating a new Atom and returning its index, we use this oppertunity to scan
1142/// its relocations and create any GOT symbols or function table indexes it may require.
1143pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symbol.Index) !Atom.Index {
1144 const gpa = wasm_file.base.comp.gpa;
1145 const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index };
1146 const atom_index = wasm_file.symbol_atom.get(loc).?;
1147 const final_index = try wasm_file.getMatchingSegment(zig_object.index, index);
1148 try wasm_file.appendAtomAtIndex(final_index, atom_index);
1149 const atom = wasm_file.getAtom(atom_index);
1150 for (atom.relocs.items) |reloc| {
1151 const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
1152 switch (reloc.relocation_type) {
1153 .R_WASM_TABLE_INDEX_I32,
1154 .R_WASM_TABLE_INDEX_I64,
1155 .R_WASM_TABLE_INDEX_SLEB,
1156 .R_WASM_TABLE_INDEX_SLEB64,
1157 => {
1158 try wasm_file.function_table.put(gpa, .{
1159 .file = zig_object.index,
1160 .index = reloc_index,
1161 }, 0);
1162 },
1163 .R_WASM_GLOBAL_INDEX_I32,
1164 .R_WASM_GLOBAL_INDEX_LEB,
1165 => {
1166 const sym = zig_object.symbol(reloc_index);
1167 if (sym.tag != .global) {
1168 try wasm_file.got_symbols.append(gpa, .{
1169 .file = zig_object.index,
1170 .index = reloc_index,
1171 });
1172 }
1173 },
1174 else => {},
1175 }
1176 }
1177 return atom_index;
1178}
1179
1180/// Creates a new Wasm function with a given symbol name and body.
1181/// Returns the symbol index of the new function.
1182pub fn createFunction(
1183 zig_object: *ZigObject,
1184 wasm_file: *Wasm,
1185 symbol_name: []const u8,
1186 func_ty: std.wasm.Type,
1187 function_body: *std.ArrayList(u8),
1188 relocations: *std.ArrayList(types.Relocation),
1189) !Symbol.Index {
1190 const gpa = wasm_file.base.comp.gpa;
1191 const sym_index = try zig_object.allocateSymbol(gpa);
1192 const sym = zig_object.symbol(sym_index);
1193 sym.tag = .function;
1194 sym.name = try zig_object.string_table.insert(gpa, symbol_name);
1195 const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
1196 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
1197
1198 const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
1199 const atom = wasm_file.getAtomPtr(atom_index);
1200 atom.size = @intCast(function_body.items.len);
1201 atom.code = function_body.moveToUnmanaged();
1202 atom.relocs = relocations.moveToUnmanaged();
1203
1204 try zig_object.synthetic_functions.append(gpa, atom_index);
1205 return sym_index;
1206}
1207
1208/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
1209fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
1210 const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
1211 idx
1212 else idx: {
1213 const len: u32 = @intCast(zig_object.functions.items.len);
1214 _ = try zig_object.functions.addOne(gpa);
1215 break :idx len;
1216 };
1217 zig_object.functions.items[index] = func;
1218
1219 return index;
1220}
1221
1222pub fn flushModule(zig_object: *ZigObject, wasm_file: *Wasm) !void {
1223 try zig_object.populateErrorNameTable(wasm_file);
1224 try zig_object.setupErrorsLen(wasm_file);
1225}
1226
1227const build_options = @import("build_options");
1228const builtin = @import("builtin");
1229const codegen = @import("../../codegen.zig");
1230const link = @import("../../link.zig");
1231const log = std.log.scoped(.zig_object);
1232const std = @import("std");
1233const types = @import("types.zig");
1234
1235const Air = @import("../../Air.zig");
1236const Atom = @import("Atom.zig");
1237const Dwarf = @import("../Dwarf.zig");
1238const File = @import("file.zig").File;
1239const InternPool = @import("../../InternPool.zig");
1240const Liveness = @import("../../Liveness.zig");
1241const Module = @import("../../Module.zig");
1242const StringTable = @import("../StringTable.zig");
1243const Symbol = @import("Symbol.zig");
1244const Type = @import("../../type.zig").Type;
1245const TypedValue = @import("../../TypedValue.zig");
1246const Value = @import("../../Value.zig");
1247const Wasm = @import("../Wasm.zig");
1248const ZigObject = @This();
src/link/Wasm/file.zig created+132
......@@ -0,0 +1,132 @@
1pub const File = union(enum) {
2 zig_object: *ZigObject,
3 object: *Object,
4
5 pub const Index = enum(u16) {
6 null = std.math.maxInt(u16),
7 _,
8 };
9
10 pub fn path(file: File) []const u8 {
11 return switch (file) {
12 inline else => |obj| obj.path,
13 };
14 }
15
16 pub fn segmentInfo(file: File) []const types.Segment {
17 return switch (file) {
18 .zig_object => |obj| obj.segment_info.items,
19 .object => |obj| obj.segment_info,
20 };
21 }
22
23 pub fn symbol(file: File, index: Symbol.Index) *Symbol {
24 return switch (file) {
25 .zig_object => |obj| &obj.symbols.items[@intFromEnum(index)],
26 .object => |obj| &obj.symtable[@intFromEnum(index)],
27 };
28 }
29
30 pub fn symbols(file: File) []const Symbol {
31 return switch (file) {
32 .zig_object => |obj| obj.symbols.items,
33 .object => |obj| obj.symtable,
34 };
35 }
36
37 pub fn symbolName(file: File, index: Symbol.Index) []const u8 {
38 switch (file) {
39 .zig_object => |obj| {
40 const sym = obj.symbols.items[@intFromEnum(index)];
41 return obj.string_table.get(sym.name).?;
42 },
43 .object => |obj| {
44 const sym = obj.symtable[@intFromEnum(index)];
45 return obj.string_table.get(sym.name);
46 },
47 }
48 }
49
50 pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: Symbol.Index) !AtomIndex {
51 return switch (file) {
52 inline else => |obj| obj.parseSymbolIntoAtom(wasm_file, index),
53 };
54 }
55
56 /// For a given symbol index, find its corresponding import.
57 /// Asserts import exists.
58 pub fn import(file: File, symbol_index: Symbol.Index) types.Import {
59 return switch (file) {
60 .zig_object => |obj| obj.imports.get(symbol_index).?,
61 .object => |obj| obj.findImport(obj.symtable[@intFromEnum(symbol_index)]),
62 };
63 }
64
65 /// For a given offset, returns its string value.
66 /// Asserts string exists in the object string table.
67 pub fn string(file: File, offset: u32) []const u8 {
68 return switch (file) {
69 .zig_object => |obj| obj.string_table.get(offset).?,
70 .object => |obj| obj.string_table.get(offset),
71 };
72 }
73
74 pub fn importedGlobals(file: File) u32 {
75 return switch (file) {
76 inline else => |obj| obj.imported_globals_count,
77 };
78 }
79
80 pub fn importedFunctions(file: File) u32 {
81 return switch (file) {
82 inline else => |obj| obj.imported_functions_count,
83 };
84 }
85
86 pub fn importedTables(file: File) u32 {
87 return switch (file) {
88 inline else => |obj| obj.imported_tables_count,
89 };
90 }
91
92 pub fn function(file: File, sym_index: Symbol.Index) std.wasm.Func {
93 switch (file) {
94 .zig_object => |obj| {
95 const sym = obj.symbols.items[@intFromEnum(sym_index)];
96 return obj.functions.items[sym.index];
97 },
98 .object => |obj| {
99 const sym = obj.symtable[@intFromEnum(sym_index)];
100 return obj.functions[sym.index - obj.imported_functions_count];
101 },
102 }
103 }
104
105 pub fn globals(file: File) []const std.wasm.Global {
106 return switch (file) {
107 .zig_object => |obj| obj.globals.items,
108 .object => |obj| obj.globals,
109 };
110 }
111
112 pub fn funcTypes(file: File) []const std.wasm.Type {
113 return switch (file) {
114 .zig_object => |obj| obj.func_types.items,
115 .object => |obj| obj.func_types,
116 };
117 }
118
119 pub const Entry = union(enum) {
120 zig_object: ZigObject,
121 object: Object,
122 };
123};
124
125const std = @import("std");
126const types = @import("types.zig");
127
128const AtomIndex = @import("Atom.zig").Index;
129const Object = @import("Object.zig");
130const Symbol = @import("Symbol.zig");
131const Wasm = @import("../Wasm.zig");
132const ZigObject = @import("ZigObject.zig");
test/link.zig+12-10
......@@ -35,11 +35,10 @@ pub const cases = [_]Case{
3535 },
3636
3737 // WASM Cases
38 // https://github.com/ziglang/zig/issues/16938
39 //.{
40 // .build_root = "test/link/wasm/archive",
41 // .import = @import("link/wasm/archive/build.zig"),
42 //},
38 .{
39 .build_root = "test/link/wasm/archive",
40 .import = @import("link/wasm/archive/build.zig"),
41 },
4342 .{
4443 .build_root = "test/link/wasm/basic-features",
4544 .import = @import("link/wasm/basic-features/build.zig"),
......@@ -52,11 +51,10 @@ pub const cases = [_]Case{
5251 .build_root = "test/link/wasm/export",
5352 .import = @import("link/wasm/export/build.zig"),
5453 },
55 // https://github.com/ziglang/zig/issues/16937
56 //.{
57 // .build_root = "test/link/wasm/export-data",
58 // .import = @import("link/wasm/export-data/build.zig"),
59 //},
54 .{
55 .build_root = "test/link/wasm/export-data",
56 .import = @import("link/wasm/export-data/build.zig"),
57 },
6058 .{
6159 .build_root = "test/link/wasm/extern",
6260 .import = @import("link/wasm/extern/build.zig"),
......@@ -81,6 +79,10 @@ pub const cases = [_]Case{
8179 .build_root = "test/link/wasm/segments",
8280 .import = @import("link/wasm/segments/build.zig"),
8381 },
82 .{
83 .build_root = "test/link/wasm/shared-memory",
84 .import = @import("link/wasm/shared-memory/build.zig"),
85 },
8486 .{
8587 .build_root = "test/link/wasm/stack_pointer",
8688 .import = @import("link/wasm/stack_pointer/build.zig"),
test/link/wasm/archive/build.zig+2-1
......@@ -19,12 +19,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
1919 .name = "main",
2020 .root_source_file = .{ .path = "main.zig" },
2121 .optimize = optimize,
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
22 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
2323 .strip = false,
2424 });
2525 lib.entry = .disabled;
2626 lib.use_llvm = false;
2727 lib.use_lld = false;
28 lib.root_module.export_symbol_names = &.{"foo"};
2829
2930 const check = lib.checkObject();
3031 check.checkInHeaders();
test/link/wasm/export-data/build.zig+1-1
......@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
1313 .name = "lib",
1414 .root_source_file = .{ .path = "lib.zig" },
1515 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
16 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
16 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
1717 });
1818 lib.entry = .disabled;
1919 lib.use_lld = false;
test/link/wasm/shared-memory/build.zig+40-45
......@@ -11,37 +11,39 @@ pub fn build(b: *std.Build) void {
1111}
1212
1313fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
14 const lib = b.addExecutable(.{
14 const exe = b.addExecutable(.{
1515 .name = "lib",
1616 .root_source_file = .{ .path = "lib.zig" },
17 .target = .{
17 .target = b.resolveTargetQuery(.{
1818 .cpu_arch = .wasm32,
1919 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
2020 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
2121 .os_tag = .freestanding,
22 },
22 }),
2323 .optimize = optimize_mode,
2424 .strip = false,
2525 .single_threaded = false,
2626 });
27 lib.entry = .disabled;
28 lib.use_lld = false;
29 lib.import_memory = true;
30 lib.export_memory = true;
31 lib.shared_memory = true;
32 lib.max_memory = 67108864;
33 lib.root_module.export_symbol_names = &.{"foo"};
27 exe.entry = .disabled;
28 exe.use_lld = false;
29 exe.import_memory = true;
30 exe.export_memory = true;
31 exe.shared_memory = true;
32 exe.max_memory = 67108864;
33 exe.root_module.export_symbol_names = &.{"foo"};
3434
35 const check_lib = lib.checkObject();
35 const check_exe = exe.checkObject();
3636
37 check_lib.checkStart("Section import");
38 check_lib.checkNext("entries 1");
39 check_lib.checkNext("module env");
40 check_lib.checkNext("name memory"); // ensure we are importing memory
37 check_exe.checkInHeaders();
38 check_exe.checkExact("Section import");
39 check_exe.checkExact("entries 1");
40 check_exe.checkExact("module env");
41 check_exe.checkExact("name memory"); // ensure we are importing memory
4142
42 check_lib.checkStart("Section export");
43 check_lib.checkNext("entries 2");
44 check_lib.checkNext("name memory"); // ensure we also export memory again
43 check_exe.checkInHeaders();
44 check_exe.checkExact("Section export");
45 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name memory"); // ensure we also export memory again
4547
4648 // This section *must* be emit as the start function is set to the index
4749 // of __wasm_init_memory
......@@ -49,49 +51,42 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
4951 // This means we won't have __wasm_init_memory in such case, and therefore
5052 // should also not have a section "start"
5153 if (optimize_mode == .Debug) {
52 check_lib.checkStart("Section start");
54 check_exe.checkInHeaders();
55 check_exe.checkExact("Section start");
5356 }
5457
5558 // This section is only and *must* be emit when shared-memory is enabled
5659 // release modes will have the TLS segment optimized out in our test-case.
5760 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");
59 check_lib.checkNext("count 3");
61 check_exe.checkInHeaders();
62 check_exe.checkExact("Section data_count");
63 check_exe.checkExact("count 1");
6064 }
6165
62 check_lib.checkStart("Section custom");
63 check_lib.checkNext("name name");
64 check_lib.checkNext("type function");
66 check_exe.checkInHeaders();
67 check_exe.checkExact("Section custom");
68 check_exe.checkExact("name name");
69 check_exe.checkExact("type function");
6570 if (optimize_mode == .Debug) {
66 check_lib.checkNext("name __wasm_init_memory");
71 check_exe.checkExact("name __wasm_init_memory");
6772 }
68 check_lib.checkNext("name __wasm_init_tls");
69 check_lib.checkNext("type global");
73 check_exe.checkExact("name __wasm_init_tls");
74 check_exe.checkExact("type global");
7075
7176 // In debug mode the symbol __tls_base is resolved to an undefined symbol
7277 // from the object file, hence its placement differs than in release modes
7378 // where the entire tls segment is optimized away, and tls_base will have
7479 // its original position.
75 if (optimize_mode == .Debug) {
76 check_lib.checkNext("name __tls_size");
77 check_lib.checkNext("name __tls_align");
78 check_lib.checkNext("name __tls_base");
79 } else {
80 check_lib.checkNext("name __tls_base");
81 check_lib.checkNext("name __tls_size");
82 check_lib.checkNext("name __tls_align");
83 }
80 check_exe.checkExact("name __tls_base");
81 check_exe.checkExact("name __tls_size");
82 check_exe.checkExact("name __tls_align");
8483
85 check_lib.checkNext("type data_segment");
84 check_exe.checkExact("type data_segment");
8685 if (optimize_mode == .Debug) {
87 check_lib.checkNext("names 3");
88 check_lib.checkNext("index 0");
89 check_lib.checkNext("name .rodata");
90 check_lib.checkNext("index 1");
91 check_lib.checkNext("name .bss");
92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
86 check_exe.checkExact("names 1");
87 check_exe.checkExact("index 0");
88 check_exe.checkExact("name .tdata");
9489 }
9590
96 test_step.dependOn(&check_lib.step);
91 test_step.dependOn(&check_exe.step);
9792}
test/link/wasm/type/build.zig+17-16
......@@ -13,31 +13,32 @@ pub fn build(b: *std.Build) void {
1313}
1414
1515fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addExecutable(.{
16 const exe = b.addExecutable(.{
1717 .name = "lib",
1818 .root_source_file = .{ .path = "lib.zig" },
1919 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
2020 .optimize = optimize,
2121 .strip = false,
2222 });
23 lib.entry = .disabled;
24 lib.use_llvm = false;
25 lib.use_lld = false;
26 b.installArtifact(lib);
23 exe.entry = .disabled;
24 exe.use_llvm = false;
25 exe.use_lld = false;
26 exe.root_module.export_symbol_names = &.{"foo"};
27 b.installArtifact(exe);
2728
28 const check_lib = lib.checkObject();
29 check_lib.checkInHeaders();
30 check_lib.checkExact("Section type");
29 const check_exe = exe.checkObject();
30 check_exe.checkInHeaders();
31 check_exe.checkExact("Section type");
3132 // only 2 entries, although we have more functions.
3233 // This is to test functions with the same function signature
3334 // have their types deduplicated.
34 check_lib.checkExact("entries 2");
35 check_lib.checkExact("params 1");
36 check_lib.checkExact("type i32");
37 check_lib.checkExact("returns 1");
38 check_lib.checkExact("type i64");
39 check_lib.checkExact("params 0");
40 check_lib.checkExact("returns 0");
35 check_exe.checkExact("entries 2");
36 check_exe.checkExact("params 1");
37 check_exe.checkExact("type i32");
38 check_exe.checkExact("returns 1");
39 check_exe.checkExact("type i64");
40 check_exe.checkExact("params 0");
41 check_exe.checkExact("returns 0");
4142
42 test_step.dependOn(&check_lib.step);
43 test_step.dependOn(&check_exe.step);
4344}