authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-06 18:27:00-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
logbffa14860078fe466e9f005dfc56869e195281b5
tree43e22f8708b784ea5d1f5f2e7415761ff56014c4
parente521879e4730fd85a92081c0040db7dc5daad8a3

wasm codegen: fix some compilation errors


5 files changed, 161 insertions(+), 106 deletions(-)

src/Zcu.zig+11
......@@ -4120,3 +4120,14 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg)
41204120 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
41214121 return error.CodegenFail;
41224122}
4123
4124/// Check if nav is an alias to a function, in which case we want to lower the
4125/// actual nav, rather than the alias itself.
4126pub fn chaseNav(zcu: *const Zcu, nav: InternPool.Nav.Index) InternPool.Nav.Index {
4127 return switch (zcu.intern_pool.indexToKey(zcu.navValue(nav).toIntern())) {
4128 .func => |f| f.owner_nav,
4129 .variable => |variable| variable.owner_nav,
4130 .@"extern" => |@"extern"| @"extern".owner_nav,
4131 else => nav,
4132 };
4133}
src/arch/wasm/CodeGen.zig+44-77
......@@ -146,19 +146,14 @@ const WValue = union(enum) {
146146 float32: f32,
147147 /// A constant 64bit float value
148148 float64: f64,
149 /// A value that represents a pointer to the data section.
150 memory: InternPool.Index,
151 /// A value that represents a parent pointer and an offset
152 /// from that pointer. i.e. when slicing with constant values.
153 memory_offset: struct {
154 pointer: InternPool.Index,
155 /// Offset will be set as addend when relocating
156 offset: u32,
149 nav_ref: struct {
150 nav_index: InternPool.Nav.Index,
151 offset: i32 = 0,
152 },
153 uav_ref: struct {
154 ip_index: InternPool.Index,
155 offset: i32 = 0,
157156 },
158 /// Represents a function pointer
159 /// In wasm function pointers are indexes into a function table,
160 /// rather than an address in the data section.
161 function_index: InternPool.Index,
162157 /// Offset from the bottom of the virtual stack, with the offset
163158 /// pointing to where the value lives.
164159 stack_offset: struct {
......@@ -752,7 +747,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
752747 const ty = func.typeOf(ref);
753748 if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {
754749 gop.value_ptr.* = .none;
755 return gop.value_ptr.*;
750 return .none;
756751 }
757752
758753 // 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 {
762757 // In the other cases, we will simply lower the constant to a value that fits
763758 // into a single local (such as a pointer, integer, bool, etc).
764759 const result: WValue = if (isByRef(ty, pt, func.target))
765 .{ .memory = val.toIntern() }
760 .{ .uav_ref = .{ .ip_index = val.toIntern() } }
766761 else
767762 try func.lowerConstant(val, ty);
768763
......@@ -956,6 +951,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
956951 u32 => @field(extra, field.name),
957952 i32 => @bitCast(@field(extra, field.name)),
958953 InternPool.Index => @intFromEnum(@field(extra, field.name)),
954 InternPool.Nav.Index => @intFromEnum(@field(extra, field.name)),
959955 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
960956 });
961957 }
......@@ -1028,17 +1024,36 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
10281024 .imm128 => |val| try func.addImm128(val),
10291025 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
10301026 .float64 => |val| try func.addFloat64(val),
1031 .memory => |ptr| try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = ptr } }),
1032 .memory_offset => |mo| try func.addInst(.{
1033 .tag = .uav_ref_off,
1034 .data = .{
1035 .payload = try func.addExtra(Mir.UavRefOff{
1036 .ip_index = mo.pointer,
1037 .offset = @intCast(mo.offset), // TODO should not be an assert
1038 }),
1039 },
1040 }),
1041 .function_index => |index| try func.addIpIndex(.function_index, index),
1027 .nav_ref => |nav_ref| {
1028 if (nav_ref.offset == 0) {
1029 try func.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
1030 } else {
1031 try func.addInst(.{
1032 .tag = .nav_ref_off,
1033 .data = .{
1034 .payload = try func.addExtra(Mir.NavRefOff{
1035 .nav_index = nav_ref.nav_index,
1036 .offset = nav_ref.offset,
1037 }),
1038 },
1039 });
1040 }
1041 },
1042 .uav_ref => |uav| {
1043 if (uav.offset == 0) {
1044 try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = uav.ip_index } });
1045 } else {
1046 try func.addInst(.{
1047 .tag = .uav_ref_off,
1048 .data = .{
1049 .payload = try func.addExtra(Mir.UavRefOff{
1050 .ip_index = uav.ip_index,
1051 .offset = uav.offset,
1052 }),
1053 },
1054 });
1055 }
1056 },
10421057 .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
10431058 }
10441059}
......@@ -1466,10 +1481,7 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value:
14661481 assert(ty_classes[0] == .direct);
14671482 const scalar_type = abi.scalarType(ty, zcu);
14681483 switch (value) {
1469 .memory,
1470 .memory_offset,
1471 .stack_offset,
1472 => _ = try func.load(value, scalar_type, 0),
1484 .nav_ref, .stack_offset => _ = try func.load(value, scalar_type, 0),
14731485 .dead => unreachable,
14741486 else => try func.emitWValue(value),
14751487 }
......@@ -3117,8 +3129,8 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
31173129 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
31183130 const offset: u64 = prev_offset + ptr.byte_offset;
31193131 return switch (ptr.base_addr) {
3120 .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)),
3121 .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)),
3132 .nav => |nav| return .{ .nav_ref = .{ .nav_index = zcu.chaseNav(nav), .offset = @intCast(offset) } },
3133 .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset) } },
31223134 .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
31233135 .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}),
31243136 .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
31623174 };
31633175}
31643176
3165fn lowerUavRef(
3166 func: *CodeGen,
3167 uav: InternPool.Key.Ptr.BaseAddr.Uav,
3168 offset: u32,
3169) InnerError!WValue {
3170 const pt = func.pt;
3171 const zcu = pt.zcu;
3172 const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav.val));
3173
3174 const is_fn_body = ty.zigTypeTag(zcu) == .@"fn";
3175 if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(zcu)) {
3176 return .{ .imm32 = 0xaaaaaaaa };
3177 }
3178
3179 return if (is_fn_body) .{
3180 .function_index = uav.val,
3181 } else if (offset == 0) .{
3182 .memory = uav.val,
3183 } else .{ .memory_offset = .{
3184 .pointer = uav.val,
3185 .offset = offset,
3186 } };
3187}
3188
3189fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {
3190 const pt = func.pt;
3191 const zcu = pt.zcu;
3192 const ip = &zcu.intern_pool;
3193
3194 const nav_ty = ip.getNav(nav_index).typeOf(ip);
3195 if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
3196 return .{ .imm32 = 0xaaaaaaaa };
3197 }
3198
3199 const atom_index = try func.wasm.getOrCreateAtomForNav(pt, nav_index);
3200 const atom = func.wasm.getAtom(atom_index);
3201
3202 const target_sym_index = @intFromEnum(atom.sym_index);
3203 if (ip.isFunctionType(nav_ty)) {
3204 return .{ .function_index = target_sym_index };
3205 } else if (offset == 0) {
3206 return .{ .memory = target_sym_index };
3207 } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
3208}
3209
32103177/// Asserts that `isByRef` returns `false` for `ty`.
32113178fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32123179 const pt = func.pt;
......@@ -3307,7 +3274,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33073274 .f64 => |f64_val| return .{ .float64 = f64_val },
33083275 else => unreachable,
33093276 },
3310 .slice => return .{ .memory = val.toIntern() },
3277 .slice => unreachable, // isByRef == true
33113278 .ptr => return func.lowerPtr(val.toIntern(), 0),
33123279 .opt => if (ty.optionalReprIsPayload(zcu)) {
33133280 const pl_ty = ty.optionalChild(zcu);
src/arch/wasm/Emit.zig+63-12
......@@ -33,6 +33,9 @@ pub fn lowerToCode(emit: *Emit) Error!void {
3333 var inst: u32 = 0;
3434
3535 loop: switch (tags[inst]) {
36 .dbg_epilogue_begin => {
37 return;
38 },
3639 .block, .loop => {
3740 const block_type = datas[inst].block_type;
3841 try code.ensureUnusedCapacity(gpa, 2);
......@@ -42,28 +45,31 @@ pub fn lowerToCode(emit: *Emit) Error!void {
4245 inst += 1;
4346 continue :loop tags[inst];
4447 },
45
4648 .uav_ref => {
4749 try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
48
4950 inst += 1;
5051 continue :loop tags[inst];
5152 },
5253 .uav_ref_off => {
5354 try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);
54
5555 inst += 1;
5656 continue :loop tags[inst];
5757 },
58
59 .dbg_line => {
58 .nav_ref => {
59 try navRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
6060 inst += 1;
6161 continue :loop tags[inst];
6262 },
63 .dbg_epilogue_begin => {
64 return;
63 .nav_ref_off => {
64 try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data);
65 inst += 1;
66 continue :loop tags[inst];
6567 },
6668
69 .dbg_line => {
70 inst += 1;
71 continue :loop tags[inst];
72 },
6773 .br_if, .br, .memory_grow, .memory_size => {
6874 try code.ensureUnusedCapacity(gpa, 11);
6975 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
......@@ -431,7 +437,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
431437
432438 _ => unreachable,
433439 }
434 unreachable;
440 comptime unreachable;
435441 },
436442 .simd_prefix => {
437443 try code.ensureUnusedCapacity(gpa, 6 + 20);
......@@ -487,7 +493,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
487493 },
488494 _ => unreachable,
489495 }
490 unreachable;
496 comptime unreachable;
491497 },
492498 .atomics_prefix => {
493499 try code.ensureUnusedCapacity(gpa, 6 + 20);
......@@ -576,13 +582,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {
576582 continue :loop tags[inst];
577583 },
578584 }
579 unreachable;
585 comptime unreachable;
580586 },
581587 }
582 unreachable;
588 comptime unreachable;
583589}
584590
585/// Assert 20 unused capacity.
591/// Asserts 20 unused capacity.
586592fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
587593 assert(code.unusedCapacitySlice().len >= 20);
588594 // 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
619625 const addr: i64 = try wasm.uavAddr(data.ip_index);
620626 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
621627}
628
629fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff) !void {
630 const comp = wasm.base.comp;
631 const zcu = comp.zcu.?;
632 const ip = &zcu.intern_pool;
633 const gpa = comp.gpa;
634 const is_obj = comp.config.output_mode == .Obj;
635 const target = &comp.root_mod.resolved_target.result;
636 const nav_ty = ip.getNav(data.nav_index).typeOf(ip);
637
638 try code.ensureUnusedCapacity(gpa, 11);
639
640 if (ip.isFunctionType(nav_ty)) {
641 code.appendAssumeCapacity(std.wasm.Opcode.i32_const);
642 assert(data.offset == 0);
643 if (is_obj) {
644 try wasm.out_relocs.append(gpa, .{
645 .offset = @intCast(code.items.len),
646 .index = try wasm.navSymbolIndex(data.nav_index),
647 .tag = .TABLE_INDEX_SLEB,
648 .addend = data.offset,
649 });
650 code.appendNTimesAssumeCapacity(0, 5);
651 } else {
652 const addr: i64 = try wasm.navAddr(data.nav_index);
653 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
654 }
655 } else {
656 const is_wasm32 = target.cpu.arch == .wasm32;
657 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
658 code.appendAssumeCapacity(@intFromEnum(opcode));
659 if (is_obj) {
660 try wasm.out_relocs.append(gpa, .{
661 .offset = @intCast(code.items.len),
662 .index = try wasm.navSymbolIndex(data.nav_index),
663 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
664 .addend = data.offset,
665 });
666 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
667 } else {
668 const addr: i64 = try wasm.navAddr(data.nav_index);
669 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
670 }
671 }
672}
src/arch/wasm/Mir.zig+38-16
......@@ -32,8 +32,12 @@ pub const Inst = struct {
3232
3333 /// Some tags match wasm opcode values to facilitate trivial lowering.
3434 pub const Tag = enum(u8) {
35 /// Uses `nop`
35 /// Uses `tag`.
3636 @"unreachable" = 0x00,
37 /// Emits epilogue begin debug information. Marks the end of the function.
38 ///
39 /// Uses `tag` (no additional data).
40 dbg_epilogue_begin,
3741 /// Creates a new block that can be jump from.
3842 ///
3943 /// Type of the block is given in data `block_type`
......@@ -46,34 +50,51 @@ pub const Inst = struct {
4650 /// memory address of an unnamed constant. When emitting an object
4751 /// file, this adds a relocation.
4852 ///
49 /// Data is `ip_index`.
53 /// This may not refer to a function.
54 ///
55 /// Uses `ip_index`.
5056 uav_ref,
5157 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
5258 /// memory address of an unnamed constant, offset by an integer value.
5359 /// When emitting an object file, this adds a relocation.
5460 ///
55 /// Data is `payload` pointing to a `UavRefOff`.
61 /// This may not refer to a function.
62 ///
63 /// Uses `payload` pointing to a `UavRefOff`.
5664 uav_ref_off,
65 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
66 /// memory address of a named constant.
67 ///
68 /// When this refers to a function, this always lowers to an i32_const
69 /// which is the function index. When emitting an object file, this
70 /// adds a `Wasm.Relocation.Tag.TABLE_INDEX_SLEB` relocation.
71 ///
72 /// Uses `nav_index`.
73 nav_ref,
74 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
75 /// memory address of named constant, offset by an integer value.
76 /// When emitting an object file, this adds a relocation.
77 ///
78 /// This may not refer to a function.
79 ///
80 /// Uses `payload` pointing to a `NavRefOff`.
81 nav_ref_off,
5782 /// Inserts debug information about the current line and column
5883 /// of the source code
5984 ///
6085 /// Uses `payload` of which the payload type is `DbgLineColumn`
61 dbg_line = 0x06,
62 /// Emits epilogue begin debug information. Marks the end of the function.
63 ///
64 /// Uses `nop`
65 dbg_epilogue_begin = 0x07,
86 dbg_line,
6687 /// Represents the end of a function body or an initialization expression
6788 ///
68 /// Payload is `nop`
89 /// Uses `tag` (no additional data).
6990 end = 0x0B,
7091 /// Breaks from the current block to a label
7192 ///
72 /// Data is `label` where index represents the label to jump to
93 /// Uses `label` where index represents the label to jump to
7394 br = 0x0C,
7495 /// Breaks from the current block if the stack value is non-zero
7596 ///
76 /// Data is `label` where index represents the label to jump to
97 /// Uses `label` where index represents the label to jump to
7798 br_if = 0x0D,
7899 /// Jump table that takes the stack value as an index where each value
79100 /// represents the label to jump to.
......@@ -82,7 +103,7 @@ pub const Inst = struct {
82103 br_table = 0x0E,
83104 /// Returns from the function
84105 ///
85 /// Uses `nop`
106 /// Uses `tag`.
86107 @"return" = 0x0F,
87108 /// Calls a function using `nav_index`.
88109 call_nav,
......@@ -98,10 +119,6 @@ pub const Inst = struct {
98119 /// The function is the auto-generated tag name function for the type
99120 /// provided in `ip_index`.
100121 call_tag_name,
101 /// Lowers to an i32_const containing the index of a function.
102 /// When emitting an object file, this adds a relocation.
103 /// Uses `ip_index`.
104 function_index,
105122
106123 /// Pops three values from the stack and pushes
107124 /// the first or second value dependent on the third value.
......@@ -663,6 +680,11 @@ pub const UavRefOff = struct {
663680 offset: i32,
664681};
665682
683pub const NavRefOff = struct {
684 nav_index: InternPool.Nav.Index,
685 offset: i32,
686};
687
666688/// Maps a source line with wasm bytecode
667689pub const DbgLineColumn = struct {
668690 line: u32,
src/link/Wasm.zig+5-1
......@@ -1508,6 +1508,10 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
15081508
15091509 dev.check(.wasm_backend);
15101510
1511 // This converts AIR to MIR but does not yet lower to wasm code.
1512 // That lowering happens during `flush`, after garbage collection, which
1513 // can affect function and global indexes, which affects the LEB integer
1514 // encoding, which affects the output binary size.
15111515 try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{
15121516 .function = try CodeGen.function(wasm, pt, func_index, air, liveness),
15131517 });
......@@ -1729,7 +1733,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
17291733 continue;
17301734 }
17311735 }
1732 wasm.functions_len = @intCast(wasm.functions.items.len);
1736 wasm.functions_len = @intCast(wasm.functions.entries.len);
17331737 wasm.function_imports_init_keys = try gpa.dupe(String, wasm.function_imports.keys());
17341738 wasm.function_imports_init_vals = try gpa.dupe(FunctionImportId, wasm.function_imports.vals());
17351739 wasm.function_exports_len = @intCast(wasm.function_exports.items.len);