| ... | ... | @@ -498,6 +498,8 @@ pub const Context = struct { |
| 498 | 498 | /// List of all locals' types generated throughout this declaration |
| 499 | 499 | /// used to emit locals count at start of 'code' section. |
| 500 | 500 | locals: std.ArrayListUnmanaged(u8), |
| 501 | /// The Target we're emitting (used to call intInfo) |
| 502 | target: std.Target, |
| 501 | 503 | |
| 502 | 504 | const InnerError = error{ |
| 503 | 505 | OutOfMemory, |
| ... | ... | @@ -529,17 +531,22 @@ pub const Context = struct { |
| 529 | 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 |
| 533 | | fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 { |
| 534 | /// Using a given `Type`, returns the corresponding wasm Valtype |
| 535 | fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype { |
| 534 | 536 | return switch (ty.tag()) { |
| 535 | | .f32 => wasm.valtype(.f32), |
| 536 | | .f64 => wasm.valtype(.f64), |
| 537 | | .u32, .i32, .bool => wasm.valtype(.i32), |
| 538 | | .u64, .i64 => wasm.valtype(.i64), |
| 539 | | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), |
| 537 | .f32 => .f32, |
| 538 | .f64 => .f64, |
| 539 | .u32, .i32, .bool => .i32, |
| 540 | .u64, .i64 => .i64, |
| 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 | 550 | /// Using a given `Type`, returns the corresponding wasm value type |
| 544 | 551 | /// Differently from `genValtype` this also allows `void` to create a block |
| 545 | 552 | /// with no return type |
| ... | ... | @@ -643,7 +650,7 @@ pub const Context = struct { |
| 643 | 650 | |
| 644 | 651 | fn genInst(self: *Context, inst: *Inst) InnerError!WValue { |
| 645 | 652 | return switch (inst.tag) { |
| 646 | | .add => self.genAdd(inst.castTag(.add).?), |
| 653 | .add => self.genBinOp(inst.castTag(.add).?, .add), |
| 647 | 654 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 648 | 655 | .arg => self.genArg(inst.castTag(.arg).?), |
| 649 | 656 | .block => self.genBlock(inst.castTag(.block).?), |
| ... | ... | @@ -661,12 +668,12 @@ pub const Context = struct { |
| 661 | 668 | .dbg_stmt => WValue.none, |
| 662 | 669 | .load => self.genLoad(inst.castTag(.load).?), |
| 663 | 670 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 664 | | .mul => self.genMul(inst.castTag(.mul).?), |
| 671 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| 665 | 672 | .not => self.genNot(inst.castTag(.not).?), |
| 666 | 673 | .ret => self.genRet(inst.castTag(.ret).?), |
| 667 | 674 | .retvoid => WValue.none, |
| 668 | 675 | .store => self.genStore(inst.castTag(.store).?), |
| 669 | | .sub => self.genSub(inst.castTag(.sub).?), |
| 676 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 670 | 677 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 671 | 678 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 672 | 679 | }; |
| ... | ... | @@ -747,94 +754,59 @@ pub const Context = struct { |
| 747 | 754 | return WValue{ .local = self.local_index }; |
| 748 | 755 | } |
| 749 | 756 | |
| 750 | | fn genAdd(self: *Context, inst: *Inst.BinOp) 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 { |
| 757 | fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue { |
| 789 | 758 | const lhs = self.resolveInst(inst.lhs); |
| 790 | 759 | const rhs = self.resolveInst(inst.rhs); |
| 791 | 760 | |
| 792 | 761 | try self.emitWValue(lhs); |
| 793 | 762 | try self.emitWValue(rhs); |
| 794 | 763 | |
| 795 | | const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { |
| 796 | | .u32, .i32 => .i32_mul, |
| 797 | | .u64, .i64 => .i64_mul, |
| 798 | | .f32 => .f32_mul, |
| 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 | | |
| 764 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 765 | .op = op, |
| 766 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 767 | }); |
| 803 | 768 | try self.code.append(wasm.opcode(opcode)); |
| 804 | 769 | return .none; |
| 805 | 770 | } |
| 806 | 771 | |
| 807 | 772 | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { |
| 808 | 773 | const writer = self.code.writer(); |
| 809 | | switch (inst.base.ty.tag()) { |
| 810 | | .u32 => { |
| 811 | | try writer.writeByte(wasm.opcode(.i32_const)); |
| 812 | | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); |
| 774 | switch (inst.base.ty.zigTypeTag()) { |
| 775 | .Int => { |
| 776 | // write opcode |
| 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 | 790 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 791 | // write constant |
| 816 | 792 | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 817 | 793 | }, |
| 818 | | .u64 => { |
| 819 | | try writer.writeByte(wasm.opcode(.i64_const)); |
| 820 | | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); |
| 821 | | }, |
| 822 | | .i64 => { |
| 823 | | try writer.writeByte(wasm.opcode(.i64_const)); |
| 824 | | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 825 | | }, |
| 826 | | .f32 => { |
| 827 | | try writer.writeByte(wasm.opcode(.f32_const)); |
| 828 | | // TODO: enforce LE byte order |
| 829 | | try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); |
| 830 | | }, |
| 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))); |
| 794 | .Float => { |
| 795 | // write opcode |
| 796 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 797 | .op = .@"const", |
| 798 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), |
| 799 | }); |
| 800 | try writer.writeByte(wasm.opcode(opcode)); |
| 801 | // write constant |
| 802 | switch (inst.base.ty.floatBits(self.target)) { |
| 803 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))), |
| 804 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))), |
| 805 | else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), |
| 806 | } |
| 835 | 807 | }, |
| 836 | | .void => {}, |
| 837 | | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}), |
| 808 | .Void => {}, |
| 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 | 907 | try self.emitWValue(lhs); |
| 936 | 908 | try self.emitWValue(rhs); |
| 937 | 909 | |
| 938 | | const opcode_maybe: ?wasm.Opcode = switch (op) { |
| 939 | | .lt => @as(?wasm.Opcode, switch (ty) { |
| 940 | | .i32 => .i32_lt_s, |
| 941 | | .u32 => .i32_lt_u, |
| 942 | | .i64 => .i64_lt_s, |
| 943 | | .u64 => .i64_lt_u, |
| 944 | | .f32 => .f32_lt, |
| 945 | | .f64 => .f64_lt, |
| 946 | | else => null, |
| 947 | | }), |
| 948 | | .lte => @as(?wasm.Opcode, switch (ty) { |
| 949 | | .i32 => .i32_le_s, |
| 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 | | |
| 910 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 911 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), |
| 912 | .op = switch (op) { |
| 913 | .lt => .lt, |
| 914 | .lte => .le, |
| 915 | .eq => .eq, |
| 916 | .neq => .ne, |
| 917 | .gte => .ge, |
| 918 | .gt => .gt, |
| 919 | }, |
| 920 | .signedness = inst.lhs.ty.intInfo(self.target).signedness, |
| 921 | }); |
| 994 | 922 | try self.code.append(wasm.opcode(opcode)); |
| 995 | 923 | return WValue{ .code_offset = offset }; |
| 996 | 924 | } |