| ... | ... | @@ -31,6 +31,9 @@ const WValue = union(enum) { |
| 31 | 31 | code_offset: usize, |
| 32 | 32 | /// The label of the block, used by breaks to find its relative distance |
| 33 | 33 | block_idx: u32, |
| 34 | /// Used for variables that create multiple locals on the stack when allocated |
| 35 | /// such as structs and optionals. |
| 36 | multi_value: u32, |
| 34 | 37 | }; |
| 35 | 38 | |
| 36 | 39 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -587,8 +590,9 @@ pub const Context = struct { |
| 587 | 590 | fn emitWValue(self: *Context, val: WValue) InnerError!void { |
| 588 | 591 | const writer = self.code.writer(); |
| 589 | 592 | switch (val) { |
| 590 | | .block_idx => unreachable, |
| 591 | | .none, .code_offset => {}, |
| 593 | .block_idx => unreachable, // block_idx cannot be referenced |
| 594 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| 595 | .none, .code_offset => {}, // no-op |
| 592 | 596 | .local => |idx| { |
| 593 | 597 | try writer.writeByte(wasm.opcode(.local_get)); |
| 594 | 598 | try leb.writeULEB128(writer, idx); |
| ... | ... | @@ -795,7 +799,7 @@ pub const Context = struct { |
| 795 | 799 | |
| 796 | 800 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 797 | 801 | const elem_type = inst.base.ty.elemType(); |
| 798 | | const local_value = WValue{ .local = self.local_index }; |
| 802 | const initial_index = self.local_index; |
| 799 | 803 | |
| 800 | 804 | switch (elem_type.zigTypeTag()) { |
| 801 | 805 | .Struct => { |
| ... | ... | @@ -810,16 +814,15 @@ pub const Context = struct { |
| 810 | 814 | self.locals.appendAssumeCapacity(val_type); |
| 811 | 815 | self.local_index += 1; |
| 812 | 816 | } |
| 817 | return WValue{ .multi_value = initial_index }; |
| 813 | 818 | }, |
| 814 | | // TODO: Add more types that require extra locals such as optionals |
| 815 | 819 | else => { |
| 816 | 820 | const valtype = try self.genValtype(inst.base.src, elem_type); |
| 817 | 821 | try self.locals.append(self.gpa, valtype); |
| 818 | 822 | self.local_index += 1; |
| 823 | return WValue{ .local = initial_index }; |
| 819 | 824 | }, |
| 820 | 825 | } |
| 821 | | |
| 822 | | return local_value; |
| 823 | 826 | } |
| 824 | 827 | |
| 825 | 828 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | ... | @@ -827,10 +830,20 @@ pub const Context = struct { |
| 827 | 830 | |
| 828 | 831 | const lhs = self.resolveInst(inst.lhs); |
| 829 | 832 | const rhs = self.resolveInst(inst.rhs); |
| 830 | | try self.emitWValue(rhs); |
| 831 | 833 | |
| 832 | | try writer.writeByte(wasm.opcode(.local_set)); |
| 833 | | try leb.writeULEB128(writer, lhs.local); |
| 834 | switch (lhs) { |
| 835 | // When assigning a value to a multi_value such as a struct, |
| 836 | // we simply assign the local_index to the rhs one. |
| 837 | // This allows us to update struct fields without having to individually |
| 838 | // set each local as each field's index will be calculated off the struct's base index |
| 839 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 840 | .local => |local| { |
| 841 | try self.emitWValue(rhs); |
| 842 | try writer.writeByte(wasm.opcode(.local_set)); |
| 843 | try leb.writeULEB128(writer, lhs.local); |
| 844 | }, |
| 845 | else => unreachable, |
| 846 | } |
| 834 | 847 | return .none; |
| 835 | 848 | } |
| 836 | 849 | |
| ... | ... | @@ -1115,6 +1128,6 @@ pub const Context = struct { |
| 1115 | 1128 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1116 | 1129 | const struct_ptr = self.resolveInst(inst.struct_ptr); |
| 1117 | 1130 | |
| 1118 | | return WValue{ .local = struct_ptr.local + @intCast(u32, inst.field_index) }; |
| 1131 | return WValue{ .local = struct_ptr.multi_value + @intCast(u32, inst.field_index) }; |
| 1119 | 1132 | } |
| 1120 | 1133 | }; |