| ... | ... | @@ -580,7 +580,7 @@ pub const Context = struct { |
| 580 | 580 | .Pointer, |
| 581 | 581 | .ErrorSet, |
| 582 | 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 | 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 | 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 | 666 | fn genFunctype(self: *Context) InnerError!void { |
| 618 | 667 | assert(self.decl.has_tv); |
| 619 | 668 | const ty = self.decl.ty; |
| ... | ... | @@ -636,8 +685,19 @@ pub const Context = struct { |
| 636 | 685 | |
| 637 | 686 | // return type |
| 638 | 687 | const return_type = ty.fnReturnType(); |
| 639 | | switch (return_type.tag()) { |
| 640 | | .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 688 | switch (return_type.zigTypeTag()) { |
| 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 | 701 | else => |ret_type| { |
| 642 | 702 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 643 | 703 | // Can we maybe get the source index of the return type? |
| ... | ... | @@ -763,6 +823,7 @@ pub const Context = struct { |
| 763 | 823 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 764 | 824 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 765 | 825 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), |
| 826 | .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?), |
| 766 | 827 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 767 | 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 | 848 | const func_inst = inst.func.castTag(.constant).?; |
| 788 | 849 | const func_val = inst.func.value().?; |
| 789 | 850 | |
| 790 | | const target = blk: { |
| 851 | const target: *Decl = blk: { |
| 791 | 852 | if (func_val.castTag(.function)) |func| { |
| 792 | 853 | break :blk func.data.owner_decl; |
| 793 | 854 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| ... | ... | @@ -815,50 +876,7 @@ pub const Context = struct { |
| 815 | 876 | |
| 816 | 877 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 817 | 878 | const elem_type = inst.base.ty.elemType(); |
| 818 | | const initial_index = self.local_index; |
| 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 | | } |
| 879 | return self.allocLocal(elem_type); |
| 862 | 880 | } |
| 863 | 881 | |
| 864 | 882 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | ... | @@ -867,21 +885,6 @@ pub const Context = struct { |
| 867 | 885 | const lhs = self.resolveInst(inst.lhs); |
| 868 | 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 | 888 | switch (lhs) { |
| 886 | 889 | .multi_value => |multi_value| switch (rhs) { |
| 887 | 890 | // When assigning a value to a multi_value such as a struct, |
| ... | ... | @@ -889,8 +892,8 @@ pub const Context = struct { |
| 889 | 892 | // This allows us to update struct fields without having to individually |
| 890 | 893 | // set each local as each field's index will be calculated off the struct's base index |
| 891 | 894 | .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 |
| 895 | .constant, .none => { |
| 896 | // emit all values onto the stack if constant |
| 894 | 897 | try self.emitWValue(rhs); |
| 895 | 898 | |
| 896 | 899 | // for each local, pop the stack value into the local |
| ... | ... | @@ -1037,8 +1040,14 @@ pub const Context = struct { |
| 1037 | 1040 | const payload_type = ty.errorUnionChild(); |
| 1038 | 1041 | if (value.getError()) |_| { |
| 1039 | 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 | 1048 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1049 | |
| 1050 | // write the error value |
| 1042 | 1051 | try self.emitConstant(src, data, error_type); |
| 1043 | 1052 | } else { |
| 1044 | 1053 | // payload first |
| ... | ... | @@ -1287,4 +1296,8 @@ pub const Context = struct { |
| 1287 | 1296 | // payload's local index is that of its multi_value index, so convert it to a `WValue.local` |
| 1288 | 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 | }; |