| ... | @@ -580,7 +580,7 @@ pub const Context = struct { | ... | @@ -580,7 +580,7 @@ pub const Context = struct { |
| 580 | .Pointer, | 580 | .Pointer, |
| 581 | .ErrorSet, | 581 | .ErrorSet, |
| 582 | => wasm.Valtype.i32, | 582 | => wasm.Valtype.i32, |
| 583 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually by genAlloc(). | 583 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually. |
| 584 | 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()}), |
| 585 | }; | 585 | }; |
| 586 | } | 586 | } |
| ... | @@ -614,6 +614,55 @@ pub const Context = struct { | ... | @@ -614,6 +614,55 @@ pub const Context = struct { |
| 614 | } | 614 | } |
| 615 | } | 615 | } |
| 616 | | 616 | |
| | 617 | /// Creates one or multiple locals for a given `Type`. |
| | 618 | /// Returns a corresponding `Wvalue` that can either be of tag |
| | 619 | /// local or multi_value |
| | 620 | fn allocLocal(self: *Context, ty: Type) InnerError!WValue { |
| | 621 | const initial_index = self.local_index; |
| | 622 | switch (ty.zigTypeTag()) { |
| | 623 | .Struct => { |
| | 624 | // for each struct field, generate a local |
| | 625 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; |
| | 626 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + struct_data.fields.count()); |
| | 627 | for (struct_data.fields.items()) |entry| { |
| | 628 | const val_type = try self.genValtype( |
| | 629 | .{ .node_offset = struct_data.node_offset }, |
| | 630 | entry.value.ty, |
| | 631 | ); |
| | 632 | self.locals.appendAssumeCapacity(val_type); |
| | 633 | self.local_index += 1; |
| | 634 | } |
| | 635 | return WValue{ .multi_value = .{ |
| | 636 | .index = initial_index, |
| | 637 | .count = @intCast(u32, struct_data.fields.count()), |
| | 638 | } }; |
| | 639 | }, |
| | 640 | .ErrorUnion => { |
| | 641 | // generate a local for both the error and the payload. |
| | 642 | const payload_type = ty.errorUnionChild(); |
| | 643 | |
| | 644 | // we emit the payload value as the first local, and the error as the second |
| | 645 | // The first local is also used to find the index of the error. |
| | 646 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); |
| | 647 | const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type); |
| | 648 | self.locals.appendAssumeCapacity(val_type); |
| | 649 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| | 650 | self.local_index += 2; |
| | 651 | |
| | 652 | return WValue{ .multi_value = .{ |
| | 653 | .index = initial_index, |
| | 654 | .count = 2, |
| | 655 | } }; |
| | 656 | }, |
| | 657 | else => { |
| | 658 | const valtype = try self.genValtype(.{ .node_offset = 0 }, ty); |
| | 659 | try self.locals.append(self.gpa, valtype); |
| | 660 | self.local_index += 1; |
| | 661 | return WValue{ .local = initial_index }; |
| | 662 | }, |
| | 663 | } |
| | 664 | } |
| | 665 | |
| 617 | fn genFunctype(self: *Context) InnerError!void { | 666 | fn genFunctype(self: *Context) InnerError!void { |
| 618 | assert(self.decl.has_tv); | 667 | assert(self.decl.has_tv); |
| 619 | const ty = self.decl.ty; | 668 | const ty = self.decl.ty; |
| ... | @@ -636,8 +685,19 @@ pub const Context = struct { | ... | @@ -636,8 +685,19 @@ pub const Context = struct { |
| 636 | | 685 | |
| 637 | // return type | 686 | // return type |
| 638 | const return_type = ty.fnReturnType(); | 687 | const return_type = ty.fnReturnType(); |
| 639 | switch (return_type.tag()) { | 688 | switch (return_type.zigTypeTag()) { |
| 640 | .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), | 689 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| | 690 | .Struct => return self.fail(.{ .node_offset = 0 }, "TODO: Implement struct as return type for wasm", .{}), |
| | 691 | .Optional => return self.fail(.{ .node_offset = 0 }, "TODO: Implement optionals as return type for wasm", .{}), |
| | 692 | .ErrorUnion => { |
| | 693 | try leb.writeULEB128(writer, @as(u32, 2)); |
| | 694 | const val_type = try self.genValtype( |
| | 695 | .{ .node_offset = 0 }, |
| | 696 | return_type.errorUnionChild(), |
| | 697 | ); |
| | 698 | try writer.writeByte(val_type); |
| | 699 | try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer. |
| | 700 | }, |
| 641 | else => |ret_type| { | 701 | else => |ret_type| { |
| 642 | try leb.writeULEB128(writer, @as(u32, 1)); | 702 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 643 | // Can we maybe get the source index of the return type? | 703 | // Can we maybe get the source index of the return type? |
| ... | @@ -763,6 +823,7 @@ pub const Context = struct { | ... | @@ -763,6 +823,7 @@ pub const Context = struct { |
| 763 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), | 823 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 764 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 824 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 765 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), | 825 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), |
| | 826 | .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?), |
| 766 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), | 827 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 767 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), | 828 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 768 | }; | 829 | }; |
| ... | @@ -787,7 +848,7 @@ pub const Context = struct { | ... | @@ -787,7 +848,7 @@ pub const Context = struct { |
| 787 | const func_inst = inst.func.castTag(.constant).?; | 848 | const func_inst = inst.func.castTag(.constant).?; |
| 788 | const func_val = inst.func.value().?; | 849 | const func_val = inst.func.value().?; |
| 789 | | 850 | |
| 790 | const target = blk: { | 851 | const target: *Decl = blk: { |
| 791 | if (func_val.castTag(.function)) |func| { | 852 | if (func_val.castTag(.function)) |func| { |
| 792 | break :blk func.data.owner_decl; | 853 | break :blk func.data.owner_decl; |
| 793 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { | 854 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| ... | @@ -815,50 +876,7 @@ pub const Context = struct { | ... | @@ -815,50 +876,7 @@ pub const Context = struct { |
| 815 | | 876 | |
| 816 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { | 877 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 817 | const elem_type = inst.base.ty.elemType(); | 878 | const elem_type = inst.base.ty.elemType(); |
| 818 | const initial_index = self.local_index; | 879 | return self.allocLocal(elem_type); |
| 819 | | | |
| 820 | switch (elem_type.zigTypeTag()) { | | |
| 821 | .Struct => { | | |
| 822 | // for each struct field, generate a local | | |
| 823 | const struct_data: *Module.Struct = elem_type.castTag(.@"struct").?.data; | | |
| 824 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + struct_data.fields.count()); | | |
| 825 | for (struct_data.fields.items()) |entry| { | | |
| 826 | const val_type = try self.genValtype( | | |
| 827 | .{ .node_offset = struct_data.node_offset }, | | |
| 828 | entry.value.ty, | | |
| 829 | ); | | |
| 830 | self.locals.appendAssumeCapacity(val_type); | | |
| 831 | self.local_index += 1; | | |
| 832 | } | | |
| 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 | } }; | | |
| 854 | }, | | |
| 855 | else => { | | |
| 856 | const valtype = try self.genValtype(inst.base.src, elem_type); | | |
| 857 | try self.locals.append(self.gpa, valtype); | | |
| 858 | self.local_index += 1; | | |
| 859 | return WValue{ .local = initial_index }; | | |
| 860 | }, | | |
| 861 | } | | |
| 862 | } | 880 | } |
| 863 | | 881 | |
| 864 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | 882 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | @@ -867,21 +885,6 @@ pub const Context = struct { | ... | @@ -867,21 +885,6 @@ pub const Context = struct { |
| 867 | const lhs = self.resolveInst(inst.lhs); | 885 | const lhs = self.resolveInst(inst.lhs); |
| 868 | const rhs = self.resolveInst(inst.rhs); | 886 | const rhs = self.resolveInst(inst.rhs); |
| 869 | | 887 | |
| 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 | | | |
| 885 | switch (lhs) { | 888 | switch (lhs) { |
| 886 | .multi_value => |multi_value| switch (rhs) { | 889 | .multi_value => |multi_value| switch (rhs) { |
| 887 | // When assigning a value to a multi_value such as a struct, | 890 | // When assigning a value to a multi_value such as a struct, |
| ... | @@ -889,8 +892,8 @@ pub const Context = struct { | ... | @@ -889,8 +892,8 @@ pub const Context = struct { |
| 889 | // This allows us to update struct fields without having to individually | 892 | // 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 | 893 | // 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! | 894 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 892 | .constant => { | 895 | .constant, .none => { |
| 893 | // emit all values onto the stack | 896 | // emit all values onto the stack if constant |
| 894 | try self.emitWValue(rhs); | 897 | try self.emitWValue(rhs); |
| 895 | | 898 | |
| 896 | // for each local, pop the stack value into the local | 899 | // for each local, pop the stack value into the local |
| ... | @@ -1037,8 +1040,14 @@ pub const Context = struct { | ... | @@ -1037,8 +1040,14 @@ pub const Context = struct { |
| 1037 | const payload_type = ty.errorUnionChild(); | 1040 | const payload_type = ty.errorUnionChild(); |
| 1038 | if (value.getError()) |_| { | 1041 | if (value.getError()) |_| { |
| 1039 | // no payload, so write a '0' const | 1042 | // no payload, so write a '0' const |
| 1040 | try writer.writeByte(wasm.opcode(.i32_const)); | 1043 | const opcode: wasm.Opcode = buildOpcode(.{ |
| | 1044 | .op = .@"const", |
| | 1045 | .valtype1 = try self.typeToValtype(src, payload_type), |
| | 1046 | }); |
| | 1047 | try writer.writeByte(wasm.opcode(opcode)); |
| 1041 | try leb.writeULEB128(writer, @as(u32, 0)); | 1048 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 1049 | |
| | 1050 | // write the error value |
| 1042 | try self.emitConstant(src, data, error_type); | 1051 | try self.emitConstant(src, data, error_type); |
| 1043 | } else { | 1052 | } else { |
| 1044 | // payload first | 1053 | // payload first |
| ... | @@ -1287,4 +1296,8 @@ pub const Context = struct { | ... | @@ -1287,4 +1296,8 @@ pub const Context = struct { |
| 1287 | // payload's local index is that of its multi_value index, so convert it to a `WValue.local` | 1296 | // 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 }; | 1297 | return WValue{ .local = operand.multi_value.index }; |
| 1289 | } | 1298 | } |
| | 1299 | |
| | 1300 | fn genWrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| | 1301 | return self.resolveInst(inst.operand); |
| | 1302 | } |
| 1290 | }; | 1303 | }; |