| ... | @@ -498,6 +498,8 @@ pub const Context = struct { | ... | @@ -498,6 +498,8 @@ pub const Context = struct { |
| 498 | /// List of all locals' types generated throughout this declaration | 498 | /// List of all locals' types generated throughout this declaration |
| 499 | /// used to emit locals count at start of 'code' section. | 499 | /// used to emit locals count at start of 'code' section. |
| 500 | locals: std.ArrayListUnmanaged(u8), | 500 | locals: std.ArrayListUnmanaged(u8), |
| | 501 | /// The Target we're emitting (used to call intInfo) |
| | 502 | target: std.Target, |
| 501 | | 503 | |
| 502 | const InnerError = error{ | 504 | const InnerError = error{ |
| 503 | OutOfMemory, | 505 | OutOfMemory, |
| ... | @@ -529,17 +531,22 @@ pub const Context = struct { | ... | @@ -529,17 +531,22 @@ pub const Context = struct { |
| 529 | return self.values.get(inst).?; // Instruction does not dominate all uses! | 531 | return self.values.get(inst).?; // Instruction does not dominate all uses! |
| 530 | } | 532 | } |
| 531 | | 533 | |
| 532 | /// Using a given `Type`, returns the corresponding wasm value type | 534 | /// Using a given `Type`, returns the corresponding wasm Valtype |
| 533 | fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 { | 535 | fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype { |
| 534 | return switch (ty.tag()) { | 536 | return switch (ty.tag()) { |
| 535 | .f32 => wasm.valtype(.f32), | 537 | .f32 => .f32, |
| 536 | .f64 => wasm.valtype(.f64), | 538 | .f64 => .f64, |
| 537 | .u32, .i32, .bool => wasm.valtype(.i32), | 539 | .u32, .i32, .bool => .i32, |
| 538 | .u64, .i64 => wasm.valtype(.i64), | 540 | .u64, .i64 => .i64, |
| 539 | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), | 541 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}), |
| 540 | }; | 542 | }; |
| 541 | } | 543 | } |
| 542 | | 544 | |
| | 545 | /// Using a given `Type`, returns the byte representation of its wasm value type |
| | 546 | fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 { |
| | 547 | return wasm.valtype(try self.typeToValtype(src, ty)); |
| | 548 | } |
| | 549 | |
| 543 | /// Using a given `Type`, returns the corresponding wasm value type | 550 | /// Using a given `Type`, returns the corresponding wasm value type |
| 544 | /// Differently from `genValtype` this also allows `void` to create a block | 551 | /// Differently from `genValtype` this also allows `void` to create a block |
| 545 | /// with no return type | 552 | /// with no return type |
| ... | @@ -643,7 +650,7 @@ pub const Context = struct { | ... | @@ -643,7 +650,7 @@ pub const Context = struct { |
| 643 | | 650 | |
| 644 | fn genInst(self: *Context, inst: *Inst) InnerError!WValue { | 651 | fn genInst(self: *Context, inst: *Inst) InnerError!WValue { |
| 645 | return switch (inst.tag) { | 652 | return switch (inst.tag) { |
| 646 | .add => self.genAdd(inst.castTag(.add).?), | 653 | .add => self.genBinOp(inst.castTag(.add).?, .add), |
| 647 | .alloc => self.genAlloc(inst.castTag(.alloc).?), | 654 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 648 | .arg => self.genArg(inst.castTag(.arg).?), | 655 | .arg => self.genArg(inst.castTag(.arg).?), |
| 649 | .block => self.genBlock(inst.castTag(.block).?), | 656 | .block => self.genBlock(inst.castTag(.block).?), |
| ... | @@ -661,12 +668,12 @@ pub const Context = struct { | ... | @@ -661,12 +668,12 @@ pub const Context = struct { |
| 661 | .dbg_stmt => WValue.none, | 668 | .dbg_stmt => WValue.none, |
| 662 | .load => self.genLoad(inst.castTag(.load).?), | 669 | .load => self.genLoad(inst.castTag(.load).?), |
| 663 | .loop => self.genLoop(inst.castTag(.loop).?), | 670 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 664 | .mul => self.genMul(inst.castTag(.mul).?), | 671 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| 665 | .not => self.genNot(inst.castTag(.not).?), | 672 | .not => self.genNot(inst.castTag(.not).?), |
| 666 | .ret => self.genRet(inst.castTag(.ret).?), | 673 | .ret => self.genRet(inst.castTag(.ret).?), |
| 667 | .retvoid => WValue.none, | 674 | .retvoid => WValue.none, |
| 668 | .store => self.genStore(inst.castTag(.store).?), | 675 | .store => self.genStore(inst.castTag(.store).?), |
| 669 | .sub => self.genSub(inst.castTag(.sub).?), | 676 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 670 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 677 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 671 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), | 678 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 672 | }; | 679 | }; |
| ... | @@ -747,94 +754,59 @@ pub const Context = struct { | ... | @@ -747,94 +754,59 @@ pub const Context = struct { |
| 747 | return WValue{ .local = self.local_index }; | 754 | return WValue{ .local = self.local_index }; |
| 748 | } | 755 | } |
| 749 | | 756 | |
| 750 | fn genAdd(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | 757 | fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue { |
| 751 | const lhs = self.resolveInst(inst.lhs); | | |
| 752 | const rhs = self.resolveInst(inst.rhs); | | |
| 753 | | | |
| 754 | try self.emitWValue(lhs); | | |
| 755 | try self.emitWValue(rhs); | | |
| 756 | | | |
| 757 | const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { | | |
| 758 | .u32, .i32 => .i32_add, | | |
| 759 | .u64, .i64 => .i64_add, | | |
| 760 | .f32 => .f32_add, | | |
| 761 | .f64 => .f64_add, | | |
| 762 | else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}), | | |
| 763 | }; | | |
| 764 | | | |
| 765 | try self.code.append(wasm.opcode(opcode)); | | |
| 766 | return .none; | | |
| 767 | } | | |
| 768 | | | |
| 769 | fn genSub(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | | |
| 770 | const lhs = self.resolveInst(inst.lhs); | | |
| 771 | const rhs = self.resolveInst(inst.rhs); | | |
| 772 | | | |
| 773 | try self.emitWValue(lhs); | | |
| 774 | try self.emitWValue(rhs); | | |
| 775 | | | |
| 776 | const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { | | |
| 777 | .u32, .i32 => .i32_sub, | | |
| 778 | .u64, .i64 => .i64_sub, | | |
| 779 | .f32 => .f32_sub, | | |
| 780 | .f64 => .f64_sub, | | |
| 781 | else => return self.fail(inst.base.src, "TODO - Implement wasm genSub for type '{s}'", .{inst.base.ty.tag()}), | | |
| 782 | }; | | |
| 783 | | | |
| 784 | try self.code.append(wasm.opcode(opcode)); | | |
| 785 | return .none; | | |
| 786 | } | | |
| 787 | | | |
| 788 | fn genMul(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | | |
| 789 | const lhs = self.resolveInst(inst.lhs); | 758 | const lhs = self.resolveInst(inst.lhs); |
| 790 | const rhs = self.resolveInst(inst.rhs); | 759 | const rhs = self.resolveInst(inst.rhs); |
| 791 | | 760 | |
| 792 | try self.emitWValue(lhs); | 761 | try self.emitWValue(lhs); |
| 793 | try self.emitWValue(rhs); | 762 | try self.emitWValue(rhs); |
| 794 | | 763 | |
| 795 | const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { | 764 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 796 | .u32, .i32 => .i32_mul, | 765 | .op = op, |
| 797 | .u64, .i64 => .i64_mul, | 766 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 798 | .f32 => .f32_mul, | 767 | }); |
| 799 | .f64 => .f64_mul, | | |
| 800 | else => return self.fail(inst.base.src, "TODO - Implement wasm genMul for type '{s}'", .{inst.base.ty.tag()}), | | |
| 801 | }; | | |
| 802 | | | |
| 803 | try self.code.append(wasm.opcode(opcode)); | 768 | try self.code.append(wasm.opcode(opcode)); |
| 804 | return .none; | 769 | return .none; |
| 805 | } | 770 | } |
| 806 | | 771 | |
| 807 | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { | 772 | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { |
| 808 | const writer = self.code.writer(); | 773 | const writer = self.code.writer(); |
| 809 | switch (inst.base.ty.tag()) { | 774 | switch (inst.base.ty.zigTypeTag()) { |
| 810 | .u32 => { | 775 | .Int => { |
| 811 | try writer.writeByte(wasm.opcode(.i32_const)); | 776 | // write opcode |
| 812 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); | 777 | const opcode: wasm.Opcode = buildOpcode(.{ |
| | 778 | .op = .@"const", |
| | 779 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| | 780 | }); |
| | 781 | try writer.writeByte(wasm.opcode(opcode)); |
| | 782 | // write constant |
| | 783 | switch (inst.base.ty.intInfo(self.target).signedness) { |
| | 784 | .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()), |
| | 785 | .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()), |
| | 786 | } |
| 813 | }, | 787 | }, |
| 814 | .i32, .bool => { | 788 | .Bool => { |
| | 789 | // write opcode |
| 815 | try writer.writeByte(wasm.opcode(.i32_const)); | 790 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 791 | // write constant |
| 816 | try leb.writeILEB128(writer, inst.val.toSignedInt()); | 792 | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 817 | }, | 793 | }, |
| 818 | .u64 => { | 794 | .Float => { |
| 819 | try writer.writeByte(wasm.opcode(.i64_const)); | 795 | // write opcode |
| 820 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); | 796 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 821 | }, | 797 | .op = .@"const", |
| 822 | .i64 => { | 798 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 823 | try writer.writeByte(wasm.opcode(.i64_const)); | 799 | }); |
| 824 | try leb.writeILEB128(writer, inst.val.toSignedInt()); | 800 | try writer.writeByte(wasm.opcode(opcode)); |
| 825 | }, | 801 | // write constant |
| 826 | .f32 => { | 802 | switch (inst.base.ty.floatBits(self.target)) { |
| 827 | try writer.writeByte(wasm.opcode(.f32_const)); | 803 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))), |
| 828 | // TODO: enforce LE byte order | 804 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))), |
| 829 | try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); | 805 | else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), |
| 830 | }, | 806 | } |
| 831 | .f64 => { | | |
| 832 | try writer.writeByte(wasm.opcode(.f64_const)); | | |
| 833 | // TODO: enforce LE byte order | | |
| 834 | try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64))); | | |
| 835 | }, | 807 | }, |
| 836 | .void => {}, | 808 | .Void => {}, |
| 837 | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}), | 809 | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}), |
| 838 | } | 810 | } |
| 839 | } | 811 | } |
| 840 | | 812 | |
| ... | @@ -935,62 +907,18 @@ pub const Context = struct { | ... | @@ -935,62 +907,18 @@ pub const Context = struct { |
| 935 | try self.emitWValue(lhs); | 907 | try self.emitWValue(lhs); |
| 936 | try self.emitWValue(rhs); | 908 | try self.emitWValue(rhs); |
| 937 | | 909 | |
| 938 | const opcode_maybe: ?wasm.Opcode = switch (op) { | 910 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 939 | .lt => @as(?wasm.Opcode, switch (ty) { | 911 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), |
| 940 | .i32 => .i32_lt_s, | 912 | .op = switch (op) { |
| 941 | .u32 => .i32_lt_u, | 913 | .lt => .lt, |
| 942 | .i64 => .i64_lt_s, | 914 | .lte => .le, |
| 943 | .u64 => .i64_lt_u, | 915 | .eq => .eq, |
| 944 | .f32 => .f32_lt, | 916 | .neq => .ne, |
| 945 | .f64 => .f64_lt, | 917 | .gte => .ge, |
| 946 | else => null, | 918 | .gt => .gt, |
| 947 | }), | 919 | }, |
| 948 | .lte => @as(?wasm.Opcode, switch (ty) { | 920 | .signedness = inst.lhs.ty.intInfo(self.target).signedness, |
| 949 | .i32 => .i32_le_s, | 921 | }); |
| 950 | .u32 => .i32_le_u, | | |
| 951 | .i64 => .i64_le_s, | | |
| 952 | .u64 => .i64_le_u, | | |
| 953 | .f32 => .f32_le, | | |
| 954 | .f64 => .f64_le, | | |
| 955 | else => null, | | |
| 956 | }), | | |
| 957 | .eq => @as(?wasm.Opcode, switch (ty) { | | |
| 958 | .i32, .u32 => .i32_eq, | | |
| 959 | .i64, .u64 => .i64_eq, | | |
| 960 | .f32 => .f32_eq, | | |
| 961 | .f64 => .f64_eq, | | |
| 962 | else => null, | | |
| 963 | }), | | |
| 964 | .gte => @as(?wasm.Opcode, switch (ty) { | | |
| 965 | .i32 => .i32_ge_s, | | |
| 966 | .u32 => .i32_ge_u, | | |
| 967 | .i64 => .i64_ge_s, | | |
| 968 | .u64 => .i64_ge_u, | | |
| 969 | .f32 => .f32_ge, | | |
| 970 | .f64 => .f64_ge, | | |
| 971 | else => null, | | |
| 972 | }), | | |
| 973 | .gt => @as(?wasm.Opcode, switch (ty) { | | |
| 974 | .i32 => .i32_gt_s, | | |
| 975 | .u32 => .i32_gt_u, | | |
| 976 | .i64 => .i64_gt_s, | | |
| 977 | .u64 => .i64_gt_u, | | |
| 978 | .f32 => .f32_gt, | | |
| 979 | .f64 => .f64_gt, | | |
| 980 | else => null, | | |
| 981 | }), | | |
| 982 | .neq => @as(?wasm.Opcode, switch (ty) { | | |
| 983 | .i32, .u32 => .i32_ne, | | |
| 984 | .i64, .u64 => .i64_ne, | | |
| 985 | .f32 => .f32_ne, | | |
| 986 | .f64 => .f64_ne, | | |
| 987 | else => null, | | |
| 988 | }), | | |
| 989 | }; | | |
| 990 | | | |
| 991 | const opcode = opcode_maybe orelse | | |
| 992 | return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) }); | | |
| 993 | | | |
| 994 | try self.code.append(wasm.opcode(opcode)); | 922 | try self.code.append(wasm.opcode(opcode)); |
| 995 | return WValue{ .code_offset = offset }; | 923 | return WValue{ .code_offset = offset }; |
| 996 | } | 924 | } |