| ... | ... | @@ -557,7 +557,14 @@ pub const Context = struct { |
| 557 | 557 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); |
| 558 | 558 | }, |
| 559 | 559 | .Bool, .Pointer => wasm.Valtype.i32, |
| 560 | | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}), |
| 560 | .Enum => switch (ty.tag()) { |
| 561 | .enum_simple => wasm.Valtype.i32, |
| 562 | else => self.typeToValtype( |
| 563 | src, |
| 564 | ty.cast(Type.Payload.EnumFull).?.data.tag_ty, |
| 565 | ), |
| 566 | }, |
| 567 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}), |
| 561 | 568 | }; |
| 562 | 569 | } |
| 563 | 570 | |
| ... | ... | @@ -586,7 +593,7 @@ pub const Context = struct { |
| 586 | 593 | try writer.writeByte(wasm.opcode(.local_get)); |
| 587 | 594 | try leb.writeULEB128(writer, idx); |
| 588 | 595 | }, |
| 589 | | .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack |
| 596 | .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack |
| 590 | 597 | } |
| 591 | 598 | } |
| 592 | 599 | |
| ... | ... | @@ -707,14 +714,15 @@ pub const Context = struct { |
| 707 | 714 | .add => self.genBinOp(inst.castTag(.add).?, .add), |
| 708 | 715 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 709 | 716 | .arg => self.genArg(inst.castTag(.arg).?), |
| 717 | .bitcast => self.genBitcast(inst.castTag(.bitcast).?), |
| 718 | .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"), |
| 719 | .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"), |
| 710 | 720 | .block => self.genBlock(inst.castTag(.block).?), |
| 721 | .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"), |
| 722 | .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"), |
| 711 | 723 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 712 | 724 | .br => self.genBr(inst.castTag(.br).?), |
| 713 | 725 | .call => self.genCall(inst.castTag(.call).?), |
| 714 | | .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"), |
| 715 | | .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"), |
| 716 | | .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"), |
| 717 | | .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"), |
| 718 | 726 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 719 | 727 | .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), |
| 720 | 728 | .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | ... | @@ -724,18 +732,18 @@ pub const Context = struct { |
| 724 | 732 | .condbr => self.genCondBr(inst.castTag(.condbr).?), |
| 725 | 733 | .constant => unreachable, |
| 726 | 734 | .dbg_stmt => WValue.none, |
| 735 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 727 | 736 | .load => self.genLoad(inst.castTag(.load).?), |
| 728 | 737 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 729 | 738 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| 730 | | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 731 | | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 732 | 739 | .not => self.genNot(inst.castTag(.not).?), |
| 733 | 740 | .ret => self.genRet(inst.castTag(.ret).?), |
| 734 | 741 | .retvoid => WValue.none, |
| 735 | 742 | .store => self.genStore(inst.castTag(.store).?), |
| 736 | 743 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 737 | 744 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 738 | | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 745 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 746 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 739 | 747 | }; |
| 740 | 748 | } |
| 741 | 749 | |
| ... | ... | @@ -750,6 +758,7 @@ pub const Context = struct { |
| 750 | 758 | // TODO: Implement tail calls |
| 751 | 759 | const operand = self.resolveInst(inst.operand); |
| 752 | 760 | try self.emitWValue(operand); |
| 761 | try self.code.append(wasm.opcode(.@"return")); |
| 753 | 762 | return .none; |
| 754 | 763 | } |
| 755 | 764 | |
| ... | ... | @@ -830,44 +839,44 @@ pub const Context = struct { |
| 830 | 839 | return .none; |
| 831 | 840 | } |
| 832 | 841 | |
| 833 | | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { |
| 842 | fn emitConstant(self: *Context, src: LazySrcLoc, value: Value, ty: Type) InnerError!void { |
| 834 | 843 | const writer = self.code.writer(); |
| 835 | | switch (inst.base.ty.zigTypeTag()) { |
| 844 | switch (ty.zigTypeTag()) { |
| 836 | 845 | .Int => { |
| 837 | 846 | // write opcode |
| 838 | 847 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 839 | 848 | .op = .@"const", |
| 840 | | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 849 | .valtype1 = try self.typeToValtype(src, ty), |
| 841 | 850 | }); |
| 842 | 851 | try writer.writeByte(wasm.opcode(opcode)); |
| 843 | 852 | // write constant |
| 844 | | switch (inst.base.ty.intInfo(self.target).signedness) { |
| 845 | | .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()), |
| 846 | | .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()), |
| 853 | switch (ty.intInfo(self.target).signedness) { |
| 854 | .signed => try leb.writeILEB128(writer, value.toSignedInt()), |
| 855 | .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()), |
| 847 | 856 | } |
| 848 | 857 | }, |
| 849 | 858 | .Bool => { |
| 850 | 859 | // write opcode |
| 851 | 860 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 852 | 861 | // write constant |
| 853 | | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 862 | try leb.writeILEB128(writer, value.toSignedInt()); |
| 854 | 863 | }, |
| 855 | 864 | .Float => { |
| 856 | 865 | // write opcode |
| 857 | 866 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 858 | 867 | .op = .@"const", |
| 859 | | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 868 | .valtype1 = try self.typeToValtype(src, ty), |
| 860 | 869 | }); |
| 861 | 870 | try writer.writeByte(wasm.opcode(opcode)); |
| 862 | 871 | // write constant |
| 863 | | switch (inst.base.ty.floatBits(self.target)) { |
| 864 | | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))), |
| 865 | | 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))), |
| 866 | | else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), |
| 872 | switch (ty.floatBits(self.target)) { |
| 873 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))), |
| 874 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))), |
| 875 | else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), |
| 867 | 876 | } |
| 868 | 877 | }, |
| 869 | 878 | .Pointer => { |
| 870 | | if (inst.val.castTag(.decl_ref)) |payload| { |
| 879 | if (value.castTag(.decl_ref)) |payload| { |
| 871 | 880 | const decl = payload.data; |
| 872 | 881 | |
| 873 | 882 | // offset into the offset table within the 'data' section |
| ... | ... | @@ -880,10 +889,35 @@ pub const Context = struct { |
| 880 | 889 | try writer.writeByte(wasm.opcode(.i32_load)); |
| 881 | 890 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 882 | 891 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 883 | | } else return self.fail(inst.base.src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{inst.val.tag()}); |
| 892 | } else return self.fail(src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()}); |
| 884 | 893 | }, |
| 885 | 894 | .Void => {}, |
| 886 | | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}), |
| 895 | .Enum => { |
| 896 | if (value.castTag(.enum_field_index)) |field_index| { |
| 897 | switch (ty.tag()) { |
| 898 | .enum_simple => { |
| 899 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 900 | try leb.writeULEB128(writer, field_index.data); |
| 901 | }, |
| 902 | .enum_full, .enum_nonexhaustive => { |
| 903 | const enum_full = ty.cast(Type.Payload.EnumFull).?.data; |
| 904 | if (enum_full.values.count() != 0) { |
| 905 | const tag_val = enum_full.values.entries.items[field_index.data].key; |
| 906 | try self.emitConstant(src, tag_val, enum_full.tag_ty); |
| 907 | } else { |
| 908 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 909 | try leb.writeULEB128(writer, field_index.data); |
| 910 | } |
| 911 | }, |
| 912 | else => unreachable, |
| 913 | } |
| 914 | } else { |
| 915 | var int_tag_buffer: Type.Payload.Bits = undefined; |
| 916 | const int_tag_ty = ty.intTagType(&int_tag_buffer); |
| 917 | try self.emitConstant(src, value, int_tag_ty); |
| 918 | } |
| 919 | }, |
| 920 | else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 887 | 921 | } |
| 888 | 922 | } |
| 889 | 923 | |
| ... | ... | @@ -984,6 +1018,13 @@ pub const Context = struct { |
| 984 | 1018 | try self.emitWValue(lhs); |
| 985 | 1019 | try self.emitWValue(rhs); |
| 986 | 1020 | |
| 1021 | const signedness: std.builtin.Signedness = blk: { |
| 1022 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 1023 | if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 1024 | |
| 1025 | // incase of an actual integer, we emit the correct signedness |
| 1026 | break :blk inst.lhs.ty.intInfo(self.target).signedness; |
| 1027 | }; |
| 987 | 1028 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 988 | 1029 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), |
| 989 | 1030 | .op = switch (op) { |
| ... | ... | @@ -994,7 +1035,7 @@ pub const Context = struct { |
| 994 | 1035 | .gte => .ge, |
| 995 | 1036 | .gt => .gt, |
| 996 | 1037 | }, |
| 997 | | .signedness = inst.lhs.ty.intInfo(self.target).signedness, |
| 1038 | .signedness = signedness, |
| 998 | 1039 | }); |
| 999 | 1040 | try self.code.append(wasm.opcode(opcode)); |
| 1000 | 1041 | return WValue{ .code_offset = offset }; |
| ... | ... | @@ -1045,4 +1086,8 @@ pub const Context = struct { |
| 1045 | 1086 | try self.code.append(wasm.opcode(.@"unreachable")); |
| 1046 | 1087 | return .none; |
| 1047 | 1088 | } |
| 1089 | |
| 1090 | fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue { |
| 1091 | return self.resolveInst(bitcast.operand); |
| 1092 | } |
| 1048 | 1093 | }; |