From 943dac3e8558712096d55a30897057d75444178c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 Dec 2024 23:22:09 -0800 Subject: [PATCH] compiler: add type safety for export indices --- src/Sema.zig | 10 +- src/Zcu.zig | 24 ++-- src/Zcu/PerThread.zig | 16 +-- src/arch/wasm/CodeGen.zig | 23 ++-- src/arch/wasm/Mir.zig | 8 +- src/codegen/c.zig | 8 +- src/codegen/llvm.zig | 2 +- src/link.zig | 2 +- src/link/C.zig | 2 +- src/link/Elf.zig | 2 +- src/link/Elf/ZigObject.zig | 2 +- src/link/MachO.zig | 2 +- src/link/MachO/ZigObject.zig | 2 +- src/link/NvPtx.zig | 2 +- src/link/Plan9.zig | 6 +- src/link/SpirV.zig | 4 +- src/link/Wasm.zig | 213 +++++++++++++++++++++++++---------- src/link/Wasm/Flush.zig | 11 +- 18 files changed, 213 insertions(+), 126 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index be98c4fef2..4a62e495c7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void { // So, pick up and delete any existing exports. This strategy performs // redundant work, but that's okay, because this case is exceedingly rare. if (zcu.single_exports.get(sema.owner)) |export_idx| { - try sema.exports.append(gpa, zcu.all_exports.items[export_idx]); + try sema.exports.append(gpa, export_idx.ptr(zcu).*); } else if (zcu.multi_exports.get(sema.owner)) |info| { try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); } @@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void { // `sema.exports` is completed; store the data into the `Zcu`. if (sema.exports.items.len == 1) { try zcu.single_exports.ensureUnusedCapacity(gpa, 1); - const export_idx = zcu.free_exports.popOrNull() orelse idx: { + const export_idx: Zcu.Export.Index = zcu.free_exports.popOrNull() orelse idx: { _ = try zcu.all_exports.addOne(gpa); - break :idx zcu.all_exports.items.len - 1; + break :idx @enumFromInt(zcu.all_exports.items.len - 1); }; - zcu.all_exports.items[export_idx] = sema.exports.items[0]; - zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, @intCast(export_idx)); + export_idx.ptr(zcu).* = sema.exports.items[0]; + zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); } else { try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); const exports_base = zcu.all_exports.items.len; diff --git a/src/Zcu.zig b/src/Zcu.zig index ccc4177f33..c100e8b5f8 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory, all_exports: std.ArrayListUnmanaged(Export) = .empty, /// This is a list of free indices in `all_exports`. These indices may be reused by exports from /// future semantic analysis. -free_exports: std.ArrayListUnmanaged(u32) = .empty, +free_exports: std.ArrayListUnmanaged(Export.Index) = .empty, /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` /// whose analysis triggered the export. -single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, +single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, Export.Index) = .empty, /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. /// The exports are `all_exports.items[index..][0..len]`. multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { @@ -145,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty, -/// Key is index into `all_exports`. -failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .empty, +failed_exports: std.AutoArrayHashMapUnmanaged(Export.Index, *ErrorMsg) = .empty, /// If analysis failed due to a cimport error, the corresponding Clang errors /// are stored here. cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty, @@ -3101,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { const gpa = zcu.gpa; const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| - .{ kv.value, 1 } + .{ @intFromEnum(kv.value), 1 } else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| .{ info.value.index, info.value.len } else @@ -3115,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports // within a single update. if (dev.env.supports(.incremental)) { - for (exports, exports_base..) |exp, export_idx| { + for (exports, exports_base..) |exp, export_index_usize| { + const export_idx: Export.Index = @enumFromInt(export_index_usize); if (zcu.comp.bin_file) |lf| { lf.deleteExport(exp.exported, exp.opts.name); } - if (zcu.failed_exports.fetchSwapRemove(@intCast(export_idx))) |failed_kv| { + if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| { failed_kv.value.destroy(gpa); } } @@ -3131,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { return; }; for (exports_base..exports_base + exports_len) |export_idx| { - zcu.free_exports.appendAssumeCapacity(@intCast(export_idx)); + zcu.free_exports.appendAssumeCapacity(@enumFromInt(export_idx)); } } @@ -3277,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void { pub fn handleUpdateExports( zcu: *Zcu, - export_indices: []const u32, + export_indices: []const Export.Index, result: link.File.UpdateExportsError!void, ) Allocator.Error!void { const gpa = zcu.gpa; @@ -3285,12 +3285,10 @@ pub fn handleUpdateExports( error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { const export_idx = export_indices[0]; - const new_export = &zcu.all_exports.items[export_idx]; + const new_export = export_idx.ptr(zcu); new_export.status = .failed_retryable; try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); - const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{ - @errorName(err), - }); + const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)}); zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); }, }; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index b1f5a973d9..ab5f2b2994 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2815,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void { const gpa = zcu.gpa; // First, construct a mapping of every exported value and Nav to the indices of all its different exports. - var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(u32)) = .empty; - var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(u32)) = .empty; + var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; + var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; defer { for (nav_exports.values()) |*exports| { exports.deinit(gpa); @@ -2835,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); for (zcu.single_exports.values()) |export_idx| { - const exp = zcu.all_exports.items[export_idx]; + const exp = export_idx.ptr(zcu); const value_ptr, const found_existing = switch (exp.exported) { .nav => |nav| gop: { const gop = try nav_exports.getOrPut(gpa, nav); @@ -2863,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { }, }; if (!found_existing) value_ptr.* = .{}; - try value_ptr.append(gpa, @intCast(export_idx)); + try value_ptr.append(gpa, @enumFromInt(export_idx)); } } @@ -2882,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void { } } -const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, u32); +const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index); fn processExportsInner( pt: Zcu.PerThread, symbol_exports: *SymbolExports, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) error{OutOfMemory}!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; for (export_indices) |export_idx| { - const new_export = &zcu.all_exports.items[export_idx]; + const new_export = export_idx.ptr(zcu); const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); if (gop.found_existing) { new_export.status = .failed_retryable; @@ -2904,7 +2904,7 @@ fn processExportsInner( new_export.opts.name.fmt(ip), }); errdefer msg.destroy(gpa); - const other_export = zcu.all_exports.items[gop.value_ptr.*]; + const other_export = gop.value_ptr.ptr(zcu); try zcu.errNote(other_export.src, msg, "other symbol here", .{}); zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); new_export.status = .failed; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e3febb4451..f7a0c44d3c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; -const ArrayList = std.ArrayList; const assert = std.debug.assert; const testing = std.testing; const leb = std.leb; @@ -631,7 +630,7 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { /// Maps `loop` instructions to their label. `br` to here repeats the loop. loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, /// `bytes` contains the wasm bytecode belonging to the 'code' section. -code: *ArrayList(u8), +code: *std.ArrayListUnmanaged(u8), /// The index the next local generated will have /// NOTE: arguments share the index with locals therefore the first variable /// will have the index that comes after the last argument's index @@ -639,8 +638,6 @@ local_index: u32 = 0, /// The index of the current argument. /// Used to track which argument is being referenced in `airArg`. arg_index: u32 = 0, -/// If codegen fails, an error messages will be allocated and saved in `err_msg` -err_msg: *Zcu.ErrorMsg, /// List of all locals' types generated throughout this declaration /// used to emit locals count at start of 'code' section. locals: std.ArrayListUnmanaged(u8), @@ -732,10 +729,9 @@ pub fn deinit(func: *CodeGen) void { func.* = undefined; } -/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig -fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError { - func.err_msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args); - return error.CodegenFail; +fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { + const msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args); + return func.pt.zcu.codegenFailMsg(func.owner_nav, msg); } /// Resolves the `WValue` for the given instruction `inst` @@ -1173,9 +1169,9 @@ pub fn generate( func_index: InternPool.Index, air: Air, liveness: Liveness, - code: *std.ArrayList(u8), + code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!codegen.Result { +) codegen.CodeGenError!void { const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); @@ -1189,7 +1185,6 @@ pub fn generate( .code = code, .owner_nav = func.owner_nav, .src_loc = src_loc, - .err_msg = undefined, .locals = .{}, .target = target, .bin_file = bin_file.cast(.wasm).?, @@ -1199,11 +1194,9 @@ pub fn generate( defer code_gen.deinit(); genFunc(&code_gen) catch |err| switch (err) { - error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg }, - else => |e| return e, + error.CodegenFail => return error.CodegenFail, + else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), }; - - return codegen.Result.ok; } fn genFunc(func: *CodeGen) InnerError!void { diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 3a7ae67534..46acc189ac 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -80,15 +80,15 @@ pub const Inst = struct { /// /// Uses `nop` @"return" = 0x0F, - /// Calls a function using `nav_index`. - call_nav, - /// Calls a function using `func_index`. - call_func, /// Calls a function pointer by its function signature /// and index into the function table. /// /// Uses `label` call_indirect = 0x11, + /// Calls a function using `nav_index`. + call_nav, + /// Calls a function using `func_index`. + call_func, /// Calls a function by its index. /// /// The function is the auto-generated tag name function for the type diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5ce1ecc21c..6fee1beb89 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3052,12 +3052,12 @@ pub fn genDeclValue( try w.writeAll(";\n"); } -pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const u32) !void { +pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; const fwd = dg.fwdDeclWriter(); - const main_name = zcu.all_exports.items[export_indices[0]].opts.name; + const main_name = export_indices[0].ptr(zcu).opts.name; try fwd.writeAll("#define "); switch (exported) { .nav => |nav| try dg.renderNavName(fwd, nav), @@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const const exported_val = exported.getValue(zcu); if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| { - const @"export" = &zcu.all_exports.items[export_index]; + const @"export" = export_index.ptr(zcu); try fwd.writeAll("zig_extern "); if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); try dg.renderFunctionSignature( @@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const else => true, }; for (export_indices) |export_index| { - const @"export" = &zcu.all_exports.items[export_index]; + const @"export" = export_index.ptr(zcu); try fwd.writeAll("zig_extern "); if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); const extern_name = @"export".opts.name.toSlice(ip); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aad6dad78b..977eeab9da 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1810,7 +1810,7 @@ pub const Object = struct { self: *Object, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) link.File.UpdateExportsError!void { assert(std.meta.eql(pt, self.pt)); const zcu = pt.zcu; diff --git a/src/link.zig b/src/link.zig index 22eff87e59..eef6e82700 100644 --- a/src/link.zig +++ b/src/link.zig @@ -817,7 +817,7 @@ pub const File = struct { base: *File, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) UpdateExportsError!void { switch (base.tag) { inline else => |tag| { diff --git a/src/link/C.zig b/src/link/C.zig index dd42af1c9c..c5c11b0caf 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -840,7 +840,7 @@ pub fn updateExports( self: *C, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) !void { const zcu = pt.zcu; const gpa = zcu.gpa; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7dd092782a..47145762de 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2393,7 +2393,7 @@ pub fn updateExports( self: *Elf, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) link.File.UpdateExportsError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 524a280723..bfbc1e2bc2 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1745,7 +1745,7 @@ pub fn updateExports( elf_file: *Elf, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) link.File.UpdateExportsError!void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f317e43b9d..e92bd58f29 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3056,7 +3056,7 @@ pub fn updateExports( self: *MachO, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) link.File.UpdateExportsError!void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index afb011d46f..cbfeee682c 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -1246,7 +1246,7 @@ pub fn updateExports( macho_file: *MachO, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) link.File.UpdateExportsError!void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index 84fc015552..ab82bb9de8 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -100,7 +100,7 @@ pub fn updateExports( self: *NvPtx, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) !void { if (build_options.skip_non_native and builtin.object_format != .nvptx) @panic("Attempted to compile for object format that was disabled by build configuration"); diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 15069b0bdc..f4b5cfb9d7 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged( data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty, /// When `updateExports` is called, we store the export indices here, to be used /// during flush. -nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u32) = .empty, +nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty, lazy_syms: LazySymbolTable = .{}, @@ -1007,7 +1007,7 @@ pub fn updateExports( self: *Plan9, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) !void { const gpa = self.base.comp.gpa; switch (exported) { @@ -1018,7 +1018,7 @@ pub fn updateExports( gpa.free(kv.value); } try self.nav_exports.ensureUnusedCapacity(gpa, 1); - const duped_indices = try gpa.dupe(u32, export_indices); + const duped_indices = try gpa.dupe(Zcu.Export.Index, export_indices); self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices); }, } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index c353d9ddd1..a5b9615c5e 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -155,7 +155,7 @@ pub fn updateExports( self: *SpirV, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) !void { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -190,7 +190,7 @@ pub fn updateExports( }; for (export_indices) |export_idx| { - const exp = zcu.all_exports.items[export_idx]; + const exp = export_idx.ptr(zcu); try self.object.spv.declareEntryPoint( spv_decl_index, exp.opts.name.toSlice(ip), diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 0d3ff9a9ee..05c40d31ad 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -56,6 +56,10 @@ base: link.File, /// string_table entries for them. Alternately those sites could be moved to /// use a different byte array for this purpose. string_bytes: std.ArrayListUnmanaged(u8), +/// Sometimes we have logic that wants to borrow string bytes to store +/// arbitrary things in there. In this case it is not allowed to intern new +/// strings during this time. This safety lock is used to detect misuses. +string_bytes_lock: std.debug.SafetyLock = .{}, /// Omitted when serializing linker state. string_table: String.Table, /// Symbol name of the entry function to export @@ -202,6 +206,10 @@ any_exports_updated: bool = true, /// Index into `objects`. pub const ObjectIndex = enum(u32) { _, + + pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object { + return &wasm.objects.items[@intFromEnum(index)]; + } }; /// Index into `functions`. @@ -269,12 +277,26 @@ pub const SourceLocation = enum(u32) { }; } + pub fn unpack(sl: SourceLocation, wasm: *const Wasm) Unpacked { + return switch (sl) { + .zig_object_nofile => .zig_object_nofile, + .none => .none, + _ => { + const i = @intFromEnum(sl); + if (i < wasm.objects.items.len) return .{ .object_index = @enumFromInt(i) }; + const sl_index = i - wasm.objects.items.len; + _ = sl_index; + @panic("TODO"); + }, + }; + } + pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void { const diags = &wasm.base.comp.link_diags; switch (sl.unpack(wasm)) { .none => unreachable, .zig_object_nofile => diags.addError("zig compilation unit: " ++ f, args), - .object_index => |i| diags.addError("{}: " ++ f, .{wasm.objects.items[i].path} ++ args), + .object_index => |i| diags.addError("{}: " ++ f, .{i.ptr(wasm).path} ++ args), .source_location_index => @panic("TODO"), } } @@ -520,6 +542,10 @@ pub const FunctionImport = extern struct { /// Index into `object_function_imports`. pub const Index = enum(u32) { _, + + pub fn ptr(index: FunctionImport.Index, wasm: *const Wasm) *FunctionImport { + return &wasm.object_function_imports.items[@intFromEnum(index)]; + } }; }; @@ -543,7 +569,8 @@ pub const GlobalImport = extern struct { source_location: SourceLocation, resolution: Resolution, - /// Represents a synthetic global, or a global from an object. + /// Represents a synthetic global, a global from an object, or a global + /// from the Zcu. pub const Resolution = enum(u32) { unresolved, __heap_base, @@ -556,6 +583,68 @@ pub const GlobalImport = extern struct { // Next, index into `object_globals`. // Next, index into `navs`. _, + + const first_object_global = @intFromEnum(Resolution.__zig_error_name_table) + 1; + + pub const Unpacked = union(enum) { + unresolved, + __heap_base, + __heap_end, + __stack_pointer, + __tls_align, + __tls_base, + __tls_size, + __zig_error_name_table, + object_global: ObjectGlobalIndex, + nav: Nav.Index, + }; + + pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { + return switch (r) { + .unresolved => .unresolved, + .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs, + .__wasm_call_ctors => .__wasm_call_ctors, + .__wasm_init_memory => .__wasm_init_memory, + .__wasm_init_tls => .__wasm_init_tls, + .__zig_error_names => .__zig_error_names, + _ => { + const i: u32 = @intFromEnum(r); + const object_global_index = i - first_object_global; + if (object_global_index < wasm.object_globals.items.len) + return .{ .object_global = @enumFromInt(object_global_index) }; + const nav_index = object_global_index - wasm.object_globals.items.len; + return .{ .nav = @enumFromInt(nav_index) }; + }, + }; + } + + pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { + return switch (unpacked) { + .unresolved => .unresolved, + .__heap_base => .__heap_base, + .__heap_end => .__heap_end, + .__stack_pointer => .__stack_pointer, + .__tls_align => .__tls_align, + .__tls_base => .__tls_base, + .__tls_size => .__tls_size, + .__zig_error_name_table => .__zig_error_name_table, + .object_global => |i| @enumFromInt(first_object_global + @intFromEnum(i)), + .nav => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)), + }; + } + + pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { + return pack(wasm, .{ .nav = @enumFromInt(wasm.navs.getIndex(ip_nav).?) }); + } + }; + + /// Index into `object_global_imports`. + pub const Index = enum(u32) { + _, + + pub fn ptr(index: Index, wasm: *const Wasm) *GlobalImport { + return &wasm.object_global_imports.items[@intFromEnum(index)]; + } }; }; @@ -634,20 +723,6 @@ pub const ObjectSectionIndex = enum(u32) { _, }; -/// Index into `object_function_imports`. -pub const ObjectFunctionImportIndex = enum(u32) { - _, - - pub fn ptr(index: ObjectFunctionImportIndex, wasm: *const Wasm) *FunctionImport { - return &wasm.object_function_imports.items[@intFromEnum(index)]; - } -}; - -/// Index into `object_global_imports`. -pub const ObjectGlobalImportIndex = enum(u32) { - _, -}; - /// Index into `object_table_imports`. pub const ObjectTableImportIndex = enum(u32) { _, @@ -861,11 +936,35 @@ pub const ValtypeList = enum(u32) { } }; +/// Index into `imports`. +pub const ZcuImportIndex = enum(u32) { + _, +}; + /// 0. Index into `object_function_imports`. /// 1. Index into `imports`. pub const FunctionImportId = enum(u32) { _, + pub const Unpacked = union(enum) { + object_function_import: FunctionImport.Index, + zcu_import: ZcuImportIndex, + }; + + pub fn pack(unpacked: Unpacked, wasm: *const Wasm) FunctionImportId { + return switch (unpacked) { + .object_function_import => |i| @enumFromInt(@intFromEnum(i)), + .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_function_imports.entries.len), + }; + } + + pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked { + const i = @intFromEnum(id); + if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @enumFromInt(i) }; + const zcu_import_i = i - wasm.object_function_imports.entries.len; + return .{ .zcu_import = @enumFromInt(zcu_import_i) }; + } + /// This function is allowed O(N) lookup because it is only called during /// diagnostic generation. pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation { @@ -873,10 +972,10 @@ pub const FunctionImportId = enum(u32) { .object_function_import => |obj_func_index| { // TODO binary search for (wasm.objects.items, 0..) |o, i| { - if (o.function_imports.off <= obj_func_index and - o.function_imports.off + o.function_imports.len > obj_func_index) + if (o.function_imports.off <= @intFromEnum(obj_func_index) and + o.function_imports.off + o.function_imports.len > @intFromEnum(obj_func_index)) { - return .pack(wasm, .{ .object_index = @enumFromInt(i) }); + return .pack(.{ .object_index = @enumFromInt(i) }, wasm); } } else unreachable; }, @@ -890,17 +989,36 @@ pub const FunctionImportId = enum(u32) { pub const GlobalImportId = enum(u32) { _, + pub const Unpacked = union(enum) { + object_global_import: GlobalImport.Index, + zcu_import: ZcuImportIndex, + }; + + pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId { + return switch (unpacked) { + .object_global_import => |i| @enumFromInt(@intFromEnum(i)), + .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_global_imports.entries.len), + }; + } + + pub fn unpack(id: GlobalImportId, wasm: *const Wasm) Unpacked { + const i = @intFromEnum(id); + if (i < wasm.object_global_imports.entries.len) return .{ .object_global_import = @enumFromInt(i) }; + const zcu_import_i = i - wasm.object_global_imports.entries.len; + return .{ .zcu_import = @enumFromInt(zcu_import_i) }; + } + /// This function is allowed O(N) lookup because it is only called during /// diagnostic generation. pub fn sourceLocation(id: GlobalImportId, wasm: *const Wasm) SourceLocation { switch (id.unpack(wasm)) { - .object_global_import => |obj_func_index| { + .object_global_import => |obj_global_index| { // TODO binary search for (wasm.objects.items, 0..) |o, i| { - if (o.global_imports.off <= obj_func_index and - o.global_imports.off + o.global_imports.len > obj_func_index) + if (o.global_imports.off <= @intFromEnum(obj_global_index) and + o.global_imports.off + o.global_imports.len > @intFromEnum(obj_global_index)) { - return .pack(wasm, .{ .object_index = @enumFromInt(i) }); + return .pack(.{ .object_index = @enumFromInt(i) }, wasm); } } else unreachable; }, @@ -1330,23 +1448,13 @@ pub fn deinit(wasm: *Wasm) void { wasm.object_memories.deinit(gpa); wasm.object_data_segments.deinit(gpa); - wasm.object_relocatable_codes.deinit(gpa); wasm.object_custom_segments.deinit(gpa); - wasm.object_symbols.deinit(gpa); - wasm.object_named_segments.deinit(gpa); wasm.object_init_funcs.deinit(gpa); wasm.object_comdats.deinit(gpa); - wasm.object_relocations.deinit(gpa); wasm.object_relocations_table.deinit(gpa); wasm.object_comdat_symbols.deinit(gpa); wasm.objects.deinit(gpa); - wasm.synthetic_symbols.deinit(gpa); - wasm.undefs.deinit(gpa); - wasm.discarded.deinit(gpa); - wasm.segments.deinit(gpa); - wasm.segment_info.deinit(gpa); - wasm.func_types.deinit(gpa); wasm.function_exports.deinit(gpa); wasm.function_imports.deinit(gpa); @@ -1354,8 +1462,6 @@ pub fn deinit(wasm: *Wasm) void { wasm.globals.deinit(gpa); wasm.global_imports.deinit(gpa); wasm.table_imports.deinit(gpa); - wasm.output_globals.deinit(gpa); - wasm.exports.deinit(gpa); wasm.string_bytes.deinit(gpa); wasm.string_table.deinit(gpa); @@ -1374,12 +1480,11 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, const nav_index = func.owner_nav; const code_start: u32 = @intCast(wasm.string_bytes.items.len); - const relocs_start: u32 = @intCast(wasm.relocations.items.len); + const relocs_start: u32 = @intCast(wasm.relocations.len); wasm.string_bytes_lock.lock(); - const wasm_codegen = @import("../../arch/wasm/CodeGen.zig"); dev.check(.wasm_backend); - const result = try wasm_codegen.generate( + try CodeGen.generate( &wasm.base, pt, zcu.navSrcLoc(nav_index), @@ -1391,18 +1496,12 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, ); const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start); - const relocs_len: u32 = @intCast(wasm.relocations.items.len - relocs_start); + const relocs_len: u32 = @intCast(wasm.relocations.len - relocs_start); wasm.string_bytes_lock.unlock(); - const code: Nav.Code = switch (result) { - .ok => .{ - .off = code_start, - .len = code_len, - }, - .fail => |em| { - try pt.zcu.failed_codegen.put(gpa, nav_index, em); - return; - }, + const code: Nav.Code = .{ + .off = code_start, + .len = code_len, }; const gop = try wasm.navs.getOrPut(gpa, nav_index); @@ -1445,24 +1544,22 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index if (!nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { _ = wasm.imports.swapRemove(nav_index); - if (wasm.navs.swapRemove(nav_index)) |old| { - _ = old; + if (wasm.navs.swapRemove(nav_index)) { @panic("TODO reclaim resources"); } return; } if (is_extern) { - try wasm.imports.put(nav_index, {}); - if (wasm.navs.swapRemove(nav_index)) |old| { - _ = old; + try wasm.imports.put(gpa, nav_index, {}); + if (wasm.navs.swapRemove(nav_index)) { @panic("TODO reclaim resources"); } return; } const code_start: u32 = @intCast(wasm.string_bytes.items.len); - const relocs_start: u32 = @intCast(wasm.relocations.items.len); + const relocs_start: u32 = @intCast(wasm.relocations.len); wasm.string_bytes_lock.lock(); const res = try codegen.generateSymbol( @@ -1475,7 +1572,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index ); const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start); - const relocs_len: u32 = @intCast(wasm.relocations.items.len - relocs_start); + const relocs_len: u32 = @intCast(wasm.relocations.len - relocs_start); wasm.string_bytes_lock.unlock(); const code: Nav.Code = switch (res) { @@ -1531,7 +1628,7 @@ pub fn updateExports( wasm: *Wasm, pt: Zcu.PerThread, exported: Zcu.Exported, - export_indices: []const u32, + export_indices: []const Zcu.Export.Index, ) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -1668,7 +1765,7 @@ fn markFunction( wasm: *Wasm, name: String, import: *FunctionImport, - func_index: ObjectFunctionImportIndex, + func_index: FunctionImport.Index, ) error{OutOfMemory}!void { if (import.flags.alive) return; import.flags.alive = true; @@ -1712,7 +1809,7 @@ fn markGlobal( wasm: *Wasm, name: String, import: *GlobalImport, - global_index: ObjectGlobalImportIndex, + global_index: GlobalImport.Index, ) !void { if (import.flags.alive) return; import.flags.alive = true; diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 9d073899bd..fa47333a6e 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -137,13 +137,12 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) anyerror!void { // Merge and order the data segments. Depends on garbage collection so that // unused segments can be omitted. - try f.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len); + try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len); for (wasm.object_data_segments.items, 0..) |*ds, i| { if (!ds.flags.alive) continue; + const data_segment_index: Wasm.DataSegment.Index = @enumFromInt(i); any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !isBss(wasm, ds.name)); - f.data_segments.putAssumeCapacityNoClobber(@intCast(i), .{ - .offset = undefined, - }); + f.data_segments.putAssumeCapacityNoClobber(data_segment_index, .{ .offset = undefined }); } try wasm.functions.ensureUnusedCapacity(gpa, 3); @@ -1082,8 +1081,8 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8) // try writeCustomSectionHeader(binary_bytes.items, header_offset, size); //} -fn isBss(wasm: *Wasm, name: String) bool { - const s = name.slice(wasm); +fn isBss(wasm: *Wasm, optional_name: Wasm.OptionalString) bool { + const s = optional_name.slice(wasm) orelse return false; return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss."); }