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 {...@@ -1286,8 +1286,9 @@ fn genFunc(func: *CodeGen) InnerError!void {
1286 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);1286 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
1287 defer prologue.deinit();1287 defer prologue.deinit();
12881288
1289 const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym);
1289 // load stack pointer1290 // load stack pointer
1290 try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });1291 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
1291 // store stack pointer so we can restore it when we return from the function1292 // store stack pointer so we can restore it when we return from the function
1292 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });1293 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
1293 // get the total stack size1294 // get the total stack size
...@@ -1303,7 +1304,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1303,7 +1304,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
1303 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });1304 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
1304 // Store the current stack pointer value into the global stack pointer so other function calls will1305 // Store the current stack pointer value into the global stack pointer so other function calls will
1305 // start from this value instead and not overwrite the current stack.1306 // 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
1308 // reserve space and insert all prologue instructions at the front of the instruction list1309 // reserve space and insert all prologue instructions at the front of the instruction list
1309 // We insert them in reserve order as there is no insertSlice in multiArrayList.1310 // We insert them in reserve order as there is no insertSlice in multiArrayList.
...@@ -1502,7 +1503,7 @@ fn restoreStackPointer(func: *CodeGen) !void {...@@ -1502,7 +1503,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
1502 try func.emitWValue(func.initial_stack_value);1503 try func.emitWValue(func.initial_stack_value);
15031504
1504 // save its value in the global stack pointer1505 // 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));
1506}1507}
15071508
1508/// From a given type, will create space on the virtual stack to store the value of such type.1509/// 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...@@ -2205,7 +2206,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2205 const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type);2206 const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type);
2206 try func.bin_file.addOrUpdateImport(2207 try func.bin_file.addOrUpdateImport(
2207 mod.intern_pool.stringToSlice(ext_decl.name),2208 mod.intern_pool.stringToSlice(ext_decl.name),
2208 atom.getSymbolIndex().?,2209 atom.sym_index,
2209 mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name),2210 mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name),
2210 type_index,2211 type_index,
2211 );2212 );
...@@ -2239,8 +2240,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2239,8 +2240,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2239 }2240 }
22402241
2241 if (callee) |direct| {2242 if (callee) |direct| {
2242 const atom_index = func.bin_file.decls.get(direct).?;2243 const atom_index = func.bin_file.zigObjectPtr().?.decls_map.get(direct).?.atom;
2243 try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);2244 try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
2244 } else {2245 } else {
2245 // in this case we call a function pointer2246 // in this case we call a function pointer
2246 // so load its value onto the stack2247 // so load its value onto the stack
...@@ -2251,7 +2252,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2251,7 +2252,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2251 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), mod);2252 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), mod);
2252 defer fn_type.deinit(func.gpa);2253 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);
2255 try func.addLabel(.call_indirect, fn_type_index);2256 try func.addLabel(.call_indirect, fn_type_index);
2256 }2257 }
22572258
...@@ -3157,8 +3158,8 @@ fn lowerAnonDeclRef(...@@ -3157,8 +3158,8 @@ fn lowerAnonDeclRef(
3157 return error.CodegenFail;3158 return error.CodegenFail;
3158 },3159 },
3159 }3160 }
3160 const target_atom_index = func.bin_file.anon_decls.get(decl_val).?;3161 const target_atom_index = func.bin_file.zigObjectPtr().?.anon_decls.get(decl_val).?;
3161 const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?;3162 const target_sym_index = @intFromEnum(func.bin_file.getAtom(target_atom_index).sym_index);
3162 if (is_fn_body) {3163 if (is_fn_body) {
3163 return WValue{ .function_index = target_sym_index };3164 return WValue{ .function_index = target_sym_index };
3164 } else if (offset == 0) {3165 } else if (offset == 0) {
...@@ -3189,9 +3190,8 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl...@@ -3189,9 +3190,8 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl
3189 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);3190 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
3190 const atom = func.bin_file.getAtom(atom_index);3191 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);
3193 if (decl.ty.zigTypeTag(mod) == .Fn) {3194 if (decl.ty.zigTypeTag(mod) == .Fn) {
3194 try func.bin_file.addTableFunction(target_sym_index);
3195 return WValue{ .function_index = target_sym_index };3195 return WValue{ .function_index = target_sym_index };
3196 } else if (offset == 0) {3196 } else if (offset == 0) {
3197 return WValue{ .memory = target_sym_index };3197 return WValue{ .memory = target_sym_index };
...@@ -3712,7 +3712,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3712,7 +3712,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3712 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3712 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3713 const operand = try func.resolveInst(un_op);3713 const operand = try func.resolveInst(un_op);
3714 const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);3714 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
3717 try func.emitWValue(operand);3717 try func.emitWValue(operand);
3718 const mod = func.bin_file.base.comp.module.?;3718 const mod = func.bin_file.base.comp.module.?;
...@@ -7154,7 +7154,7 @@ fn callIntrinsic(...@@ -7154,7 +7154,7 @@ fn callIntrinsic(
7154 args: []const WValue,7154 args: []const WValue,
7155) InnerError!WValue {7155) InnerError!WValue {
7156 assert(param_types.len == args.len);7156 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| {
7158 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});7158 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
7159 };7159 };
71607160
...@@ -7162,7 +7162,7 @@ fn callIntrinsic(...@@ -7162,7 +7162,7 @@ fn callIntrinsic(
7162 const mod = func.bin_file.base.comp.module.?;7162 const mod = func.bin_file.base.comp.module.?;
7163 var func_type = try genFunctype(func.gpa, .C, param_types, return_type, mod);7163 var func_type = try genFunctype(func.gpa, .C, param_types, return_type, mod);
7164 defer func_type.deinit(func.gpa);7164 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);
7166 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);7166 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
71677167
7168 const want_sret_param = firstParamSRet(.C, return_type, mod);7168 const want_sret_param = firstParamSRet(.C, return_type, mod);
...@@ -7182,7 +7182,7 @@ fn callIntrinsic(...@@ -7182,7 +7182,7 @@ fn callIntrinsic(
7182 }7182 }
71837183
7184 // Actually call our intrinsic7184 // Actually call our intrinsic
7185 try func.addLabel(.call, symbol_index);7185 try func.addLabel(.call, @intFromEnum(symbol_index));
71867186
7187 if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) {7187 if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) {
7188 return WValue.none;7188 return WValue.none;
...@@ -7225,7 +7225,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {...@@ -7225,7 +7225,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
72257225
7226 // check if we already generated code for this.7226 // check if we already generated code for this.
7227 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {7227 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
7228 return loc.index;7228 return @intFromEnum(loc.index);
7229 }7229 }
72307230
7231 const int_tag_ty = enum_ty.intTagType(mod);7231 const int_tag_ty = enum_ty.intTagType(mod);
...@@ -7365,7 +7365,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {...@@ -7365,7 +7365,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
73657365
7366 const slice_ty = Type.slice_const_u8_sentinel_0;7366 const slice_ty = Type.slice_const_u8_sentinel_0;
7367 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, mod);7367 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);
7369}7370}
73707371
7371fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7372fn 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 {...@@ -310,7 +310,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
310 const global_offset = emit.offset();310 const global_offset = emit.offset();
311 try emit.code.appendSlice(&buf);311 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;
314 const atom = emit.bin_file.getAtomPtr(atom_index);314 const atom = emit.bin_file.getAtomPtr(atom_index);
315 try atom.relocs.append(gpa, .{315 try atom.relocs.append(gpa, .{
316 .index = label,316 .index = label,
...@@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
370 try emit.code.appendSlice(&buf);370 try emit.code.appendSlice(&buf);
371371
372 if (label != 0) {372 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;
374 const atom = emit.bin_file.getAtomPtr(atom_index);374 const atom = emit.bin_file.getAtomPtr(atom_index);
375 try atom.relocs.append(gpa, .{375 try atom.relocs.append(gpa, .{
376 .offset = call_offset,376 .offset = call_offset,
...@@ -385,7 +385,19 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -385,7 +385,19 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
385 try emit.code.append(std.wasm.opcode(.call_indirect));385 try emit.code.append(std.wasm.opcode(.call_indirect));
386 // NOTE: If we remove unused function types in the future for incremental386 // NOTE: If we remove unused function types in the future for incremental
387 // linking, we must also emit a relocation for this `type_index`387 // 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 }
389 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index401 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
390}402}
391403
...@@ -400,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -400,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
400 try emit.code.appendSlice(&buf);412 try emit.code.appendSlice(&buf);
401413
402 if (symbol_index != 0) {414 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;
404 const atom = emit.bin_file.getAtomPtr(atom_index);416 const atom = emit.bin_file.getAtomPtr(atom_index);
405 try atom.relocs.append(gpa, .{417 try atom.relocs.append(gpa, .{
406 .offset = index_offset,418 .offset = index_offset,
...@@ -431,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -431,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
431 }443 }
432444
433 if (mem.pointer != 0) {445 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;
435 const atom = emit.bin_file.getAtomPtr(atom_index);447 const atom = emit.bin_file.getAtomPtr(atom_index);
436 try atom.relocs.append(gpa, .{448 try atom.relocs.append(gpa, .{
437 .offset = mem_offset,449 .offset = mem_offset,
src/link/Dwarf.zig+82-84
...@@ -1297,9 +1297,9 @@ pub fn commitDeclState(...@@ -1297,9 +1297,9 @@ pub fn commitDeclState(
1297 }1297 }
1298 },1298 },
1299 .wasm => {1299 .wasm => {
1300 const wasm_file = self.bin_file.cast(File.Wasm).?;1300 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1301 const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;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);1302 // writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
1303 },1303 },
1304 else => unreachable,1304 else => unreachable,
1305 }1305 }
...@@ -1390,26 +1390,26 @@ pub fn commitDeclState(...@@ -1390,26 +1390,26 @@ pub fn commitDeclState(
1390 },1390 },
13911391
1392 .wasm => {1392 .wasm => {
1393 const wasm_file = self.bin_file.cast(File.Wasm).?;1393 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1394 const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);1394 // const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
1395 const debug_line = &atom.code;1395 // const debug_line = &atom.code;
1396 const segment_size = debug_line.items.len;1396 // const segment_size = debug_line.items.len;
1397 if (needed_size != segment_size) {1397 // if (needed_size != segment_size) {
1398 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});1398 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1399 if (needed_size > segment_size) {1399 // if (needed_size > segment_size) {
1400 log.debug(" allocating {d} bytes for 'debug line' information", .{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);1401 // try debug_line.resize(self.allocator, needed_size);
1402 @memset(debug_line.items[segment_size..], 0);1402 // @memset(debug_line.items[segment_size..], 0);
1403 }1403 // }
1404 debug_line.items.len = needed_size;1404 // debug_line.items.len = needed_size;
1405 }1405 // }
1406 writeDbgLineNopsBuffered(1406 // writeDbgLineNopsBuffered(
1407 debug_line.items,1407 // debug_line.items,
1408 src_fn.off,1408 // src_fn.off,
1409 prev_padding_size,1409 // prev_padding_size,
1410 dbg_line_buffer.items,1410 // dbg_line_buffer.items,
1411 next_padding_size,1411 // next_padding_size,
1412 );1412 // );
1413 },1413 },
1414 else => unreachable,1414 else => unreachable,
1415 }1415 }
...@@ -1553,10 +1553,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)...@@ -1553,10 +1553,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)
1553 }1553 }
1554 },1554 },
1555 .wasm => {1555 .wasm => {
1556 const wasm_file = self.bin_file.cast(File.Wasm).?;1556 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1557 const debug_info_index = wasm_file.debug_info_atom.?;1557 // const debug_info_index = wasm_file.debug_info_atom.?;
1558 const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;1558 // const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
1559 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);1559 // try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
1560 },1560 },
1561 else => unreachable,1561 else => unreachable,
1562 }1562 }
...@@ -1594,7 +1594,6 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons...@@ -1594,7 +1594,6 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
1594 // This logic is nearly identical to the logic above in `updateDecl` for1594 // This logic is nearly identical to the logic above in `updateDecl` for
1595 // `SrcFn` and the line number programs. If you are editing this logic, you1595 // `SrcFn` and the line number programs. If you are editing this logic, you
1596 // probably need to edit that logic too.1596 // probably need to edit that logic too.
1597 const gpa = self.allocator;
15981597
1599 const atom = self.getAtom(.di_atom, atom_index);1598 const atom = self.getAtom(.di_atom, atom_index);
1600 const last_decl_index = self.di_atom_last_index.?;1599 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...@@ -1665,31 +1664,31 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
1665 },1664 },
16661665
1667 .wasm => {1666 .wasm => {
1668 const wasm_file = self.bin_file.cast(File.Wasm).?;1667 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1669 const info_atom = wasm_file.debug_info_atom.?;1668 // const info_atom = wasm_file.debug_info_atom.?;
1670 const debug_info = &wasm_file.getAtomPtr(info_atom).code;1669 // const debug_info = &wasm_file.getAtomPtr(info_atom).code;
1671 const segment_size = debug_info.items.len;1670 // const segment_size = debug_info.items.len;
1672 if (needed_size != segment_size) {1671 // if (needed_size != segment_size) {
1673 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});1672 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1674 if (needed_size > segment_size) {1673 // if (needed_size > segment_size) {
1675 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});1674 // log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
1676 try debug_info.resize(self.allocator, needed_size);1675 // try debug_info.resize(self.allocator, needed_size);
1677 @memset(debug_info.items[segment_size..], 0);1676 // @memset(debug_info.items[segment_size..], 0);
1678 }1677 // }
1679 debug_info.items.len = needed_size;1678 // debug_info.items.len = needed_size;
1680 }1679 // }
1681 log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{1680 // 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,1681 // debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
1683 });1682 // });
1684 try writeDbgInfoNopsToArrayList(1683 // try writeDbgInfoNopsToArrayList(
1685 gpa,1684 // gpa,
1686 debug_info,1685 // debug_info,
1687 atom.off,1686 // atom.off,
1688 prev_padding_size,1687 // prev_padding_size,
1689 dbg_info_buf,1688 // dbg_info_buf,
1690 next_padding_size,1689 // next_padding_size,
1691 trailing_zero,1690 // trailing_zero,
1692 );1691 // );
1693 },1692 },
1694 else => unreachable,1693 else => unreachable,
1695 }1694 }
...@@ -1735,10 +1734,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D...@@ -1735,10 +1734,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D
1735 }1734 }
1736 },1735 },
1737 .wasm => {1736 .wasm => {
1738 const wasm_file = self.bin_file.cast(File.Wasm).?;1737 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1739 const offset = atom.off + self.getRelocDbgLineOff();1738 // const offset = atom.off + self.getRelocDbgLineOff();
1740 const line_atom_index = wasm_file.debug_line_atom.?;1739 // const line_atom_index = wasm_file.debug_line_atom.?;
1741 wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;1740 // wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
1742 },1741 },
1743 else => unreachable,1742 else => unreachable,
1744 }1743 }
...@@ -1803,7 +1802,6 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void {...@@ -1803,7 +1802,6 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void {
1803}1802}
18041803
1805pub fn writeDbgAbbrev(self: *Dwarf) !void {1804pub fn writeDbgAbbrev(self: *Dwarf) !void {
1806 const gpa = self.allocator;
1807 // These are LEB encoded but since the values are all less than 1271805 // These are LEB encoded but since the values are all less than 127
1808 // we can simply append these bytes.1806 // we can simply append these bytes.
1809 // zig fmt: off1807 // zig fmt: off
...@@ -1960,10 +1958,10 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {...@@ -1960,10 +1958,10 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
1960 }1958 }
1961 },1959 },
1962 .wasm => {1960 .wasm => {
1963 const wasm_file = self.bin_file.cast(File.Wasm).?;1961 // const wasm_file = self.bin_file.cast(File.Wasm).?;
1964 const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;1962 // const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
1965 try debug_abbrev.resize(gpa, needed_size);1963 // try debug_abbrev.resize(gpa, needed_size);
1966 debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;1964 // debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
1967 },1965 },
1968 else => unreachable,1966 else => unreachable,
1969 }1967 }
...@@ -2055,9 +2053,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64)...@@ -2055,9 +2053,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64)
2055 }2053 }
2056 },2054 },
2057 .wasm => {2055 .wasm => {
2058 const wasm_file = self.bin_file.cast(File.Wasm).?;2056 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2059 const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;2057 // 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);2058 // try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
2061 },2059 },
2062 else => unreachable,2060 else => unreachable,
2063 }2061 }
...@@ -2318,7 +2316,6 @@ fn writeDbgInfoNopsToArrayList(...@@ -2318,7 +2316,6 @@ fn writeDbgInfoNopsToArrayList(
23182316
2319pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {2317pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
2320 const comp = self.bin_file.comp;2318 const comp = self.bin_file.comp;
2321 const gpa = comp.gpa;
2322 const target = comp.root_mod.resolved_target.result;2319 const target = comp.root_mod.resolved_target.result;
2323 const target_endian = target.cpu.arch.endian();2320 const target_endian = target.cpu.arch.endian();
2324 const ptr_width_bytes = self.ptrWidthBytes();2321 const ptr_width_bytes = self.ptrWidthBytes();
...@@ -2391,10 +2388,10 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {...@@ -2391,10 +2388,10 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
2391 }2388 }
2392 },2389 },
2393 .wasm => {2390 .wasm => {
2394 const wasm_file = self.bin_file.cast(File.Wasm).?;2391 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2395 const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;2392 // const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
2396 try debug_ranges.resize(gpa, needed_size);2393 // try debug_ranges.resize(gpa, needed_size);
2397 @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);2394 // @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
2398 },2395 },
2399 else => unreachable,2396 else => unreachable,
2400 }2397 }
...@@ -2548,14 +2545,15 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2548,14 +2545,15 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2548 }2545 }
2549 },2546 },
2550 .wasm => {2547 .wasm => {
2551 const wasm_file = self.bin_file.cast(File.Wasm).?;2548 _ = &buffer;
2552 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;2549 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2553 {2550 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2554 const src = debug_line.items[first_fn.off..];2551 // {
2555 @memcpy(buffer[0..src.len], src);2552 // const src = debug_line.items[first_fn.off..];
2556 }2553 // @memcpy(buffer[0..src.len], src);
2557 try debug_line.resize(self.allocator, debug_line.items.len + delta);2554 // }
2558 @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);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);
2559 },2557 },
2560 else => unreachable,2558 else => unreachable,
2561 }2559 }
...@@ -2604,9 +2602,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2604,9 +2602,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2604 }2602 }
2605 },2603 },
2606 .wasm => {2604 .wasm => {
2607 const wasm_file = self.bin_file.cast(File.Wasm).?;2605 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2608 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;2606 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2609 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);2607 // writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
2610 },2608 },
2611 else => unreachable,2609 else => unreachable,
2612 }2610 }
...@@ -2754,9 +2752,9 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {...@@ -2754,9 +2752,9 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
2754 }2752 }
2755 },2753 },
2756 .wasm => {2754 .wasm => {
2757 const wasm_file = self.bin_file.cast(File.Wasm).?;2755 // const wasm_file = self.bin_file.cast(File.Wasm).?;
2758 const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;2756 // const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2759 debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;2757 // debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
2760 },2758 },
2761 else => unreachable,2759 else => unreachable,
2762 }2760 }
src/link/Wasm.zig+543-1695
...@@ -1,67 +1,78 @@...@@ -1,67 +1,78 @@
1const Wasm = @This();1const Wasm = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");4
5const mem = std.mem;
6const Allocator = std.mem.Allocator;
7const assert = std.debug.assert;5const assert = std.debug.assert;
6const build_options = @import("build_options");
7const builtin = @import("builtin");
8const codegen = @import("../codegen.zig");
8const fs = std.fs;9const fs = std.fs;
9const leb = std.leb;10const 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");
19const link = @import("../link.zig");11const link = @import("../link.zig");
20const lldMain = @import("../main.zig").lldMain;12const lldMain = @import("../main.zig").lldMain;
13const log = std.log.scoped(.link);
14const gc_log = std.log.scoped(.gc);
15const mem = std.mem;
21const trace = @import("../tracy.zig").trace;16const trace = @import("../tracy.zig").trace;
22const build_options = @import("build_options");17const types = @import("Wasm/types.zig");
23const wasi_libc = @import("../wasi_libc.zig");18const wasi_libc = @import("../wasi_libc.zig");
24const Cache = std.Build.Cache;19
25const Type = @import("../type.zig").Type;
26const Value = @import("../Value.zig");
27const TypedValue = @import("../TypedValue.zig");
28const LlvmObject = @import("../codegen/llvm.zig").Object;
29const Air = @import("../Air.zig");20const 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");
30const Liveness = @import("../Liveness.zig");29const Liveness = @import("../Liveness.zig");
31const Symbol = @import("Wasm/Symbol.zig");30const LlvmObject = @import("../codegen/llvm.zig").Object;
31const Module = @import("../Module.zig");
32const Object = @import("Wasm/Object.zig");32const Object = @import("Wasm/Object.zig");
33const Archive = @import("Wasm/Archive.zig");33const Symbol = @import("Wasm/Symbol.zig");
34const types = @import("Wasm/types.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");
35pub const Relocation = types.Relocation;39pub const Relocation = types.Relocation;
3640
37pub const base_tag: link.File.Tag = .wasm;41pub const base_tag: link.File.Tag = .wasm;
3842
39base: link.File,43base: link.File,
44/// Symbol name of the entry function to export
40entry_name: ?[]const u8,45entry_name: ?[]const u8,
46/// When true, will allow undefined symbols
41import_symbols: bool,47import_symbols: bool,
48/// List of *global* symbol names to export to the host environment.
42export_symbol_names: []const []const u8,49export_symbol_names: []const []const u8,
50/// When defined, sets the start of the data section.
43global_base: ?u64,51global_base: ?u64,
52/// When defined, sets the initial memory size of the memory.
44initial_memory: ?u64,53initial_memory: ?u64,
54/// When defined, sets the maximum memory size of the memory.
45max_memory: ?u64,55max_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,
46/// Output name of the file60/// Output name of the file
47name: []const u8,61name: []const u8,
48/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.62/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
49llvm_object: ?*LlvmObject = null,63llvm_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) = .{},
50/// When importing objects from the host environment, a name must be supplied.69/// When importing objects from the host environment, a name must be supplied.
51/// LLVM uses "env" by default when none is given. This would be a good default for Zig70/// LLVM uses "env" by default when none is given. This would be a good default for Zig
52/// to support existing code.71/// to support existing code.
53/// TODO: Allow setting this through a flag?72/// TODO: Allow setting this through a flag?
54host_name: []const u8 = "env",73host_name: []const u8 = "env",
55/// List of all `Decl` that are currently alive.74/// List of symbols generated by the linker.
56/// Each index maps to the corresponding `Atom.Index`.75synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .{},
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) = .{},
65/// Maps atoms to their segment index76/// Maps atoms to their segment index
66atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},77atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
67/// List of all atoms.78/// List of all atoms.
...@@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},...@@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
107segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},118segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},
108/// Deduplicated string table for strings used by symbols, imports and exports.119/// Deduplicated string table for strings used by symbols, imports and exports.
109string_table: StringTable = .{},120string_table: StringTable = .{},
110/// Debug information for wasm
111dwarf: ?Dwarf = null,
112121
113// Output sections122// Output sections
114/// Output type section123/// Output type section
...@@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},...@@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
116/// Output function section where the key is the original125/// Output function section where the key is the original
117/// function index and the value is function.126/// function index and the value is function.
118/// This allows us to map multiple symbols to the same function.127/// 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) = .{},
120/// Output global section132/// Output global section
121wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},133wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
122/// Memory section134/// Memory section
...@@ -143,7 +155,7 @@ entry: ?u32 = null,...@@ -143,7 +155,7 @@ entry: ?u32 = null,
143function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},155function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
144156
145/// All object files and their data which are linked into the final binary157/// All object files and their data which are linked into the final binary
146objects: std.ArrayListUnmanaged(Object) = .{},158objects: std.ArrayListUnmanaged(File.Index) = .{},
147/// All archive files that are lazy loaded.159/// All archive files that are lazy loaded.
148/// e.g. when an undefined symbol references a symbol from the archive.160/// e.g. when an undefined symbol references a symbol from the archive.
149archives: std.ArrayListUnmanaged(Archive) = .{},161archives: std.ArrayListUnmanaged(Archive) = .{},
...@@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{},...@@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{},
165/// data of a symbol, such as its size, or its offset to perform a relocation.177/// data of a symbol, such as its size, or its offset to perform a relocation.
166/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.178/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
167symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},179symbol_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
203pub const Alignment = types.Alignment;181pub const Alignment = types.Alignment;
204182
...@@ -226,39 +204,33 @@ pub const Segment = struct {...@@ -226,39 +204,33 @@ pub const Segment = struct {
226 }204 }
227};205};
228206
229pub const Export = struct {
230 sym_index: ?u32 = null,
231};
232
233pub const SymbolLoc = struct {207pub const SymbolLoc = struct {
234 /// The index of the symbol within the specified file208 /// The index of the symbol within the specified file
235 index: u32,209 index: Symbol.Index,
236 /// The index of the object file where the symbol resides.210 /// The index of the object file where the symbol resides.
237 /// When this is `null` the symbol comes from a non-object file.211 file: File.Index,
238 file: ?u16,
239212
240 /// From a given location, returns the corresponding symbol in the wasm binary213 /// From a given location, returns the corresponding symbol in the wasm binary
241 pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol {214 pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol {
242 if (wasm_bin.discarded.get(loc)) |new_loc| {215 if (wasm_file.discarded.get(loc)) |new_loc| {
243 return new_loc.getSymbol(wasm_bin);216 return new_loc.getSymbol(wasm_file);
244 }217 }
245 if (loc.file) |object_index| {218 if (wasm_file.file(loc.file)) |obj_file| {
246 const object = wasm_bin.objects.items[object_index];219 return obj_file.symbol(loc.index);
247 return &object.symtable[loc.index];
248 }220 }
249 return &wasm_bin.symbols.items[loc.index];221 return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
250 }222 }
251223
252 /// From a given location, returns the name of the symbol.224 /// From a given location, returns the name of the symbol.
253 pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 {225 pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 {
254 if (wasm_bin.discarded.get(loc)) |new_loc| {226 if (wasm_file.discarded.get(loc)) |new_loc| {
255 return new_loc.getName(wasm_bin);227 return new_loc.getName(wasm_file);
256 }228 }
257 if (loc.file) |object_index| {229 if (wasm_file.file(loc.file)) |obj_file| {
258 const object = wasm_bin.objects.items[object_index];230 return obj_file.symbolName(loc.index);
259 return object.string_table.get(object.symtable[loc.index].name);
260 }231 }
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);
262 }234 }
263235
264 /// From a given symbol location, returns the final location.236 /// From a given symbol location, returns the final location.
...@@ -266,9 +238,9 @@ pub const SymbolLoc = struct {...@@ -266,9 +238,9 @@ pub const SymbolLoc = struct {
266 /// in a different file, this will return said location.238 /// in a different file, this will return said location.
267 /// If the symbol wasn't replaced by another, this will return239 /// If the symbol wasn't replaced by another, this will return
268 /// the given location itwasm.240 /// the given location itwasm.
269 pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc {241 pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc {
270 if (wasm_bin.discarded.get(loc)) |new_loc| {242 if (wasm_file.discarded.get(loc)) |new_loc| {
271 return new_loc.finalLoc(wasm_bin);243 return new_loc.finalLoc(wasm_file);
272 }244 }
273 return loc;245 return loc;
274 }246 }
...@@ -280,9 +252,9 @@ pub const InitFuncLoc = struct {...@@ -280,9 +252,9 @@ pub const InitFuncLoc = struct {
280 /// object file index in the list of objects.252 /// object file index in the list of objects.
281 /// Unlike `SymbolLoc` this cannot be `null` as we never define253 /// Unlike `SymbolLoc` this cannot be `null` as we never define
282 /// our own ctors.254 /// our own ctors.
283 file: u16,255 file: File.Index,
284 /// Symbol index within the corresponding object file.256 /// Symbol index within the corresponding object file.
285 index: u32,257 index: Symbol.Index,
286 /// The priority in which the constructor must be called.258 /// The priority in which the constructor must be called.
287 priority: u32,259 priority: u32,
288260
...@@ -459,7 +431,7 @@ pub fn createEmpty(...@@ -459,7 +431,7 @@ pub fn createEmpty(
459 // can be passed to LLD.431 // can be passed to LLD.
460 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;432 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, .{
463 .truncate = true,435 .truncate = true,
464 .read = true,436 .read = true,
465 .mode = if (fs.has_executable_bit)437 .mode = if (fs.has_executable_bit)
...@@ -470,7 +442,6 @@ pub fn createEmpty(...@@ -470,7 +442,6 @@ pub fn createEmpty(
470 else442 else
471 0,443 0,
472 });444 });
473 wasm.base.file = file;
474 wasm.name = sub_path;445 wasm.name = sub_path;
475446
476 // create stack pointer symbol447 // create stack pointer symbol
...@@ -550,6 +521,7 @@ pub fn createEmpty(...@@ -550,6 +521,7 @@ pub fn createEmpty(
550 const symbol = loc.getSymbol(wasm);521 const symbol = loc.getSymbol(wasm);
551 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);522 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
552 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);523 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
524 symbol.mark();
553 try wasm.wasm_globals.append(gpa, .{525 try wasm.wasm_globals.append(gpa, .{
554 .global_type = .{ .valtype = .i32, .mutable = true },526 .global_type = .{ .valtype = .i32, .mutable = true },
555 .init = .{ .i32_const = undefined },527 .init = .{ .i32_const = undefined },
...@@ -560,6 +532,7 @@ pub fn createEmpty(...@@ -560,6 +532,7 @@ pub fn createEmpty(
560 const symbol = loc.getSymbol(wasm);532 const symbol = loc.getSymbol(wasm);
561 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);533 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
562 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);534 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
535 symbol.mark();
563 try wasm.wasm_globals.append(gpa, .{536 try wasm.wasm_globals.append(gpa, .{
564 .global_type = .{ .valtype = .i32, .mutable = false },537 .global_type = .{ .valtype = .i32, .mutable = false },
565 .init = .{ .i32_const = undefined },538 .init = .{ .i32_const = undefined },
...@@ -570,6 +543,7 @@ pub fn createEmpty(...@@ -570,6 +543,7 @@ pub fn createEmpty(
570 const symbol = loc.getSymbol(wasm);543 const symbol = loc.getSymbol(wasm);
571 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);544 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
572 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);545 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
546 symbol.mark();
573 try wasm.wasm_globals.append(gpa, .{547 try wasm.wasm_globals.append(gpa, .{
574 .global_type = .{ .valtype = .i32, .mutable = false },548 .global_type = .{ .valtype = .i32, .mutable = false },
575 .init = .{ .i32_const = undefined },549 .init = .{ .i32_const = undefined },
...@@ -582,9 +556,64 @@ pub fn createEmpty(...@@ -582,9 +556,64 @@ pub fn createEmpty(
582 }556 }
583 }557 }
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
585 return wasm;573 return wasm;
586}574}
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
588/// For a given name, creates a new global synthetic symbol.617/// For a given name, creates a new global synthetic symbol.
589/// Leaves index undefined and the default flags (0).618/// Leaves index undefined and the default flags (0).
590fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc {619fn 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...@@ -594,10 +623,10 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
594}623}
595624
596fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {625fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {
597 const sym_index = @as(u32, @intCast(wasm.symbols.items.len));626 const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
598 const loc: SymbolLoc = .{ .index = sym_index, .file = null };627 const loc: SymbolLoc = .{ .index = sym_index, .file = .null };
599 const gpa = wasm.base.comp.gpa;628 const gpa = wasm.base.comp.gpa;
600 try wasm.symbols.append(gpa, .{629 try wasm.synthetic_symbols.append(gpa, .{
601 .name = name_offset,630 .name = name_offset,
602 .flags = 0,631 .flags = 0,
603 .tag = tag,632 .tag = tag,
...@@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !...@@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !
609 return loc;638 return loc;
610}639}
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
630fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {641fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
631 for (files) |path| {642 for (files) |path| {
632 if (try wasm.parseObjectFile(path)) continue;643 if (try wasm.parseObjectFile(path)) continue;
...@@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {...@@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
639/// file and parsed successfully. Returns false when file is not an object file.650/// file and parsed successfully. Returns false when file is not an object file.
640/// May return an error instead when parsing failed.651/// May return an error instead when parsing failed.
641fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {652fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
642 const file = try fs.cwd().openFile(path, .{});653 const obj_file = try fs.cwd().openFile(path, .{});
643 errdefer file.close();654 errdefer obj_file.close();
644655
645 const gpa = wasm.base.comp.gpa;656 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) {
647 error.InvalidMagicByte, error.NotObjectFile => return false,658 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 },
649 };665 };
650 errdefer object.deinit(gpa);666 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);
652 return true;670 return true;
653}671}
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
674/// Creates a new empty `Atom` and returns its `Atom.Index`673/// 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 {
676 const gpa = wasm.base.comp.gpa;675 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);
678 const atom = try wasm.managed_atoms.addOne(gpa);677 const atom = try wasm.managed_atoms.addOne(gpa);
679 atom.* = Atom.empty;678 atom.* = .{ .file = file_index, .sym_index = sym_index };
680 atom.sym_index = try wasm.allocateSymbol();679 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
681 try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index);
682680
683 return index;681 return index;
684}682}
685683
686pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {684pub 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)];
688}686}
689687
690pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {688pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
691 return &wasm.managed_atoms.items[index];689 return &wasm.managed_atoms.items[@intFromEnum(index)];
692}690}
693691
694/// Parses an archive file and will then parse each object file692/// 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 {...@@ -702,11 +700,11 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
702fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {700fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
703 const gpa = wasm.base.comp.gpa;701 const gpa = wasm.base.comp.gpa;
704702
705 const file = try fs.cwd().openFile(path, .{});703 const archive_file = try fs.cwd().openFile(path, .{});
706 errdefer file.close();704 errdefer archive_file.close();
707705
708 var archive: Archive = .{706 var archive: Archive = .{
709 .file = file,707 .file = archive_file,
710 .name = path,708 .name = path,
711 };709 };
712 archive.parse(gpa) catch |err| switch (err) {710 archive.parse(gpa) catch |err| switch (err) {
...@@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714 archive.deinit(gpa);712 archive.deinit(gpa);
715 return false;713 return false;
716 },714 },
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 },
718 };721 };
719722
720 if (!force_load) {723 if (!force_load) {
...@@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
736 }739 }
737740
738 for (offsets.keys()) |file_offset| {741 for (offsets.keys()) |file_offset| {
739 const object = try wasm.objects.addOne(gpa);742 var object = archive.parseObject(wasm, file_offset) catch |e| {
740 object.* = try archive.parseObject(gpa, file_offset);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);
741 }751 }
742752
743 return true;753 return true;
...@@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool {...@@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool {
752 return false;762 return false;
753}763}
754764
755fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {765fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
756 const gpa = wasm.base.comp.gpa;766 const gpa = wasm.base.comp.gpa;
757 const object: Object = wasm.objects.items[object_index];767 const obj_file = wasm.file(file_index).?;
758 log.debug("Resolving symbols in object: '{s}'", .{object.name});768 log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()});
759769
760 for (object.symtable, 0..) |symbol, i| {770 for (obj_file.symbols(), 0..) |symbol, i| {
761 const sym_index = @as(u32, @intCast(i));771 const sym_index: Symbol.Index = @enumFromInt(i);
762 const location: SymbolLoc = .{772 const location: SymbolLoc = .{ .file = file_index, .index = sym_index };
763 .file = object_index,773 const sym_name = obj_file.string(symbol.name);
764 .index = sym_index,
765 };
766 const sym_name = object.string_table.get(symbol.name);
767 if (mem.eql(u8, sym_name, "__indirect_function_table")) {774 if (mem.eql(u8, sym_name, "__indirect_function_table")) {
768 continue;775 continue;
769 }776 }
...@@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
771778
772 if (symbol.isLocal()) {779 if (symbol.isLocal()) {
773 if (symbol.isUndefined()) {780 if (symbol.isUndefined()) {
774 log.err("Local symbols are not allowed to reference imports", .{});781 var err = try wasm.addErrorWithNotes(1);
775 log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name });782 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
776 return error.UndefinedLocal;783 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
777 }784 }
778 try wasm.resolved_symbols.putNoClobber(gpa, location, {});785 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
779 continue;786 continue;
...@@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
792799
793 const existing_loc = maybe_existing.value_ptr.*;800 const existing_loc = maybe_existing.value_ptr.*;
794 const existing_sym: *Symbol = existing_loc.getSymbol(wasm);801 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: {804 const existing_file_path = if (existing_file) |existing_obj_file|
797 break :blk wasm.objects.items[file].name;805 existing_obj_file.path()
798 } else wasm.name;806 else
807 wasm.name;
799808
800 if (!existing_sym.isUndefined()) outer: {809 if (!existing_sym.isUndefined()) outer: {
801 if (!symbol.isUndefined()) inner: {810 if (!symbol.isUndefined()) inner: {
...@@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
806 break :outer; // existing is weak, while new one isn't. Replace it.815 break :outer; // existing is weak, while new one isn't. Replace it.
807 }816 }
808 // both are defined and weak, we have a symbol collision.817 // both are defined and weak, we have a symbol collision.
809 log.err("symbol '{s}' defined multiple times", .{sym_name});818 var err = try wasm.addErrorWithNotes(2);
810 log.err(" first definition in '{s}'", .{existing_file_path});819 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
811 log.err(" next definition in '{s}'", .{object.name});820 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
812 return error.SymbolCollision;821 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
813 }822 }
814823
815 try wasm.discarded.put(gpa, location, existing_loc);824 try wasm.discarded.put(gpa, location, existing_loc);
...@@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
817 }826 }
818827
819 if (symbol.tag != existing_sym.tag) {828 if (symbol.tag != existing_sym.tag) {
820 log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) });829 var err = try wasm.addErrorWithNotes(2);
821 log.err(" first definition in '{s}'", .{existing_file_path});830 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
822 log.err(" next definition in '{s}'", .{object.name});831 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
823 return error.SymbolMismatchingType;832 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
824 }833 }
825834
826 if (existing_sym.isUndefined() and symbol.isUndefined()) {835 if (existing_sym.isUndefined() and symbol.isUndefined()) {
827 // only verify module/import name for function symbols836 // only verify module/import name for function symbols
828 if (symbol.tag == .function) {837 if (symbol.tag == .function) {
829 const existing_name = if (existing_loc.file) |file_index| blk: {838 const existing_name = if (existing_file) |existing_obj| blk: {
830 const obj = wasm.objects.items[file_index];839 const imp = existing_obj.import(existing_loc.index);
831 const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;840 break :blk existing_obj.string(imp.module_name);
832 break :blk obj.string_table.get(name_index);
833 } else blk: {841 } else blk: {
834 const name_index = wasm.imports.get(existing_loc).?.module_name;842 const name_index = wasm.imports.get(existing_loc).?.module_name;
835 break :blk wasm.string_table.get(name_index);843 break :blk wasm.string_table.get(name_index);
836 };844 };
837845
838 const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;846 const imp = obj_file.import(sym_index);
839 const module_name = object.string_table.get(module_index);847 const module_name = obj_file.string(imp.module_name);
840 if (!mem.eql(u8, existing_name, module_name)) {848 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}'", .{
842 sym_name,851 sym_name,
843 existing_name,852 existing_name,
844 module_name,853 module_name,
845 });854 });
846 log.err(" first definition in '{s}'", .{existing_file_path});855 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
847 log.err(" next definition in '{s}'", .{object.name});856 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
848 return error.ModuleNameMismatch;
849 }857 }
850 }858 }
851859
...@@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
858 const existing_ty = wasm.getGlobalType(existing_loc);866 const existing_ty = wasm.getGlobalType(existing_loc);
859 const new_ty = wasm.getGlobalType(location);867 const new_ty = wasm.getGlobalType(location);
860 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {868 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
861 log.err("symbol '{s}' mismatching global types", .{sym_name});869 var err = try wasm.addErrorWithNotes(2);
862 log.err(" first definition in '{s}'", .{existing_file_path});870 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
863 log.err(" next definition in '{s}'", .{object.name});871 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
864 return error.GlobalTypeMismatch;872 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
865 }873 }
866 }874 }
867875
...@@ -869,11 +877,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -869,11 +877,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
869 const existing_ty = wasm.getFunctionSignature(existing_loc);877 const existing_ty = wasm.getFunctionSignature(existing_loc);
870 const new_ty = wasm.getFunctionSignature(location);878 const new_ty = wasm.getFunctionSignature(location);
871 if (!existing_ty.eql(new_ty)) {879 if (!existing_ty.eql(new_ty)) {
872 log.err("symbol '{s}' mismatching function signatures.", .{sym_name});880 var err = try wasm.addErrorWithNotes(3);
873 log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty });881 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
874 log.err(" first definition in '{s}'", .{existing_file_path});882 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
875 log.err(" next definition in '{s}'", .{object.name});883 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
876 return error.FunctionSignatureMismatch;884 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
877 }885 }
878 }886 }
879887
...@@ -888,7 +896,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {...@@ -888,7 +896,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
888 // simply overwrite with the new symbol896 // simply overwrite with the new symbol
889 log.debug("Overwriting symbol '{s}'", .{sym_name});897 log.debug("Overwriting symbol '{s}'", .{sym_name});
890 log.debug(" old definition in '{s}'", .{existing_file_path});898 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()});
892 try wasm.discarded.putNoClobber(gpa, existing_loc, location);900 try wasm.discarded.putNoClobber(gpa, existing_loc, location);
893 maybe_existing.value_ptr.* = location;901 maybe_existing.value_ptr.* = location;
894 try wasm.globals.put(gpa, sym_name_index, location);902 try wasm.globals.put(gpa, sym_name_index, location);
...@@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
920 // Symbol is found in unparsed object file within current archive.928 // Symbol is found in unparsed object file within current archive.
921 // Parse object and and resolve symbols again before we check remaining929 // Parse object and and resolve symbols again before we check remaining
922 // undefined symbols.930 // undefined symbols.
923 const object_file_index: u16 = @intCast(wasm.objects.items.len);931 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
924 const object = try archive.parseObject(gpa, offset.items[0]);932 var err_note = try wasm.addErrorWithNotes(1);
925 try wasm.objects.append(gpa, object);933 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
926 try wasm.resolveSymbolsInObject(object_file_index);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
928 // continue loop for any remaining undefined symbols that still exist942 // continue loop for any remaining undefined symbols that still exist
929 // after resolving last object file943 // after resolving last object file
...@@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {...@@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
953 if (!wasm.hasPassiveInitializationSegments()) {967 if (!wasm.hasPassiveInitializationSegments()) {
954 return;968 return;
955 }969 }
970 const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function);
971 sym_loc.getSymbol(wasm).mark();
956972
957 const flag_address: u32 = if (shared_memory) address: {973 const flag_address: u32 = if (shared_memory) address: {
958 // when we have passive initialization segments and shared memory974 // when we have passive initialization segments and shared memory
...@@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {...@@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
1115 return;1131 return;
1116 }1132 }
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();
1119 var function_body = std.ArrayList(u8).init(gpa);1136 var function_body = std.ArrayList(u8).init(gpa);
1120 defer function_body.deinit();1137 defer function_body.deinit();
1121 const writer = function_body.writer();1138 const writer = function_body.writer();
...@@ -1185,9 +1202,10 @@ fn validateFeatures(...@@ -1185,9 +1202,10 @@ fn validateFeatures(
11851202
1186 // extract all the used, disallowed and required features from each1203 // extract all the used, disallowed and required features from each
1187 // linked object file so we can test them.1204 // 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;
1189 for (object.features) |feature| {1207 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);
1191 switch (feature.prefix) {1209 switch (feature.prefix) {
1192 .used => {1210 .used => {
1193 used[@intFromEnum(feature.tag)] = value;1211 used[@intFromEnum(feature.tag)] = value;
...@@ -1218,29 +1236,30 @@ fn validateFeatures(...@@ -1218,29 +1236,30 @@ fn validateFeatures(
1218 allowed[used_index] = is_enabled;1236 allowed[used_index] = is_enabled;
1219 emit_features_count.* += @intFromBool(is_enabled);1237 emit_features_count.* += @intFromBool(is_enabled);
1220 } else if (is_enabled and !allowed[used_index]) {1238 } 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))});1239 var err = try wasm.addErrorWithNotes(1);
1222 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});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});
1223 valid_feature_set = false;1242 valid_feature_set = false;
1224 }1243 }
1225 }1244 }
12261245
1227 if (!valid_feature_set) {1246 if (!valid_feature_set) {
1228 return error.InvalidFeatureSet;1247 return error.FlushFailure;
1229 }1248 }
12301249
1231 if (shared_memory) {1250 if (shared_memory) {
1232 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];1251 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
1233 if (@as(u1, @truncate(disallowed_feature)) != 0) {1252 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1234 log.err(1253 try wasm.addErrorWithoutNotes(
1235 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",1254 "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},
1237 );1256 );
1238 valid_feature_set = false;1257 valid_feature_set = false;
1239 }1258 }
12401259
1241 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1260 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1242 if (!allowed[@intFromEnum(feature)]) {1261 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});
1244 }1263 }
1245 }1264 }
1246 }1265 }
...@@ -1248,21 +1267,23 @@ fn validateFeatures(...@@ -1248,21 +1267,23 @@ fn validateFeatures(
1248 if (has_tls) {1267 if (has_tls) {
1249 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1268 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1250 if (!allowed[@intFromEnum(feature)]) {1269 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});
1252 }1271 }
1253 }1272 }
1254 }1273 }
1255 // For each linked object, validate the required and disallowed features1274 // For each linked object, validate the required and disallowed features
1256 for (wasm.objects.items) |object| {1275 for (wasm.objects.items) |file_index| {
1257 var object_used_features = [_]bool{false} ** known_features_count;1276 var object_used_features = [_]bool{false} ** known_features_count;
1277 const object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
1258 for (object.features) |feature| {1278 for (object.features) |feature| {
1259 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.1279 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
1260 // from here a feature is always used1280 // from here a feature is always used
1261 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];1281 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1262 if (@as(u1, @truncate(disallowed_feature)) != 0) {1282 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1263 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});1283 var err = try wasm.addErrorWithNotes(2);
1264 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});1284 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
1265 log.err(" used in '{s}'", .{object.name});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});
1266 valid_feature_set = false;1287 valid_feature_set = false;
1267 }1288 }
12681289
...@@ -1273,16 +1294,17 @@ fn validateFeatures(...@@ -1273,16 +1294,17 @@ fn validateFeatures(
1273 for (required, 0..) |required_feature, feature_index| {1294 for (required, 0..) |required_feature, feature_index| {
1274 const is_required = @as(u1, @truncate(required_feature)) != 0;1295 const is_required = @as(u1, @truncate(required_feature)) != 0;
1275 if (is_required and !object_used_features[feature_index]) {1296 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))});1297 var err = try wasm.addErrorWithNotes(2);
1277 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});1298 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1278 log.err(" missing in '{s}'", .{object.name});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});
1279 valid_feature_set = false;1301 valid_feature_set = false;
1280 }1302 }
1281 }1303 }
1282 }1304 }
12831305
1284 if (!valid_feature_set) {1306 if (!valid_feature_set) {
1285 return error.InvalidFeatureSet;1307 return error.FlushFailure;
1286 }1308 }
12871309
1288 to_emit.* = allowed;1310 to_emit.* = allowed;
...@@ -1329,13 +1351,6 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -1329,13 +1351,6 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
1329 }1351 }
1330 }1352 }
1331 }1353 }
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 }
1339}1354}
13401355
1341// Tries to find a global symbol by its name. Returns null when not found,1356// Tries to find a global symbol by its name. Returns null when not found,
...@@ -1355,16 +1370,18 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {...@@ -1355,16 +1370,18 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1355 const symbol = undef.getSymbol(wasm);1370 const symbol = undef.getSymbol(wasm);
1356 if (symbol.tag == .data) {1371 if (symbol.tag == .data) {
1357 found_undefined_symbols = true;1372 found_undefined_symbols = true;
1358 const file_name = if (undef.file) |file_index| name: {1373 const file_name = if (wasm.file(undef.file)) |obj_file|
1359 break :name wasm.objects.items[file_index].name;1374 obj_file.path()
1360 } else wasm.name;1375 else
1376 wasm.name;
1361 const symbol_name = undef.getName(wasm);1377 const symbol_name = undef.getName(wasm);
1362 log.err("could not resolve undefined symbol '{s}'", .{symbol_name});1378 var err = try wasm.addErrorWithNotes(1);
1363 log.err(" defined in '{s}'", .{file_name});1379 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
1380 try err.addNote(wasm, "defined in '{s}'", .{file_name});
1364 }1381 }
1365 }1382 }
1366 if (found_undefined_symbols) {1383 if (found_undefined_symbols) {
1367 return error.UndefinedSymbol;1384 return error.FlushFailure;
1368 }1385 }
1369}1386}
13701387
...@@ -1378,54 +1395,28 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1378,54 +1395,28 @@ pub fn deinit(wasm: *Wasm) void {
1378 for (wasm.segment_info.values()) |segment_info| {1395 for (wasm.segment_info.values()) |segment_info| {
1379 gpa.free(segment_info.name);1396 gpa.free(segment_info.name);
1380 }1397 }
1381 for (wasm.objects.items) |*object| {1398 if (wasm.zigObjectPtr()) |zig_obj| {
1382 object.deinit(gpa);1399 zig_obj.deinit(wasm);
1400 }
1401 for (wasm.objects.items) |obj_index| {
1402 wasm.file(obj_index).?.object.deinit(gpa);
1383 }1403 }
13841404
1385 for (wasm.archives.items) |*archive| {1405 for (wasm.archives.items) |*archive| {
1386 archive.deinit(gpa);1406 archive.deinit(gpa);
1387 }1407 }
13881408
1389 // For decls and anon decls we free the memory of its atoms.1409 if (wasm.findGlobalSymbol("__wasm_init_tls")) |loc| {
1390 // The memory of atoms parsed from object files is managed by1410 const atom = wasm.symbol_atom.get(loc).?;
1391 // the object file itself, and therefore we can skip those.1411 wasm.getAtomPtr(atom).deinit(gpa);
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);
1416 }1412 }
14171413
1418 wasm.decls.deinit(gpa);1414 wasm.synthetic_symbols.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);
1423 wasm.globals.deinit(gpa);1415 wasm.globals.deinit(gpa);
1424 wasm.resolved_symbols.deinit(gpa);1416 wasm.resolved_symbols.deinit(gpa);
1425 wasm.undefs.deinit(gpa);1417 wasm.undefs.deinit(gpa);
1426 wasm.discarded.deinit(gpa);1418 wasm.discarded.deinit(gpa);
1427 wasm.symbol_atom.deinit(gpa);1419 wasm.symbol_atom.deinit(gpa);
1428 wasm.export_names.deinit(gpa);
1429 wasm.atoms.deinit(gpa);1420 wasm.atoms.deinit(gpa);
1430 wasm.managed_atoms.deinit(gpa);1421 wasm.managed_atoms.deinit(gpa);
1431 wasm.segments.deinit(gpa);1422 wasm.segments.deinit(gpa);
...@@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void {
1445 wasm.exports.deinit(gpa);1436 wasm.exports.deinit(gpa);
14461437
1447 wasm.string_table.deinit(gpa);1438 wasm.string_table.deinit(gpa);
1448 wasm.synthetic_functions.deinit(gpa);1439 wasm.files.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;
1475}1440}
14761441
1477pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {1442pub 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:...@@ -1479,64 +1444,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air:
1479 @panic("Attempted to compile for object format that was disabled by build configuration");1444 @panic("Attempted to compile for object format that was disabled by build configuration");
1480 }1445 }
1481 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);1446 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
14821447 try wasm.zigObjectPtr().?.updateFunc(wasm, mod, func_index, air, liveness);
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);
1540}1448}
15411449
1542// Generate code for the Decl, storing it in memory to be later written to1450// 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) !...@@ -1546,84 +1454,12 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !
1546 @panic("Attempted to compile for object format that was disabled by build configuration");1454 @panic("Attempted to compile for object format that was disabled by build configuration");
1547 }1455 }
1548 if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);1456 if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
15491457 try wasm.zigObjectPtr().?.updateDecl(wasm, mod, decl_index);
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);
1595}1458}
15961459
1597pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void {1460pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void {
1598 if (wasm.llvm_object) |_| return;1461 if (wasm.llvm_object) |_| return;
1599 if (wasm.dwarf) |*dw| {1462 try wasm.zigObjectPtr().?.updateDeclLineNumber(mod, decl_index);
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);
1627}1463}
16281464
1629/// From a given symbol location, returns its `wasm.GlobalType`.1465/// From a given symbol location, returns its `wasm.GlobalType`.
...@@ -1632,13 +1468,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {...@@ -1632,13 +1468,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
1632 const symbol = loc.getSymbol(wasm);1468 const symbol = loc.getSymbol(wasm);
1633 assert(symbol.tag == .global);1469 assert(symbol.tag == .global);
1634 const is_undefined = symbol.isUndefined();1470 const is_undefined = symbol.isUndefined();
1635 if (loc.file) |file_index| {1471 if (wasm.file(loc.file)) |obj_file| {
1636 const obj: Object = wasm.objects.items[file_index];
1637 if (is_undefined) {1472 if (is_undefined) {
1638 return obj.findImport(.global, symbol.index).kind.global;1473 return obj_file.import(loc.index).kind.global;
1639 }1474 }
1640 const import_global_count = obj.importedCountByKind(.global);1475 return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type;
1641 return obj.globals[symbol.index - import_global_count].global_type;
1642 }1476 }
1643 if (is_undefined) {1477 if (is_undefined) {
1644 return wasm.imports.get(loc).?.kind.global;1478 return wasm.imports.get(loc).?.kind.global;
...@@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {...@@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
1652 const symbol = loc.getSymbol(wasm);1486 const symbol = loc.getSymbol(wasm);
1653 assert(symbol.tag == .function);1487 assert(symbol.tag == .function);
1654 const is_undefined = symbol.isUndefined();1488 const is_undefined = symbol.isUndefined();
1655 if (loc.file) |file_index| {1489 if (wasm.file(loc.file)) |obj_file| {
1656 const obj: Object = wasm.objects.items[file_index];
1657 if (is_undefined) {1490 if (is_undefined) {
1658 const ty_index = obj.findImport(.function, symbol.index).kind.function;1491 const ty_index = obj_file.import(loc.index).kind.function;
1659 return obj.func_types[ty_index];1492 return obj_file.funcTypes()[ty_index];
1660 }1493 }
1661 const import_function_count = obj.importedCountByKind(.function);1494 const type_index = obj_file.function(loc.index).type_index;
1662 const type_index = obj.functions[symbol.index - import_function_count].type_index;1495 return obj_file.funcTypes()[type_index];
1663 return obj.func_types[type_index];
1664 }1496 }
1665 if (is_undefined) {1497 if (is_undefined) {
1666 const ty_index = wasm.imports.get(loc).?.kind.function;1498 const ty_index = wasm.imports.get(loc).?.kind.function;
...@@ -1673,118 +1505,16 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {...@@ -1673,118 +1505,16 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
1673/// Returns the symbol index of the local1505/// Returns the symbol index of the local
1674/// The given `decl` is the parent decl whom owns the constant.1506/// The given `decl` is the parent decl whom owns the constant.
1675pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {1507pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
1676 const gpa = wasm.base.comp.gpa;1508 return wasm.zigObjectPtr().?.lowerUnnamedConst(wasm, tv, decl_index);
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 };
1752}1509}
17531510
1754/// Returns the symbol index from a symbol of which its flag is set global,1511/// Returns the symbol index from a symbol of which its flag is set global,
1755/// such as an exported or imported symbol.1512/// such as an exported or imported symbol.
1756/// If the symbol does not yet exist, creates a new one symbol instead1513/// If the symbol does not yet exist, creates a new one symbol instead
1757/// and then returns the index to it.1514/// 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 {
1759 _ = lib_name;1516 _ = lib_name;
1760 const gpa = wasm.base.comp.gpa;1517 return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name);
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;
1788}1518}
17891519
1790/// For a given decl, find the given symbol index's atom, and create a relocation for the type.1520/// 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(...@@ -1794,42 +1524,7 @@ pub fn getDeclVAddr(
1794 decl_index: InternPool.DeclIndex,1524 decl_index: InternPool.DeclIndex,
1795 reloc_info: link.File.RelocInfo,1525 reloc_info: link.File.RelocInfo,
1796) !u64 {1526) !u64 {
1797 const target = wasm.base.comp.root_mod.resolved_target.result;1527 return wasm.zigObjectPtr().?.getDeclVAddr(wasm, decl_index, reloc_info);
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;
1833}1528}
18341529
1835pub fn lowerAnonDecl(1530pub fn lowerAnonDecl(
...@@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl(...@@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl(
1838 explicit_alignment: Alignment,1533 explicit_alignment: Alignment,
1839 src_loc: Module.SrcLoc,1534 src_loc: Module.SrcLoc,
1840) !codegen.Result {1535) !codegen.Result {
1841 const gpa = wasm.base.comp.gpa;1536 return wasm.zigObjectPtr().?.lowerAnonDecl(wasm, decl_val, explicit_alignment, src_loc);
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;
1867}1537}
18681538
1869pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {1539pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
1870 const gpa = wasm.base.comp.gpa;1540 return wasm.zigObjectPtr().?.getAnonDeclVAddr(wasm, decl_val, reloc_info);
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;
1905}1541}
19061542
1907pub fn deleteDeclExport(1543pub fn deleteDeclExport(
...@@ -1909,19 +1545,8 @@ pub fn deleteDeclExport(...@@ -1909,19 +1545,8 @@ pub fn deleteDeclExport(
1909 decl_index: InternPool.DeclIndex,1545 decl_index: InternPool.DeclIndex,
1910 name: InternPool.NullTerminatedString,1546 name: InternPool.NullTerminatedString,
1911) void {1547) void {
1912 _ = name;
1913 if (wasm.llvm_object) |_| return;1548 if (wasm.llvm_object) |_| return;
1914 const atom_index = wasm.decls.get(decl_index) orelse return;1549 return wasm.zigObjectPtr().?.deleteDeclExport(wasm, decl_index, name);
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 }
1925}1550}
19261551
1927pub fn updateExports(1552pub fn updateExports(
...@@ -1934,159 +1559,12 @@ pub fn updateExports(...@@ -1934,159 +1559,12 @@ pub fn updateExports(
1934 @panic("Attempted to compile for object format that was disabled by build configuration");1559 @panic("Attempted to compile for object format that was disabled by build configuration");
1935 }1560 }
1936 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);1561 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
19371562 return wasm.zigObjectPtr().?.updateExports(wasm, mod, exported, exports);
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 }
2051}1563}
20521564
2053pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {1565pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
2054 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);1566 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
2055 const gpa = wasm.base.comp.gpa;1567 return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index);
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);
2090}1568}
20911569
2092/// Assigns indexes to all indirect functions.1570/// Assigns indexes to all indirect functions.
...@@ -2118,203 +1596,6 @@ fn mapFunctionTable(wasm: *Wasm) void {...@@ -2118,203 +1596,6 @@ fn mapFunctionTable(wasm: *Wasm) void {
2118 }1596 }
2119}1597}
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
2318/// From a given index, append the given `Atom` at the back of the linked list.1599/// From a given index, append the given `Atom` at the back of the linked list.
2319/// Simply inserts it into the map of atoms when it doesn't exist yet.1600/// Simply inserts it into the map of atoms when it doesn't exist yet.
2320pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {1601pub 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...@@ -2328,40 +1609,9 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void
2328 }1609 }
2329}1610}
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
2361fn allocateAtoms(wasm: *Wasm) !void {1612fn allocateAtoms(wasm: *Wasm) !void {
2362 // first sort the data segments1613 // first sort the data segments
2363 try sortDataSegments(wasm);1614 try sortDataSegments(wasm);
2364 try allocateDebugAtoms(wasm);
23651615
2366 var it = wasm.atoms.iterator();1616 var it = wasm.atoms.iterator();
2367 while (it.next()) |entry| {1617 while (it.next()) |entry| {
...@@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void {
2379 // Ensure we get the original symbol, so we verify the correct symbol on whether1629 // Ensure we get the original symbol, so we verify the correct symbol on whether
2380 // it is dead or not and ensure an atom is removed when dead.1630 // it is dead or not and ensure an atom is removed when dead.
2381 // This is required as we may have parsed aliases into atoms.1631 // This is required as we may have parsed aliases into atoms.
2382 const sym = if (symbol_loc.file) |object_index| sym: {1632 const sym = if (wasm.file(symbol_loc.file)) |obj_file|
2383 const object = wasm.objects.items[object_index];1633 obj_file.symbol(symbol_loc.index).*
2384 break :sym object.symtable[symbol_loc.index];1634 else
2385 } else wasm.symbols.items[symbol_loc.index];1635 wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)];
23861636
2387 // Dead symbols must be unlinked from the linked-list to prevent them1637 // Dead symbols must be unlinked from the linked-list to prevent them
2388 // from being emit into the binary.1638 // from being emit into the binary.
2389 if (sym.isDead()) {1639 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) {
2391 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update1641 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
2392 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that1642 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
2393 // was removed and therefore do not emit any code at all.1643 // was removed and therefore do not emit any code at all.
2394 entry.value_ptr.* = atom.prev.?;1644 entry.value_ptr.* = atom.prev;
2395 }1645 }
2396 atom_index = atom.prev orelse break;1646 if (atom.prev == .null) break;
2397 atom.prev = null;1647 atom_index = atom.prev;
1648 atom.prev = .null;
2398 continue;1649 continue;
2399 }1650 }
2400 offset = @intCast(atom.alignment.forward(offset));1651 offset = @intCast(atom.alignment.forward(offset));
...@@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void {
2406 atom.size,1657 atom.size,
2407 });1658 });
2408 offset += atom.size;1659 offset += atom.size;
2409 atom_index = atom.prev orelse break;1660 if (atom.prev == .null) break;
1661 atom_index = atom.prev;
2410 }1662 }
2411 segment.size = @intCast(segment.alignment.forward(offset));1663 segment.size = @intCast(segment.alignment.forward(offset));
2412 }1664 }
...@@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {...@@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {
24281680
2429 const atom = wasm.getAtom(atom_index);1681 const atom = wasm.getAtom(atom_index);
2430 const merge_segment = wasm.base.comp.config.output_mode != .Obj;1682 const merge_segment = wasm.base.comp.config.output_mode != .Obj;
2431 const segment_info = if (atom.file) |object_index| blk: {1683 const segment_info = if (atom.file != .null)
2432 break :blk wasm.objects.items[object_index].segment_info;1684 wasm.file(atom.file).?.segmentInfo()
2433 } else wasm.segment_info.values();1685 else
1686 wasm.segment_info.values();
2434 const segment_name = segment_info[symbol.index].outputName(merge_segment);1687 const segment_name = segment_info[symbol.index].outputName(merge_segment);
2435 const segment_index = wasm.data_segments.get(segment_name).?;1688 const segment_index = wasm.data_segments.get(segment_name).?;
2436 const segment = wasm.segments.items[segment_index];1689 const segment = wasm.segments.items[segment_index];
...@@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void {...@@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void {
2486/// contain any parameters.1739/// contain any parameters.
2487fn setupInitFunctions(wasm: *Wasm) !void {1740fn setupInitFunctions(wasm: *Wasm) !void {
2488 const gpa = wasm.base.comp.gpa;1741 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;
2490 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);1745 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
2491 for (object.init_funcs) |init_func| {1746 for (object.init_funcs) |init_func| {
2492 const symbol = object.symtable[init_func.symbol_index];1747 const symbol = object.symtable[init_func.symbol_index];
2493 const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {1748 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);
2495 break :ty object.func_types[imp.kind.function];1750 break :ty object.func_types[imp.kind.function];
2496 } else ty: {1751 } else ty: {
2497 const func_index = symbol.index - object.importedCountByKind(.function);1752 const func_index = symbol.index - object.imported_functions_count;
2498 const func = object.functions[func_index];1753 const func = object.functions[func_index];
2499 break :ty object.func_types[func.type_index];1754 break :ty object.func_types[func.type_index];
2500 };1755 };
2501 if (ty.params.len != 0) {1756 if (ty.params.len != 0) {
2502 log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});1757 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
2503 return error.InvalidInitFunc;
2504 }1758 }
2505 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});1759 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
2506 wasm.init_funcs.appendAssumeCapacity(.{1760 wasm.init_funcs.appendAssumeCapacity(.{
2507 .index = init_func.symbol_index,1761 .index = @enumFromInt(init_func.symbol_index),
2508 .file = @as(u16, @intCast(file_index)),1762 .file = file_index,
2509 .priority = init_func.priority,1763 .priority = init_func.priority,
2510 });1764 });
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 });
2512 }1766 }
2513 }1767 }
25141768
...@@ -2521,34 +1775,6 @@ fn setupInitFunctions(wasm: *Wasm) !void {...@@ -2521,34 +1775,6 @@ fn setupInitFunctions(wasm: *Wasm) !void {
2521 }1775 }
2522}1776}
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
2552/// Creates a function body for the `__wasm_call_ctors` symbol.1778/// Creates a function body for the `__wasm_call_ctors` symbol.
2553/// Loops over all constructors found in `init_funcs` and calls them1779/// Loops over all constructors found in `init_funcs` and calls them
2554/// respectively based on their priority which was sorted by `setupInitFunctions`.1780/// respectively based on their priority which was sorted by `setupInitFunctions`.
...@@ -2609,8 +1835,7 @@ fn createSyntheticFunction(...@@ -2609,8 +1835,7 @@ fn createSyntheticFunction(
2609 function_body: *std.ArrayList(u8),1835 function_body: *std.ArrayList(u8),
2610) !void {1836) !void {
2611 const gpa = wasm.base.comp.gpa;1837 const gpa = wasm.base.comp.gpa;
2612 const loc = wasm.findGlobalSymbol(symbol_name) orelse1838 const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol?
2613 try wasm.createSyntheticSymbol(symbol_name, .function);
2614 const symbol = loc.getSymbol(wasm);1839 const symbol = loc.getSymbol(wasm);
2615 if (symbol.isDead()) {1840 if (symbol.isDead()) {
2616 return;1841 return;
...@@ -2620,32 +1845,21 @@ fn createSyntheticFunction(...@@ -2620,32 +1845,21 @@ fn createSyntheticFunction(
2620 const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));1845 const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
2621 try wasm.functions.putNoClobber(1846 try wasm.functions.putNoClobber(
2622 gpa,1847 gpa,
2623 .{ .file = null, .index = func_index },1848 .{ .file = .null, .index = func_index },
2624 .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },1849 .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
2625 );1850 );
2626 symbol.index = func_index;1851 symbol.index = func_index;
26271852
2628 // create the atom that will be output into the final binary1853 // create the atom that will be output into the final binary
2629 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));1854 const atom_index = try wasm.createAtom(loc.index, .null);
2630 const atom = try wasm.managed_atoms.addOne(gpa);1855 const atom = wasm.getAtomPtr(atom_index);
2631 atom.* = .{1856 atom.size = @intCast(function_body.items.len);
2632 .size = @as(u32, @intCast(function_body.items.len)),1857 atom.code = function_body.moveToUnmanaged();
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 };
2641 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);1858 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
2642 try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
2643}1859}
26441860
2645/// Unlike `createSyntheticFunction` this function is to be called by1861/// Unlike `createSyntheticFunction` this function is to be called by
2646/// the codegeneration backend. This will not allocate the created Atom yet,1862/// 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.
2649/// Returns the index of the symbol.1863/// Returns the index of the symbol.
2650pub fn createFunction(1864pub fn createFunction(
2651 wasm: *Wasm,1865 wasm: *Wasm,
...@@ -2653,37 +1867,8 @@ pub fn createFunction(...@@ -2653,37 +1867,8 @@ pub fn createFunction(
2653 func_ty: std.wasm.Type,1867 func_ty: std.wasm.Type,
2654 function_body: *std.ArrayList(u8),1868 function_body: *std.ArrayList(u8),
2655 relocations: *std.ArrayList(Relocation),1869 relocations: *std.ArrayList(Relocation),
2656) !u32 {1870) !Symbol.Index {
2657 const gpa = wasm.base.comp.gpa;1871 return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
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;
2687}1872}
26881873
2689/// If required, sets the function index in the `start` section.1874/// If required, sets the function index in the `start` section.
...@@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {...@@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
27001885
2701 if (!shared_memory) return;1886 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
2703 var function_body = std.ArrayList(u8).init(gpa);1891 var function_body = std.ArrayList(u8).init(gpa);
2704 defer function_body.deinit();1892 defer function_body.deinit();
2705 const writer = function_body.writer();1893 const writer = function_body.writer();
...@@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {...@@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
2748 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {1936 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
2749 try writer.writeByte(std.wasm.opcode(.call));1937 try writer.writeByte(std.wasm.opcode(.call));
2750 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);1938 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
1939 loc.getSymbol(wasm).mark();
2751 }1940 }
27521941
2753 try writer.writeByte(std.wasm.opcode(.end));1942 try writer.writeByte(std.wasm.opcode(.end));
...@@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {...@@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
2762fn setupImports(wasm: *Wasm) !void {1951fn setupImports(wasm: *Wasm) !void {
2763 const gpa = wasm.base.comp.gpa;1952 const gpa = wasm.base.comp.gpa;
2764 log.debug("Merging imports", .{});1953 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
2777 for (wasm.resolved_symbols.keys()) |symbol_loc| {1954 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2778 const file_index = symbol_loc.file orelse {1955 const obj_file = wasm.file(symbol_loc.file) orelse {
2779 // imports generated by Zig code are already in the `import` section1956 // Synthetic symbols will already exist in the `import` section
2780 continue;1957 continue;
2781 };1958 };
27821959
...@@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void {...@@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void {
2789 }1966 }
27901967
2791 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});1968 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
2792 const object = wasm.objects.items[file_index];1969 const import = obj_file.import(symbol_loc.index);
2793 const import = object.findImport(symbol.tag.externalType(), symbol.index);
27941970
2795 // We copy the import to a new import to ensure the names contain references1971 // We copy the import to a new import to ensure the names contain references
2796 // to the internal string table, rather than of the object file.1972 // to the internal string table, rather than of the object file.
2797 const new_imp: types.Import = .{1973 const new_imp: types.Import = .{
2798 .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)),1974 .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)),
2799 .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)),1975 .name = try wasm.string_table.put(gpa, obj_file.string(import.name)),
2800 .kind = import.kind,1976 .kind = import.kind,
2801 };1977 };
2802 // TODO: De-duplicate imports when they contain the same names and type1978 // TODO: De-duplicate imports when they contain the same names and type
...@@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void {...@@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void {
2847 defer removed_duplicates.deinit();2023 defer removed_duplicates.deinit();
28482024
2849 for (wasm.resolved_symbols.keys()) |sym_loc| {2025 for (wasm.resolved_symbols.keys()) |sym_loc| {
2850 if (sym_loc.file == null) {2026 const obj_file = wasm.file(sym_loc.file) orelse {
2851 // Zig code-generated symbols are already within the sections and do not2027 // Synthetic symbols already live in the corresponding sections.
2852 // require to be merged
2853 continue;2028 continue;
2854 }2029 };
2855
2856 const object = &wasm.objects.items[sym_loc.file.?];
2857 const symbol = &object.symtable[sym_loc.index];
28582030
2859 if (symbol.isDead() or2031 const symbol = obj_file.symbol(sym_loc.index);
2860 symbol.isUndefined() or2032 if (symbol.isDead() or symbol.isUndefined()) {
2861 (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table))
2862 {
2863 // Skip undefined symbols as they go in the `import` section2033 // Skip undefined symbols as they go in the `import` section
2864 // Also skip symbols that do not need to have a section merged.
2865 continue;2034 continue;
2866 }2035 }
28672036
2868 const offset = object.importedCountByKind(symbol.tag.externalType());
2869 const index = symbol.index - offset;
2870 switch (symbol.tag) {2037 switch (symbol.tag) {
2871 .function => {2038 .function => {
2872 const gop = try wasm.functions.getOrPut(2039 const gop = try wasm.functions.getOrPut(
...@@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void {...@@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void {
2877 // We found an alias to the same function, discard this symbol in favor of2044 // We found an alias to the same function, discard this symbol in favor of
2878 // the original symbol and point the discard function to it. This ensures2045 // the original symbol and point the discard function to it. This ensures
2879 // we only emit a single function, instead of duplicates.2046 // we only emit a single function, instead of duplicates.
2880 symbol.unmark();2047 // we favor keeping the global over a local.
2881 try wasm.discarded.putNoClobber(2048 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
2882 gpa,2049 const original_sym = original_loc.getSymbol(wasm);
2883 sym_loc,2050 if (original_sym.isLocal() and symbol.isGlobal()) {
2884 .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index },2051 original_sym.unmark();
2885 );2052 try wasm.discarded.put(gpa, original_loc, sym_loc);
2886 try removed_duplicates.append(sym_loc);2053 try removed_duplicates.append(original_loc);
2887 continue;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 }
2888 }2060 }
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 };
2890 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;2062 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
2891 },2063 },
2892 .global => {2064 .global => {
2893 const original_global = object.globals[index];2065 const index = symbol.index - obj_file.importedFunctions();
2066 const original_global = obj_file.globals()[index];
2894 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;2067 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2895 try wasm.wasm_globals.append(gpa, original_global);2068 try wasm.wasm_globals.append(gpa, original_global);
2896 },2069 },
2897 .table => {2070 .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];
2899 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;2075 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
2900 try wasm.tables.append(gpa, original_table);2076 try wasm.tables.append(gpa, original_table);
2901 },2077 },
2902 else => unreachable,2078 .dead, .undefined => unreachable,
2079 else => {},
2903 }2080 }
2904 }2081 }
29052082
2906 // For any removed duplicates, remove them from the resolved symbols list2083 // For any removed duplicates, remove them from the resolved symbols list
2907 for (removed_duplicates.items) |sym_loc| {2084 for (removed_duplicates.items) |sym_loc| {
2908 assert(wasm.resolved_symbols.swapRemove(sym_loc));2085 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2086 gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)});
2909 }2087 }
29102088
2911 log.debug("Merged ({d}) functions", .{wasm.functions.count()});2089 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
...@@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void {...@@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void {
2926 defer dirty.deinit();2104 defer dirty.deinit();
29272105
2928 for (wasm.resolved_symbols.keys()) |sym_loc| {2106 for (wasm.resolved_symbols.keys()) |sym_loc| {
2929 if (sym_loc.file == null) {2107 const obj_file = wasm.file(sym_loc.file) orelse {
2930 // zig code-generated symbols are already present in final type section2108 // zig code-generated symbols are already present in final type section
2931 continue;2109 continue;
2932 }2110 };
2933 const object = wasm.objects.items[sym_loc.file.?];2111
2934 const symbol = object.symtable[sym_loc.index];2112 const symbol = obj_file.symbol(sym_loc.index);
2935 if (symbol.tag != .function or symbol.isDead()) {2113 if (symbol.tag != .function or symbol.isDead()) {
2936 // Only functions have types. Only retrieve the type of referenced functions.2114 // Only functions have types. Only retrieve the type of referenced functions.
2937 continue;2115 continue;
...@@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void {...@@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void {
2940 if (symbol.isUndefined()) {2118 if (symbol.isUndefined()) {
2941 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});2119 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
2942 const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;2120 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];
2944 import.kind.function = try wasm.putOrGetFuncType(original_type);2122 import.kind.function = try wasm.putOrGetFuncType(original_type);
2945 } else if (!dirty.contains(symbol.index)) {2123 } else if (!dirty.contains(symbol.index)) {
2946 log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});2124 log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});
2947 const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;2125 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]);
2949 dirty.putAssumeCapacityNoClobber(symbol.index, {});2127 dirty.putAssumeCapacityNoClobber(symbol.index, {});
2950 }2128 }
2951 }2129 }
2952 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});2130 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
2953}2131}
29542132
2955fn setupExports(wasm: *Wasm) !void {2133fn checkExportNames(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
2961 const force_exp_names = wasm.export_symbol_names;2134 const force_exp_names = wasm.export_symbol_names;
2962 if (force_exp_names.len > 0) {2135 if (force_exp_names.len > 0) {
2963 var failed_exports = false;2136 var failed_exports = false;
29642137
2965 for (force_exp_names) |exp_name| {2138 for (force_exp_names) |exp_name| {
2966 const loc = wasm.findGlobalSymbol(exp_name) orelse {2139 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});
2968 failed_exports = true;2141 failed_exports = true;
2969 continue;2142 continue;
2970 };2143 };
...@@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void {
2974 }2147 }
29752148
2976 if (failed_exports) {2149 if (failed_exports) {
2977 return error.MissingSymbol;2150 return error.FlushFailure;
2978 }2151 }
2979 }2152 }
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
2981 for (wasm.resolved_symbols.keys()) |sym_loc| {2161 for (wasm.resolved_symbols.keys()) |sym_loc| {
2982 const symbol = sym_loc.getSymbol(wasm);2162 const symbol = sym_loc.getSymbol(wasm);
2983 if (!symbol.isExported(comp.config.rdynamic)) continue;2163 if (!symbol.isExported(comp.config.rdynamic)) continue;
29842164
2985 const sym_name = sym_loc.getName(wasm);2165 const sym_name = sym_loc.getName(wasm);
2986 const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: {2166 const export_name = if (sym_loc.file == .null)
2987 if (sym_loc.file == null) break :blk symbol.name;2167 symbol.name
2988 break :blk try wasm.string_table.put(gpa, sym_name);2168 else
2989 };2169 try wasm.string_table.put(gpa, sym_name);
2990 const exp: types.Export = if (symbol.tag == .data) exp: {2170 const exp: types.Export = if (symbol.tag == .data) exp: {
2991 const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));2171 const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
2992 try wasm.wasm_globals.append(gpa, .{2172 try wasm.wasm_globals.append(gpa, .{
...@@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void {...@@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void {
3020 const entry_name = wasm.entry_name orelse return;2200 const entry_name = wasm.entry_name orelse return;
30212201
3022 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {2202 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
3023 log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});2203 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
3024 return error.MissingSymbol;2204 return error.FlushFailure;
3025 };2205 };
30262206
3027 const symbol = symbol_loc.getSymbol(wasm);2207 const symbol = symbol_loc.getSymbol(wasm);
3028 if (symbol.tag != .function) {2208 if (symbol.tag != .function) {
3029 log.err("Entry symbol '{s}' is not a function", .{entry_name});2209 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
3030 return error.InvalidEntryKind;2210 return error.FlushFailure;
3031 }2211 }
30322212
3033 // Ensure the symbol is exported so host environment can access it2213 // Ensure the symbol is exported so host environment can access it
...@@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void {
30552235
3056 const is_obj = comp.config.output_mode == .Obj;2236 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
3058 if (place_stack_first and !is_obj) {2243 if (place_stack_first and !is_obj) {
3059 memory_ptr = stack_alignment.forward(memory_ptr);2244 memory_ptr = stack_alignment.forward(memory_ptr);
3060 memory_ptr += wasm.base.stack_size;2245 memory_ptr += wasm.base.stack_size;
3061 // We always put the stack pointer global at index 02246 // 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 }
3063 }2250 }
30642251
3065 var offset: u32 = @as(u32, @intCast(memory_ptr));2252 var offset: u32 = @as(u32, @intCast(memory_ptr));
...@@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void {
3098 memory_ptr = mem.alignForward(u64, memory_ptr, 4);2285 memory_ptr = mem.alignForward(u64, memory_ptr, 4);
3099 const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);2286 const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
3100 const sym = loc.getSymbol(wasm);2287 const sym = loc.getSymbol(wasm);
2288 sym.mark();
3101 sym.virtual_address = @as(u32, @intCast(memory_ptr));2289 sym.virtual_address = @as(u32, @intCast(memory_ptr));
3102 memory_ptr += 4;2290 memory_ptr += 4;
3103 }2291 }
...@@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void {
3105 if (!place_stack_first and !is_obj) {2293 if (!place_stack_first and !is_obj) {
3106 memory_ptr = stack_alignment.forward(memory_ptr);2294 memory_ptr = stack_alignment.forward(memory_ptr);
3107 memory_ptr += wasm.base.stack_size;2295 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 }
3109 }2299 }
31102300
3111 // One of the linked object files has a reference to the __heap_base symbol.2301 // One of the linked object files has a reference to the __heap_base symbol.
...@@ -3121,16 +2311,13 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3121,16 +2311,13 @@ fn setupMemory(wasm: *Wasm) !void {
31212311
3122 if (wasm.initial_memory) |initial_memory| {2312 if (wasm.initial_memory) |initial_memory| {
3123 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {2313 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
3124 log.err("Initial memory must be {d}-byte aligned", .{page_size});2314 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
3125 return error.MissAlignment;
3126 }2315 }
3127 if (memory_ptr > initial_memory) {2316 if (memory_ptr > initial_memory) {
3128 log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr});2317 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
3129 return error.MemoryTooSmall;
3130 }2318 }
3131 if (initial_memory > max_memory_allowed) {2319 if (initial_memory > max_memory_allowed) {
3132 log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});2320 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
3133 return error.MemoryTooBig;
3134 }2321 }
3135 memory_ptr = initial_memory;2322 memory_ptr = initial_memory;
3136 }2323 }
...@@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void {
31472334
3148 if (wasm.max_memory) |max_memory| {2335 if (wasm.max_memory) |max_memory| {
3149 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {2336 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
3150 log.err("Maximum memory must be {d}-byte aligned", .{page_size});2337 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
3151 return error.MissAlignment;
3152 }2338 }
3153 if (memory_ptr > max_memory) {2339 if (memory_ptr > max_memory) {
3154 log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});2340 try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
3155 return error.MemoryTooSmall;
3156 }2341 }
3157 if (max_memory > max_memory_allowed) {2342 if (max_memory > max_memory_allowed) {
3158 log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});2343 try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
3159 return error.MemoryTooBig;
3160 }2344 }
3161 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));2345 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
3162 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);2346 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
...@@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void {
3170/// From a given object's index and the index of the segment, returns the corresponding2354/// From a given object's index and the index of the segment, returns the corresponding
3171/// index of the segment within the final data section. When the segment does not yet2355/// index of the segment within the final data section. When the segment does not yet
3172/// exist, a new one will be initialized and appended. The new index will be returned in that case.2356/// 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 {
3174 const comp = wasm.base.comp;2358 const comp = wasm.base.comp;
3175 const gpa = comp.gpa;2359 const gpa = comp.gpa;
3176 const object: Object = wasm.objects.items[object_index];2360 const obj_file = wasm.file(file_index).?;
3177 const symbol = object.symtable[symbol_index];2361 const symbol = obj_file.symbols()[@intFromEnum(symbol_index)];
3178 const index: u32 = @intCast(wasm.segments.items.len);2362 const index: u32 = @intCast(wasm.segments.items.len);
3179 const shared_memory = comp.config.shared_memory;2363 const shared_memory = comp.config.shared_memory;
31802364
3181 switch (symbol.tag) {2365 switch (symbol.tag) {
3182 .data => {2366 .data => {
3183 const segment_info = object.segment_info[symbol.index];2367 const segment_info = obj_file.segmentInfo()[symbol.index];
3184 const merge_segment = comp.config.output_mode != .Obj;2368 const merge_segment = comp.config.output_mode != .Obj;
3185 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));2369 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
3186 if (!result.found_existing) {2370 if (!result.found_existing) {
...@@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3...@@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
3209 break :blk index;2393 break :blk index;
3210 },2394 },
3211 .section => {2395 .section => {
3212 const section_name = object.string_table.get(symbol.name);2396 const section_name = obj_file.symbolName(symbol_index);
3213 if (mem.eql(u8, section_name, ".debug_info")) {2397 if (mem.eql(u8, section_name, ".debug_info")) {
3214 return wasm.debug_info_index orelse blk: {2398 return wasm.debug_info_index orelse blk: {
3215 wasm.debug_info_index = index;2399 wasm.debug_info_index = index;
...@@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3...@@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
3257 wasm.debug_str_index = index;2441 wasm.debug_str_index = index;
3258 try wasm.appendDummySegment();2442 try wasm.appendDummySegment();
3259 break :blk index;2443 break :blk index;
3260 };2444 };
3261 } else {2445 } else {
3262 log.warn("found unknown section '{s}'", .{section_name});2446 var err = try wasm.addErrorWithNotes(1);
3263 return error.UnexpectedValue;2447 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
3264 }2448 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
3265 },2449 return error.UnexpectedValue;
3266 else => unreachable,2450 }
3267 }2451 },
3268}2452 else => unreachable,
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});
3379 }2453 }
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 });
3390}2454}
33912455
3392/// From a given index variable, creates a new debug section.2456/// Appends a new segment with default field values
3393/// This initializes the index, appends a new segment,2457fn appendDummySegment(wasm: *Wasm) !void {
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 {
3415 const gpa = wasm.base.comp.gpa;2458 const gpa = wasm.base.comp.gpa;
34162459 try wasm.segments.append(gpa, .{
3417 for (wasm.segment_info.values()) |segment_info| {2460 .alignment = .@"1",
3418 gpa.free(segment_info.name);2461 .size = 0,
3419 }2462 .offset = 0,
34202463 .flags = 0,
3421 var atom_it = wasm.decls.valueIterator();2464 });
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;
3448}2465}
34492466
3450pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {2467pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
3451 const comp = wasm.base.comp;2468 const comp = wasm.base.comp;
3452 const use_lld = build_options.have_llvm and comp.config.use_lld;2469 const use_lld = build_options.have_llvm and comp.config.use_lld;
3453 const use_llvm = comp.config.use_llvm;
34542470
3455 if (use_lld) {2471 if (use_lld) {
3456 return wasm.linkWithLLD(arena, prog_node);2472 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);
3461 }2473 }
2474 return wasm.flushModule(arena, prog_node);
3462}2475}
34632476
3464/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.2477/// 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 {
3466 const tracy = trace(@src());2479 const tracy = trace(@src());
3467 defer tracy.end();2480 defer tracy.end();
34682481
3469 const comp = wasm.base.comp;2482 const comp = wasm.base.comp;
3470 const shared_memory = comp.config.shared_memory;2483 if (wasm.llvm_object) |llvm_object| {
3471 const import_memory = comp.config.import_memory;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
3473 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.2493 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
3474 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});2494 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
3475 const opt_zcu = comp.module;2495 const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: {
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
3484 if (fs.path.dirname(full_out_path)) |dirname| {2496 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 });
3486 } else {2498 } else {
3487 break :blk wasm.base.zcu_object_sub_path.?;2499 break :blk path;
3488 }2500 }
3489 } else null;2501 } 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
3570 // Positional arguments to the linker such as object files and static archives.2503 // Positional arguments to the linker such as object files and static archives.
3571 var positionals = std.ArrayList([]const u8).init(arena);2504 var positionals = std.ArrayList([]const u8).init(arena);
3572 try positionals.ensureUnusedCapacity(objects.len);2505 try positionals.ensureUnusedCapacity(comp.objects.len);
35732506
3574 const target = comp.root_mod.resolved_target.result;2507 const target = comp.root_mod.resolved_target.result;
3575 const output_mode = comp.config.output_mode;2508 const output_mode = comp.config.output_mode;
...@@ -3578,6 +2511,10 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin...@@ -3578,6 +2511,10 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
3578 const link_libcpp = comp.config.link_libcpp;2511 const link_libcpp = comp.config.link_libcpp;
3579 const wasi_exec_model = comp.config.wasi_exec_model;2512 const wasi_exec_model = comp.config.wasi_exec_model;
35802513
2514 if (wasm.zigObjectPtr()) |zig_object| {
2515 try zig_object.flushModule(wasm);
2516 }
2517
3581 // When the target os is WASI, we allow linking with WASI-LIBC2518 // When the target os is WASI, we allow linking with WASI-LIBC
3582 if (target.os.tag == .wasi) {2519 if (target.os.tag == .wasi) {
3583 const is_exe_or_dyn_lib = output_mode == .Exe or2520 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...@@ -3609,7 +2546,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
3609 try positionals.append(path);2546 try positionals.append(path);
3610 }2547 }
36112548
3612 for (objects) |object| {2549 for (comp.objects) |object| {
3613 try positionals.append(object.path);2550 try positionals.append(object.path);
3614 }2551 }
36152552
...@@ -3622,171 +2559,35 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin...@@ -3622,171 +2559,35 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
36222559
3623 try wasm.parseInputFiles(positionals.items);2560 try wasm.parseInputFiles(positionals.items);
36242561
3625 for (wasm.objects.items, 0..) |_, object_index| {2562 if (wasm.zig_object_index != .null) {
3626 try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index)));2563 try wasm.resolveSymbolsInObject(wasm.zig_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);
3701 }2564 }
37022565 if (comp.link_errors.items.len > 0) return error.FlushFailure;
3703 if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path);2566 for (wasm.objects.items) |object_index| {
3704 if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path);2567 try wasm.resolveSymbolsInObject(object_index);
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)));
3710 }2568 }
2569 if (comp.link_errors.items.len > 0) return error.FlushFailure;
37112570
3712 var emit_features_count: u32 = 0;2571 var emit_features_count: u32 = 0;
3713 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;2572 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
3714 try wasm.validateFeatures(&enabled_features, &emit_features_count);2573 try wasm.validateFeatures(&enabled_features, &emit_features_count);
3715 try wasm.resolveSymbolsInArchives();2574 try wasm.resolveSymbolsInArchives();
2575 if (comp.link_errors.items.len > 0) return error.FlushFailure;
3716 try wasm.resolveLazySymbols();2576 try wasm.resolveLazySymbols();
3717 try wasm.checkUndefinedSymbols();2577 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();
3722 try wasm.setupInitFunctions();2580 try wasm.setupInitFunctions();
2581 if (comp.link_errors.items.len > 0) return error.FlushFailure;
3723 try wasm.setupStart();2582 try wasm.setupStart();
2583
3724 try wasm.markReferences();2584 try wasm.markReferences();
3725 try wasm.setupErrorsLen();
3726 try wasm.setupImports();2585 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
3786 try wasm.mergeSections();2586 try wasm.mergeSections();
3787 try wasm.mergeTypes();2587 try wasm.mergeTypes();
3788 try wasm.allocateAtoms();2588 try wasm.allocateAtoms();
3789 try wasm.setupMemory();2589 try wasm.setupMemory();
2590 if (comp.link_errors.items.len > 0) return error.FlushFailure;
3790 wasm.allocateVirtualAddresses();2591 wasm.allocateVirtualAddresses();
3791 wasm.mapFunctionTable();2592 wasm.mapFunctionTable();
3792 try wasm.initializeCallCtorsFunction();2593 try wasm.initializeCallCtorsFunction();
...@@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)...@@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
3796 try wasm.setupStartSection();2597 try wasm.setupStartSection();
3797 try wasm.setupExports();2598 try wasm.setupExports();
3798 try wasm.writeToFile(enabled_features, emit_features_count, arena);2599 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2600 if (comp.link_errors.items.len > 0) return error.FlushFailure;
3799}2601}
38002602
3801/// Writes the WebAssembly in-memory module to the file2603/// Writes the WebAssembly in-memory module to the file
...@@ -4021,7 +2823,9 @@ fn writeToFile(...@@ -4021,7 +2823,9 @@ fn writeToFile(
4021 try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));2823 try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
4022 var symbol_it = wasm.function_table.keyIterator();2824 var symbol_it = wasm.function_table.keyIterator();
4023 while (symbol_it.next()) |symbol_loc_ptr| {2825 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);
4025 try leb.writeULEB128(binary_writer, sym.index);2829 try leb.writeULEB128(binary_writer, sym.index);
4026 }2830 }
40272831
...@@ -4124,8 +2928,8 @@ fn writeToFile(...@@ -4124,8 +2928,8 @@ fn writeToFile(
4124 try binary_writer.writeAll(atom.code.items);2928 try binary_writer.writeAll(atom.code.items);
41252929
4126 current_offset += atom.size;2930 current_offset += atom.size;
4127 if (atom.prev) |prev| {2931 if (atom.prev != .null) {
4128 atom_index = prev;2932 atom_index = atom.prev;
4129 } else {2933 } else {
4130 // also pad with zeroes when last atom to ensure2934 // also pad with zeroes when last atom to ensure
4131 // segments are aligned.2935 // segments are aligned.
...@@ -4191,19 +2995,9 @@ fn writeToFile(...@@ -4191,19 +2995,9 @@ fn writeToFile(
4191 }) catch unreachable;2995 }) catch unreachable;
4192 try emitBuildIdSection(&binary_bytes, str);2996 try emitBuildIdSection(&binary_bytes, str);
4193 },2997 },
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)}),
4195 }2999 }
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
4207 var debug_bytes = std.ArrayList(u8).init(gpa);3001 var debug_bytes = std.ArrayList(u8).init(gpa);
4208 defer debug_bytes.deinit();3002 defer debug_bytes.deinit();
42093003
...@@ -4229,7 +3023,8 @@ fn writeToFile(...@@ -4229,7 +3023,8 @@ fn writeToFile(
4229 while (true) {3023 while (true) {
4230 atom.resolveRelocs(wasm);3024 atom.resolveRelocs(wasm);
4231 try debug_bytes.appendSlice(atom.code.items);3025 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);
4233 }3028 }
4234 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);3029 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
4235 debug_bytes.clearRetainingCapacity();3030 debug_bytes.clearRetainingCapacity();
...@@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:...@@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
5004 try leb.writeULEB128(writer, @intFromEnum(symbol.tag));3799 try leb.writeULEB128(writer, @intFromEnum(symbol.tag));
5005 try leb.writeULEB128(writer, symbol.flags);3800 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);
5008 switch (symbol.tag) {3803 switch (symbol.tag) {
5009 .data => {3804 .data => {
5010 try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len)));3805 try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len)));
...@@ -5098,7 +3893,7 @@ fn emitCodeRelocations(...@@ -5098,7 +3893,7 @@ fn emitCodeRelocations(
5098 size_offset += getULEB128Size(atom.size);3893 size_offset += getULEB128Size(atom.size);
5099 for (atom.relocs.items) |relocation| {3894 for (atom.relocs.items) |relocation| {
5100 count += 1;3895 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) };
5102 const symbol_index = symbol_table.get(sym_loc).?;3897 const symbol_index = symbol_table.get(sym_loc).?;
5103 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));3898 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
5104 const offset = atom.offset + relocation.offset + size_offset;3899 const offset = atom.offset + relocation.offset + size_offset;
...@@ -5109,7 +3904,8 @@ fn emitCodeRelocations(...@@ -5109,7 +3904,8 @@ fn emitCodeRelocations(
5109 }3904 }
5110 log.debug("Emit relocation: {}", .{relocation});3905 log.debug("Emit relocation: {}", .{relocation});
5111 }3906 }
5112 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;3907 if (atom.prev == .null) break;
3908 atom = wasm.getAtomPtr(atom.prev);
5113 }3909 }
5114 if (count == 0) return;3910 if (count == 0) return;
5115 var buf: [5]u8 = undefined;3911 var buf: [5]u8 = undefined;
...@@ -5145,10 +3941,7 @@ fn emitDataRelocations(...@@ -5145,10 +3941,7 @@ fn emitDataRelocations(
5145 size_offset += getULEB128Size(atom.size);3941 size_offset += getULEB128Size(atom.size);
5146 for (atom.relocs.items) |relocation| {3942 for (atom.relocs.items) |relocation| {
5147 count += 1;3943 count += 1;
5148 const sym_loc: SymbolLoc = .{3944 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
5149 .file = atom.file,
5150 .index = relocation.index,
5151 };
5152 const symbol_index = symbol_table.get(sym_loc).?;3945 const symbol_index = symbol_table.get(sym_loc).?;
5153 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));3946 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
5154 const offset = atom.offset + relocation.offset + size_offset;3947 const offset = atom.offset + relocation.offset + size_offset;
...@@ -5159,7 +3952,8 @@ fn emitDataRelocations(...@@ -5159,7 +3952,8 @@ fn emitDataRelocations(
5159 }3952 }
5160 log.debug("Emit relocation: {}", .{relocation});3953 log.debug("Emit relocation: {}", .{relocation});
5161 }3954 }
5162 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;3955 if (atom.prev == .null) break;
3956 atom = wasm.getAtomPtr(atom.prev);
5163 }3957 }
5164 }3958 }
5165 if (count == 0) return;3959 if (count == 0) return;
...@@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {...@@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
5185 return false;3979 return false;
5186}3980}
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
5196/// Searches for a matching function signature. When no matching signature is found,3982/// Searches for a matching function signature. When no matching signature is found,
5197/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.3983/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
5198pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {3984pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
5199 if (wasm.getTypeIndex(func_type)) |index| {3985 if (wasm.getTypeIndex(func_type)) |index| {
5200 return index;3986 return index;
5201 }3987 }
5202 const gpa = wasm.base.comp.gpa;
52033988
5204 // functype does not exist.3989 // functype does not exist.
3990 const gpa = wasm.base.comp.gpa;
5205 const index: u32 = @intCast(wasm.func_types.items.len);3991 const index: u32 = @intCast(wasm.func_types.items.len);
5206 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);3992 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
5207 errdefer gpa.free(params);3993 errdefer gpa.free(params);
...@@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {...@@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
5218/// Asserts declaration has an associated `Atom`.4004/// Asserts declaration has an associated `Atom`.
5219/// Returns the index into the list of types.4005/// Returns the index into the list of types.
5220pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {4006pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {
5221 const gpa = wasm.base.comp.gpa;4007 return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, decl_index, func_type);
5222 const atom_index = wasm.decls.get(decl_index).?;4008}
5223 const index = try wasm.putOrGetFuncType(func_type);4009
5224 try wasm.atom_types.put(gpa, atom_index, index);4010/// Returns the symbol index of the error name table.
5225 return index;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);
5226}4023}
52274024
5228/// Verifies all resolved symbols and checks whether itself needs to be marked alive,4025/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
...@@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void {...@@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void {
5244 // Debug sections may require to be parsed and marked when it contains4041 // Debug sections may require to be parsed and marked when it contains
5245 // relocations to alive symbols.4042 // relocations to alive symbols.
5246 if (sym.tag == .section and comp.config.debug_format != .strip) {4043 if (sym.tag == .section and comp.config.debug_format != .strip) {
5247 const file = sym_loc.file orelse continue; // Incremental debug info is done independently4044 const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently
5248 const object = &wasm.objects.items[file];4045 _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index);
5249 const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);4046 sym.mark();
5250 const atom = wasm.getAtom(atom_index);
5251 const atom_sym = atom.symbolLoc().getSymbol(wasm);
5252 atom_sym.mark();
5253 }4047 }
5254 }4048 }
5255}4049}
...@@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {...@@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
5265 return;4059 return;
5266 }4060 }
5267 symbol.mark();4061 symbol.mark();
4062 gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)});
5268 if (symbol.isUndefined()) {4063 if (symbol.isUndefined()) {
5269 // undefined symbols do not have an associated `Atom` and therefore also4064 // undefined symbols do not have an associated `Atom` and therefore also
5270 // do not contain relocations.4065 // do not contain relocations.
5271 return;4066 return;
5272 }4067 }
52734068
5274 const atom_index = if (loc.file) |file_index| idx: {4069 const atom_index = if (wasm.file(loc.file)) |obj_file|
5275 const object = &wasm.objects.items[file_index];4070 try obj_file.parseSymbolIntoAtom(wasm, loc.index)
5276 const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);4071 else
5277 break :idx atom_index;4072 wasm.symbol_atom.get(loc) orelse return;
5278 } else wasm.symbol_atom.get(loc) orelse return;
52794073
5280 const atom = wasm.getAtom(atom_index);4074 const atom = wasm.getAtom(atom_index);
5281 for (atom.relocs.items) |reloc| {4075 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 };
5283 try wasm.mark(target_loc.finalLoc(wasm));4077 try wasm.mark(target_loc.finalLoc(wasm));
5284 }4078 }
5285}4079}
...@@ -5290,3 +4084,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8...@@ -5290,3 +4084,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
5290 .command => "_start",4084 .command => "_start",
5291 };4085 };
5292}4086}
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 @@...@@ -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
12file: fs.File,1file: fs.File,
13name: []const u8,2name: []const u8,
143
...@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype...@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
151 const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));140 const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));
152 defer allocator.free(sym_tab);141 defer allocator.free(sym_tab);
153142
154 reader.readNoEof(sym_tab) catch {143 reader.readNoEof(sym_tab) catch return error.IncompleteSymbolTable;
155 log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len});
156 return error.MalformedArchive;
157 };
158144
159 var i: usize = 0;145 var i: usize = 0;
160 var pos: usize = 0;146 var pos: usize = 0;
...@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype...@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
178fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {164fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {
179 const header: ar_hdr = try reader.readStruct(ar_hdr);165 const header: ar_hdr = try reader.readStruct(ar_hdr);
180 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {166 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
181 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });167 return error.InvalidHeaderDelimiter;
182 return error.MalformedArchive;
183 }168 }
184 if (!mem.eql(u8, header.ar_name[0..2], "//")) {169 if (!mem.eql(u8, header.ar_name[0..2], "//")) {
185 log.err("invalid archive. Long name table missing", .{});170 return error.MissingTableName;
186 return error.MalformedArchive;
187 }171 }
188 const table_size = try header.size();172 const table_size = try header.size();
189 const long_file_names = try allocator.alloc(u8, table_size);173 const long_file_names = try allocator.alloc(u8, table_size);
...@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi...@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi
194178
195/// From a given file offset, starts reading for a file header.179/// From a given file offset, starts reading for a file header.
196/// When found, parses the object file into an `Object` and returns it.180/// 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;
198 try archive.file.seekTo(file_offset);183 try archive.file.seekTo(file_offset);
199 const reader = archive.file.reader();184 const reader = archive.file.reader();
200 const header = try reader.readStruct(ar_hdr);185 const header = try reader.readStruct(ar_hdr);
...@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob...@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob
202 try archive.file.seekTo(0);187 try archive.file.seekTo(0);
203188
204 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {189 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
205 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });190 return error.InvalidHeaderDelimiter;
206 return error.MalformedArchive;
207 }191 }
208192
209 const object_name = try archive.parseName(header);193 const object_name = try archive.parseName(header);
210 const name = name: {194 const name = name: {
211 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;195 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
212 const path = try std.os.realpath(archive.name, &buffer);196 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 });
214 };198 };
215 defer allocator.free(name);199 defer gpa.free(name);
216200
217 const object_file = try std.fs.cwd().openFile(archive.name, .{});201 const object_file = try std.fs.cwd().openFile(archive.name, .{});
218 errdefer object_file.close();202 errdefer object_file.close();
219203
220 const object_file_size = try header.size();204 const object_file_size = try header.size();
221 try object_file.seekTo(current_offset);205 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);
223}207}
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 @@...@@ -1,53 +1,34 @@
1const Atom = @This();1/// Represents the index of the file this atom was generated from.
22/// This is 'null' when the atom was generated by a synthetic linker symbol.
3const std = @import("std");3file: FileIndex,
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
13/// symbol index of the symbol representing this atom4/// symbol index of the symbol representing this atom
14sym_index: u32,5sym_index: Symbol.Index,
15/// Size of the atom, used to calculate section sizes in the final binary6/// Size of the atom, used to calculate section sizes in the final binary
16size: u32,7size: u32 = 0,
17/// List of relocations belonging to this atom8/// List of relocations belonging to this atom
18relocs: std.ArrayListUnmanaged(types.Relocation) = .{},9relocs: std.ArrayListUnmanaged(types.Relocation) = .{},
19/// Contains the binary data of an atom, which can be non-relocated10/// Contains the binary data of an atom, which can be non-relocated
20code: std.ArrayListUnmanaged(u8) = .{},11code: std.ArrayListUnmanaged(u8) = .{},
21/// For code this is 1, for data this is set to the highest value of all segments12/// For code this is 1, for data this is set to the highest value of all segments
22alignment: Wasm.Alignment,13alignment: Wasm.Alignment = .@"1",
23/// Offset into the section where the atom lives, this already accounts14/// Offset into the section where the atom lives, this already accounts
24/// for alignment.15/// for alignment.
25offset: u32,16offset: u32 = 0,
26/// The original offset within the object file. This value is substracted from17/// The original offset within the object file. This value is substracted from
27/// relocation offsets to determine where in the `data` to rewrite the value18/// relocation offsets to determine where in the `data` to rewrite the value
28original_offset: u32,19original_offset: u32 = 0,
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,
32/// Previous atom in relation to this atom.20/// Previous atom in relation to this atom.
33/// is null when this atom is the first in its order21/// is null when this atom is the first in its order
34prev: ?Atom.Index,22prev: Atom.Index = .null,
35/// Contains atoms local to a decl, all managed by this `Atom`.23/// Contains atoms local to a decl, all managed by this `Atom`.
36/// When the parent atom is being freed, it will also do so for all local atoms.24/// When the parent atom is being freed, it will also do so for all local atoms.
37locals: std.ArrayListUnmanaged(Atom.Index) = .{},25locals: std.ArrayListUnmanaged(Atom.Index) = .{},
3826
39/// Alias to an unsigned 32-bit integer27/// Represents the index of an Atom where `null` is considered
40pub const Index = u32;28/// an invalid atom.
4129pub const Index = enum(u32) {
42/// Represents a default empty wasm `Atom`30 null = std.math.maxInt(u32),
43pub const empty: Atom = .{31 _,
44 .alignment = .@"1",
45 .file = null,
46 .offset = 0,
47 .prev = null,
48 .size = 0,
49 .sym_index = 0,
50 .original_offset = 0,
51};32};
5233
53/// Frees all resources owned by this `Atom`.34/// Frees all resources owned by this `Atom`.
...@@ -69,7 +50,7 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio...@@ -69,7 +50,7 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio
69 _ = fmt;50 _ = fmt;
70 _ = options;51 _ = options;
71 try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{52 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),
73 atom.alignment,54 atom.alignment,
74 atom.size,55 atom.size,
75 atom.offset,56 atom.offset,
...@@ -81,11 +62,6 @@ pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {...@@ -81,11 +62,6 @@ pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
81 return .{ .file = atom.file, .index = atom.sym_index };62 return .{ .file = atom.file, .index = atom.sym_index };
82}63}
8364
84pub fn getSymbolIndex(atom: Atom) ?u32 {
85 if (atom.sym_index == 0) return null;
86 return atom.sym_index;
87}
88
89/// Resolves the relocations within the atom, writing the new value65/// Resolves the relocations within the atom, writing the new value
90/// at the calculated offset.66/// at the calculated offset.
91pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {67pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
...@@ -99,7 +75,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {...@@ -99,7 +75,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
99 for (atom.relocs.items) |reloc| {75 for (atom.relocs.items) |reloc| {
100 const value = atom.relocationValue(reloc, wasm_bin);76 const value = atom.relocationValue(reloc, wasm_bin);
101 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{77 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),
103 symbol_name,79 symbol_name,
104 reloc.offset,80 reloc.offset,
105 value,81 value,
...@@ -138,7 +114,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {...@@ -138,7 +114,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
138/// All values will be represented as a `u64` as all values can fit within it.114/// All values will be represented as a `u64` as all values can fit within it.
139/// The final value must be casted to the correct size.115/// The final value must be casted to the correct size.
140fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {116fn 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);
142 const symbol = target_loc.getSymbol(wasm_bin);118 const symbol = target_loc.getSymbol(wasm_bin);
143 if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and119 if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
144 symbol.tag != .section and120 symbol.tag != .section and
...@@ -154,13 +130,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -154,13 +130,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
154 .R_WASM_TABLE_INDEX_I64,130 .R_WASM_TABLE_INDEX_I64,
155 .R_WASM_TABLE_INDEX_SLEB,131 .R_WASM_TABLE_INDEX_SLEB,
156 .R_WASM_TABLE_INDEX_SLEB64,132 .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,
158 .R_WASM_TYPE_INDEX_LEB => {134 .R_WASM_TYPE_INDEX_LEB => {
159 const file_index = atom.file orelse {135 const obj_file = wasm_bin.file(atom.file) orelse return relocation.index;
160 return relocation.index;136 const original_type = obj_file.funcTypes()[relocation.index];
161 };
162
163 const original_type = wasm_bin.objects.items[file_index].func_types[relocation.index];
164 return wasm_bin.getTypeIndex(original_type).?;137 return wasm_bin.getTypeIndex(original_type).?;
165 },138 },
166 .R_WASM_GLOBAL_INDEX_I32,139 .R_WASM_GLOBAL_INDEX_I32,
...@@ -217,3 +190,15 @@ fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {...@@ -217,3 +190,15 @@ fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
217 }190 }
218 return null;191 return null;
219}192}
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");...@@ -9,19 +9,22 @@ const std = @import("std");
9const Wasm = @import("../Wasm.zig");9const Wasm = @import("../Wasm.zig");
10const Symbol = @import("Symbol.zig");10const Symbol = @import("Symbol.zig");
11const Alignment = types.Alignment;11const Alignment = types.Alignment;
12const File = @import("file.zig").File;
1213
13const Allocator = std.mem.Allocator;14const Allocator = std.mem.Allocator;
14const leb = std.leb;15const leb = std.leb;
15const meta = std.meta;16const 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,
19/// Wasm spec version used for this `Object`22/// Wasm spec version used for this `Object`
20version: u32 = 0,23version: u32 = 0,
21/// The file descriptor that represents the wasm object file.24/// The file descriptor that represents the wasm object file.
22file: ?std.fs.File = null,25file: ?std.fs.File = null,
23/// Name (read path) of the object file.26/// Name (read path) of the object file.
24name: []const u8,27path: []const u8,
25/// Parsed type section28/// Parsed type section
26func_types: []const std.wasm.Type = &.{},29func_types: []const std.wasm.Type = &.{},
27/// A list of all imports for this module30/// A list of all imports for this module
...@@ -64,6 +67,12 @@ relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableDat...@@ -64,6 +67,12 @@ relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableDat
64/// import name, module name and export names. Each string will be deduplicated67/// import name, module name and export names. Each string will be deduplicated
65/// and returns an offset into the table.68/// and returns an offset into the table.
66string_table: Wasm.StringTable = .{},69string_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
68/// Represents a single item within a section (depending on its `type`)77/// Represents a single item within a section (depending on its `type`)
69const RelocatableData = struct {78const RelocatableData = struct {
...@@ -118,15 +127,16 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro...@@ -118,15 +127,16 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
118/// This also parses and verifies the object file.127/// This also parses and verifies the object file.
119/// When a max size is given, will only parse up to the given size,128/// When a max size is given, will only parse up to the given size,
120/// else will read until the end of the file.129/// 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;
122 var object: Object = .{132 var object: Object = .{
123 .file = file,133 .file = file,
124 .name = try gpa.dupe(u8, name),134 .path = try gpa.dupe(u8, name),
125 };135 };
126136
127 var is_object_file: bool = false;137 var is_object_file: bool = false;
128 const size = maybe_max_size orelse size: {138 const size = maybe_max_size orelse size: {
129 errdefer gpa.free(object.name);139 errdefer gpa.free(object.path);
130 const stat = try file.stat();140 const stat = try file.stat();
131 break :size @as(usize, @intCast(stat.size));141 break :size @as(usize, @intCast(stat.size));
132 };142 };
...@@ -142,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz...@@ -142,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
142 }152 }
143 var fbs = std.io.fixedBufferStream(file_contents);153 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);
146 errdefer object.deinit(gpa);156 errdefer object.deinit(gpa);
147 if (!is_object_file) return error.NotObjectFile;157 if (!is_object_file) return error.NotObjectFile;
148158
...@@ -193,68 +203,59 @@ pub fn deinit(object: *Object, gpa: Allocator) void {...@@ -193,68 +203,59 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
193 }203 }
194 object.relocatable_data.deinit(gpa);204 object.relocatable_data.deinit(gpa);
195 object.string_table.deinit(gpa);205 object.string_table.deinit(gpa);
196 gpa.free(object.name);206 gpa.free(object.path);
197 object.* = undefined;207 object.* = undefined;
198}208}
199209
200/// Finds the import within the list of imports from a given kind and index of that kind.210/// Finds the import within the list of imports from a given kind and index of that kind.
201/// Asserts the import exists211/// 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 {
203 var i: u32 = 0;213 var i: u32 = 0;
204 return for (object.imports) |import| {214 return for (object.imports) |import| {
205 if (std.meta.activeTag(import.kind) == import_kind) {215 if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {
206 if (i == index) return import;216 if (i == sym.index) return import;
207 i += 1;217 i += 1;
208 }218 }
209 } else unreachable; // Only existing imports are allowed to be found219 } else unreachable; // Only existing imports are allowed to be found
210}220}
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
225/// Checks if the object file is an MVP version.222/// Checks if the object file is an MVP version.
226/// When that's the case, we check if there's an import table definiton with its name223/// When that's the case, we check if there's an import table definiton with its name
227/// set to '__indirect_function_table". When that's also the case,224/// set to '__indirect_function_table". When that's also the case,
228/// we initialize a new table symbol that corresponds to that import and return that symbol.225/// we initialize a new table symbol that corresponds to that import and return that symbol.
229///226///
230/// When the object file is *NOT* MVP, we return `null`.227/// When the object file is *NOT* MVP, we return `null`.
231fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {228fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
232 var table_count: usize = 0;229 var table_count: usize = 0;
233 for (object.symtable) |sym| {230 for (object.symtable) |sym| {
234 if (sym.tag == .table) table_count += 1;231 if (sym.tag == .table) table_count += 1;
235 }232 }
236233
237 const import_table_count = object.importedCountByKind(.table);
238
239 // For each import table, we also have a symbol so this is not a legacy object file234 // 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
242 if (table_count != 0) {237 if (table_count != 0) {
243 log.err("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{238 var err = try wasm_file.addErrorWithNotes(1);
244 import_table_count,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,
245 table_count,241 table_count,
246 });242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
247 return error.MissingTableSymbols;244 return error.MissingTableSymbols;
248 }245 }
249246
250 // MVP object files cannot have any table definitions, only imports (for the indirect function table).247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
251 if (object.tables.len > 0) {248 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});
253 return error.UnexpectedTable;252 return error.UnexpectedTable;
254 }253 }
255254
256 if (import_table_count != 1) {255 if (object.imported_tables_count != 1) {
257 log.err("Found more than one table import, but no representing table symbols", .{});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});
258 return error.MissingTableSymbols;259 return error.MissingTableSymbols;
259 }260 }
260261
...@@ -265,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {...@@ -265,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
265 } else unreachable;266 } else unreachable;
266267
267 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {268 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});
269 return error.MissingTableSymbols;272 return error.MissingTableSymbols;
270 }273 }
271274
...@@ -318,8 +321,8 @@ pub const ParseError = error{...@@ -318,8 +321,8 @@ pub const ParseError = error{
318 UnknownFeature,321 UnknownFeature,
319};322};
320323
321fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {324fn parse(object: *Object, gpa: Allocator, wasm_file: *const Wasm, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
322 var parser = Parser(@TypeOf(reader)).init(object, reader);325 var parser = Parser(@TypeOf(reader)).init(object, wasm_file, reader);
323 return parser.parseObject(gpa, is_object_file);326 return parser.parseObject(gpa, is_object_file);
324}327}
325328
...@@ -331,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {...@@ -331,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {
331 reader: std.io.CountingReader(ReaderType),334 reader: std.io.CountingReader(ReaderType),
332 /// Object file we're building335 /// Object file we're building
333 object: *Object,336 object: *Object,
337 /// Read-only reference to the WebAssembly linker
338 wasm_file: *const Wasm,
334339
335 fn init(object: *Object, reader: ReaderType) ObjectParser {340 fn init(object: *Object, wasm_file: *const Wasm, reader: ReaderType) ObjectParser {
336 return .{ .object = object, .reader = std.io.countingReader(reader) };341 return .{ .object = object, .wasm_file = wasm_file, .reader = std.io.countingReader(reader) };
337 }342 }
338343
339 /// Verifies that the first 4 bytes contains \0Asm344 /// Verifies that the first 4 bytes contains \0Asm
...@@ -427,16 +432,25 @@ fn Parser(comptime ReaderType: type) type {...@@ -427,16 +432,25 @@ fn Parser(comptime ReaderType: type) type {
427432
428 const kind = try readEnum(std.wasm.ExternalKind, reader);433 const kind = try readEnum(std.wasm.ExternalKind, reader);
429 const kind_value: std.wasm.Import.Kind = switch (kind) {434 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 },
431 .memory => .{ .memory = try readLimits(reader) },439 .memory => .{ .memory = try readLimits(reader) },
432 .global => .{ .global = .{440 .global => val: {
433 .valtype = try readEnum(std.wasm.Valtype, reader),441 parser.object.imported_globals_count += 1;
434 .mutable = (try reader.readByte()) == 0x01,442 break :val .{ .global = .{
435 } },443 .valtype = try readEnum(std.wasm.Valtype, reader),
436 .table => .{ .table = .{444 .mutable = (try reader.readByte()) == 0x01,
437 .reftype = try readEnum(std.wasm.RefType, reader),445 } };
438 .limits = try readLimits(reader),446 },
439 } },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 },
440 };454 };
441455
442 import.* = .{456 import.* = .{
...@@ -513,7 +527,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -513,7 +527,7 @@ fn Parser(comptime ReaderType: type) type {
513 const start = reader.context.bytes_left;527 const start = reader.context.bytes_left;
514 var index: u32 = 0;528 var index: u32 = 0;
515 const count = try readLeb(u32, reader);529 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;
517 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);531 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
518 defer relocatable_data.deinit();532 defer relocatable_data.deinit();
519 while (index < count) : (index += 1) {533 while (index < count) : (index += 1) {
...@@ -582,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {...@@ -582,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {
582 try reader.readNoEof(name);596 try reader.readNoEof(name);
583597
584 const tag = types.known_features.get(name) orelse {598 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});
586 return error.UnknownFeature;602 return error.UnknownFeature;
587 };603 };
588 feature.* = .{604 feature.* = .{
...@@ -751,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -751,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {
751767
752 // we found all symbols, check for indirect function table768 // we found all symbols, check for indirect function table
753 // in case of an MVP object file769 // in case of an MVP object file
754 if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| {770 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm_file)) |symbol| {
755 try symbols.append(symbol);771 try symbols.append(symbol);
756 log.debug("Found legacy indirect function table. Created symbol", .{});772 log.debug("Found legacy indirect function table. Created symbol", .{});
757 }773 }
...@@ -830,7 +846,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -830,7 +846,7 @@ fn Parser(comptime ReaderType: type) type {
830 defer gpa.free(name);846 defer gpa.free(name);
831 try reader.readNoEof(name);847 try reader.readNoEof(name);
832 break :name try parser.object.string_table.put(gpa, name);848 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;
834 },850 },
835 }851 }
836 return symbol;852 return symbol;
...@@ -904,12 +920,12 @@ fn assertEnd(reader: anytype) !void {...@@ -904,12 +920,12 @@ fn assertEnd(reader: anytype) !void {
904}920}
905921
906/// Parses an object file into atoms, for code and data sections922/// 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 {
908 const comp = wasm.base.comp;924 const comp = wasm.base.comp;
909 const gpa = comp.gpa;925 const gpa = comp.gpa;
910 const symbol = &object.symtable[symbol_index];926 const symbol = &object.symtable[@intFromEnum(symbol_index)];
911 const relocatable_data: RelocatableData = switch (symbol.tag) {927 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],
913 .data => object.relocatable_data.get(.data).?[symbol.index],929 .data => object.relocatable_data.get(.data).?[symbol.index],
914 .section => blk: {930 .section => blk: {
915 const data = object.relocatable_data.get(.custom).?;931 const data = object.relocatable_data.get(.custom).?;
...@@ -922,19 +938,16 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32...@@ -922,19 +938,16 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
922 },938 },
923 else => unreachable,939 else => unreachable,
924 };940 };
925 const final_index = try wasm.getMatchingSegment(object_index, symbol_index);941 const final_index = try wasm.getMatchingSegment(object.index, symbol_index);
926 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));942 const atom_index = try wasm.createAtom(symbol_index, object.index);
927 const atom = try wasm.managed_atoms.addOne(gpa);
928 atom.* = Atom.empty;
929 try wasm.appendAtomAtIndex(final_index, atom_index);943 try wasm.appendAtomAtIndex(final_index, atom_index);
930944
931 atom.sym_index = symbol_index;945 const atom = wasm.getAtomPtr(atom_index);
932 atom.file = object_index;
933 atom.size = relocatable_data.size;946 atom.size = relocatable_data.size;
934 atom.alignment = relocatable_data.getAlignment(object);947 atom.alignment = relocatable_data.getAlignment(object);
935 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);948 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
936 atom.original_offset = relocatable_data.offset;949 atom.original_offset = relocatable_data.offset;
937 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);950
938 const segment: *Wasm.Segment = &wasm.segments.items[final_index];951 const segment: *Wasm.Segment = &wasm.segments.items[final_index];
939 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned952 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
940 segment.alignment = segment.alignment.max(atom.alignment);953 segment.alignment = segment.alignment.max(atom.alignment);
...@@ -952,8 +965,8 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32...@@ -952,8 +965,8 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
952 .R_WASM_TABLE_INDEX_SLEB64,965 .R_WASM_TABLE_INDEX_SLEB64,
953 => {966 => {
954 try wasm.function_table.put(gpa, .{967 try wasm.function_table.put(gpa, .{
955 .file = object_index,968 .file = object.index,
956 .index = reloc.index,969 .index = @enumFromInt(reloc.index),
957 }, 0);970 }, 0);
958 },971 },
959 .R_WASM_GLOBAL_INDEX_I32,972 .R_WASM_GLOBAL_INDEX_I32,
...@@ -961,10 +974,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32...@@ -961,10 +974,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
961 => {974 => {
962 const sym = object.symtable[reloc.index];975 const sym = object.symtable[reloc.index];
963 if (sym.tag != .global) {976 if (sym.tag != .global) {
964 try wasm.got_symbols.append(977 try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) });
965 gpa,
966 .{ .file = object_index, .index = reloc.index },
967 );
968 }978 }
969 },979 },
970 else => {},980 else => {},
src/link/Wasm/Symbol.zig+11-5
...@@ -1,12 +1,8 @@...@@ -1,12 +1,8 @@
1//! Represents a wasm symbol. Containing all of its properties,1//! Represents a WebAssembly symbol. Containing all of its properties,
2//! as well as providing helper methods to determine its functionality2//! as well as providing helper methods to determine its functionality
3//! and how it will/must be linked.3//! and how it will/must be linked.
4//! The name of the symbol can be found by providing the offset, found4//! The name of the symbol can be found by providing the offset, found
5//! on the `name` field, to a string table in the wasm binary or object file.5//! 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
11/// Bitfield containings flags for a symbol7/// Bitfield containings flags for a symbol
12/// Can contain any of the flags defined in `Flag`8/// Can contain any of the flags defined in `Flag`
...@@ -24,6 +20,12 @@ tag: Tag,...@@ -24,6 +20,12 @@ tag: Tag,
24/// This differs from the offset of an `Atom` which is relative to the start of a segment.20/// This differs from the offset of an `Atom` which is relative to the start of a segment.
25virtual_address: u32,21virtual_address: u32,
2622
23/// Represents a symbol index where `null` represents an invalid index.
24pub const Index = enum(u32) {
25 null,
26 _,
27};
28
27pub const Tag = enum {29pub const Tag = enum {
28 function,30 function,
29 data,31 data,
...@@ -202,3 +204,7 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO...@@ -202,3 +204,7 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO
202 .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },204 .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
203 );205 );
204}206}
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{...@@ -35,11 +35,10 @@ pub const cases = [_]Case{
35 },35 },
3636
37 // WASM Cases37 // WASM Cases
38 // https://github.com/ziglang/zig/issues/1693838 .{
39 //.{39 .build_root = "test/link/wasm/archive",
40 // .build_root = "test/link/wasm/archive",40 .import = @import("link/wasm/archive/build.zig"),
41 // .import = @import("link/wasm/archive/build.zig"),41 },
42 //},
43 .{42 .{
44 .build_root = "test/link/wasm/basic-features",43 .build_root = "test/link/wasm/basic-features",
45 .import = @import("link/wasm/basic-features/build.zig"),44 .import = @import("link/wasm/basic-features/build.zig"),
...@@ -52,11 +51,10 @@ pub const cases = [_]Case{...@@ -52,11 +51,10 @@ pub const cases = [_]Case{
52 .build_root = "test/link/wasm/export",51 .build_root = "test/link/wasm/export",
53 .import = @import("link/wasm/export/build.zig"),52 .import = @import("link/wasm/export/build.zig"),
54 },53 },
55 // https://github.com/ziglang/zig/issues/1693754 .{
56 //.{55 .build_root = "test/link/wasm/export-data",
57 // .build_root = "test/link/wasm/export-data",56 .import = @import("link/wasm/export-data/build.zig"),
58 // .import = @import("link/wasm/export-data/build.zig"),57 },
59 //},
60 .{58 .{
61 .build_root = "test/link/wasm/extern",59 .build_root = "test/link/wasm/extern",
62 .import = @import("link/wasm/extern/build.zig"),60 .import = @import("link/wasm/extern/build.zig"),
...@@ -81,6 +79,10 @@ pub const cases = [_]Case{...@@ -81,6 +79,10 @@ pub const cases = [_]Case{
81 .build_root = "test/link/wasm/segments",79 .build_root = "test/link/wasm/segments",
82 .import = @import("link/wasm/segments/build.zig"),80 .import = @import("link/wasm/segments/build.zig"),
83 },81 },
82 .{
83 .build_root = "test/link/wasm/shared-memory",
84 .import = @import("link/wasm/shared-memory/build.zig"),
85 },
84 .{86 .{
85 .build_root = "test/link/wasm/stack_pointer",87 .build_root = "test/link/wasm/stack_pointer",
86 .import = @import("link/wasm/stack_pointer/build.zig"),88 .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...@@ -19,12 +19,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
19 .name = "main",19 .name = "main",
20 .root_source_file = .{ .path = "main.zig" },20 .root_source_file = .{ .path = "main.zig" },
21 .optimize = optimize,21 .optimize = optimize,
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },22 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
23 .strip = false,23 .strip = false,
24 });24 });
25 lib.entry = .disabled;25 lib.entry = .disabled;
26 lib.use_llvm = false;26 lib.use_llvm = false;
27 lib.use_lld = false;27 lib.use_lld = false;
28 lib.root_module.export_symbol_names = &.{"foo"};
2829
29 const check = lib.checkObject();30 const check = lib.checkObject();
30 check.checkInHeaders();31 check.checkInHeaders();
test/link/wasm/export-data/build.zig+1-1
...@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {...@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
13 .name = "lib",13 .name = "lib",
14 .root_source_file = .{ .path = "lib.zig" },14 .root_source_file = .{ .path = "lib.zig" },
15 .optimize = .ReleaseSafe, // to make the output deterministic in address positions15 .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 }),
17 });17 });
18 lib.entry = .disabled;18 lib.entry = .disabled;
19 lib.use_lld = false;19 lib.use_lld = false;
test/link/wasm/shared-memory/build.zig+40-45
...@@ -11,37 +11,39 @@ pub fn build(b: *std.Build) void {...@@ -11,37 +11,39 @@ pub fn build(b: *std.Build) void {
11}11}
1212
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {13fn 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(.{
15 .name = "lib",15 .name = "lib",
16 .root_source_file = .{ .path = "lib.zig" },16 .root_source_file = .{ .path = "lib.zig" },
17 .target = .{17 .target = b.resolveTargetQuery(.{
18 .cpu_arch = .wasm32,18 .cpu_arch = .wasm32,
19 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },19 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
20 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),20 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
21 .os_tag = .freestanding,21 .os_tag = .freestanding,
22 },22 }),
23 .optimize = optimize_mode,23 .optimize = optimize_mode,
24 .strip = false,24 .strip = false,
25 .single_threaded = false,25 .single_threaded = false,
26 });26 });
27 lib.entry = .disabled;27 exe.entry = .disabled;
28 lib.use_lld = false;28 exe.use_lld = false;
29 lib.import_memory = true;29 exe.import_memory = true;
30 lib.export_memory = true;30 exe.export_memory = true;
31 lib.shared_memory = true;31 exe.shared_memory = true;
32 lib.max_memory = 67108864;32 exe.max_memory = 67108864;
33 lib.root_module.export_symbol_names = &.{"foo"};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");37 check_exe.checkInHeaders();
38 check_lib.checkNext("entries 1");38 check_exe.checkExact("Section import");
39 check_lib.checkNext("module env");39 check_exe.checkExact("entries 1");
40 check_lib.checkNext("name memory"); // ensure we are importing memory40 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_exe.checkInHeaders();
43 check_lib.checkNext("entries 2");44 check_exe.checkExact("Section export");
44 check_lib.checkNext("name memory"); // ensure we also export memory again45 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name memory"); // ensure we also export memory again
4547
46 // This section *must* be emit as the start function is set to the index48 // This section *must* be emit as the start function is set to the index
47 // of __wasm_init_memory49 // of __wasm_init_memory
...@@ -49,49 +51,42 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -49,49 +51,42 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
49 // This means we won't have __wasm_init_memory in such case, and therefore51 // This means we won't have __wasm_init_memory in such case, and therefore
50 // should also not have a section "start"52 // should also not have a section "start"
51 if (optimize_mode == .Debug) {53 if (optimize_mode == .Debug) {
52 check_lib.checkStart("Section start");54 check_exe.checkInHeaders();
55 check_exe.checkExact("Section start");
53 }56 }
5457
55 // This section is only and *must* be emit when shared-memory is enabled58 // This section is only and *must* be emit when shared-memory is enabled
56 // release modes will have the TLS segment optimized out in our test-case.59 // release modes will have the TLS segment optimized out in our test-case.
57 if (optimize_mode == .Debug) {60 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");61 check_exe.checkInHeaders();
59 check_lib.checkNext("count 3");62 check_exe.checkExact("Section data_count");
63 check_exe.checkExact("count 1");
60 }64 }
6165
62 check_lib.checkStart("Section custom");66 check_exe.checkInHeaders();
63 check_lib.checkNext("name name");67 check_exe.checkExact("Section custom");
64 check_lib.checkNext("type function");68 check_exe.checkExact("name name");
69 check_exe.checkExact("type function");
65 if (optimize_mode == .Debug) {70 if (optimize_mode == .Debug) {
66 check_lib.checkNext("name __wasm_init_memory");71 check_exe.checkExact("name __wasm_init_memory");
67 }72 }
68 check_lib.checkNext("name __wasm_init_tls");73 check_exe.checkExact("name __wasm_init_tls");
69 check_lib.checkNext("type global");74 check_exe.checkExact("type global");
7075
71 // In debug mode the symbol __tls_base is resolved to an undefined symbol76 // In debug mode the symbol __tls_base is resolved to an undefined symbol
72 // from the object file, hence its placement differs than in release modes77 // from the object file, hence its placement differs than in release modes
73 // where the entire tls segment is optimized away, and tls_base will have78 // where the entire tls segment is optimized away, and tls_base will have
74 // its original position.79 // its original position.
75 if (optimize_mode == .Debug) {80 check_exe.checkExact("name __tls_base");
76 check_lib.checkNext("name __tls_size");81 check_exe.checkExact("name __tls_size");
77 check_lib.checkNext("name __tls_align");82 check_exe.checkExact("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 }
8483
85 check_lib.checkNext("type data_segment");84 check_exe.checkExact("type data_segment");
86 if (optimize_mode == .Debug) {85 if (optimize_mode == .Debug) {
87 check_lib.checkNext("names 3");86 check_exe.checkExact("names 1");
88 check_lib.checkNext("index 0");87 check_exe.checkExact("index 0");
89 check_lib.checkNext("name .rodata");88 check_exe.checkExact("name .tdata");
90 check_lib.checkNext("index 1");
91 check_lib.checkNext("name .bss");
92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
94 }89 }
9590
96 test_step.dependOn(&check_lib.step);91 test_step.dependOn(&check_exe.step);
97}92}
test/link/wasm/type/build.zig+17-16
...@@ -13,31 +13,32 @@ pub fn build(b: *std.Build) void {...@@ -13,31 +13,32 @@ pub fn build(b: *std.Build) void {
13}13}
1414
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addExecutable(.{16 const exe = b.addExecutable(.{
17 .name = "lib",17 .name = "lib",
18 .root_source_file = .{ .path = "lib.zig" },18 .root_source_file = .{ .path = "lib.zig" },
19 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),19 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
20 .optimize = optimize,20 .optimize = optimize,
21 .strip = false,21 .strip = false,
22 });22 });
23 lib.entry = .disabled;23 exe.entry = .disabled;
24 lib.use_llvm = false;24 exe.use_llvm = false;
25 lib.use_lld = false;25 exe.use_lld = false;
26 b.installArtifact(lib);26 exe.root_module.export_symbol_names = &.{"foo"};
27 b.installArtifact(exe);
2728
28 const check_lib = lib.checkObject();29 const check_exe = exe.checkObject();
29 check_lib.checkInHeaders();30 check_exe.checkInHeaders();
30 check_lib.checkExact("Section type");31 check_exe.checkExact("Section type");
31 // only 2 entries, although we have more functions.32 // only 2 entries, although we have more functions.
32 // This is to test functions with the same function signature33 // This is to test functions with the same function signature
33 // have their types deduplicated.34 // have their types deduplicated.
34 check_lib.checkExact("entries 2");35 check_exe.checkExact("entries 2");
35 check_lib.checkExact("params 1");36 check_exe.checkExact("params 1");
36 check_lib.checkExact("type i32");37 check_exe.checkExact("type i32");
37 check_lib.checkExact("returns 1");38 check_exe.checkExact("returns 1");
38 check_lib.checkExact("type i64");39 check_exe.checkExact("type i64");
39 check_lib.checkExact("params 0");40 check_exe.checkExact("params 0");
40 check_lib.checkExact("returns 0");41 check_exe.checkExact("returns 0");
4142
42 test_step.dependOn(&check_lib.step);43 test_step.dependOn(&check_exe.step);
43}44}