| ... | ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { |
| 30 | 30 | code_offset: usize, |
| 31 | 31 | /// Used for variables that create multiple locals on the stack when allocated |
| 32 | 32 | /// such as structs and optionals. |
| 33 | | multi_value: u32, |
| 33 | multi_value: struct { |
| 34 | /// The index of the first local variable |
| 35 | index: u32, |
| 36 | /// The count of local variables this `WValue` consists of. |
| 37 | /// i.e. an ErrorUnion has a 'count' of 2. |
| 38 | count: u32, |
| 39 | }, |
| 34 | 40 | }; |
| 35 | 41 | |
| 36 | 42 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -572,9 +578,9 @@ pub const Context = struct { |
| 572 | 578 | }, |
| 573 | 579 | .Bool, |
| 574 | 580 | .Pointer, |
| 575 | | .Struct, |
| 576 | 581 | .ErrorSet, |
| 577 | 582 | => wasm.Valtype.i32, |
| 583 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually by genAlloc(). |
| 578 | 584 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}), |
| 579 | 585 | }; |
| 580 | 586 | } |
| ... | ... | @@ -744,6 +750,7 @@ pub const Context = struct { |
| 744 | 750 | .constant => unreachable, |
| 745 | 751 | .dbg_stmt => WValue.none, |
| 746 | 752 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 753 | .is_err => self.genIsErr(inst.castTag(.is_err).?), |
| 747 | 754 | .load => self.genLoad(inst.castTag(.load).?), |
| 748 | 755 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 749 | 756 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | ... | @@ -755,6 +762,7 @@ pub const Context = struct { |
| 755 | 762 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 756 | 763 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 757 | 764 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 765 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), |
| 758 | 766 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 759 | 767 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 760 | 768 | }; |
| ... | ... | @@ -822,7 +830,27 @@ pub const Context = struct { |
| 822 | 830 | self.locals.appendAssumeCapacity(val_type); |
| 823 | 831 | self.local_index += 1; |
| 824 | 832 | } |
| 825 | | return WValue{ .multi_value = initial_index }; |
| 833 | return WValue{ .multi_value = .{ |
| 834 | .index = initial_index, |
| 835 | .count = @intCast(u32, struct_data.fields.count()), |
| 836 | } }; |
| 837 | }, |
| 838 | .ErrorUnion => { |
| 839 | // generate a local for both the error and the payload. |
| 840 | const payload_type = elem_type.errorUnionChild(); |
| 841 | |
| 842 | // we emit the payload value as the first local, and the error as the second |
| 843 | // The first local is also used to find the index of the error. |
| 844 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); |
| 845 | const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type); |
| 846 | self.locals.appendAssumeCapacity(val_type); |
| 847 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| 848 | self.local_index += 2; |
| 849 | |
| 850 | return WValue{ .multi_value = .{ |
| 851 | .index = initial_index, |
| 852 | .count = 2, |
| 853 | } }; |
| 826 | 854 | }, |
| 827 | 855 | else => { |
| 828 | 856 | const valtype = try self.genValtype(inst.base.src, elem_type); |
| ... | ... | @@ -839,12 +867,43 @@ pub const Context = struct { |
| 839 | 867 | const lhs = self.resolveInst(inst.lhs); |
| 840 | 868 | const rhs = self.resolveInst(inst.rhs); |
| 841 | 869 | |
| 870 | // switch (lhs) { |
| 871 | // // When assigning a value to a multi_value such as a struct, |
| 872 | // // we simply assign the local_index to the rhs one. |
| 873 | // // This allows us to update struct fields without having to individually |
| 874 | // // set each local as each field's index will be calculated off the struct's base index |
| 875 | // .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 876 | // .local => |local| { |
| 877 | // try self.emitWValue(rhs); |
| 878 | // try writer.writeByte(wasm.opcode(.local_set)); |
| 879 | // try leb.writeULEB128(writer, lhs.local); |
| 880 | // }, |
| 881 | // else => unreachable, |
| 882 | // } |
| 883 | // return .none; |
| 884 | |
| 842 | 885 | switch (lhs) { |
| 843 | | // When assigning a value to a multi_value such as a struct, |
| 844 | | // we simply assign the local_index to the rhs one. |
| 845 | | // This allows us to update struct fields without having to individually |
| 846 | | // set each local as each field's index will be calculated off the struct's base index |
| 847 | | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 886 | .multi_value => |multi_value| switch (rhs) { |
| 887 | // When assigning a value to a multi_value such as a struct, |
| 888 | // we simply assign the local_index to the rhs one. |
| 889 | // This allows us to update struct fields without having to individually |
| 890 | // set each local as each field's index will be calculated off the struct's base index |
| 891 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 892 | .constant => { |
| 893 | // emit all values onto the stack |
| 894 | try self.emitWValue(rhs); |
| 895 | |
| 896 | // for each local, pop the stack value into the local |
| 897 | // As the last element is on top of the stack, we must populate the locals |
| 898 | // in reverse. |
| 899 | var i: u32 = multi_value.count; |
| 900 | while (i > 0) : (i -= 1) { |
| 901 | try writer.writeByte(wasm.opcode(.local_set)); |
| 902 | try leb.writeULEB128(writer, multi_value.index + i - 1); |
| 903 | } |
| 904 | }, |
| 905 | else => unreachable, |
| 906 | }, |
| 848 | 907 | .local => |local| { |
| 849 | 908 | try self.emitWValue(rhs); |
| 850 | 909 | try writer.writeByte(wasm.opcode(.local_set)); |
| ... | ... | @@ -972,6 +1031,23 @@ pub const Context = struct { |
| 972 | 1031 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 973 | 1032 | try leb.writeULEB128(writer, error_index); |
| 974 | 1033 | }, |
| 1034 | .ErrorUnion => { |
| 1035 | const data = value.castTag(.error_union).?.data; |
| 1036 | const error_type = ty.errorUnionSet(); |
| 1037 | const payload_type = ty.errorUnionChild(); |
| 1038 | if (value.getError()) |_| { |
| 1039 | // no payload, so write a '0' const |
| 1040 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1041 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1042 | try self.emitConstant(src, data, error_type); |
| 1043 | } else { |
| 1044 | // payload first |
| 1045 | try self.emitConstant(src, data, payload_type); |
| 1046 | // no error, so write a '0' const |
| 1047 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1048 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1049 | } |
| 1050 | }, |
| 975 | 1051 | else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 976 | 1052 | } |
| 977 | 1053 | } |
| ... | ... | @@ -1144,7 +1220,7 @@ pub const Context = struct { |
| 1144 | 1220 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1145 | 1221 | const struct_ptr = self.resolveInst(inst.struct_ptr); |
| 1146 | 1222 | |
| 1147 | | return WValue{ .local = struct_ptr.multi_value + @intCast(u32, inst.field_index) }; |
| 1223 | return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) }; |
| 1148 | 1224 | } |
| 1149 | 1225 | |
| 1150 | 1226 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { |
| ... | ... | @@ -1187,4 +1263,28 @@ pub const Context = struct { |
| 1187 | 1263 | |
| 1188 | 1264 | return .none; |
| 1189 | 1265 | } |
| 1266 | |
| 1267 | fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 1268 | const operand = self.resolveInst(inst.operand); |
| 1269 | const offset = self.code.items.len; |
| 1270 | const writer = self.code.writer(); |
| 1271 | |
| 1272 | // load the error value which is the payload's multi_value index + 1 |
| 1273 | try self.emitWValue(.{ .local = operand.multi_value.index + 1 }); |
| 1274 | // Compare the error value with '0' |
| 1275 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1276 | try leb.writeILEB128(writer, @as(i32, 0)); |
| 1277 | |
| 1278 | // we want to break out of the condition if they're *not* equal, |
| 1279 | // because that means there's an error. |
| 1280 | try writer.writeByte(wasm.opcode(.i32_ne)); |
| 1281 | |
| 1282 | return WValue{ .code_offset = offset }; |
| 1283 | } |
| 1284 | |
| 1285 | fn genUnwrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 1286 | const operand = self.resolveInst(inst.operand); |
| 1287 | // payload's local index is that of its multi_value index, so convert it to a `WValue.local` |
| 1288 | return WValue{ .local = operand.multi_value.index }; |
| 1289 | } |
| 1190 | 1290 | }; |