authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-26 16:14:57+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-26 16:14:57+01:00
log9f224ebd9a4f26d0dc5b0937d3d5df8fe78f1c9d
treee3bbcb01c6c19953004d0a28efc789a5015e9f4a
parent075e2eaaafb1038ce623713d9dc4947171d5c255

stage2: add zero- and sign-extend moves to x86_64

* remove `LoweringError` error set from `Emit.zig` - it actually was less than helpful; it's better to either not throw an error since there can be instructions with mismatching operand sizes such as `movsx` or assert on a by instruction-basis. Currently, let's just pass through and see how we fare. * when moving integers into registers, check for signedness and move with zero- or sign-extension if source operand is smaller than 8 bytes. The destination operand is always assumed to be full-width, i.e., 8 bytes. * clean up `airTrunc` a little to match the rest of CodeGen inst implementations.

5 files changed, 275 insertions(+), 200 deletions(-)

src/arch/x86_64/CodeGen.zig+105-60
......@@ -769,6 +769,11 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
769769/// Use a pointer instruction as the basis for allocating stack memory.
770770fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
771771 const elem_ty = self.air.typeOfIndex(inst).elemType();
772
773 if (!elem_ty.hasRuntimeBits()) {
774 return self.allocMem(inst, 8, 8);
775 }
776
772777 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
773778 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
774779 };
......@@ -904,30 +909,28 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
904909 if (self.liveness.isUnused(inst))
905910 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
906911
907 const operand = try self.resolveInst(ty_op.operand);
908
909912 const src_ty = self.air.typeOf(ty_op.operand);
910913 const dst_ty = self.air.typeOfIndex(inst);
914 const operand = try self.resolveInst(ty_op.operand);
911915
912916 const src_ty_size = src_ty.abiSize(self.target.*);
913917 const dst_ty_size = dst_ty.abiSize(self.target.*);
918
914919 if (src_ty_size > 8 or dst_ty_size > 8) {
915920 return self.fail("TODO implement trunc for abi sizes larger than 8", .{});
916921 }
917922
918 const src_reg = if (operand == .register) operand.register else blk: {
919 const tmp_reg = try self.register_manager.allocReg(inst, &.{});
920 try self.genSetReg(dst_ty, tmp_reg, operand);
921 break :blk tmp_reg;
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)) };
922933 };
923 const dst_reg = try self.register_manager.allocReg(inst, &.{});
924
925 // "convert" the src register to the smaller type so only those bytes are moved
926 const small_src = MCValue{ .register = registerAlias(src_reg, @intCast(u32, dst_ty_size)) };
927
928 try self.genSetReg(dst_ty, dst_reg, small_src);
929
930 const result = MCValue{ .register = registerAlias(dst_reg, @intCast(u32, dst_ty_size)) };
931934
932935 // when truncating a `u16` to `u5`, for example, those top 3 bits in the result
933936 // have to be removed. this only happens if the dst if not a power-of-two size.
......@@ -935,16 +938,10 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
935938 const is_power_of_two = (dst_bit_size & (dst_bit_size - 1)) == 0;
936939 if (!is_power_of_two or dst_bit_size < 8) {
937940 const mask = (~@as(u64, 0)) >> @intCast(u6, (64 - dst_ty.bitSize(self.target.*)));
938 try self.genBinMathOpMir(
939 .@"and",
940 dst_ty,
941 dst_ty.intInfo(self.target.*).signedness,
942 result,
943 .{ .immediate = mask },
944 );
941 try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, .{ .immediate = mask });
945942 }
946943
947 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
944 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
948945}
949946
950947fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1406,7 +1403,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
14061403 // TODO we could allocate register here, but need to except addr register and potentially
14071404 // offset register.
14081405 const dst_mcv = try self.allocRegOrMem(inst, false);
1409 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() }, .{
14101407 .register = offset_reg.to64(),
14111408 });
14121409 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, slice_ptr_field_type);
......@@ -1454,13 +1451,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
14541451 // TODO we could allocate register here, but need to except addr register and potentially
14551452 // offset register.
14561453 const dst_mcv = try self.allocRegOrMem(inst, false);
1457 try self.genBinMathOpMir(
1458 .add,
1459 array_ty,
1460 .unsigned,
1461 .{ .register = addr_reg.to64() },
1462 .{ .register = offset_reg.to64() },
1463 );
1454 try self.genBinMathOpMir(.add, array_ty, .{ .register = addr_reg.to64() }, .{ .register = offset_reg.to64() });
14641455 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
14651456 break :result dst_mcv;
14661457 };
......@@ -1498,7 +1489,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
14981489 else => return self.fail("TODO implement ptr_elem_ptr when ptr is {}", .{ptr}),
14991490 }
15001491 };
1501 try self.genBinMathOpMir(.add, ptr_ty, .unsigned, dst_mcv, .{ .register = offset_reg });
1492 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
15021493 break :result dst_mcv;
15031494 };
15041495 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -1929,11 +1920,11 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
19291920 const dst_ty = self.air.typeOfIndex(inst);
19301921 const air_tags = self.air.instructions.items(.tag);
19311922 switch (air_tags[inst]) {
1932 .add, .addwrap, .ptr_add => try self.genBinMathOpMir(.add, dst_ty, .unsigned, dst_mcv, src_mcv),
1933 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, .unsigned, dst_mcv, src_mcv),
1934 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, .unsigned, dst_mcv, src_mcv),
1935 .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, .unsigned, dst_mcv, src_mcv),
1936 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, .unsigned, dst_mcv, src_mcv),
1923 .add, .addwrap, .ptr_add => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),
1924 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),
1925 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),
1926 .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv),
1927 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),
19371928 .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),
19381929 else => unreachable,
19391930 }
......@@ -1941,14 +1932,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
19411932 return dst_mcv;
19421933}
19431934
1944fn genBinMathOpMir(
1945 self: *Self,
1946 mir_tag: Mir.Inst.Tag,
1947 dst_ty: Type,
1948 signedness: std.builtin.Signedness,
1949 dst_mcv: MCValue,
1950 src_mcv: MCValue,
1951) !void {
1935fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
19521936 switch (dst_mcv) {
19531937 .none => unreachable,
19541938 .undef => unreachable,
......@@ -1965,16 +1949,11 @@ fn genBinMathOpMir(
19651949 .ptr_stack_offset => unreachable,
19661950 .ptr_embedded_in_code => unreachable,
19671951 .register => |src_reg| {
1968 // TODO think more carefully about this: is this actually correct?
1969 const reg_size = if (mir_tag == .cmp and signedness == .signed)
1970 @divExact(dst_reg.size(), 8)
1971 else
1972 @divExact(src_reg.size(), 8);
19731952 _ = try self.addInst(.{
19741953 .tag = mir_tag,
19751954 .ops = (Mir.Ops{
1976 .reg1 = registerAlias(dst_reg, reg_size),
1977 .reg2 = registerAlias(src_reg, reg_size),
1955 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
1956 .reg2 = src_reg,
19781957 }).encode(),
19791958 .data = undefined,
19801959 });
......@@ -2521,7 +2500,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
25212500 // This instruction supports only signed 32-bit immediates at most.
25222501 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
25232502
2524 try self.genBinMathOpMir(.cmp, ty, signedness, dst_mcv, src_mcv);
2503 try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv);
25252504 break :result switch (signedness) {
25262505 .signed => MCValue{ .compare_flags_signed = op },
25272506 .unsigned => MCValue{ .compare_flags_unsigned = op },
......@@ -2738,7 +2717,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
27382717}
27392718
27402719fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2741 try self.genBinMathOpMir(.cmp, ty, .unsigned, operand, MCValue{ .immediate = 0 });
2720 try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 });
27422721 return MCValue{ .compare_flags_unsigned = .eq };
27432722}
27442723
......@@ -2755,7 +2734,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
27552734 return MCValue{ .immediate = 0 }; // always false
27562735 } else if (!payload_type.hasRuntimeBits()) {
27572736 if (err_type.abiSize(self.target.*) <= 8) {
2758 try self.genBinMathOpMir(.cmp, err_type, .unsigned, operand, MCValue{ .immediate = 0 });
2737 try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 });
27592738 return MCValue{ .compare_flags_unsigned = .gt };
27602739 } else {
27612740 return self.fail("TODO isErr for errors with size larger than register size", .{});
......@@ -3627,10 +3606,10 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn
36273606}
36283607
36293608fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3609 const abi_size = ty.abiSize(self.target.*);
36303610 switch (mcv) {
36313611 .dead => unreachable,
36323612 .ptr_stack_offset => |unadjusted_off| {
3633 const ptr_abi_size = ty.abiSize(self.target.*);
36343613 const elem_ty = ty.childType();
36353614 const elem_abi_size = elem_ty.abiSize(self.target.*);
36363615 const off = unadjusted_off + @intCast(i32, elem_abi_size);
......@@ -3640,7 +3619,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
36403619 _ = try self.addInst(.{
36413620 .tag = .lea,
36423621 .ops = (Mir.Ops{
3643 .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),
3622 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
36443623 .reg2 = .rbp,
36453624 }).encode(),
36463625 .data = .{ .imm = @bitCast(u32, -off) },
......@@ -3694,15 +3673,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
36943673 _ = try self.addInst(.{
36953674 .tag = .xor,
36963675 .ops = (Mir.Ops{
3697 .reg1 = reg.to64(),
3698 .reg2 = reg.to64(),
3676 .reg1 = reg.to32(),
3677 .reg2 = reg.to32(),
36993678 }).encode(),
37003679 .data = undefined,
37013680 });
37023681 return;
37033682 }
37043683 if (x <= math.maxInt(i32)) {
3705 const abi_size = ty.abiSize(self.target.*);
37063684 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
37073685 _ = try self.addInst(.{
37083686 .tag = .mov,
......@@ -3746,6 +3724,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
37463724 if (src_reg.id() == reg.id())
37473725 return;
37483726
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
37493755 _ = try self.addInst(.{
37503756 .tag = .mov,
37513757 .ops = (Mir.Ops{
......@@ -3819,11 +3825,50 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
38193825 }
38203826 },
38213827 .stack_offset => |unadjusted_off| {
3822 const abi_size = ty.abiSize(self.target.*);
38233828 const off = unadjusted_off + @intCast(i32, abi_size);
38243829 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
38253830 return self.fail("stack offset too large", .{});
38263831 }
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
38273872 _ = try self.addInst(.{
38283873 .tag = .mov,
38293874 .ops = (Mir.Ops{
src/arch/x86_64/Emit.zig+137-139
......@@ -125,6 +125,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
125125 .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst),
126126 .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
128131 .movabs => try emit.mirMovabs(inst),
129132
130133 .lea => try emit.mirLea(inst),
......@@ -188,14 +191,6 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
188191 return error.EmitFail;
189192}
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
199194fn fixupRelocs(emit: *Emit) InnerError!void {
200195 // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.
201196 // 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 {
210205}
211206
212207fn mirBrk(emit: *Emit) InnerError!void {
213 return lowerToZoEnc(.brk, emit.code) catch |err| emit.failWithLoweringError(err);
208 return lowerToZoEnc(.brk, emit.code);
214209}
215210
216211fn mirNop(emit: *Emit) InnerError!void {
217 return lowerToZoEnc(.nop, emit.code) catch |err| emit.failWithLoweringError(err);
212 return lowerToZoEnc(.nop, emit.code);
218213}
219214
220215fn mirSyscall(emit: *Emit) InnerError!void {
221 return lowerToZoEnc(.syscall, emit.code) catch |err| emit.failWithLoweringError(err);
216 return lowerToZoEnc(.syscall, emit.code);
222217}
223218
224219fn 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 {
226221 switch (ops.flags) {
227222 0b00 => {
228223 // PUSH/POP reg
229 return lowerToOEnc(tag, ops.reg1, emit.code) catch |err| emit.failWithLoweringError(err);
224 return lowerToOEnc(tag, ops.reg1, emit.code);
230225 },
231226 0b01 => {
232227 // PUSH/POP r/m64
......@@ -238,14 +233,13 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
238233 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{
239234 .disp = imm,
240235 .base = ops.reg1,
241 }), emit.code) catch |err| emit.failWithLoweringError(err);
236 }), emit.code);
242237 },
243238 0b10 => {
244239 // PUSH imm32
245240 assert(tag == .push);
246241 const imm = emit.mir.instructions.items(.data)[inst].imm;
247 return lowerToIEnc(.push, imm, emit.code) catch |err|
248 emit.failWithLoweringError(err);
242 return lowerToIEnc(.push, imm, emit.code);
249243 },
250244 0b11 => unreachable,
251245 }
......@@ -259,15 +253,15 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
259253 for (bits.callee_preserved_regs) |reg, i| {
260254 if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
261255 if (tag == .push) {
262 lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
256 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
263257 .disp = @bitCast(u32, -@intCast(i32, disp)),
264258 .base = ops.reg1,
265 }), reg.to64(), emit.code) catch |err| return emit.failWithLoweringError(err);
259 }), reg.to64(), emit.code);
266260 } else {
267 lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{
261 try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{
268262 .disp = @bitCast(u32, -@intCast(i32, disp)),
269263 .base = ops.reg1,
270 }), emit.code) catch |err| return emit.failWithLoweringError(err);
264 }), emit.code);
271265 }
272266 disp += 8;
273267 }
......@@ -279,8 +273,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
279273 0b00 => {
280274 const target = emit.mir.instructions.items(.data)[inst].inst;
281275 const source = emit.code.items.len;
282 lowerToDEnc(tag, 0, emit.code) catch |err|
283 return emit.failWithLoweringError(err);
276 try lowerToDEnc(tag, 0, emit.code);
284277 try emit.relocs.append(emit.bin_file.allocator, .{
285278 .source = source,
286279 .target = target,
......@@ -296,11 +289,10 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
296289 16 => .word_ptr,
297290 else => .qword_ptr,
298291 };
299 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code) catch |err|
300 emit.failWithLoweringError(err);
292 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code);
301293 }
302294 // 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);
304296 },
305297 0b10 => {
306298 // JMP/CALL r/m64
......@@ -308,7 +300,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
308300 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
309301 .disp = imm,
310302 .base = ops.reg1,
311 }), emit.code) catch |err| emit.failWithLoweringError(err);
303 }), emit.code);
312304 },
313305 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}),
314306 }
......@@ -337,8 +329,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr
337329 else => unreachable,
338330 };
339331 const source = emit.code.items.len;
340 lowerToDEnc(tag, 0, emit.code) catch |err|
341 return emit.failWithLoweringError(err);
332 try lowerToDEnc(tag, 0, emit.code);
342333 try emit.relocs.append(emit.bin_file.allocator, .{
343334 .source = source,
344335 .target = target,
......@@ -368,8 +359,7 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
368359 },
369360 else => unreachable,
370361 };
371 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code) catch |err|
372 emit.failWithLoweringError(err);
362 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
373363}
374364
375365fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -385,11 +375,9 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
385375 if (ops.reg1.to64() == .rax) {
386376 // TEST rax, imm32
387377 // I
388 return lowerToIEnc(.@"test", imm, emit.code) catch |err|
389 emit.failWithLoweringError(err);
378 return lowerToIEnc(.@"test", imm, emit.code);
390379 }
391 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err|
392 emit.failWithLoweringError(err);
380 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
393381 }
394382 // TEST r/m64, r64
395383 return emit.fail("TODO TEST r/m64, r64", .{});
......@@ -407,19 +395,19 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
407395 // RETF imm16
408396 // I
409397 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);
411399 },
412400 0b01 => {
413 return lowerToZoEnc(.ret_far, emit.code) catch |err| emit.failWithLoweringError(err);
401 return lowerToZoEnc(.ret_far, emit.code);
414402 },
415403 0b10 => {
416404 // RET imm16
417405 // I
418406 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);
420408 },
421409 0b11 => {
422 return lowerToZoEnc(.ret_near, emit.code) catch |err| emit.failWithLoweringError(err);
410 return lowerToZoEnc(.ret_near, emit.code);
423411 },
424412 }
425413}
......@@ -432,13 +420,11 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
432420 // mov reg1, imm32
433421 // MI
434422 const imm = emit.mir.instructions.items(.data)[inst].imm;
435 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err|
436 emit.failWithLoweringError(err);
423 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
437424 }
438425 // mov reg1, reg2
439426 // RM
440 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err|
441 emit.failWithLoweringError(err);
427 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
442428 },
443429 0b01 => {
444430 // mov reg1, [reg2 + imm32]
......@@ -448,7 +434,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
448434 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
449435 .disp = imm,
450436 .base = src_reg,
451 }), emit.code) catch |err| emit.failWithLoweringError(err);
437 }), emit.code);
452438 },
453439 0b10 => {
454440 if (ops.reg2 == .none) {
......@@ -460,7 +446,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
460446 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
461447 .disp = imm,
462448 .base = ops.reg1,
463 }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);
449 }), ops.reg2, emit.code);
464450 },
465451 0b11 => {
466452 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 {
482468 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
483469 .disp = imm_pair.dest_off,
484470 .base = ops.reg1,
485 }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err);
471 }), imm_pair.operand, emit.code);
486472}
487473
488474inline fn setRexWRegister(reg: Register) bool {
......@@ -517,7 +503,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
517503 .disp = imm,
518504 .base = ops.reg2,
519505 .scale_index = scale_index,
520 }), emit.code) catch |err| emit.failWithLoweringError(err);
506 }), emit.code);
521507}
522508
523509fn 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
534520 .disp = 0,
535521 .base = ops.reg1,
536522 .scale_index = scale_index,
537 }), imm, emit.code) catch |err| emit.failWithLoweringError(err);
523 }), imm, emit.code);
538524 }
539525 // OP [reg1 + scale*rax + imm32], reg2
540526 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
541527 .disp = imm,
542528 .base = ops.reg1,
543529 .scale_index = scale_index,
544 }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);
530 }), ops.reg2, emit.code);
545531}
546532
547533fn 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
558544 .disp = imm_pair.dest_off,
559545 .base = ops.reg1,
560546 .scale_index = scale_index,
561 }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err);
547 }), imm_pair.operand, emit.code);
562548}
563549
564550fn 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
581567 .disp = imm_pair.dest_off,
582568 .base = ops.reg1,
583569 .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 }
585629}
586630
587631fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -596,16 +640,16 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
596640 if (ops.flags == 0b00) {
597641 // movabs reg, imm64
598642 // OI
599 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err);
643 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);
600644 }
601645 if (ops.reg1 == .none) {
602646 // movabs moffs64, rax
603647 // TD
604 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err);
648 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code);
605649 }
606650 // movabs rax, moffs64
607651 // FD
608 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err);
652 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
609653}
610654
611655fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -614,13 +658,11 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
614658 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
615659 switch (ops.flags) {
616660 0b00 => {
617 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err|
618 emit.failWithLoweringError(err);
661 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
619662 },
620663 0b10 => {
621664 const imm = emit.mir.instructions.items(.data)[inst].imm;
622 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code) catch |err|
623 emit.failWithLoweringError(err);
665 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
624666 },
625667 else => return emit.fail("TODO implement imul", .{}),
626668 }
......@@ -644,18 +686,18 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
644686 .base = src_reg,
645687 }),
646688 emit.code,
647 ) catch |err| emit.failWithLoweringError(err);
689 );
648690 },
649691 0b01 => {
650692 // lea reg1, [rip + imm32]
651693 // RM
652694 const start_offset = emit.code.items.len;
653 lowerToRmEnc(
695 try lowerToRmEnc(
654696 .lea,
655697 ops.reg1,
656698 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
657699 emit.code,
658 ) catch |err| return emit.failWithLoweringError(err);
700 );
659701 const end_offset = emit.code.items.len;
660702 // Backpatch the displacement
661703 const payload = emit.mir.instructions.items(.data)[inst].payload;
......@@ -666,12 +708,12 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
666708 0b10 => {
667709 // lea reg1, [rip + reloc]
668710 // RM
669 lowerToRmEnc(
711 try lowerToRmEnc(
670712 .lea,
671713 ops.reg1,
672714 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
673715 emit.code,
674 ) catch |err| return emit.failWithLoweringError(err);
716 );
675717 const end_offset = emit.code.items.len;
676718 const got_entry = emit.mir.instructions.items(.data)[inst].got_entry;
677719 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
......@@ -710,7 +752,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
710752 .scale_index = scale_index,
711753 }),
712754 emit.code,
713 ) catch |err| emit.failWithLoweringError(err);
755 );
714756 },
715757 }
716758}
......@@ -721,8 +763,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
721763 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
722764 const offset = blk: {
723765 // callq
724 lowerToDEnc(.call_near, 0, emit.code) catch |err|
725 return emit.failWithLoweringError(err);
766 try lowerToDEnc(.call_near, 0, emit.code);
726767 break :blk @intCast(u32, emit.code.items.len) - 4;
727768 };
728769 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
......@@ -934,6 +975,9 @@ const Tag = enum {
934975 sbb,
935976 cmp,
936977 mov,
978 movsx,
979 movsxd,
980 movzx,
937981 lea,
938982 jmp_near,
939983 call_near,
......@@ -1200,6 +1244,9 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
12001244 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),
12011245 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),
12021246 .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),
12031250 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
12041251 .imul => OpCode.twoByte(0x0f, 0xaf),
12051252 else => null,
......@@ -1367,6 +1414,10 @@ const Memory = struct {
13671414 encoder.disp32(@bitCast(i32, mem_op.disp));
13681415 }
13691416 }
1417
1418 fn size(memory: Memory) u64 {
1419 return memory.ptr_size.size();
1420 }
13701421};
13711422
13721423fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
......@@ -1411,21 +1462,22 @@ const RegisterOrMemory = union(enum) {
14111462 },
14121463 };
14131464 }
1414};
14151465
1416const LoweringError = error{
1417 OutOfMemory,
1418 OperandSizeMismatch,
1419 RaxOperandExpected,
1466 fn size(reg_or_mem: RegisterOrMemory) u64 {
1467 return switch (reg_or_mem) {
1468 .register => |reg| reg.size(),
1469 .memory => |memory| memory.size(),
1470 };
1471 }
14201472};
14211473
1422fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) LoweringError!void {
1474fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
14231475 const opc = getOpCode(tag, .zo, false).?;
14241476 const encoder = try Encoder.init(code, 1);
14251477 opc.encode(encoder);
14261478}
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 {
14291481 if (tag == .ret_far or tag == .ret_near) {
14301482 const encoder = try Encoder.init(code, 3);
14311483 const opc = getOpCode(tag, .i, false).?;
......@@ -1442,10 +1494,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void
14421494 encodeImm(encoder, imm, immOpSize(imm));
14431495}
14441496
1445fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {
1446 if (reg.size() != 16 and reg.size() != 64) {
1447 return error.OperandSizeMismatch; // TODO correct for push/pop, but is it universal?
1448 }
1497fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
14491498 const opc = getOpCode(tag, .o, false).?;
14501499 const encoder = try Encoder.init(code, 3);
14511500 if (reg.size() == 16) {
......@@ -1458,26 +1507,18 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!
14581507 opc.encodeWithReg(encoder, reg);
14591508}
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 {
14621511 const opc = getOpCode(tag, .d, false).?;
14631512 const encoder = try Encoder.init(code, 6);
14641513 opc.encode(encoder);
14651514 encoder.imm32(@bitCast(i32, imm));
14661515}
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 {
14691518 const opc = getOpCode(tag, .m, false).?;
14701519 const modrm_ext = getModRmExt(tag).?;
14711520 switch (reg_or_mem) {
14721521 .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 }
14811522 const encoder = try Encoder.init(code, 4);
14821523 if (reg.size() == 16) {
14831524 encoder.prefix16BitMode();
......@@ -1493,17 +1534,11 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
14931534 encoder.modRm_direct(modrm_ext, reg.lowId());
14941535 },
14951536 .memory => |mem_op| {
1496 if (mem_op.ptr_size != .qword_ptr and mem_op.ptr_size != .word_ptr) {
1497 return error.OperandSizeMismatch;
1498 }
14991537 const encoder = try Encoder.init(code, 8);
15001538 if (mem_op.ptr_size == .word_ptr) {
15011539 encoder.prefix16BitMode();
15021540 }
15031541 if (mem_op.base) |base| {
1504 if (base.size() != 64) {
1505 return error.OperandSizeMismatch;
1506 }
15071542 encoder.rex(.{
15081543 .w = false,
15091544 .b = base.isExtended(),
......@@ -1515,18 +1550,15 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
15151550 }
15161551}
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 {
15191554 return lowerToTdFdEnc(tag, reg, moffs, code, true);
15201555}
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 {
15231558 return lowerToTdFdEnc(tag, reg, moffs, code, false);
15241559}
15251560
1526fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) LoweringError!void {
1527 if (reg.lowId() != Register.rax.lowId()) {
1528 return error.RaxOperandExpected;
1529 }
1561fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void {
15301562 const opc = if (td)
15311563 getOpCode(tag, .td, reg.size() == 8).?
15321564 else
......@@ -1548,7 +1580,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8),
15481580 }
15491581}
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 {
15521584 const opc = getOpCode(tag, .oi, reg.size() == 8).?;
15531585 const encoder = try Encoder.init(code, 10);
15541586 if (reg.size() == 16) {
......@@ -1568,11 +1600,11 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Low
15681600 }
15691601}
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 {
15721604 const modrm_ext = getModRmExt(tag).?;
1605 const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?;
15731606 switch (reg_or_mem) {
15741607 .register => |dst_reg| {
1575 const opc = getOpCode(tag, .mi, dst_reg.size() == 8).?;
15761608 const encoder = try Encoder.init(code, 7);
15771609 if (dst_reg.size() == 16) {
15781610 // 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
15891621 encodeImm(encoder, imm, dst_reg.size());
15901622 },
15911623 .memory => |dst_mem| {
1592 const opc = getOpCode(tag, .mi, dst_mem.ptr_size == .byte_ptr).?;
15931624 const encoder = try Encoder.init(code, 12);
15941625 if (dst_mem.ptr_size == .word_ptr) {
15951626 encoder.prefix16BitMode();
15961627 }
15971628 if (dst_mem.base) |base| {
1598 if (base.size() != 64) {
1599 return error.OperandSizeMismatch;
1600 }
16011629 encoder.rex(.{
16021630 .w = dst_mem.ptr_size == .qword_ptr,
16031631 .b = base.isExtended(),
......@@ -1619,13 +1647,10 @@ fn lowerToRmEnc(
16191647 reg: Register,
16201648 reg_or_mem: RegisterOrMemory,
16211649 code: *std.ArrayList(u8),
1622) LoweringError!void {
1623 const opc = getOpCode(tag, .rm, reg.size() == 8).?;
1650) InnerError!void {
1651 const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?;
16241652 switch (reg_or_mem) {
16251653 .register => |src_reg| {
1626 if (reg.size() != src_reg.size()) {
1627 return error.OperandSizeMismatch;
1628 }
16291654 const encoder = try Encoder.init(code, 4);
16301655 encoder.rex(.{
16311656 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
......@@ -1636,9 +1661,6 @@ fn lowerToRmEnc(
16361661 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
16371662 },
16381663 .memory => |src_mem| {
1639 if (reg.size() != src_mem.ptr_size.size()) {
1640 return error.OperandSizeMismatch;
1641 }
16421664 const encoder = try Encoder.init(code, 9);
16431665 if (reg.size() == 16) {
16441666 encoder.prefix16BitMode();
......@@ -1646,9 +1668,6 @@ fn lowerToRmEnc(
16461668 if (src_mem.base) |base| {
16471669 // TODO handle 32-bit base register - requires prefix 0x67
16481670 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1649 if (base.size() != 64) {
1650 return error.OperandSizeMismatch;
1651 }
16521671 encoder.rex(.{
16531672 .w = setRexWRegister(reg),
16541673 .r = reg.isExtended(),
......@@ -1671,13 +1690,10 @@ fn lowerToMrEnc(
16711690 reg_or_mem: RegisterOrMemory,
16721691 reg: Register,
16731692 code: *std.ArrayList(u8),
1674) LoweringError!void {
1675 const opc = getOpCode(tag, .mr, reg.size() == 8).?;
1693) InnerError!void {
1694 const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?;
16761695 switch (reg_or_mem) {
16771696 .register => |dst_reg| {
1678 if (dst_reg.size() != reg.size()) {
1679 return error.OperandSizeMismatch;
1680 }
16811697 const encoder = try Encoder.init(code, 3);
16821698 encoder.rex(.{
16831699 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),
......@@ -1688,17 +1704,11 @@ fn lowerToMrEnc(
16881704 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
16891705 },
16901706 .memory => |dst_mem| {
1691 if (dst_mem.ptr_size.size() != reg.size()) {
1692 return error.OperandSizeMismatch;
1693 }
16941707 const encoder = try Encoder.init(code, 9);
16951708 if (reg.size() == 16) {
16961709 encoder.prefix16BitMode();
16971710 }
16981711 if (dst_mem.base) |base| {
1699 if (base.size() != 64) {
1700 return error.OperandSizeMismatch;
1701 }
17021712 encoder.rex(.{
17031713 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
17041714 .r = reg.isExtended(),
......@@ -1722,10 +1732,7 @@ fn lowerToRmiEnc(
17221732 reg_or_mem: RegisterOrMemory,
17231733 imm: u32,
17241734 code: *std.ArrayList(u8),
1725) LoweringError!void {
1726 if (reg.size() == 8) {
1727 return error.OperandSizeMismatch;
1728 }
1735) InnerError!void {
17291736 const opc = getOpCode(tag, .rmi, false).?;
17301737 const encoder = try Encoder.init(code, 13);
17311738 if (reg.size() == 16) {
......@@ -1733,9 +1740,6 @@ fn lowerToRmiEnc(
17331740 }
17341741 switch (reg_or_mem) {
17351742 .register => |src_reg| {
1736 if (reg.size() != src_reg.size()) {
1737 return error.OperandSizeMismatch;
1738 }
17391743 encoder.rex(.{
17401744 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
17411745 .r = reg.isExtended(),
......@@ -1748,12 +1752,6 @@ fn lowerToRmiEnc(
17481752 if (src_mem.base) |base| {
17491753 // TODO handle 32-bit base register - requires prefix 0x67
17501754 // 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 }
17571755 encoder.rex(.{
17581756 .w = setRexWRegister(reg),
17591757 .r = reg.isExtended(),
src/arch/x86_64/Mir.zig+13
......@@ -185,6 +185,19 @@ pub const Inst = struct {
185185 mov_scale_imm,
186186 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
188201 /// ops flags: form:
189202 /// 0b00 reg1, [reg2 + imm32]
190203 /// 0b00 reg1, [ds:imm32]
test/behavior.zig+1-1
......@@ -35,6 +35,7 @@ test {
3535 _ = @import("behavior/pub_enum.zig");
3636 _ = @import("behavior/slice_sentinel_comptime.zig");
3737 _ = @import("behavior/type.zig");
38 _ = @import("behavior/truncate.zig");
3839
3940 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {
4041 // Tests that pass for stage1, llvm backend, C backend, wasm backend.
......@@ -71,7 +72,6 @@ test {
7172 _ = @import("behavior/src.zig");
7273 _ = @import("behavior/struct.zig");
7374 _ = @import("behavior/this.zig");
74 _ = @import("behavior/truncate.zig");
7575 _ = @import("behavior/try.zig");
7676 _ = @import("behavior/type_info.zig");
7777 _ = @import("behavior/undefined.zig");
test/behavior/truncate.zig+19
......@@ -1,53 +1,72 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34
45test "truncate u0 to larger integer allowed and has comptime known result" {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7
58 var x: u0 = 0;
69 const y = @truncate(u8, x);
710 comptime try expect(y == 0);
811}
912
1013test "truncate.u0.literal" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15
1116 var z = @truncate(u0, 0);
1217 try expect(z == 0);
1318}
1419
1520test "truncate.u0.const" {
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
22
1623 const c0: usize = 0;
1724 var z = @truncate(u0, c0);
1825 try expect(z == 0);
1926}
2027
2128test "truncate.u0.var" {
29 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
30
2231 var d: u8 = 2;
2332 var z = @truncate(u0, d);
2433 try expect(z == 0);
2534}
2635
2736test "truncate i0 to larger integer allowed and has comptime known result" {
37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
38
2839 var x: i0 = 0;
2940 const y = @truncate(i8, x);
3041 comptime try expect(y == 0);
3142}
3243
3344test "truncate.i0.literal" {
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
46
3447 var z = @truncate(i0, 0);
3548 try expect(z == 0);
3649}
3750
3851test "truncate.i0.const" {
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
53
3954 const c0: isize = 0;
4055 var z = @truncate(i0, c0);
4156 try expect(z == 0);
4257}
4358
4459test "truncate.i0.var" {
60 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
61
4562 var d: i8 = 2;
4663 var z = @truncate(i0, d);
4764 try expect(z == 0);
4865}
4966
5067test "truncate on comptime integer" {
68 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
69
5170 var x = @truncate(u16, 9999);
5271 try expect(x == 9999);
5372 var y = @truncate(u16, -21555);