| ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { | ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { |
| 30 | code_offset: usize, | 30 | code_offset: usize, |
| 31 | /// Used for variables that create multiple locals on the stack when allocated | 31 | /// Used for variables that create multiple locals on the stack when allocated |
| 32 | /// such as structs and optionals. | 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 | /// Wasm ops, but without input/output/signedness information | 42 | /// Wasm ops, but without input/output/signedness information |
| ... | @@ -572,9 +578,9 @@ pub const Context = struct { | ... | @@ -572,9 +578,9 @@ pub const Context = struct { |
| 572 | }, | 578 | }, |
| 573 | .Bool, | 579 | .Bool, |
| 574 | .Pointer, | 580 | .Pointer, |
| 575 | .Struct, | | |
| 576 | .ErrorSet, | 581 | .ErrorSet, |
| 577 | => wasm.Valtype.i32, | 582 | => wasm.Valtype.i32, |
| | 583 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually by genAlloc(). |
| 578 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}), | 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,6 +750,7 @@ pub const Context = struct { |
| 744 | .constant => unreachable, | 750 | .constant => unreachable, |
| 745 | .dbg_stmt => WValue.none, | 751 | .dbg_stmt => WValue.none, |
| 746 | .div => self.genBinOp(inst.castTag(.div).?, .div), | 752 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| | 753 | .is_err => self.genIsErr(inst.castTag(.is_err).?), |
| 747 | .load => self.genLoad(inst.castTag(.load).?), | 754 | .load => self.genLoad(inst.castTag(.load).?), |
| 748 | .loop => self.genLoop(inst.castTag(.loop).?), | 755 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 749 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | 756 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | @@ -755,6 +762,7 @@ pub const Context = struct { | ... | @@ -755,6 +762,7 @@ pub const Context = struct { |
| 755 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), | 762 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 756 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), | 763 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 757 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 764 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| | 765 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), |
| 758 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), | 766 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 759 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), | 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,7 +830,27 @@ pub const Context = struct { |
| 822 | self.locals.appendAssumeCapacity(val_type); | 830 | self.locals.appendAssumeCapacity(val_type); |
| 823 | self.local_index += 1; | 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 | else => { | 855 | else => { |
| 828 | const valtype = try self.genValtype(inst.base.src, elem_type); | 856 | const valtype = try self.genValtype(inst.base.src, elem_type); |
| ... | @@ -839,12 +867,43 @@ pub const Context = struct { | ... | @@ -839,12 +867,43 @@ pub const Context = struct { |
| 839 | const lhs = self.resolveInst(inst.lhs); | 867 | const lhs = self.resolveInst(inst.lhs); |
| 840 | const rhs = self.resolveInst(inst.rhs); | 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 | switch (lhs) { | 885 | switch (lhs) { |
| 843 | // When assigning a value to a multi_value such as a struct, | 886 | .multi_value => |multi_value| switch (rhs) { |
| 844 | // we simply assign the local_index to the rhs one. | 887 | // When assigning a value to a multi_value such as a struct, |
| 845 | // This allows us to update struct fields without having to individually | 888 | // we simply assign the local_index to the rhs one. |
| 846 | // set each local as each field's index will be calculated off the struct's base index | 889 | // This allows us to update struct fields without having to individually |
| 847 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! | 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 | .local => |local| { | 907 | .local => |local| { |
| 849 | try self.emitWValue(rhs); | 908 | try self.emitWValue(rhs); |
| 850 | try writer.writeByte(wasm.opcode(.local_set)); | 909 | try writer.writeByte(wasm.opcode(.local_set)); |
| ... | @@ -972,6 +1031,23 @@ pub const Context = struct { | ... | @@ -972,6 +1031,23 @@ pub const Context = struct { |
| 972 | try writer.writeByte(wasm.opcode(.i32_const)); | 1031 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 973 | try leb.writeULEB128(writer, error_index); | 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 | else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), | 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,7 +1220,7 @@ pub const Context = struct { |
| 1144 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { | 1220 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1145 | const struct_ptr = self.resolveInst(inst.struct_ptr); | 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 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { | 1226 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { |
| ... | @@ -1187,4 +1263,28 @@ pub const Context = struct { | ... | @@ -1187,4 +1263,28 @@ pub const Context = struct { |
| 1187 | | 1263 | |
| 1188 | return .none; | 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 | }; |