authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-26 16:19:15+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-26 16:19:15+01:00
logaa4eaea778d090e7f0fde74409106d02c075d1ec
treee3bbcb01c6c19953004d0a28efc789a5015e9f4a
parent2c9a5e791ba17987061b057083c99158e85f17d1
parent9f224ebd9a4f26d0dc5b0937d3d5df8fe78f1c9d

Merge branch 'mparadinha-x64-trunc'


5 files changed, 293 insertions(+), 180 deletions(-)

src/arch/x86_64/CodeGen.zig+123-40
...@@ -769,6 +769,11 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u...@@ -769,6 +769,11 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
769/// Use a pointer instruction as the basis for allocating stack memory.769/// Use a pointer instruction as the basis for allocating stack memory.
770fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {770fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
771 const elem_ty = self.air.typeOfIndex(inst).elemType();771 const elem_ty = self.air.typeOfIndex(inst).elemType();
772
773 if (!elem_ty.hasRuntimeBits()) {
774 return self.allocMem(inst, 8, 8);
775 }
776
772 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {777 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
773 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});778 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
774 };779 };
...@@ -904,9 +909,39 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -904,9 +909,39 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
904 if (self.liveness.isUnused(inst))909 if (self.liveness.isUnused(inst))
905 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });910 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
906911
912 const src_ty = self.air.typeOf(ty_op.operand);
913 const dst_ty = self.air.typeOfIndex(inst);
907 const operand = try self.resolveInst(ty_op.operand);914 const operand = try self.resolveInst(ty_op.operand);
908 _ = operand;915
909 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});916 const src_ty_size = src_ty.abiSize(self.target.*);
917 const dst_ty_size = dst_ty.abiSize(self.target.*);
918
919 if (src_ty_size > 8 or dst_ty_size > 8) {
920 return self.fail("TODO implement trunc for abi sizes larger than 8", .{});
921 }
922
923 const dst_mcv = blk: {
924 const reg = switch (operand) {
925 .register => |reg| reg,
926 else => inner: {
927 const reg = try self.register_manager.allocReg(inst, &.{});
928 try self.genSetReg(src_ty, reg, operand);
929 break :inner reg;
930 },
931 };
932 break :blk .{ .register = registerAlias(reg, @intCast(u32, dst_ty_size)) };
933 };
934
935 // when truncating a `u16` to `u5`, for example, those top 3 bits in the result
936 // have to be removed. this only happens if the dst if not a power-of-two size.
937 const dst_bit_size = dst_ty.bitSize(self.target.*);
938 const is_power_of_two = (dst_bit_size & (dst_bit_size - 1)) == 0;
939 if (!is_power_of_two or dst_bit_size < 8) {
940 const mask = (~@as(u64, 0)) >> @intCast(u6, (64 - dst_ty.bitSize(self.target.*)));
941 try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, .{ .immediate = mask });
942 }
943
944 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
910}945}
911946
912fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {947fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1368,7 +1403,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1368,7 +1403,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1368 // TODO we could allocate register here, but need to except addr register and potentially1403 // TODO we could allocate register here, but need to except addr register and potentially
1369 // offset register.1404 // offset register.
1370 const dst_mcv = try self.allocRegOrMem(inst, false);1405 const dst_mcv = try self.allocRegOrMem(inst, false);
1371 try self.genBinMathOpMir(.add, slice_ptr_field_type, .unsigned, .{ .register = addr_reg.to64() }, .{1406 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{
1372 .register = offset_reg.to64(),1407 .register = offset_reg.to64(),
1373 });1408 });
1374 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, slice_ptr_field_type);1409 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, slice_ptr_field_type);
...@@ -1416,13 +1451,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1416,13 +1451,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
1416 // TODO we could allocate register here, but need to except addr register and potentially1451 // TODO we could allocate register here, but need to except addr register and potentially
1417 // offset register.1452 // offset register.
1418 const dst_mcv = try self.allocRegOrMem(inst, false);1453 const dst_mcv = try self.allocRegOrMem(inst, false);
1419 try self.genBinMathOpMir(1454 try self.genBinMathOpMir(.add, array_ty, .{ .register = addr_reg.to64() }, .{ .register = offset_reg.to64() });
1420 .add,
1421 array_ty,
1422 .unsigned,
1423 .{ .register = addr_reg.to64() },
1424 .{ .register = offset_reg.to64() },
1425 );
1426 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);1455 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
1427 break :result dst_mcv;1456 break :result dst_mcv;
1428 };1457 };
...@@ -1460,7 +1489,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1460,7 +1489,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1460 else => return self.fail("TODO implement ptr_elem_ptr when ptr is {}", .{ptr}),1489 else => return self.fail("TODO implement ptr_elem_ptr when ptr is {}", .{ptr}),
1461 }1490 }
1462 };1491 };
1463 try self.genBinMathOpMir(.add, ptr_ty, .unsigned, dst_mcv, .{ .register = offset_reg });1492 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
1464 break :result dst_mcv;1493 break :result dst_mcv;
1465 };1494 };
1466 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });1495 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
...@@ -1891,11 +1920,11 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -1891,11 +1920,11 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
1891 const dst_ty = self.air.typeOfIndex(inst);1920 const dst_ty = self.air.typeOfIndex(inst);
1892 const air_tags = self.air.instructions.items(.tag);1921 const air_tags = self.air.instructions.items(.tag);
1893 switch (air_tags[inst]) {1922 switch (air_tags[inst]) {
1894 .add, .addwrap, .ptr_add => try self.genBinMathOpMir(.add, dst_ty, .unsigned, dst_mcv, src_mcv),1923 .add, .addwrap, .ptr_add => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),
1895 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, .unsigned, dst_mcv, src_mcv),1924 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),
1896 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, .unsigned, dst_mcv, src_mcv),1925 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),
1897 .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, .unsigned, dst_mcv, src_mcv),1926 .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv),
1898 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, .unsigned, dst_mcv, src_mcv),1927 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),
1899 .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),1928 .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),
1900 else => unreachable,1929 else => unreachable,
1901 }1930 }
...@@ -1903,14 +1932,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -1903,14 +1932,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
1903 return dst_mcv;1932 return dst_mcv;
1904}1933}
19051934
1906fn genBinMathOpMir(1935fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
1907 self: *Self,
1908 mir_tag: Mir.Inst.Tag,
1909 dst_ty: Type,
1910 signedness: std.builtin.Signedness,
1911 dst_mcv: MCValue,
1912 src_mcv: MCValue,
1913) !void {
1914 switch (dst_mcv) {1936 switch (dst_mcv) {
1915 .none => unreachable,1937 .none => unreachable,
1916 .undef => unreachable,1938 .undef => unreachable,
...@@ -1927,16 +1949,11 @@ fn genBinMathOpMir(...@@ -1927,16 +1949,11 @@ fn genBinMathOpMir(
1927 .ptr_stack_offset => unreachable,1949 .ptr_stack_offset => unreachable,
1928 .ptr_embedded_in_code => unreachable,1950 .ptr_embedded_in_code => unreachable,
1929 .register => |src_reg| {1951 .register => |src_reg| {
1930 // TODO think more carefully about this: is this actually correct?
1931 const reg_size = if (mir_tag == .cmp and signedness == .signed)
1932 @divExact(dst_reg.size(), 8)
1933 else
1934 @divExact(src_reg.size(), 8);
1935 _ = try self.addInst(.{1952 _ = try self.addInst(.{
1936 .tag = mir_tag,1953 .tag = mir_tag,
1937 .ops = (Mir.Ops{1954 .ops = (Mir.Ops{
1938 .reg1 = registerAlias(dst_reg, reg_size),1955 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
1939 .reg2 = registerAlias(src_reg, reg_size),1956 .reg2 = src_reg,
1940 }).encode(),1957 }).encode(),
1941 .data = undefined,1958 .data = undefined,
1942 });1959 });
...@@ -2483,7 +2500,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -2483,7 +2500,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2483 // This instruction supports only signed 32-bit immediates at most.2500 // This instruction supports only signed 32-bit immediates at most.
2484 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);2501 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
24852502
2486 try self.genBinMathOpMir(.cmp, ty, signedness, dst_mcv, src_mcv);2503 try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv);
2487 break :result switch (signedness) {2504 break :result switch (signedness) {
2488 .signed => MCValue{ .compare_flags_signed = op },2505 .signed => MCValue{ .compare_flags_signed = op },
2489 .unsigned => MCValue{ .compare_flags_unsigned = op },2506 .unsigned => MCValue{ .compare_flags_unsigned = op },
...@@ -2700,7 +2717,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2700,7 +2717,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2700}2717}
27012718
2702fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {2719fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2703 try self.genBinMathOpMir(.cmp, ty, .unsigned, operand, MCValue{ .immediate = 0 });2720 try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 });
2704 return MCValue{ .compare_flags_unsigned = .eq };2721 return MCValue{ .compare_flags_unsigned = .eq };
2705}2722}
27062723
...@@ -2717,7 +2734,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -2717,7 +2734,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2717 return MCValue{ .immediate = 0 }; // always false2734 return MCValue{ .immediate = 0 }; // always false
2718 } else if (!payload_type.hasRuntimeBits()) {2735 } else if (!payload_type.hasRuntimeBits()) {
2719 if (err_type.abiSize(self.target.*) <= 8) {2736 if (err_type.abiSize(self.target.*) <= 8) {
2720 try self.genBinMathOpMir(.cmp, err_type, .unsigned, operand, MCValue{ .immediate = 0 });2737 try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 });
2721 return MCValue{ .compare_flags_unsigned = .gt };2738 return MCValue{ .compare_flags_unsigned = .gt };
2722 } else {2739 } else {
2723 return self.fail("TODO isErr for errors with size larger than register size", .{});2740 return self.fail("TODO isErr for errors with size larger than register size", .{});
...@@ -3589,10 +3606,10 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn...@@ -3589,10 +3606,10 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn
3589}3606}
35903607
3591fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {3608fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3609 const abi_size = ty.abiSize(self.target.*);
3592 switch (mcv) {3610 switch (mcv) {
3593 .dead => unreachable,3611 .dead => unreachable,
3594 .ptr_stack_offset => |unadjusted_off| {3612 .ptr_stack_offset => |unadjusted_off| {
3595 const ptr_abi_size = ty.abiSize(self.target.*);
3596 const elem_ty = ty.childType();3613 const elem_ty = ty.childType();
3597 const elem_abi_size = elem_ty.abiSize(self.target.*);3614 const elem_abi_size = elem_ty.abiSize(self.target.*);
3598 const off = unadjusted_off + @intCast(i32, elem_abi_size);3615 const off = unadjusted_off + @intCast(i32, elem_abi_size);
...@@ -3602,7 +3619,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3602,7 +3619,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3602 _ = try self.addInst(.{3619 _ = try self.addInst(.{
3603 .tag = .lea,3620 .tag = .lea,
3604 .ops = (Mir.Ops{3621 .ops = (Mir.Ops{
3605 .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),3622 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
3606 .reg2 = .rbp,3623 .reg2 = .rbp,
3607 }).encode(),3624 }).encode(),
3608 .data = .{ .imm = @bitCast(u32, -off) },3625 .data = .{ .imm = @bitCast(u32, -off) },
...@@ -3656,15 +3673,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3656,15 +3673,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3656 _ = try self.addInst(.{3673 _ = try self.addInst(.{
3657 .tag = .xor,3674 .tag = .xor,
3658 .ops = (Mir.Ops{3675 .ops = (Mir.Ops{
3659 .reg1 = reg.to64(),3676 .reg1 = reg.to32(),
3660 .reg2 = reg.to64(),3677 .reg2 = reg.to32(),
3661 }).encode(),3678 }).encode(),
3662 .data = undefined,3679 .data = undefined,
3663 });3680 });
3664 return;3681 return;
3665 }3682 }
3666 if (x <= math.maxInt(i32)) {3683 if (x <= math.maxInt(i32)) {
3667 const abi_size = ty.abiSize(self.target.*);
3668 // Next best case: if we set the lower four bytes, the upper four will be zeroed.3684 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
3669 _ = try self.addInst(.{3685 _ = try self.addInst(.{
3670 .tag = .mov,3686 .tag = .mov,
...@@ -3708,6 +3724,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3708,6 +3724,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3708 if (src_reg.id() == reg.id())3724 if (src_reg.id() == reg.id())
3709 return;3725 return;
37103726
3727 if (ty.zigTypeTag() == .Int) blk: {
3728 switch (ty.intInfo(self.target.*).signedness) {
3729 .signed => {
3730 if (abi_size > 4) break :blk;
3731 _ = try self.addInst(.{
3732 .tag = .mov_sign_extend,
3733 .ops = (Mir.Ops{
3734 .reg1 = reg.to64(),
3735 .reg2 = src_reg,
3736 }).encode(),
3737 .data = undefined,
3738 });
3739 },
3740 .unsigned => {
3741 if (abi_size > 2) break :blk;
3742 _ = try self.addInst(.{
3743 .tag = .mov_zero_extend,
3744 .ops = (Mir.Ops{
3745 .reg1 = reg.to64(),
3746 .reg2 = src_reg,
3747 }).encode(),
3748 .data = undefined,
3749 });
3750 },
3751 }
3752 return;
3753 }
3754
3711 _ = try self.addInst(.{3755 _ = try self.addInst(.{
3712 .tag = .mov,3756 .tag = .mov,
3713 .ops = (Mir.Ops{3757 .ops = (Mir.Ops{
...@@ -3781,11 +3825,50 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3781,11 +3825,50 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3781 }3825 }
3782 },3826 },
3783 .stack_offset => |unadjusted_off| {3827 .stack_offset => |unadjusted_off| {
3784 const abi_size = ty.abiSize(self.target.*);
3785 const off = unadjusted_off + @intCast(i32, abi_size);3828 const off = unadjusted_off + @intCast(i32, abi_size);
3786 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {3829 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
3787 return self.fail("stack offset too large", .{});3830 return self.fail("stack offset too large", .{});
3788 }3831 }
3832
3833 if (ty.zigTypeTag() == .Int) blk: {
3834 switch (ty.intInfo(self.target.*).signedness) {
3835 .signed => {
3836 const flags: u2 = switch (abi_size) {
3837 1 => 0b01,
3838 2 => 0b10,
3839 4 => 0b11,
3840 else => break :blk,
3841 };
3842 _ = try self.addInst(.{
3843 .tag = .mov_sign_extend,
3844 .ops = (Mir.Ops{
3845 .reg1 = reg.to64(),
3846 .reg2 = .rbp,
3847 .flags = flags,
3848 }).encode(),
3849 .data = .{ .imm = @bitCast(u32, -off) },
3850 });
3851 },
3852 .unsigned => {
3853 const flags: u2 = switch (abi_size) {
3854 1 => 0b01,
3855 2 => 0b10,
3856 else => break :blk,
3857 };
3858 _ = try self.addInst(.{
3859 .tag = .mov_zero_extend,
3860 .ops = (Mir.Ops{
3861 .reg1 = reg.to64(),
3862 .reg2 = .rbp,
3863 .flags = flags,
3864 }).encode(),
3865 .data = .{ .imm = @bitCast(u32, -off) },
3866 });
3867 },
3868 }
3869 return;
3870 }
3871
3789 _ = try self.addInst(.{3872 _ = try self.addInst(.{
3790 .tag = .mov,3873 .tag = .mov,
3791 .ops = (Mir.Ops{3874 .ops = (Mir.Ops{
src/arch/x86_64/Emit.zig+137-139
...@@ -125,6 +125,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -125,6 +125,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
125 .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst),125 .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst),
126 .mov_mem_index_imm => try emit.mirArithMemIndexImm(.mov, inst),126 .mov_mem_index_imm => try emit.mirArithMemIndexImm(.mov, inst),
127127
128 .mov_sign_extend => try emit.mirMovSignExtend(inst),
129 .mov_zero_extend => try emit.mirMovZeroExtend(inst),
130
128 .movabs => try emit.mirMovabs(inst),131 .movabs => try emit.mirMovabs(inst),
129132
130 .lea => try emit.mirLea(inst),133 .lea => try emit.mirLea(inst),
...@@ -188,14 +191,6 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {...@@ -188,14 +191,6 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
188 return error.EmitFail;191 return error.EmitFail;
189}192}
190193
191fn failWithLoweringError(emit: *Emit, err: LoweringError) InnerError {
192 return switch (err) {
193 error.RaxOperandExpected => emit.fail("Register.rax expected as destination operand", .{}),
194 error.OperandSizeMismatch => emit.fail("operand size mismatch", .{}),
195 else => |e| e,
196 };
197}
198
199fn fixupRelocs(emit: *Emit) InnerError!void {194fn fixupRelocs(emit: *Emit) InnerError!void {
200 // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.195 // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.
201 // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest196 // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest
...@@ -210,15 +205,15 @@ fn fixupRelocs(emit: *Emit) InnerError!void {...@@ -210,15 +205,15 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
210}205}
211206
212fn mirBrk(emit: *Emit) InnerError!void {207fn mirBrk(emit: *Emit) InnerError!void {
213 return lowerToZoEnc(.brk, emit.code) catch |err| emit.failWithLoweringError(err);208 return lowerToZoEnc(.brk, emit.code);
214}209}
215210
216fn mirNop(emit: *Emit) InnerError!void {211fn mirNop(emit: *Emit) InnerError!void {
217 return lowerToZoEnc(.nop, emit.code) catch |err| emit.failWithLoweringError(err);212 return lowerToZoEnc(.nop, emit.code);
218}213}
219214
220fn mirSyscall(emit: *Emit) InnerError!void {215fn mirSyscall(emit: *Emit) InnerError!void {
221 return lowerToZoEnc(.syscall, emit.code) catch |err| emit.failWithLoweringError(err);216 return lowerToZoEnc(.syscall, emit.code);
222}217}
223218
224fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {219fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
...@@ -226,7 +221,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -226,7 +221,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
226 switch (ops.flags) {221 switch (ops.flags) {
227 0b00 => {222 0b00 => {
228 // PUSH/POP reg223 // PUSH/POP reg
229 return lowerToOEnc(tag, ops.reg1, emit.code) catch |err| emit.failWithLoweringError(err);224 return lowerToOEnc(tag, ops.reg1, emit.code);
230 },225 },
231 0b01 => {226 0b01 => {
232 // PUSH/POP r/m64227 // PUSH/POP r/m64
...@@ -238,14 +233,13 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -238,14 +233,13 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
238 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{233 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{
239 .disp = imm,234 .disp = imm,
240 .base = ops.reg1,235 .base = ops.reg1,
241 }), emit.code) catch |err| emit.failWithLoweringError(err);236 }), emit.code);
242 },237 },
243 0b10 => {238 0b10 => {
244 // PUSH imm32239 // PUSH imm32
245 assert(tag == .push);240 assert(tag == .push);
246 const imm = emit.mir.instructions.items(.data)[inst].imm;241 const imm = emit.mir.instructions.items(.data)[inst].imm;
247 return lowerToIEnc(.push, imm, emit.code) catch |err|242 return lowerToIEnc(.push, imm, emit.code);
248 emit.failWithLoweringError(err);
249 },243 },
250 0b11 => unreachable,244 0b11 => unreachable,
251 }245 }
...@@ -259,15 +253,15 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I...@@ -259,15 +253,15 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
259 for (bits.callee_preserved_regs) |reg, i| {253 for (bits.callee_preserved_regs) |reg, i| {
260 if ((regs >> @intCast(u5, i)) & 1 == 0) continue;254 if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
261 if (tag == .push) {255 if (tag == .push) {
262 lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{256 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
263 .disp = @bitCast(u32, -@intCast(i32, disp)),257 .disp = @bitCast(u32, -@intCast(i32, disp)),
264 .base = ops.reg1,258 .base = ops.reg1,
265 }), reg.to64(), emit.code) catch |err| return emit.failWithLoweringError(err);259 }), reg.to64(), emit.code);
266 } else {260 } else {
267 lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{261 try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{
268 .disp = @bitCast(u32, -@intCast(i32, disp)),262 .disp = @bitCast(u32, -@intCast(i32, disp)),
269 .base = ops.reg1,263 .base = ops.reg1,
270 }), emit.code) catch |err| return emit.failWithLoweringError(err);264 }), emit.code);
271 }265 }
272 disp += 8;266 disp += 8;
273 }267 }
...@@ -279,8 +273,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -279,8 +273,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
279 0b00 => {273 0b00 => {
280 const target = emit.mir.instructions.items(.data)[inst].inst;274 const target = emit.mir.instructions.items(.data)[inst].inst;
281 const source = emit.code.items.len;275 const source = emit.code.items.len;
282 lowerToDEnc(tag, 0, emit.code) catch |err|276 try lowerToDEnc(tag, 0, emit.code);
283 return emit.failWithLoweringError(err);
284 try emit.relocs.append(emit.bin_file.allocator, .{277 try emit.relocs.append(emit.bin_file.allocator, .{
285 .source = source,278 .source = source,
286 .target = target,279 .target = target,
...@@ -296,11 +289,10 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -296,11 +289,10 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
296 16 => .word_ptr,289 16 => .word_ptr,
297 else => .qword_ptr,290 else => .qword_ptr,
298 };291 };
299 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code) catch |err|292 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code);
300 emit.failWithLoweringError(err);
301 }293 }
302 // JMP/CALL reg294 // JMP/CALL reg
303 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code) catch |err| emit.failWithLoweringError(err);295 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
304 },296 },
305 0b10 => {297 0b10 => {
306 // JMP/CALL r/m64298 // JMP/CALL r/m64
...@@ -308,7 +300,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -308,7 +300,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
308 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{300 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
309 .disp = imm,301 .disp = imm,
310 .base = ops.reg1,302 .base = ops.reg1,
311 }), emit.code) catch |err| emit.failWithLoweringError(err);303 }), emit.code);
312 },304 },
313 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}),305 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}),
314 }306 }
...@@ -337,8 +329,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr...@@ -337,8 +329,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr
337 else => unreachable,329 else => unreachable,
338 };330 };
339 const source = emit.code.items.len;331 const source = emit.code.items.len;
340 lowerToDEnc(tag, 0, emit.code) catch |err|332 try lowerToDEnc(tag, 0, emit.code);
341 return emit.failWithLoweringError(err);
342 try emit.relocs.append(emit.bin_file.allocator, .{333 try emit.relocs.append(emit.bin_file.allocator, .{
343 .source = source,334 .source = source,
344 .target = target,335 .target = target,
...@@ -368,8 +359,7 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne...@@ -368,8 +359,7 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
368 },359 },
369 else => unreachable,360 else => unreachable,
370 };361 };
371 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code) catch |err|362 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
372 emit.failWithLoweringError(err);
373}363}
374364
375fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {365fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -385,11 +375,9 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -385,11 +375,9 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
385 if (ops.reg1.to64() == .rax) {375 if (ops.reg1.to64() == .rax) {
386 // TEST rax, imm32376 // TEST rax, imm32
387 // I377 // I
388 return lowerToIEnc(.@"test", imm, emit.code) catch |err|378 return lowerToIEnc(.@"test", imm, emit.code);
389 emit.failWithLoweringError(err);
390 }379 }
391 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err|380 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
392 emit.failWithLoweringError(err);
393 }381 }
394 // TEST r/m64, r64382 // TEST r/m64, r64
395 return emit.fail("TODO TEST r/m64, r64", .{});383 return emit.fail("TODO TEST r/m64, r64", .{});
...@@ -407,19 +395,19 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -407,19 +395,19 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
407 // RETF imm16395 // RETF imm16
408 // I396 // I
409 const imm = emit.mir.instructions.items(.data)[inst].imm;397 const imm = emit.mir.instructions.items(.data)[inst].imm;
410 return lowerToIEnc(.ret_far, imm, emit.code) catch |err| emit.failWithLoweringError(err);398 return lowerToIEnc(.ret_far, imm, emit.code);
411 },399 },
412 0b01 => {400 0b01 => {
413 return lowerToZoEnc(.ret_far, emit.code) catch |err| emit.failWithLoweringError(err);401 return lowerToZoEnc(.ret_far, emit.code);
414 },402 },
415 0b10 => {403 0b10 => {
416 // RET imm16404 // RET imm16
417 // I405 // I
418 const imm = emit.mir.instructions.items(.data)[inst].imm;406 const imm = emit.mir.instructions.items(.data)[inst].imm;
419 return lowerToIEnc(.ret_near, imm, emit.code) catch |err| emit.failWithLoweringError(err);407 return lowerToIEnc(.ret_near, imm, emit.code);
420 },408 },
421 0b11 => {409 0b11 => {
422 return lowerToZoEnc(.ret_near, emit.code) catch |err| emit.failWithLoweringError(err);410 return lowerToZoEnc(.ret_near, emit.code);
423 },411 },
424 }412 }
425}413}
...@@ -432,13 +420,11 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -432,13 +420,11 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
432 // mov reg1, imm32420 // mov reg1, imm32
433 // MI421 // MI
434 const imm = emit.mir.instructions.items(.data)[inst].imm;422 const imm = emit.mir.instructions.items(.data)[inst].imm;
435 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err|423 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
436 emit.failWithLoweringError(err);
437 }424 }
438 // mov reg1, reg2425 // mov reg1, reg2
439 // RM426 // RM
440 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err|427 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
441 emit.failWithLoweringError(err);
442 },428 },
443 0b01 => {429 0b01 => {
444 // mov reg1, [reg2 + imm32]430 // mov reg1, [reg2 + imm32]
...@@ -448,7 +434,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -448,7 +434,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
448 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{434 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
449 .disp = imm,435 .disp = imm,
450 .base = src_reg,436 .base = src_reg,
451 }), emit.code) catch |err| emit.failWithLoweringError(err);437 }), emit.code);
452 },438 },
453 0b10 => {439 0b10 => {
454 if (ops.reg2 == .none) {440 if (ops.reg2 == .none) {
...@@ -460,7 +446,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -460,7 +446,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
460 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{446 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
461 .disp = imm,447 .disp = imm,
462 .base = ops.reg1,448 .base = ops.reg1,
463 }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);449 }), ops.reg2, emit.code);
464 },450 },
465 0b11 => {451 0b11 => {
466 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});452 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});
...@@ -482,7 +468,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -482,7 +468,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
482 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{468 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
483 .disp = imm_pair.dest_off,469 .disp = imm_pair.dest_off,
484 .base = ops.reg1,470 .base = ops.reg1,
485 }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err);471 }), imm_pair.operand, emit.code);
486}472}
487473
488inline fn setRexWRegister(reg: Register) bool {474inline fn setRexWRegister(reg: Register) bool {
...@@ -517,7 +503,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -517,7 +503,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
517 .disp = imm,503 .disp = imm,
518 .base = ops.reg2,504 .base = ops.reg2,
519 .scale_index = scale_index,505 .scale_index = scale_index,
520 }), emit.code) catch |err| emit.failWithLoweringError(err);506 }), emit.code);
521}507}
522508
523fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {509fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
...@@ -534,14 +520,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -534,14 +520,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
534 .disp = 0,520 .disp = 0,
535 .base = ops.reg1,521 .base = ops.reg1,
536 .scale_index = scale_index,522 .scale_index = scale_index,
537 }), imm, emit.code) catch |err| emit.failWithLoweringError(err);523 }), imm, emit.code);
538 }524 }
539 // OP [reg1 + scale*rax + imm32], reg2525 // OP [reg1 + scale*rax + imm32], reg2
540 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{526 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
541 .disp = imm,527 .disp = imm,
542 .base = ops.reg1,528 .base = ops.reg1,
543 .scale_index = scale_index,529 .scale_index = scale_index,
544 }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);530 }), ops.reg2, emit.code);
545}531}
546532
547fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {533fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
...@@ -558,7 +544,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -558,7 +544,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
558 .disp = imm_pair.dest_off,544 .disp = imm_pair.dest_off,
559 .base = ops.reg1,545 .base = ops.reg1,
560 .scale_index = scale_index,546 .scale_index = scale_index,
561 }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err);547 }), imm_pair.operand, emit.code);
562}548}
563549
564fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {550fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
...@@ -581,7 +567,65 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v...@@ -581,7 +567,65 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v
581 .disp = imm_pair.dest_off,567 .disp = imm_pair.dest_off,
582 .base = ops.reg1,568 .base = ops.reg1,
583 .scale_index = scale_index,569 .scale_index = scale_index,
584 }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err);570 }), imm_pair.operand, emit.code);
571}
572
573fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
574 const mir_tag = emit.mir.instructions.items(.tag)[inst];
575 assert(mir_tag == .mov_sign_extend);
576 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
577 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
578 switch (ops.flags) {
579 0b00 => {
580 const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx;
581 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
582 },
583 0b01 => {
584 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
585 .disp = imm,
586 .base = ops.reg2,
587 }), emit.code);
588 },
589 0b10 => {
590 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
591 .disp = imm,
592 .base = ops.reg2,
593 }), emit.code);
594 },
595 0b11 => {
596 return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{
597 .disp = imm,
598 .base = ops.reg2,
599 }), emit.code);
600 },
601 }
602}
603
604fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
605 const mir_tag = emit.mir.instructions.items(.tag)[inst];
606 assert(mir_tag == .mov_zero_extend);
607 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
608 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
609 switch (ops.flags) {
610 0b00 => {
611 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
612 },
613 0b01 => {
614 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
615 .disp = imm,
616 .base = ops.reg2,
617 }), emit.code);
618 },
619 0b10 => {
620 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
621 .disp = imm,
622 .base = ops.reg2,
623 }), emit.code);
624 },
625 0b11 => {
626 return emit.fail("TODO unused variant: movzx 0b11", .{});
627 },
628 }
585}629}
586630
587fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {631fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -596,16 +640,16 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -596,16 +640,16 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
596 if (ops.flags == 0b00) {640 if (ops.flags == 0b00) {
597 // movabs reg, imm64641 // movabs reg, imm64
598 // OI642 // OI
599 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err);643 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);
600 }644 }
601 if (ops.reg1 == .none) {645 if (ops.reg1 == .none) {
602 // movabs moffs64, rax646 // movabs moffs64, rax
603 // TD647 // TD
604 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);648 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code);
605 }649 }
606 // movabs rax, moffs64650 // movabs rax, moffs64
607 // FD651 // FD
608 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err);652 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
609}653}
610654
611fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {655fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -614,13 +658,11 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -614,13 +658,11 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
614 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);658 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
615 switch (ops.flags) {659 switch (ops.flags) {
616 0b00 => {660 0b00 => {
617 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err|661 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
618 emit.failWithLoweringError(err);
619 },662 },
620 0b10 => {663 0b10 => {
621 const imm = emit.mir.instructions.items(.data)[inst].imm;664 const imm = emit.mir.instructions.items(.data)[inst].imm;
622 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code) catch |err|665 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
623 emit.failWithLoweringError(err);
624 },666 },
625 else => return emit.fail("TODO implement imul", .{}),667 else => return emit.fail("TODO implement imul", .{}),
626 }668 }
...@@ -644,18 +686,18 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -644,18 +686,18 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
644 .base = src_reg,686 .base = src_reg,
645 }),687 }),
646 emit.code,688 emit.code,
647 ) catch |err| emit.failWithLoweringError(err);689 );
648 },690 },
649 0b01 => {691 0b01 => {
650 // lea reg1, [rip + imm32]692 // lea reg1, [rip + imm32]
651 // RM693 // RM
652 const start_offset = emit.code.items.len;694 const start_offset = emit.code.items.len;
653 lowerToRmEnc(695 try lowerToRmEnc(
654 .lea,696 .lea,
655 ops.reg1,697 ops.reg1,
656 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),698 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
657 emit.code,699 emit.code,
658 ) catch |err| return emit.failWithLoweringError(err);700 );
659 const end_offset = emit.code.items.len;701 const end_offset = emit.code.items.len;
660 // Backpatch the displacement702 // Backpatch the displacement
661 const payload = emit.mir.instructions.items(.data)[inst].payload;703 const payload = emit.mir.instructions.items(.data)[inst].payload;
...@@ -666,12 +708,12 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -666,12 +708,12 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
666 0b10 => {708 0b10 => {
667 // lea reg1, [rip + reloc]709 // lea reg1, [rip + reloc]
668 // RM710 // RM
669 lowerToRmEnc(711 try lowerToRmEnc(
670 .lea,712 .lea,
671 ops.reg1,713 ops.reg1,
672 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),714 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
673 emit.code,715 emit.code,
674 ) catch |err| return emit.failWithLoweringError(err);716 );
675 const end_offset = emit.code.items.len;717 const end_offset = emit.code.items.len;
676 const got_entry = emit.mir.instructions.items(.data)[inst].got_entry;718 const got_entry = emit.mir.instructions.items(.data)[inst].got_entry;
677 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {719 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
...@@ -710,7 +752,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -710,7 +752,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
710 .scale_index = scale_index,752 .scale_index = scale_index,
711 }),753 }),
712 emit.code,754 emit.code,
713 ) catch |err| emit.failWithLoweringError(err);755 );
714 },756 },
715 }757 }
716}758}
...@@ -721,8 +763,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -721,8 +763,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
721 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;763 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
722 const offset = blk: {764 const offset = blk: {
723 // callq765 // callq
724 lowerToDEnc(.call_near, 0, emit.code) catch |err|766 try lowerToDEnc(.call_near, 0, emit.code);
725 return emit.failWithLoweringError(err);
726 break :blk @intCast(u32, emit.code.items.len) - 4;767 break :blk @intCast(u32, emit.code.items.len) - 4;
727 };768 };
728 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {769 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
...@@ -934,6 +975,9 @@ const Tag = enum {...@@ -934,6 +975,9 @@ const Tag = enum {
934 sbb,975 sbb,
935 cmp,976 cmp,
936 mov,977 mov,
978 movsx,
979 movsxd,
980 movzx,
937 lea,981 lea,
938 jmp_near,982 jmp_near,
939 call_near,983 call_near,
...@@ -1200,6 +1244,9 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1200,6 +1244,9 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1200 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),1244 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),
1201 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),1245 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),
1202 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),1246 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),
1247 .movsx => OpCode.twoByte(0x0f, if (is_one_byte) 0xbe else 0xbf),
1248 .movsxd => OpCode.oneByte(0x63),
1249 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),
1203 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),1250 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
1204 .imul => OpCode.twoByte(0x0f, 0xaf),1251 .imul => OpCode.twoByte(0x0f, 0xaf),
1205 else => null,1252 else => null,
...@@ -1367,6 +1414,10 @@ const Memory = struct {...@@ -1367,6 +1414,10 @@ const Memory = struct {
1367 encoder.disp32(@bitCast(i32, mem_op.disp));1414 encoder.disp32(@bitCast(i32, mem_op.disp));
1368 }1415 }
1369 }1416 }
1417
1418 fn size(memory: Memory) u64 {
1419 return memory.ptr_size.size();
1420 }
1370};1421};
13711422
1372fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {1423fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
...@@ -1411,21 +1462,22 @@ const RegisterOrMemory = union(enum) {...@@ -1411,21 +1462,22 @@ const RegisterOrMemory = union(enum) {
1411 },1462 },
1412 };1463 };
1413 }1464 }
1414};
14151465
1416const LoweringError = error{1466 fn size(reg_or_mem: RegisterOrMemory) u64 {
1417 OutOfMemory,1467 return switch (reg_or_mem) {
1418 OperandSizeMismatch,1468 .register => |reg| reg.size(),
1419 RaxOperandExpected,1469 .memory => |memory| memory.size(),
1470 };
1471 }
1420};1472};
14211473
1422fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) LoweringError!void {1474fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
1423 const opc = getOpCode(tag, .zo, false).?;1475 const opc = getOpCode(tag, .zo, false).?;
1424 const encoder = try Encoder.init(code, 1);1476 const encoder = try Encoder.init(code, 1);
1425 opc.encode(encoder);1477 opc.encode(encoder);
1426}1478}
14271479
1428fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {1480fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
1429 if (tag == .ret_far or tag == .ret_near) {1481 if (tag == .ret_far or tag == .ret_near) {
1430 const encoder = try Encoder.init(code, 3);1482 const encoder = try Encoder.init(code, 3);
1431 const opc = getOpCode(tag, .i, false).?;1483 const opc = getOpCode(tag, .i, false).?;
...@@ -1442,10 +1494,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void...@@ -1442,10 +1494,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void
1442 encodeImm(encoder, imm, immOpSize(imm));1494 encodeImm(encoder, imm, immOpSize(imm));
1443}1495}
14441496
1445fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {1497fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
1446 if (reg.size() != 16 and reg.size() != 64) {
1447 return error.OperandSizeMismatch; // TODO correct for push/pop, but is it universal?
1448 }
1449 const opc = getOpCode(tag, .o, false).?;1498 const opc = getOpCode(tag, .o, false).?;
1450 const encoder = try Encoder.init(code, 3);1499 const encoder = try Encoder.init(code, 3);
1451 if (reg.size() == 16) {1500 if (reg.size() == 16) {
...@@ -1458,26 +1507,18 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!...@@ -1458,26 +1507,18 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!
1458 opc.encodeWithReg(encoder, reg);1507 opc.encodeWithReg(encoder, reg);
1459}1508}
14601509
1461fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {1510fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
1462 const opc = getOpCode(tag, .d, false).?;1511 const opc = getOpCode(tag, .d, false).?;
1463 const encoder = try Encoder.init(code, 6);1512 const encoder = try Encoder.init(code, 6);
1464 opc.encode(encoder);1513 opc.encode(encoder);
1465 encoder.imm32(@bitCast(i32, imm));1514 encoder.imm32(@bitCast(i32, imm));
1466}1515}
14671516
1468fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) LoweringError!void {1517fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
1469 const opc = getOpCode(tag, .m, false).?;1518 const opc = getOpCode(tag, .m, false).?;
1470 const modrm_ext = getModRmExt(tag).?;1519 const modrm_ext = getModRmExt(tag).?;
1471 switch (reg_or_mem) {1520 switch (reg_or_mem) {
1472 .register => |reg| {1521 .register => |reg| {
1473 const op_size_mismatch = blk: {
1474 if (tag.isSetCC() and reg.size() == 8)
1475 break :blk false;
1476 break :blk reg.size() != 64 and reg.size() != 16;
1477 };
1478 if (op_size_mismatch) {
1479 return error.OperandSizeMismatch;
1480 }
1481 const encoder = try Encoder.init(code, 4);1522 const encoder = try Encoder.init(code, 4);
1482 if (reg.size() == 16) {1523 if (reg.size() == 16) {
1483 encoder.prefix16BitMode();1524 encoder.prefix16BitMode();
...@@ -1493,17 +1534,11 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -1493,17 +1534,11 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
1493 encoder.modRm_direct(modrm_ext, reg.lowId());1534 encoder.modRm_direct(modrm_ext, reg.lowId());
1494 },1535 },
1495 .memory => |mem_op| {1536 .memory => |mem_op| {
1496 if (mem_op.ptr_size != .qword_ptr and mem_op.ptr_size != .word_ptr) {
1497 return error.OperandSizeMismatch;
1498 }
1499 const encoder = try Encoder.init(code, 8);1537 const encoder = try Encoder.init(code, 8);
1500 if (mem_op.ptr_size == .word_ptr) {1538 if (mem_op.ptr_size == .word_ptr) {
1501 encoder.prefix16BitMode();1539 encoder.prefix16BitMode();
1502 }1540 }
1503 if (mem_op.base) |base| {1541 if (mem_op.base) |base| {
1504 if (base.size() != 64) {
1505 return error.OperandSizeMismatch;
1506 }
1507 encoder.rex(.{1542 encoder.rex(.{
1508 .w = false,1543 .w = false,
1509 .b = base.isExtended(),1544 .b = base.isExtended(),
...@@ -1515,18 +1550,15 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -1515,18 +1550,15 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
1515 }1550 }
1516}1551}
15171552
1518fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {1553fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
1519 return lowerToTdFdEnc(tag, reg, moffs, code, true);1554 return lowerToTdFdEnc(tag, reg, moffs, code, true);
1520}1555}
15211556
1522fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) LoweringError!void {1557fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) InnerError!void {
1523 return lowerToTdFdEnc(tag, reg, moffs, code, false);1558 return lowerToTdFdEnc(tag, reg, moffs, code, false);
1524}1559}
15251560
1526fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) LoweringError!void {1561fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void {
1527 if (reg.lowId() != Register.rax.lowId()) {
1528 return error.RaxOperandExpected;
1529 }
1530 const opc = if (td)1562 const opc = if (td)
1531 getOpCode(tag, .td, reg.size() == 8).?1563 getOpCode(tag, .td, reg.size() == 8).?
1532 else1564 else
...@@ -1548,7 +1580,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8),...@@ -1548,7 +1580,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8),
1548 }1580 }
1549}1581}
15501582
1551fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) LoweringError!void {1583fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void {
1552 const opc = getOpCode(tag, .oi, reg.size() == 8).?;1584 const opc = getOpCode(tag, .oi, reg.size() == 8).?;
1553 const encoder = try Encoder.init(code, 10);1585 const encoder = try Encoder.init(code, 10);
1554 if (reg.size() == 16) {1586 if (reg.size() == 16) {
...@@ -1568,11 +1600,11 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Low...@@ -1568,11 +1600,11 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Low
1568 }1600 }
1569}1601}
15701602
1571fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {1603fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
1572 const modrm_ext = getModRmExt(tag).?;1604 const modrm_ext = getModRmExt(tag).?;
1605 const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?;
1573 switch (reg_or_mem) {1606 switch (reg_or_mem) {
1574 .register => |dst_reg| {1607 .register => |dst_reg| {
1575 const opc = getOpCode(tag, .mi, dst_reg.size() == 8).?;
1576 const encoder = try Encoder.init(code, 7);1608 const encoder = try Encoder.init(code, 7);
1577 if (dst_reg.size() == 16) {1609 if (dst_reg.size() == 16) {
1578 // 0x66 prefix switches to the non-default size; here we assume a switch from1610 // 0x66 prefix switches to the non-default size; here we assume a switch from
...@@ -1589,15 +1621,11 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr...@@ -1589,15 +1621,11 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr
1589 encodeImm(encoder, imm, dst_reg.size());1621 encodeImm(encoder, imm, dst_reg.size());
1590 },1622 },
1591 .memory => |dst_mem| {1623 .memory => |dst_mem| {
1592 const opc = getOpCode(tag, .mi, dst_mem.ptr_size == .byte_ptr).?;
1593 const encoder = try Encoder.init(code, 12);1624 const encoder = try Encoder.init(code, 12);
1594 if (dst_mem.ptr_size == .word_ptr) {1625 if (dst_mem.ptr_size == .word_ptr) {
1595 encoder.prefix16BitMode();1626 encoder.prefix16BitMode();
1596 }1627 }
1597 if (dst_mem.base) |base| {1628 if (dst_mem.base) |base| {
1598 if (base.size() != 64) {
1599 return error.OperandSizeMismatch;
1600 }
1601 encoder.rex(.{1629 encoder.rex(.{
1602 .w = dst_mem.ptr_size == .qword_ptr,1630 .w = dst_mem.ptr_size == .qword_ptr,
1603 .b = base.isExtended(),1631 .b = base.isExtended(),
...@@ -1619,13 +1647,10 @@ fn lowerToRmEnc(...@@ -1619,13 +1647,10 @@ fn lowerToRmEnc(
1619 reg: Register,1647 reg: Register,
1620 reg_or_mem: RegisterOrMemory,1648 reg_or_mem: RegisterOrMemory,
1621 code: *std.ArrayList(u8),1649 code: *std.ArrayList(u8),
1622) LoweringError!void {1650) InnerError!void {
1623 const opc = getOpCode(tag, .rm, reg.size() == 8).?;1651 const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?;
1624 switch (reg_or_mem) {1652 switch (reg_or_mem) {
1625 .register => |src_reg| {1653 .register => |src_reg| {
1626 if (reg.size() != src_reg.size()) {
1627 return error.OperandSizeMismatch;
1628 }
1629 const encoder = try Encoder.init(code, 4);1654 const encoder = try Encoder.init(code, 4);
1630 encoder.rex(.{1655 encoder.rex(.{
1631 .w = setRexWRegister(reg) or setRexWRegister(src_reg),1656 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
...@@ -1636,9 +1661,6 @@ fn lowerToRmEnc(...@@ -1636,9 +1661,6 @@ fn lowerToRmEnc(
1636 encoder.modRm_direct(reg.lowId(), src_reg.lowId());1661 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
1637 },1662 },
1638 .memory => |src_mem| {1663 .memory => |src_mem| {
1639 if (reg.size() != src_mem.ptr_size.size()) {
1640 return error.OperandSizeMismatch;
1641 }
1642 const encoder = try Encoder.init(code, 9);1664 const encoder = try Encoder.init(code, 9);
1643 if (reg.size() == 16) {1665 if (reg.size() == 16) {
1644 encoder.prefix16BitMode();1666 encoder.prefix16BitMode();
...@@ -1646,9 +1668,6 @@ fn lowerToRmEnc(...@@ -1646,9 +1668,6 @@ fn lowerToRmEnc(
1646 if (src_mem.base) |base| {1668 if (src_mem.base) |base| {
1647 // TODO handle 32-bit base register - requires prefix 0x671669 // TODO handle 32-bit base register - requires prefix 0x67
1648 // Intel Manual, Vol 1, chapter 3.6 and 3.6.11670 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1649 if (base.size() != 64) {
1650 return error.OperandSizeMismatch;
1651 }
1652 encoder.rex(.{1671 encoder.rex(.{
1653 .w = setRexWRegister(reg),1672 .w = setRexWRegister(reg),
1654 .r = reg.isExtended(),1673 .r = reg.isExtended(),
...@@ -1671,13 +1690,10 @@ fn lowerToMrEnc(...@@ -1671,13 +1690,10 @@ fn lowerToMrEnc(
1671 reg_or_mem: RegisterOrMemory,1690 reg_or_mem: RegisterOrMemory,
1672 reg: Register,1691 reg: Register,
1673 code: *std.ArrayList(u8),1692 code: *std.ArrayList(u8),
1674) LoweringError!void {1693) InnerError!void {
1675 const opc = getOpCode(tag, .mr, reg.size() == 8).?;1694 const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?;
1676 switch (reg_or_mem) {1695 switch (reg_or_mem) {
1677 .register => |dst_reg| {1696 .register => |dst_reg| {
1678 if (dst_reg.size() != reg.size()) {
1679 return error.OperandSizeMismatch;
1680 }
1681 const encoder = try Encoder.init(code, 3);1697 const encoder = try Encoder.init(code, 3);
1682 encoder.rex(.{1698 encoder.rex(.{
1683 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),1699 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),
...@@ -1688,17 +1704,11 @@ fn lowerToMrEnc(...@@ -1688,17 +1704,11 @@ fn lowerToMrEnc(
1688 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());1704 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
1689 },1705 },
1690 .memory => |dst_mem| {1706 .memory => |dst_mem| {
1691 if (dst_mem.ptr_size.size() != reg.size()) {
1692 return error.OperandSizeMismatch;
1693 }
1694 const encoder = try Encoder.init(code, 9);1707 const encoder = try Encoder.init(code, 9);
1695 if (reg.size() == 16) {1708 if (reg.size() == 16) {
1696 encoder.prefix16BitMode();1709 encoder.prefix16BitMode();
1697 }1710 }
1698 if (dst_mem.base) |base| {1711 if (dst_mem.base) |base| {
1699 if (base.size() != 64) {
1700 return error.OperandSizeMismatch;
1701 }
1702 encoder.rex(.{1712 encoder.rex(.{
1703 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),1713 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
1704 .r = reg.isExtended(),1714 .r = reg.isExtended(),
...@@ -1722,10 +1732,7 @@ fn lowerToRmiEnc(...@@ -1722,10 +1732,7 @@ fn lowerToRmiEnc(
1722 reg_or_mem: RegisterOrMemory,1732 reg_or_mem: RegisterOrMemory,
1723 imm: u32,1733 imm: u32,
1724 code: *std.ArrayList(u8),1734 code: *std.ArrayList(u8),
1725) LoweringError!void {1735) InnerError!void {
1726 if (reg.size() == 8) {
1727 return error.OperandSizeMismatch;
1728 }
1729 const opc = getOpCode(tag, .rmi, false).?;1736 const opc = getOpCode(tag, .rmi, false).?;
1730 const encoder = try Encoder.init(code, 13);1737 const encoder = try Encoder.init(code, 13);
1731 if (reg.size() == 16) {1738 if (reg.size() == 16) {
...@@ -1733,9 +1740,6 @@ fn lowerToRmiEnc(...@@ -1733,9 +1740,6 @@ fn lowerToRmiEnc(
1733 }1740 }
1734 switch (reg_or_mem) {1741 switch (reg_or_mem) {
1735 .register => |src_reg| {1742 .register => |src_reg| {
1736 if (reg.size() != src_reg.size()) {
1737 return error.OperandSizeMismatch;
1738 }
1739 encoder.rex(.{1743 encoder.rex(.{
1740 .w = setRexWRegister(reg) or setRexWRegister(src_reg),1744 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
1741 .r = reg.isExtended(),1745 .r = reg.isExtended(),
...@@ -1748,12 +1752,6 @@ fn lowerToRmiEnc(...@@ -1748,12 +1752,6 @@ fn lowerToRmiEnc(
1748 if (src_mem.base) |base| {1752 if (src_mem.base) |base| {
1749 // TODO handle 32-bit base register - requires prefix 0x671753 // TODO handle 32-bit base register - requires prefix 0x67
1750 // Intel Manual, Vol 1, chapter 3.6 and 3.6.11754 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1751 if (base.size() != 64) {
1752 return error.OperandSizeMismatch;
1753 }
1754 if (src_mem.ptr_size == .byte_ptr) {
1755 return error.OperandSizeMismatch;
1756 }
1757 encoder.rex(.{1755 encoder.rex(.{
1758 .w = setRexWRegister(reg),1756 .w = setRexWRegister(reg),
1759 .r = reg.isExtended(),1757 .r = reg.isExtended(),
src/arch/x86_64/Mir.zig+13
...@@ -185,6 +185,19 @@ pub const Inst = struct {...@@ -185,6 +185,19 @@ pub const Inst = struct {
185 mov_scale_imm,185 mov_scale_imm,
186 mov_mem_index_imm,186 mov_mem_index_imm,
187187
188 /// ops flags: form:
189 /// 0b00 reg1, reg2,
190 /// 0b01 reg1, byte ptr [reg2 + imm32]
191 /// 0b10 reg1, word ptr [reg2 + imm32]
192 /// 0b11 reg1, dword ptr [reg2 + imm32]
193 mov_sign_extend,
194
195 /// ops flags: form:
196 /// 0b00 reg1, reg2
197 /// 0b01 reg1, byte ptr [reg2 + imm32]
198 /// 0b10 reg1, word ptr [reg2 + imm32]
199 mov_zero_extend,
200
188 /// ops flags: form:201 /// ops flags: form:
189 /// 0b00 reg1, [reg2 + imm32]202 /// 0b00 reg1, [reg2 + imm32]
190 /// 0b00 reg1, [ds:imm32]203 /// 0b00 reg1, [ds:imm32]
test/behavior.zig+1-1
...@@ -35,6 +35,7 @@ test {...@@ -35,6 +35,7 @@ test {
35 _ = @import("behavior/pub_enum.zig");35 _ = @import("behavior/pub_enum.zig");
36 _ = @import("behavior/slice_sentinel_comptime.zig");36 _ = @import("behavior/slice_sentinel_comptime.zig");
37 _ = @import("behavior/type.zig");37 _ = @import("behavior/type.zig");
38 _ = @import("behavior/truncate.zig");
3839
39 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {40 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {
40 // Tests that pass for stage1, llvm backend, C backend, wasm backend.41 // Tests that pass for stage1, llvm backend, C backend, wasm backend.
...@@ -71,7 +72,6 @@ test {...@@ -71,7 +72,6 @@ test {
71 _ = @import("behavior/src.zig");72 _ = @import("behavior/src.zig");
72 _ = @import("behavior/struct.zig");73 _ = @import("behavior/struct.zig");
73 _ = @import("behavior/this.zig");74 _ = @import("behavior/this.zig");
74 _ = @import("behavior/truncate.zig");
75 _ = @import("behavior/try.zig");75 _ = @import("behavior/try.zig");
76 _ = @import("behavior/type_info.zig");76 _ = @import("behavior/type_info.zig");
77 _ = @import("behavior/undefined.zig");77 _ = @import("behavior/undefined.zig");
test/behavior/truncate.zig+19
...@@ -1,53 +1,72 @@...@@ -1,53 +1,72 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const expect = std.testing.expect;3const expect = std.testing.expect;
34
4test "truncate u0 to larger integer allowed and has comptime known result" {5test "truncate u0 to larger integer allowed and has comptime known result" {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7
5 var x: u0 = 0;8 var x: u0 = 0;
6 const y = @truncate(u8, x);9 const y = @truncate(u8, x);
7 comptime try expect(y == 0);10 comptime try expect(y == 0);
8}11}
912
10test "truncate.u0.literal" {13test "truncate.u0.literal" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15
11 var z = @truncate(u0, 0);16 var z = @truncate(u0, 0);
12 try expect(z == 0);17 try expect(z == 0);
13}18}
1419
15test "truncate.u0.const" {20test "truncate.u0.const" {
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
22
16 const c0: usize = 0;23 const c0: usize = 0;
17 var z = @truncate(u0, c0);24 var z = @truncate(u0, c0);
18 try expect(z == 0);25 try expect(z == 0);
19}26}
2027
21test "truncate.u0.var" {28test "truncate.u0.var" {
29 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
30
22 var d: u8 = 2;31 var d: u8 = 2;
23 var z = @truncate(u0, d);32 var z = @truncate(u0, d);
24 try expect(z == 0);33 try expect(z == 0);
25}34}
2635
27test "truncate i0 to larger integer allowed and has comptime known result" {36test "truncate i0 to larger integer allowed and has comptime known result" {
37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
38
28 var x: i0 = 0;39 var x: i0 = 0;
29 const y = @truncate(i8, x);40 const y = @truncate(i8, x);
30 comptime try expect(y == 0);41 comptime try expect(y == 0);
31}42}
3243
33test "truncate.i0.literal" {44test "truncate.i0.literal" {
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
46
34 var z = @truncate(i0, 0);47 var z = @truncate(i0, 0);
35 try expect(z == 0);48 try expect(z == 0);
36}49}
3750
38test "truncate.i0.const" {51test "truncate.i0.const" {
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
53
39 const c0: isize = 0;54 const c0: isize = 0;
40 var z = @truncate(i0, c0);55 var z = @truncate(i0, c0);
41 try expect(z == 0);56 try expect(z == 0);
42}57}
4358
44test "truncate.i0.var" {59test "truncate.i0.var" {
60 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
61
45 var d: i8 = 2;62 var d: i8 = 2;
46 var z = @truncate(i0, d);63 var z = @truncate(i0, d);
47 try expect(z == 0);64 try expect(z == 0);
48}65}
4966
50test "truncate on comptime integer" {67test "truncate on comptime integer" {
68 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
69
51 var x = @truncate(u16, 9999);70 var x = @truncate(u16, 9999);
52 try expect(x == 9999);71 try expect(x == 9999);
53 var y = @truncate(u16, -21555);72 var y = @truncate(u16, -21555);