| ... | @@ -31,6 +31,9 @@ const WValue = union(enum) { | ... | @@ -31,6 +31,9 @@ const WValue = union(enum) { |
| 31 | code_offset: usize, | 31 | code_offset: usize, |
| 32 | /// The label of the block, used by breaks to find its relative distance | 32 | /// The label of the block, used by breaks to find its relative distance |
| 33 | block_idx: u32, | 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 | /// Wasm ops, but without input/output/signedness information | 39 | /// Wasm ops, but without input/output/signedness information |
| ... | @@ -587,8 +590,9 @@ pub const Context = struct { | ... | @@ -587,8 +590,9 @@ pub const Context = struct { |
| 587 | fn emitWValue(self: *Context, val: WValue) InnerError!void { | 590 | fn emitWValue(self: *Context, val: WValue) InnerError!void { |
| 588 | const writer = self.code.writer(); | 591 | const writer = self.code.writer(); |
| 589 | switch (val) { | 592 | switch (val) { |
| 590 | .block_idx => unreachable, | 593 | .block_idx => unreachable, // block_idx cannot be referenced |
| 591 | .none, .code_offset => {}, | 594 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| | 595 | .none, .code_offset => {}, // no-op |
| 592 | .local => |idx| { | 596 | .local => |idx| { |
| 593 | try writer.writeByte(wasm.opcode(.local_get)); | 597 | try writer.writeByte(wasm.opcode(.local_get)); |
| 594 | try leb.writeULEB128(writer, idx); | 598 | try leb.writeULEB128(writer, idx); |
| ... | @@ -795,7 +799,7 @@ pub const Context = struct { | ... | @@ -795,7 +799,7 @@ pub const Context = struct { |
| 795 | | 799 | |
| 796 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { | 800 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 797 | const elem_type = inst.base.ty.elemType(); | 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 | switch (elem_type.zigTypeTag()) { | 804 | switch (elem_type.zigTypeTag()) { |
| 801 | .Struct => { | 805 | .Struct => { |
| ... | @@ -810,16 +814,15 @@ pub const Context = struct { | ... | @@ -810,16 +814,15 @@ pub const Context = struct { |
| 810 | self.locals.appendAssumeCapacity(val_type); | 814 | self.locals.appendAssumeCapacity(val_type); |
| 811 | self.local_index += 1; | 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 | else => { | 819 | else => { |
| 816 | const valtype = try self.genValtype(inst.base.src, elem_type); | 820 | const valtype = try self.genValtype(inst.base.src, elem_type); |
| 817 | try self.locals.append(self.gpa, valtype); | 821 | try self.locals.append(self.gpa, valtype); |
| 818 | self.local_index += 1; | 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 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | 828 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | @@ -827,10 +830,20 @@ pub const Context = struct { | ... | @@ -827,10 +830,20 @@ pub const Context = struct { |
| 827 | | 830 | |
| 828 | const lhs = self.resolveInst(inst.lhs); | 831 | const lhs = self.resolveInst(inst.lhs); |
| 829 | const rhs = self.resolveInst(inst.rhs); | 832 | const rhs = self.resolveInst(inst.rhs); |
| 830 | try self.emitWValue(rhs); | | |
| 831 | | 833 | |
| 832 | try writer.writeByte(wasm.opcode(.local_set)); | 834 | switch (lhs) { |
| 833 | try leb.writeULEB128(writer, lhs.local); | 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 | return .none; | 847 | return .none; |
| 835 | } | 848 | } |
| 836 | | 849 | |
| ... | @@ -1115,6 +1128,6 @@ pub const Context = struct { | ... | @@ -1115,6 +1128,6 @@ pub const Context = struct { |
| 1115 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { | 1128 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1116 | const struct_ptr = self.resolveInst(inst.struct_ptr); | 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 | }; |