Commit bffa148600

Andrew Kelley <andrew@ziglang.org>
2024-12-07 03:27:00
wasm codegen: fix some compilation errors
1 parent e521879
Changed files (5)
src/arch/wasm/CodeGen.zig
@@ -146,19 +146,14 @@ const WValue = union(enum) {
     float32: f32,
     /// A constant 64bit float value
     float64: f64,
-    /// A value that represents a pointer to the data section.
-    memory: InternPool.Index,
-    /// A value that represents a parent pointer and an offset
-    /// from that pointer. i.e. when slicing with constant values.
-    memory_offset: struct {
-        pointer: InternPool.Index,
-        /// Offset will be set as addend when relocating
-        offset: u32,
+    nav_ref: struct {
+        nav_index: InternPool.Nav.Index,
+        offset: i32 = 0,
+    },
+    uav_ref: struct {
+        ip_index: InternPool.Index,
+        offset: i32 = 0,
     },
-    /// Represents a function pointer
-    /// In wasm function pointers are indexes into a function table,
-    /// rather than an address in the data section.
-    function_index: InternPool.Index,
     /// Offset from the bottom of the virtual stack, with the offset
     /// pointing to where the value lives.
     stack_offset: struct {
@@ -752,7 +747,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
     const ty = func.typeOf(ref);
     if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {
         gop.value_ptr.* = .none;
-        return gop.value_ptr.*;
+        return .none;
     }
 
     // When we need to pass the value by reference (such as a struct), we will
@@ -762,7 +757,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
     // In the other cases, we will simply lower the constant to a value that fits
     // into a single local (such as a pointer, integer, bool, etc).
     const result: WValue = if (isByRef(ty, pt, func.target))
-        .{ .memory = val.toIntern() }
+        .{ .uav_ref = .{ .ip_index = val.toIntern() } }
     else
         try func.lowerConstant(val, ty);
 
@@ -956,6 +951,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
             u32 => @field(extra, field.name),
             i32 => @bitCast(@field(extra, field.name)),
             InternPool.Index => @intFromEnum(@field(extra, field.name)),
+            InternPool.Nav.Index => @intFromEnum(@field(extra, field.name)),
             else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
         });
     }
@@ -1028,17 +1024,36 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
         .imm128 => |val| try func.addImm128(val),
         .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
         .float64 => |val| try func.addFloat64(val),
-        .memory => |ptr| try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = ptr } }),
-        .memory_offset => |mo| try func.addInst(.{
-            .tag = .uav_ref_off,
-            .data = .{
-                .payload = try func.addExtra(Mir.UavRefOff{
-                    .ip_index = mo.pointer,
-                    .offset = @intCast(mo.offset), // TODO should not be an assert
-                }),
-            },
-        }),
-        .function_index => |index| try func.addIpIndex(.function_index, index),
+        .nav_ref => |nav_ref| {
+            if (nav_ref.offset == 0) {
+                try func.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
+            } else {
+                try func.addInst(.{
+                    .tag = .nav_ref_off,
+                    .data = .{
+                        .payload = try func.addExtra(Mir.NavRefOff{
+                            .nav_index = nav_ref.nav_index,
+                            .offset = nav_ref.offset,
+                        }),
+                    },
+                });
+            }
+        },
+        .uav_ref => |uav| {
+            if (uav.offset == 0) {
+                try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = uav.ip_index } });
+            } else {
+                try func.addInst(.{
+                    .tag = .uav_ref_off,
+                    .data = .{
+                        .payload = try func.addExtra(Mir.UavRefOff{
+                            .ip_index = uav.ip_index,
+                            .offset = uav.offset,
+                        }),
+                    },
+                });
+            }
+        },
         .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
     }
 }
@@ -1466,10 +1481,7 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value:
             assert(ty_classes[0] == .direct);
             const scalar_type = abi.scalarType(ty, zcu);
             switch (value) {
-                .memory,
-                .memory_offset,
-                .stack_offset,
-                => _ = try func.load(value, scalar_type, 0),
+                .nav_ref, .stack_offset => _ = try func.load(value, scalar_type, 0),
                 .dead => unreachable,
                 else => try func.emitWValue(value),
             }
@@ -3117,8 +3129,8 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
     const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
     const offset: u64 = prev_offset + ptr.byte_offset;
     return switch (ptr.base_addr) {
-        .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)),
-        .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)),
+        .nav => |nav| return .{ .nav_ref = .{ .nav_index = zcu.chaseNav(nav), .offset = @intCast(offset) } },
+        .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset) } },
         .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
         .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}),
         .opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset),
@@ -3162,51 +3174,6 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
     };
 }
 
-fn lowerUavRef(
-    func: *CodeGen,
-    uav: InternPool.Key.Ptr.BaseAddr.Uav,
-    offset: u32,
-) InnerError!WValue {
-    const pt = func.pt;
-    const zcu = pt.zcu;
-    const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav.val));
-
-    const is_fn_body = ty.zigTypeTag(zcu) == .@"fn";
-    if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(zcu)) {
-        return .{ .imm32 = 0xaaaaaaaa };
-    }
-
-    return if (is_fn_body) .{
-        .function_index = uav.val,
-    } else if (offset == 0) .{
-        .memory = uav.val,
-    } else .{ .memory_offset = .{
-        .pointer = uav.val,
-        .offset = offset,
-    } };
-}
-
-fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {
-    const pt = func.pt;
-    const zcu = pt.zcu;
-    const ip = &zcu.intern_pool;
-
-    const nav_ty = ip.getNav(nav_index).typeOf(ip);
-    if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
-        return .{ .imm32 = 0xaaaaaaaa };
-    }
-
-    const atom_index = try func.wasm.getOrCreateAtomForNav(pt, nav_index);
-    const atom = func.wasm.getAtom(atom_index);
-
-    const target_sym_index = @intFromEnum(atom.sym_index);
-    if (ip.isFunctionType(nav_ty)) {
-        return .{ .function_index = target_sym_index };
-    } else if (offset == 0) {
-        return .{ .memory = target_sym_index };
-    } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
-}
-
 /// Asserts that `isByRef` returns `false` for `ty`.
 fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
     const pt = func.pt;
@@ -3307,7 +3274,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
             .f64 => |f64_val| return .{ .float64 = f64_val },
             else => unreachable,
         },
-        .slice => return .{ .memory = val.toIntern() },
+        .slice => unreachable, // isByRef == true
         .ptr => return func.lowerPtr(val.toIntern(), 0),
         .opt => if (ty.optionalReprIsPayload(zcu)) {
             const pl_ty = ty.optionalChild(zcu);
src/arch/wasm/Emit.zig
@@ -33,6 +33,9 @@ pub fn lowerToCode(emit: *Emit) Error!void {
     var inst: u32 = 0;
 
     loop: switch (tags[inst]) {
+        .dbg_epilogue_begin => {
+            return;
+        },
         .block, .loop => {
             const block_type = datas[inst].block_type;
             try code.ensureUnusedCapacity(gpa, 2);
@@ -42,28 +45,31 @@ pub fn lowerToCode(emit: *Emit) Error!void {
             inst += 1;
             continue :loop tags[inst];
         },
-
         .uav_ref => {
             try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
-
             inst += 1;
             continue :loop tags[inst];
         },
         .uav_ref_off => {
             try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);
-
             inst += 1;
             continue :loop tags[inst];
         },
-
-        .dbg_line => {
+        .nav_ref => {
+            try navRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
             inst += 1;
             continue :loop tags[inst];
         },
-        .dbg_epilogue_begin => {
-            return;
+        .nav_ref_off => {
+            try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data);
+            inst += 1;
+            continue :loop tags[inst];
         },
 
+        .dbg_line => {
+            inst += 1;
+            continue :loop tags[inst];
+        },
         .br_if, .br, .memory_grow, .memory_size => {
             try code.ensureUnusedCapacity(gpa, 11);
             code.appendAssumeCapacity(@intFromEnum(tags[inst]));
@@ -431,7 +437,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
 
                 _ => unreachable,
             }
-            unreachable;
+            comptime unreachable;
         },
         .simd_prefix => {
             try code.ensureUnusedCapacity(gpa, 6 + 20);
@@ -487,7 +493,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
                 },
                 _ => unreachable,
             }
-            unreachable;
+            comptime unreachable;
         },
         .atomics_prefix => {
             try code.ensureUnusedCapacity(gpa, 6 + 20);
@@ -576,13 +582,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {
                     continue :loop tags[inst];
                 },
             }
-            unreachable;
+            comptime unreachable;
         },
     }
-    unreachable;
+    comptime unreachable;
 }
 
-/// Assert 20 unused capacity.
+/// Asserts 20 unused capacity.
 fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
     assert(code.unusedCapacitySlice().len >= 20);
     // Wasm encodes alignment as power of 2, rather than natural alignment.
@@ -619,3 +625,48 @@ fn uavRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir
     const addr: i64 = try wasm.uavAddr(data.ip_index);
     leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
 }
+
+fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff) !void {
+    const comp = wasm.base.comp;
+    const zcu = comp.zcu.?;
+    const ip = &zcu.intern_pool;
+    const gpa = comp.gpa;
+    const is_obj = comp.config.output_mode == .Obj;
+    const target = &comp.root_mod.resolved_target.result;
+    const nav_ty = ip.getNav(data.nav_index).typeOf(ip);
+
+    try code.ensureUnusedCapacity(gpa, 11);
+
+    if (ip.isFunctionType(nav_ty)) {
+        code.appendAssumeCapacity(std.wasm.Opcode.i32_const);
+        assert(data.offset == 0);
+        if (is_obj) {
+            try wasm.out_relocs.append(gpa, .{
+                .offset = @intCast(code.items.len),
+                .index = try wasm.navSymbolIndex(data.nav_index),
+                .tag = .TABLE_INDEX_SLEB,
+                .addend = data.offset,
+            });
+            code.appendNTimesAssumeCapacity(0, 5);
+        } else {
+            const addr: i64 = try wasm.navAddr(data.nav_index);
+            leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
+        }
+    } else {
+        const is_wasm32 = target.cpu.arch == .wasm32;
+        const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
+        code.appendAssumeCapacity(@intFromEnum(opcode));
+        if (is_obj) {
+            try wasm.out_relocs.append(gpa, .{
+                .offset = @intCast(code.items.len),
+                .index = try wasm.navSymbolIndex(data.nav_index),
+                .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
+                .addend = data.offset,
+            });
+            code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
+        } else {
+            const addr: i64 = try wasm.navAddr(data.nav_index);
+            leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
+        }
+    }
+}
src/arch/wasm/Mir.zig
@@ -32,8 +32,12 @@ pub const Inst = struct {
 
     /// Some tags match wasm opcode values to facilitate trivial lowering.
     pub const Tag = enum(u8) {
-        /// Uses `nop`
+        /// Uses `tag`.
         @"unreachable" = 0x00,
+        /// Emits epilogue begin debug information. Marks the end of the function.
+        ///
+        /// Uses `tag` (no additional data).
+        dbg_epilogue_begin,
         /// Creates a new block that can be jump from.
         ///
         /// Type of the block is given in data `block_type`
@@ -46,34 +50,51 @@ pub const Inst = struct {
         /// memory address of an unnamed constant. When emitting an object
         /// file, this adds a relocation.
         ///
-        /// Data is `ip_index`.
+        /// This may not refer to a function.
+        ///
+        /// Uses `ip_index`.
         uav_ref,
         /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
         /// memory address of an unnamed constant, offset by an integer value.
         /// When emitting an object file, this adds a relocation.
         ///
-        /// Data is `payload` pointing to a `UavRefOff`.
+        /// This may not refer to a function.
+        ///
+        /// Uses `payload` pointing to a `UavRefOff`.
         uav_ref_off,
+        /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
+        /// memory address of a named constant.
+        ///
+        /// When this refers to a function, this always lowers to an i32_const
+        /// which is the function index. When emitting an object file, this
+        /// adds a `Wasm.Relocation.Tag.TABLE_INDEX_SLEB` relocation.
+        ///
+        /// Uses `nav_index`.
+        nav_ref,
+        /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
+        /// memory address of named constant, offset by an integer value.
+        /// When emitting an object file, this adds a relocation.
+        ///
+        /// This may not refer to a function.
+        ///
+        /// Uses `payload` pointing to a `NavRefOff`.
+        nav_ref_off,
         /// Inserts debug information about the current line and column
         /// of the source code
         ///
         /// Uses `payload` of which the payload type is `DbgLineColumn`
-        dbg_line = 0x06,
-        /// Emits epilogue begin debug information. Marks the end of the function.
-        ///
-        /// Uses `nop`
-        dbg_epilogue_begin = 0x07,
+        dbg_line,
         /// Represents the end of a function body or an initialization expression
         ///
-        /// Payload is `nop`
+        /// Uses `tag` (no additional data).
         end = 0x0B,
         /// Breaks from the current block to a label
         ///
-        /// Data is `label` where index represents the label to jump to
+        /// Uses `label` where index represents the label to jump to
         br = 0x0C,
         /// Breaks from the current block if the stack value is non-zero
         ///
-        /// Data is `label` where index represents the label to jump to
+        /// Uses `label` where index represents the label to jump to
         br_if = 0x0D,
         /// Jump table that takes the stack value as an index where each value
         /// represents the label to jump to.
@@ -82,7 +103,7 @@ pub const Inst = struct {
         br_table = 0x0E,
         /// Returns from the function
         ///
-        /// Uses `nop`
+        /// Uses `tag`.
         @"return" = 0x0F,
         /// Calls a function using `nav_index`.
         call_nav,
@@ -98,10 +119,6 @@ pub const Inst = struct {
         /// The function is the auto-generated tag name function for the type
         /// provided in `ip_index`.
         call_tag_name,
-        /// Lowers to an i32_const containing the index of a function.
-        /// When emitting an object file, this adds a relocation.
-        /// Uses `ip_index`.
-        function_index,
 
         /// Pops three values from the stack and pushes
         /// the first or second value dependent on the third value.
@@ -663,6 +680,11 @@ pub const UavRefOff = struct {
     offset: i32,
 };
 
+pub const NavRefOff = struct {
+    nav_index: InternPool.Nav.Index,
+    offset: i32,
+};
+
 /// Maps a source line with wasm bytecode
 pub const DbgLineColumn = struct {
     line: u32,
src/link/Wasm.zig
@@ -1508,6 +1508,10 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
 
     dev.check(.wasm_backend);
 
+    // This converts AIR to MIR but does not yet lower to wasm code.
+    // That lowering happens during `flush`, after garbage collection, which
+    // can affect function and global indexes, which affects the LEB integer
+    // encoding, which affects the output binary size.
     try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{
         .function = try CodeGen.function(wasm, pt, func_index, air, liveness),
     });
@@ -1729,7 +1733,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
             continue;
         }
     }
-    wasm.functions_len = @intCast(wasm.functions.items.len);
+    wasm.functions_len = @intCast(wasm.functions.entries.len);
     wasm.function_imports_init_keys = try gpa.dupe(String, wasm.function_imports.keys());
     wasm.function_imports_init_vals = try gpa.dupe(FunctionImportId, wasm.function_imports.vals());
     wasm.function_exports_len = @intCast(wasm.function_exports.items.len);
src/Zcu.zig
@@ -4120,3 +4120,14 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg)
     zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
     return error.CodegenFail;
 }
+
+/// Check if nav is an alias to a function, in which case we want to lower the
+/// actual nav, rather than the alias itself.
+pub fn chaseNav(zcu: *const Zcu, nav: InternPool.Nav.Index) InternPool.Nav.Index {
+    return switch (zcu.intern_pool.indexToKey(zcu.navValue(nav).toIntern())) {
+        .func => |f| f.owner_nav,
+        .variable => |variable| variable.owner_nav,
+        .@"extern" => |@"extern"| @"extern".owner_nav,
+        else => nav,
+    };
+}