Commit ee999d5a14

Andrew Kelley <andrew@ziglang.org>
2024-12-20 07:39:21
implement error table and error names data segments
1 parent 7bf53d2
Changed files (2)
src
src/link/Wasm/Flush.zig
@@ -71,10 +71,26 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
     };
     const is_obj = comp.config.output_mode == .Obj;
     const allow_undefined = is_obj or wasm.import_symbols;
-    const zcu = wasm.base.comp.zcu.?;
-    const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
 
-    {
+    if (comp.zcu) |zcu| {
+        const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
+
+        if (wasm.error_name_table_ref_count > 0) {
+            // Ensure Zcu error name structures are populated.
+            const full_error_names = ip.global_error_set.getNamesFromMainThread();
+            try wasm.error_name_offs.ensureTotalCapacity(gpa, full_error_names.len + 1);
+            if (wasm.error_name_offs.items.len == 0) {
+                // Dummy entry at index 0 to avoid a sub instruction at `@errorName` sites.
+                wasm.error_name_offs.appendAssumeCapacity(0);
+            }
+            const new_error_names = full_error_names[wasm.error_name_offs.items.len - 1 ..];
+            for (new_error_names) |error_name| {
+                wasm.error_name_offs.appendAssumeCapacity(@intCast(wasm.error_name_bytes.items.len));
+                const s: [:0]const u8 = error_name.toSlice(ip);
+                try wasm.error_name_bytes.appendSlice(gpa, s[0 .. s.len + 1]);
+            }
+        }
+
         const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;
 
         for (wasm.nav_exports.keys()) |*nav_export| {
@@ -144,7 +160,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
     // unused segments can be omitted.
     try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len +
         wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +
-        wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 1);
+        wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 2);
     if (is_obj) assert(wasm.uavs_exe.entries.len == 0);
     if (is_obj) assert(wasm.navs_exe.entries.len == 0);
     if (!is_obj) assert(wasm.uavs_obj.entries.len == 0);
@@ -170,6 +186,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
         }), @as(u32, undefined));
     }
     if (wasm.error_name_table_ref_count > 0) {
+        f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined));
         f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined));
     }
 
@@ -546,7 +563,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
                 .__tls_align => @panic("TODO"),
                 .__tls_base => @panic("TODO"),
                 .__tls_size => @panic("TODO"),
-                .__zig_error_name_table => @panic("TODO"),
                 .object_global => |i| {
                     const global = i.ptr(wasm);
                     try binary_writer.writeByte(@intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())));
@@ -730,8 +746,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
             const code_start = binary_bytes.items.len;
             append: {
                 const code = switch (segment_id.unpack(wasm)) {
+                    .__zig_error_names => {
+                        try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items);
+                        break :append;
+                    },
                     .__zig_error_name_table => {
-                        if (true) @panic("TODO lower zig error name table");
+                        if (is_obj) @panic("TODO error name table reloc");
+                        const base = f.data_segments.get(.__zig_error_names).?;
+                        if (!is64) {
+                            try emitErrorNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);
+                        } else {
+                            try emitErrorNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);
+                        }
                         break :append;
                     },
                     .object => |i| c: {
@@ -1491,3 +1517,20 @@ fn uleb128size(x: u32) u32 {
     while (value != 0) : (size += 1) value >>= 7;
     return size;
 }
+
+fn emitErrorNameTable(
+    gpa: Allocator,
+    code: *std.ArrayListUnmanaged(u8),
+    error_name_offs: []const u32,
+    error_name_bytes: []const u8,
+    base: u32,
+    comptime Int: type,
+) error{OutOfMemory}!void {
+    const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8);
+    try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * error_name_offs.len);
+    for (error_name_offs) |off| {
+        const name_len: u32 = @intCast(mem.indexOfScalar(u8, error_name_bytes[off..], 0).?);
+        mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little);
+        mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);
+    }
+}
src/link/Wasm.zig
@@ -253,6 +253,13 @@ all_zcu_locals: std.ArrayListUnmanaged(u8) = .empty,
 params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
 returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
 
+/// All Zcu error names in order, null-terminated, concatenated. No need to
+/// serialize; trivially reconstructed.
+error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
+/// For each Zcu error, in order, offset into `error_name_bytes` where the name
+/// is stored. No need to serialize; trivially reconstructed.
+error_name_offs: std.ArrayListUnmanaged(u32) = .empty,
+
 pub const UavFixup = extern struct {
     uavs_exe_index: UavsExeIndex,
     /// Index into `string_bytes`.
@@ -979,12 +986,11 @@ pub const GlobalImport = extern struct {
         __tls_align,
         __tls_base,
         __tls_size,
-        __zig_error_name_table,
         // Next, index into `object_globals`.
         // Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
         _,
 
-        const first_object_global = @intFromEnum(Resolution.__zig_error_name_table) + 1;
+        const first_object_global = @intFromEnum(Resolution.__tls_size) + 1;
 
         pub const Unpacked = union(enum) {
             unresolved,
@@ -994,7 +1000,6 @@ pub const GlobalImport = extern struct {
             __tls_align,
             __tls_base,
             __tls_size,
-            __zig_error_name_table,
             object_global: ObjectGlobalIndex,
             nav_exe: NavsExeIndex,
             nav_obj: NavsObjIndex,
@@ -1009,7 +1014,6 @@ pub const GlobalImport = extern struct {
                 .__tls_align => .__tls_align,
                 .__tls_base => .__tls_base,
                 .__tls_size => .__tls_size,
-                .__zig_error_name_table => .__zig_error_name_table,
                 _ => {
                     const i: u32 = @intFromEnum(r);
                     const object_global_index = i - first_object_global;
@@ -1036,7 +1040,6 @@ pub const GlobalImport = extern struct {
                 .__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_obj => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)),
                 .nav_exe => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)),
@@ -1062,7 +1065,6 @@ pub const GlobalImport = extern struct {
                 .__tls_align => @tagName(Unpacked.__tls_align),
                 .__tls_base => @tagName(Unpacked.__tls_base),
                 .__tls_size => @tagName(Unpacked.__tls_size),
-                .__zig_error_name_table => @tagName(Unpacked.__zig_error_name_table),
                 .object_global => |i| i.name(wasm).slice(wasm),
                 .nav_obj => |i| i.name(wasm),
                 .nav_exe => |i| i.name(wasm),
@@ -1332,6 +1334,7 @@ pub const DataSegment = extern struct {
     };
 
     pub const Id = enum(u32) {
+        __zig_error_names,
         __zig_error_name_table,
         /// First, an `ObjectDataSegmentIndex`.
         /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
@@ -1341,6 +1344,7 @@ pub const DataSegment = extern struct {
         const first_object = @intFromEnum(Id.__zig_error_name_table) + 1;
 
         pub const Unpacked = union(enum) {
+            __zig_error_names,
             __zig_error_name_table,
             object: ObjectDataSegmentIndex,
             uav_exe: UavsExeIndex,
@@ -1351,6 +1355,7 @@ pub const DataSegment = extern struct {
 
         pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Id {
             return switch (unpacked) {
+                .__zig_error_names => .__zig_error_names,
                 .__zig_error_name_table => .__zig_error_name_table,
                 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
                 inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)),
@@ -1361,6 +1366,7 @@ pub const DataSegment = extern struct {
 
         pub fn unpack(id: Id, wasm: *const Wasm) Unpacked {
             return switch (id) {
+                .__zig_error_names => .__zig_error_names,
                 .__zig_error_name_table => .__zig_error_name_table,
                 _ => {
                     const object_index = @intFromEnum(id) - first_object;
@@ -1393,7 +1399,7 @@ pub const DataSegment = extern struct {
 
         pub fn category(id: Id, wasm: *const Wasm) Category {
             return switch (unpack(id, wasm)) {
-                .__zig_error_name_table => .data,
+                .__zig_error_names, .__zig_error_name_table => .data,
                 .object => |i| {
                     const ptr = i.ptr(wasm);
                     if (ptr.flags.tls) return .tls;
@@ -1414,7 +1420,7 @@ pub const DataSegment = extern struct {
 
         pub fn isTls(id: Id, wasm: *const Wasm) bool {
             return switch (unpack(id, wasm)) {
-                .__zig_error_name_table => false,
+                .__zig_error_names, .__zig_error_name_table => false,
                 .object => |i| i.ptr(wasm).flags.tls,
                 .uav_exe, .uav_obj => false,
                 inline .nav_exe, .nav_obj => |i| {
@@ -1432,7 +1438,7 @@ pub const DataSegment = extern struct {
 
         pub fn name(id: Id, wasm: *const Wasm) []const u8 {
             return switch (unpack(id, wasm)) {
-                .__zig_error_name_table, .uav_exe, .uav_obj => ".data",
+                .__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj => ".data",
                 .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),
                 inline .nav_exe, .nav_obj => |i| {
                     const zcu = wasm.base.comp.zcu.?;
@@ -1445,6 +1451,7 @@ pub const DataSegment = extern struct {
 
         pub fn alignment(id: Id, wasm: *const Wasm) Alignment {
             return switch (unpack(id, wasm)) {
+                .__zig_error_names => .@"1",
                 .__zig_error_name_table => wasm.pointerAlignment(),
                 .object => |i| i.ptr(wasm).flags.alignment,
                 inline .uav_exe, .uav_obj => |i| {
@@ -1472,6 +1479,7 @@ pub const DataSegment = extern struct {
 
         pub fn refCount(id: Id, wasm: *const Wasm) u32 {
             return switch (unpack(id, wasm)) {
+                .__zig_error_names => @intCast(wasm.error_name_offs.items.len),
                 .__zig_error_name_table => wasm.error_name_table_ref_count,
                 .object, .uav_obj, .nav_obj => 0,
                 inline .uav_exe, .nav_exe => |i| i.value(wasm).count,
@@ -1481,7 +1489,7 @@ pub const DataSegment = extern struct {
         pub fn isPassive(id: Id, wasm: *const Wasm) bool {
             if (wasm.base.comp.config.import_memory and !id.isBss(wasm)) return true;
             return switch (unpack(id, wasm)) {
-                .__zig_error_name_table => false,
+                .__zig_error_names, .__zig_error_name_table => false,
                 .object => |i| i.ptr(wasm).flags.is_passive,
                 .uav_exe, .uav_obj, .nav_exe, .nav_obj => false,
             };
@@ -1489,7 +1497,7 @@ pub const DataSegment = extern struct {
 
         pub fn isEmpty(id: Id, wasm: *const Wasm) bool {
             return switch (unpack(id, wasm)) {
-                .__zig_error_name_table => false,
+                .__zig_error_names, .__zig_error_name_table => false,
                 .object => |i| i.ptr(wasm).payload.off == .none,
                 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,
             };
@@ -1497,10 +1505,11 @@ pub const DataSegment = extern struct {
 
         pub fn size(id: Id, wasm: *const Wasm) u32 {
             return switch (unpack(id, wasm)) {
+                .__zig_error_names => @intCast(wasm.error_name_bytes.items.len),
                 .__zig_error_name_table => {
                     const comp = wasm.base.comp;
                     const zcu = comp.zcu.?;
-                    const errors_len = 1 + zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
+                    const errors_len = wasm.error_name_offs.items.len;
                     const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu);
                     return @intCast(errors_len * elem_size);
                 },
@@ -1589,8 +1598,8 @@ const PreloadedStrings = struct {
     __wasm_init_memory: String,
     __wasm_init_memory_flag: String,
     __wasm_init_tls: String,
-    __zig_error_name_table: String,
     __zig_error_names: String,
+    __zig_error_name_table: String,
     __zig_errors_len: String,
     _initialize: String,
     _start: String,
@@ -2367,6 +2376,9 @@ pub fn deinit(wasm: *Wasm) void {
     wasm.params_scratch.deinit(gpa);
     wasm.returns_scratch.deinit(gpa);
 
+    wasm.error_name_bytes.deinit(gpa);
+    wasm.error_name_offs.deinit(gpa);
+
     wasm.missing_exports.deinit(gpa);
 }