| ... | @@ -557,7 +557,14 @@ pub const Context = struct { | ... | @@ -557,7 +557,14 @@ pub const Context = struct { |
| 557 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); | 557 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); |
| 558 | }, | 558 | }, |
| 559 | .Bool, .Pointer => wasm.Valtype.i32, | 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,7 +593,7 @@ pub const Context = struct { |
| 586 | try writer.writeByte(wasm.opcode(.local_get)); | 593 | try writer.writeByte(wasm.opcode(.local_get)); |
| 587 | try leb.writeULEB128(writer, idx); | 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,14 +714,15 @@ pub const Context = struct { |
| 707 | .add => self.genBinOp(inst.castTag(.add).?, .add), | 714 | .add => self.genBinOp(inst.castTag(.add).?, .add), |
| 708 | .alloc => self.genAlloc(inst.castTag(.alloc).?), | 715 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 709 | .arg => self.genArg(inst.castTag(.arg).?), | 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 | .block => self.genBlock(inst.castTag(.block).?), | 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 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), | 723 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 712 | .br => self.genBr(inst.castTag(.br).?), | 724 | .br => self.genBr(inst.castTag(.br).?), |
| 713 | .call => self.genCall(inst.castTag(.call).?), | 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 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), | 726 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 719 | .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), | 727 | .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), |
| 720 | .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), | 728 | .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | @@ -724,18 +732,18 @@ pub const Context = struct { | ... | @@ -724,18 +732,18 @@ pub const Context = struct { |
| 724 | .condbr => self.genCondBr(inst.castTag(.condbr).?), | 732 | .condbr => self.genCondBr(inst.castTag(.condbr).?), |
| 725 | .constant => unreachable, | 733 | .constant => unreachable, |
| 726 | .dbg_stmt => WValue.none, | 734 | .dbg_stmt => WValue.none, |
| | 735 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 727 | .load => self.genLoad(inst.castTag(.load).?), | 736 | .load => self.genLoad(inst.castTag(.load).?), |
| 728 | .loop => self.genLoop(inst.castTag(.loop).?), | 737 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 729 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | 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 | .not => self.genNot(inst.castTag(.not).?), | 739 | .not => self.genNot(inst.castTag(.not).?), |
| 733 | .ret => self.genRet(inst.castTag(.ret).?), | 740 | .ret => self.genRet(inst.castTag(.ret).?), |
| 734 | .retvoid => WValue.none, | 741 | .retvoid => WValue.none, |
| 735 | .store => self.genStore(inst.castTag(.store).?), | 742 | .store => self.genStore(inst.castTag(.store).?), |
| 736 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), | 743 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 737 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 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,6 +758,7 @@ pub const Context = struct { |
| 750 | // TODO: Implement tail calls | 758 | // TODO: Implement tail calls |
| 751 | const operand = self.resolveInst(inst.operand); | 759 | const operand = self.resolveInst(inst.operand); |
| 752 | try self.emitWValue(operand); | 760 | try self.emitWValue(operand); |
| | 761 | try self.code.append(wasm.opcode(.@"return")); |
| 753 | return .none; | 762 | return .none; |
| 754 | } | 763 | } |
| 755 | | 764 | |
| ... | @@ -830,44 +839,44 @@ pub const Context = struct { | ... | @@ -830,44 +839,44 @@ pub const Context = struct { |
| 830 | return .none; | 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 | const writer = self.code.writer(); | 843 | const writer = self.code.writer(); |
| 835 | switch (inst.base.ty.zigTypeTag()) { | 844 | switch (ty.zigTypeTag()) { |
| 836 | .Int => { | 845 | .Int => { |
| 837 | // write opcode | 846 | // write opcode |
| 838 | const opcode: wasm.Opcode = buildOpcode(.{ | 847 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 839 | .op = .@"const", | 848 | .op = .@"const", |
| 840 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), | 849 | .valtype1 = try self.typeToValtype(src, ty), |
| 841 | }); | 850 | }); |
| 842 | try writer.writeByte(wasm.opcode(opcode)); | 851 | try writer.writeByte(wasm.opcode(opcode)); |
| 843 | // write constant | 852 | // write constant |
| 844 | switch (inst.base.ty.intInfo(self.target).signedness) { | 853 | switch (ty.intInfo(self.target).signedness) { |
| 845 | .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()), | 854 | .signed => try leb.writeILEB128(writer, value.toSignedInt()), |
| 846 | .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()), | 855 | .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()), |
| 847 | } | 856 | } |
| 848 | }, | 857 | }, |
| 849 | .Bool => { | 858 | .Bool => { |
| 850 | // write opcode | 859 | // write opcode |
| 851 | try writer.writeByte(wasm.opcode(.i32_const)); | 860 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 852 | // write constant | 861 | // write constant |
| 853 | try leb.writeILEB128(writer, inst.val.toSignedInt()); | 862 | try leb.writeILEB128(writer, value.toSignedInt()); |
| 854 | }, | 863 | }, |
| 855 | .Float => { | 864 | .Float => { |
| 856 | // write opcode | 865 | // write opcode |
| 857 | const opcode: wasm.Opcode = buildOpcode(.{ | 866 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 858 | .op = .@"const", | 867 | .op = .@"const", |
| 859 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), | 868 | .valtype1 = try self.typeToValtype(src, ty), |
| 860 | }); | 869 | }); |
| 861 | try writer.writeByte(wasm.opcode(opcode)); | 870 | try writer.writeByte(wasm.opcode(opcode)); |
| 862 | // write constant | 871 | // write constant |
| 863 | switch (inst.base.ty.floatBits(self.target)) { | 872 | switch (ty.floatBits(self.target)) { |
| 864 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))), | 873 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))), |
| 865 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))), | 874 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))), |
| 866 | else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), | 875 | else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), |
| 867 | } | 876 | } |
| 868 | }, | 877 | }, |
| 869 | .Pointer => { | 878 | .Pointer => { |
| 870 | if (inst.val.castTag(.decl_ref)) |payload| { | 879 | if (value.castTag(.decl_ref)) |payload| { |
| 871 | const decl = payload.data; | 880 | const decl = payload.data; |
| 872 | | 881 | |
| 873 | // offset into the offset table within the 'data' section | 882 | // offset into the offset table within the 'data' section |
| ... | @@ -880,10 +889,35 @@ pub const Context = struct { | ... | @@ -880,10 +889,35 @@ pub const Context = struct { |
| 880 | try writer.writeByte(wasm.opcode(.i32_load)); | 889 | try writer.writeByte(wasm.opcode(.i32_load)); |
| 881 | try leb.writeULEB128(writer, @as(u32, 0)); | 890 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 882 | try leb.writeULEB128(writer, @as(u32, 0)); | 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 | .Void => {}, | 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,6 +1018,13 @@ pub const Context = struct { |
| 984 | try self.emitWValue(lhs); | 1018 | try self.emitWValue(lhs); |
| 985 | try self.emitWValue(rhs); | 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 | const opcode: wasm.Opcode = buildOpcode(.{ | 1028 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 988 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), | 1029 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), |
| 989 | .op = switch (op) { | 1030 | .op = switch (op) { |
| ... | @@ -994,7 +1035,7 @@ pub const Context = struct { | ... | @@ -994,7 +1035,7 @@ pub const Context = struct { |
| 994 | .gte => .ge, | 1035 | .gte => .ge, |
| 995 | .gt => .gt, | 1036 | .gt => .gt, |
| 996 | }, | 1037 | }, |
| 997 | .signedness = inst.lhs.ty.intInfo(self.target).signedness, | 1038 | .signedness = signedness, |
| 998 | }); | 1039 | }); |
| 999 | try self.code.append(wasm.opcode(opcode)); | 1040 | try self.code.append(wasm.opcode(opcode)); |
| 1000 | return WValue{ .code_offset = offset }; | 1041 | return WValue{ .code_offset = offset }; |
| ... | @@ -1045,4 +1086,8 @@ pub const Context = struct { | ... | @@ -1045,4 +1086,8 @@ pub const Context = struct { |
| 1045 | try self.code.append(wasm.opcode(.@"unreachable")); | 1086 | try self.code.append(wasm.opcode(.@"unreachable")); |
| 1046 | return .none; | 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 | }; |