authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-16 19:45:30+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-19 19:37:29+02:00
log9e5c8cb008f75c2e570a0e48d5d014e936c103ca
treeaf9bf9c8457a4137b1fc534e7eac8a0a4400b8ec
parent2aee2302515ba444999b82c2e40cbc35dee08baf

x64: merge general purpose with simd register into one bitset

This way, we do not have to tweak the `RegisterManager` to handle multiple register types - we have one linear space instead. Additionally we can use the bitset itself to separate the registers into overlapping (the ones that are aliases of differing bitwidths) and nonoverlapping classes (for example, AVX registers do not overlap general purpose registers, thus they can be allocated simultaneously). Another huge benefit of this simple approach is the fact that we can still refer to *all* registers regardless of their class via enum literals which makes the code so much more readable. Finally, `RegisterLock` is universal across different register classes.

7 files changed, 1158 insertions(+), 1397 deletions(-)

src/arch/x86_64/CodeGen.zig+427-633
......@@ -35,14 +35,10 @@ const caller_preserved_regs = abi.caller_preserved_regs;
3535const allocatable_registers = abi.allocatable_registers;
3636const c_abi_int_param_regs = abi.c_abi_int_param_regs;
3737const c_abi_int_return_regs = abi.c_abi_int_return_regs;
38const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers, spillInstruction);
38const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
3939const RegisterLock = RegisterManager.RegisterLock;
4040const Register = bits.Register;
4141
42const AvxRegisterManager = RegisterManagerFn(Self, AvxRegister, &abi.avx_regs, spillInstructionAvx);
43const AvxRegisterLock = AvxRegisterManager.RegisterLock;
44const AvxRegister = bits.AvxRegister;
45
4642const InnerError = error{
4743 OutOfMemory,
4844 CodegenFail,
......@@ -92,8 +88,7 @@ branch_stack: *std.ArrayList(Branch),
9288// Key is the block instruction
9389blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
9490
95register_manager: RegisterManager,
96avx_register_manager: AvxRegisterManager,
91register_manager: RegisterManager = .{},
9792/// Maps offset to what is stored there.
9893stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
9994
......@@ -133,8 +128,6 @@ pub const MCValue = union(enum) {
133128 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register,
134129 /// and the operation is a signed operation.
135130 register_overflow_signed: Register,
136 /// The value is in an AVX register.
137 avx_register: AvxRegister,
138131 /// The value is in memory at a hard-coded address.
139132 /// If the type is a pointer, it means the pointer address is at this memory location.
140133 memory: u64,
......@@ -202,7 +195,6 @@ pub const MCValue = union(enum) {
202195 fn isRegister(mcv: MCValue) bool {
203196 return switch (mcv) {
204197 .register => true,
205 .avx_register => true,
206198 else => false,
207199 };
208200 }
......@@ -304,11 +296,7 @@ pub fn generate(
304296 .mir_to_air_map = if (builtin.mode == .Debug)
305297 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
306298 else {},
307 .register_manager = undefined,
308 .avx_register_manager = undefined,
309299 };
310 function.register_manager = .{ .function = &function };
311 function.avx_register_manager = .{ .function = &function };
312300 defer function.stack.deinit(bin_file.allocator);
313301 defer function.blocks.deinit(bin_file.allocator);
314302 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
......@@ -400,17 +388,15 @@ fn gen(self: *Self) InnerError!void {
400388 if (cc != .Naked) {
401389 _ = try self.addInst(.{
402390 .tag = .push,
403 .ops = (Mir.Ops(Register, Register){
404 .reg1 = .rbp,
405 }).encode(),
391 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
406392 .data = undefined, // unused for push reg,
407393 });
408394 _ = try self.addInst(.{
409395 .tag = .mov,
410 .ops = (Mir.Ops(Register, Register){
396 .ops = Mir.Inst.Ops.encode(.{
411397 .reg1 = .rbp,
412398 .reg2 = .rsp,
413 }).encode(),
399 }),
414400 .data = undefined,
415401 });
416402 // We want to subtract the aligned stack frame size from rsp here, but we don't
......@@ -447,9 +433,7 @@ fn gen(self: *Self) InnerError!void {
447433 // push the callee_preserved_regs that were used
448434 const backpatch_push_callee_preserved_regs_i = try self.addInst(.{
449435 .tag = .push_regs_from_callee_preserved_regs,
450 .ops = (Mir.Ops(Register, Register){
451 .reg1 = .rbp,
452 }).encode(),
436 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
453437 .data = .{ .payload = undefined }, // to be backpatched
454438 });
455439
......@@ -489,9 +473,7 @@ fn gen(self: *Self) InnerError!void {
489473 // pop the callee_preserved_regs
490474 _ = try self.addInst(.{
491475 .tag = .pop_regs_from_callee_preserved_regs,
492 .ops = (Mir.Ops(Register, Register){
493 .reg1 = .rbp,
494 }).encode(),
476 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
495477 .data = .{ .payload = callee_preserved_regs_payload },
496478 });
497479
......@@ -510,17 +492,13 @@ fn gen(self: *Self) InnerError!void {
510492
511493 _ = try self.addInst(.{
512494 .tag = .pop,
513 .ops = (Mir.Ops(Register, Register){
514 .reg1 = .rbp,
515 }).encode(),
495 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
516496 .data = undefined,
517497 });
518498
519499 _ = try self.addInst(.{
520500 .tag = .ret,
521 .ops = (Mir.Ops(Register, Register){
522 .flags = 0b11,
523 }).encode(),
501 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }),
524502 .data = undefined,
525503 });
526504
......@@ -534,16 +512,12 @@ fn gen(self: *Self) InnerError!void {
534512 if (aligned_stack_end > 0) {
535513 self.mir_instructions.set(backpatch_stack_sub, .{
536514 .tag = .sub,
537 .ops = (Mir.Ops(Register, Register){
538 .reg1 = .rsp,
539 }).encode(),
515 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
540516 .data = .{ .imm = aligned_stack_end },
541517 });
542518 self.mir_instructions.set(backpatch_stack_add, .{
543519 .tag = .add,
544 .ops = (Mir.Ops(Register, Register){
545 .reg1 = .rsp,
546 }).encode(),
520 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
547521 .data = .{ .imm = aligned_stack_end },
548522 });
549523 }
......@@ -908,8 +882,8 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
908882 // TODO check if AVX available
909883 const ptr_bytes: u64 = 32;
910884 if (abi_size <= ptr_bytes) {
911 if (self.avx_register_manager.tryAllocReg(inst)) |reg| {
912 return MCValue{ .avx_register = avxRegisterAlias(reg, abi_size) };
885 if (self.register_manager.tryAllocReg(inst)) |reg| {
886 return MCValue{ .register = registerAlias(reg, abi_size) };
913887 }
914888 }
915889 },
......@@ -947,21 +921,6 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
947921 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
948922}
949923
950pub fn spillInstructionAvx(self: *Self, reg: AvxRegister, inst: Air.Inst.Index) !void {
951 const stack_mcv = try self.allocRegOrMem(inst, false);
952 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
953 const reg_mcv = self.getResolvedInstValue(inst);
954 switch (reg_mcv) {
955 .avx_register => |other| {
956 assert(reg.to256() == other.to256());
957 },
958 else => {},
959 }
960 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
961 try branch.inst_table.put(self.gpa, inst, stack_mcv);
962 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
963}
964
965924pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
966925 if (self.compare_flags_inst) |inst_to_save| {
967926 const mcv = self.getResolvedInstValue(inst_to_save);
......@@ -972,7 +931,6 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
972931 .compare_flags_signed,
973932 .compare_flags_unsigned,
974933 => try self.allocRegOrMem(inst_to_save, true),
975 .avx_register => try self.allocRegOrMem(inst_to_save, false),
976934 else => unreachable,
977935 };
978936
......@@ -990,7 +948,6 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
990948 .register_overflow_signed,
991949 .register_overflow_unsigned,
992950 => |reg| self.register_manager.freeReg(reg),
993 .avx_register => |reg| self.avx_register_manager.freeReg(reg),
994951 else => {},
995952 }
996953 }
......@@ -1236,10 +1193,10 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {
12361193 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv);
12371194 _ = try self.addInst(.{
12381195 .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below,
1239 .ops = (Mir.Ops(Register, Register){
1196 .ops = Mir.Inst.Ops.encode(.{
12401197 .reg1 = dst_mcv.register,
12411198 .reg2 = lhs_reg,
1242 }).encode(),
1199 }),
12431200 .data = undefined,
12441201 });
12451202
......@@ -1440,10 +1397,10 @@ fn genSetStackTruncatedOverflowCompare(
14401397 };
14411398 _ = try self.addInst(.{
14421399 .tag = .cond_set_byte_overflow,
1443 .ops = (Mir.Ops(Register, Register){
1400 .ops = Mir.Inst.Ops.encode(.{
14441401 .reg1 = overflow_reg.to8(),
14451402 .flags = flags,
1446 }).encode(),
1403 }),
14471404 .data = undefined,
14481405 });
14491406
......@@ -1460,10 +1417,7 @@ fn genSetStackTruncatedOverflowCompare(
14601417 const eq_reg = temp_regs[2];
14611418 _ = try self.addInst(.{
14621419 .tag = .cond_set_byte_eq_ne,
1463 .ops = (Mir.Ops(Register, Register){
1464 .reg1 = eq_reg.to8(),
1465 .flags = 0b00,
1466 }).encode(),
1420 .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }),
14671421 .data = undefined,
14681422 });
14691423
......@@ -1609,19 +1563,17 @@ fn genIntMulDivOpMir(
16091563 .signed => {
16101564 _ = try self.addInst(.{
16111565 .tag = .cwd,
1612 .ops = (Mir.Ops(Register, Register){
1613 .flags = 0b11,
1614 }).encode(),
1566 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }),
16151567 .data = undefined,
16161568 });
16171569 },
16181570 .unsigned => {
16191571 _ = try self.addInst(.{
16201572 .tag = .xor,
1621 .ops = (Mir.Ops(Register, Register){
1573 .ops = Mir.Inst.Ops.encode(.{
16221574 .reg1 = .rdx,
16231575 .reg2 = .rdx,
1624 }).encode(),
1576 }),
16251577 .data = undefined,
16261578 });
16271579 },
......@@ -1640,16 +1592,14 @@ fn genIntMulDivOpMir(
16401592 .register => |reg| {
16411593 _ = try self.addInst(.{
16421594 .tag = tag,
1643 .ops = (Mir.Ops(Register, Register){
1644 .reg1 = reg,
1645 }).encode(),
1595 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
16461596 .data = undefined,
16471597 });
16481598 },
16491599 .stack_offset => |off| {
16501600 _ = try self.addInst(.{
16511601 .tag = tag,
1652 .ops = (Mir.Ops(Register, Register){
1602 .ops = Mir.Inst.Ops.encode(.{
16531603 .reg2 = .rbp,
16541604 .flags = switch (abi_size) {
16551605 1 => 0b00,
......@@ -1658,7 +1608,7 @@ fn genIntMulDivOpMir(
16581608 8 => 0b11,
16591609 else => unreachable,
16601610 },
1661 }).encode(),
1611 }),
16621612 .data = .{ .imm = @bitCast(u32, -off) },
16631613 });
16641614 },
......@@ -1691,34 +1641,34 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
16911641
16921642 _ = try self.addInst(.{
16931643 .tag = .xor,
1694 .ops = (Mir.Ops(Register, Register){
1644 .ops = Mir.Inst.Ops.encode(.{
16951645 .reg1 = divisor.to64(),
16961646 .reg2 = dividend.to64(),
1697 }).encode(),
1647 }),
16981648 .data = undefined,
16991649 });
17001650 _ = try self.addInst(.{
17011651 .tag = .sar,
1702 .ops = (Mir.Ops(Register, Register){
1652 .ops = Mir.Inst.Ops.encode(.{
17031653 .reg1 = divisor.to64(),
17041654 .flags = 0b10,
1705 }).encode(),
1655 }),
17061656 .data = .{ .imm = 63 },
17071657 });
17081658 _ = try self.addInst(.{
17091659 .tag = .@"test",
1710 .ops = (Mir.Ops(Register, Register){
1660 .ops = Mir.Inst.Ops.encode(.{
17111661 .reg1 = .rdx,
17121662 .reg2 = .rdx,
1713 }).encode(),
1663 }),
17141664 .data = undefined,
17151665 });
17161666 _ = try self.addInst(.{
17171667 .tag = .cond_mov_eq,
1718 .ops = (Mir.Ops(Register, Register){
1668 .ops = Mir.Inst.Ops.encode(.{
17191669 .reg1 = divisor.to64(),
17201670 .reg2 = .rdx,
1721 }).encode(),
1671 }),
17221672 .data = undefined,
17231673 });
17241674 try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
......@@ -2102,11 +2052,11 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
21022052 // mov reg, [rbp - 8]
21032053 _ = try self.addInst(.{
21042054 .tag = .mov,
2105 .ops = (Mir.Ops(Register, Register){
2055 .ops = Mir.Inst.Ops.encode(.{
21062056 .reg1 = addr_reg.to64(),
21072057 .reg2 = .rbp,
21082058 .flags = 0b01,
2109 }).encode(),
2059 }),
21102060 .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
21112061 });
21122062 },
......@@ -2187,10 +2137,10 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
21872137 // lea reg, [rbp]
21882138 _ = try self.addInst(.{
21892139 .tag = .lea,
2190 .ops = (Mir.Ops(Register, Register){
2140 .ops = Mir.Inst.Ops.encode(.{
21912141 .reg1 = addr_reg.to64(),
21922142 .reg2 = .rbp,
2193 }).encode(),
2143 }),
21942144 .data = .{ .imm = @bitCast(u32, -off) },
21952145 });
21962146 },
......@@ -2198,10 +2148,10 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
21982148 // lea reg, [rbp]
21992149 _ = try self.addInst(.{
22002150 .tag = .lea,
2201 .ops = (Mir.Ops(Register, Register){
2151 .ops = Mir.Inst.Ops.encode(.{
22022152 .reg1 = addr_reg.to64(),
22032153 .reg2 = .rbp,
2204 }).encode(),
2154 }),
22052155 .data = .{ .imm = @bitCast(u32, -off) },
22062156 });
22072157 },
......@@ -2266,11 +2216,11 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
22662216 // mov dst_mcv, [dst_mcv]
22672217 _ = try self.addInst(.{
22682218 .tag = .mov,
2269 .ops = (Mir.Ops(Register, Register){
2270 .flags = 0b01,
2219 .ops = Mir.Inst.Ops.encode(.{
22712220 .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)),
22722221 .reg2 = dst_mcv.register,
2273 }).encode(),
2222 .flags = 0b01,
2223 }),
22742224 .data = .{ .imm = 0 },
22752225 });
22762226 break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };
......@@ -2532,11 +2482,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
25322482 // mov dst_reg, [reg]
25332483 _ = try self.addInst(.{
25342484 .tag = .mov,
2535 .ops = (Mir.Ops(Register, Register){
2485 .ops = Mir.Inst.Ops.encode(.{
25362486 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
25372487 .reg2 = reg,
25382488 .flags = 0b01,
2539 }).encode(),
2489 }),
25402490 .data = .{ .imm = 0 },
25412491 });
25422492 },
......@@ -2552,9 +2502,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
25522502 else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}),
25532503 }
25542504 },
2555 .avx_register => {
2556 return self.fail("TODO load for AVX register", .{});
2557 },
25582505 .memory,
25592506 .got_load,
25602507 .direct_load,
......@@ -2606,10 +2553,10 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26062553 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
26072554 _ = try self.addInst(.{
26082555 .tag = .lea_pie,
2609 .ops = (Mir.Ops(Register, Register){
2556 .ops = Mir.Inst.Ops.encode(.{
26102557 .reg1 = registerAlias(reg, abi_size),
26112558 .flags = flags,
2612 }).encode(),
2559 }),
26132560 .data = .{
26142561 .load_reloc = .{
26152562 .atom_index = fn_owner_decl.link.macho.local_sym_index,
......@@ -2670,7 +2617,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26702617 });
26712618 _ = try self.addInst(.{
26722619 .tag = .mov_mem_imm,
2673 .ops = (Mir.Ops(Register, Register){
2620 .ops = Mir.Inst.Ops.encode(.{
26742621 .reg1 = reg.to64(),
26752622 .flags = switch (abi_size) {
26762623 1 => 0b00,
......@@ -2678,7 +2625,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26782625 4 => 0b10,
26792626 else => unreachable,
26802627 },
2681 }).encode(),
2628 }),
26822629 .data = .{ .payload = payload },
26832630 });
26842631 },
......@@ -2692,11 +2639,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26922639 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
26932640 _ = try self.addInst(.{
26942641 .tag = .mov,
2695 .ops = (Mir.Ops(Register, Register){
2642 .ops = Mir.Inst.Ops.encode(.{
26962643 .reg1 = reg.to64(),
26972644 .reg2 = tmp_reg.to64(),
26982645 .flags = 0b10,
2699 }).encode(),
2646 }),
27002647 .data = .{ .imm = 0 },
27012648 });
27022649 },
......@@ -2708,11 +2655,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27082655 .register => |src_reg| {
27092656 _ = try self.addInst(.{
27102657 .tag = .mov,
2711 .ops = (Mir.Ops(Register, Register){
2658 .ops = Mir.Inst.Ops.encode(.{
27122659 .reg1 = reg.to64(),
27132660 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
27142661 .flags = 0b10,
2715 }).encode(),
2662 }),
27162663 .data = .{ .imm = 0 },
27172664 });
27182665 },
......@@ -2736,9 +2683,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27362683 },
27372684 }
27382685 },
2739 .avx_register => {
2740 return self.fail("TODO store for AVX register", .{});
2741 },
27422686 .got_load,
27432687 .direct_load,
27442688 .memory,
......@@ -2759,11 +2703,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27592703 // mov reg, [reg]
27602704 _ = try self.addInst(.{
27612705 .tag = .mov,
2762 .ops = (Mir.Ops(Register, Register){
2706 .ops = Mir.Inst.Ops.encode(.{
27632707 .reg1 = addr_reg.to64(),
27642708 .reg2 = addr_reg.to64(),
27652709 .flags = 0b01,
2766 }).encode(),
2710 }),
27672711 .data = .{ .imm = 0 },
27682712 });
27692713
......@@ -2798,21 +2742,21 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27982742 }
27992743 _ = try self.addInst(.{
28002744 .tag = .mov_mem_imm,
2801 .ops = (Mir.Ops(Register, Register){
2745 .ops = Mir.Inst.Ops.encode(.{
28022746 .reg1 = addr_reg.to64(),
28032747 .flags = flags,
2804 }).encode(),
2748 }),
28052749 .data = .{ .payload = payload },
28062750 });
28072751 },
28082752 .register => |reg| {
28092753 _ = try self.addInst(.{
28102754 .tag = .mov,
2811 .ops = (Mir.Ops(Register, Register){
2755 .ops = Mir.Inst.Ops.encode(.{
28122756 .reg1 = addr_reg.to64(),
28132757 .reg2 = reg,
28142758 .flags = 0b10,
2815 }).encode(),
2759 }),
28162760 .data = .{ .imm = 0 },
28172761 });
28182762 },
......@@ -2829,20 +2773,20 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28292773
28302774 _ = try self.addInst(.{
28312775 .tag = .mov,
2832 .ops = (Mir.Ops(Register, Register){
2776 .ops = Mir.Inst.Ops.encode(.{
28332777 .reg1 = tmp_reg,
28342778 .reg2 = tmp_reg,
28352779 .flags = 0b01,
2836 }).encode(),
2780 }),
28372781 .data = .{ .imm = 0 },
28382782 });
28392783 _ = try self.addInst(.{
28402784 .tag = .mov,
2841 .ops = (Mir.Ops(Register, Register){
2785 .ops = Mir.Inst.Ops.encode(.{
28422786 .reg1 = addr_reg.to64(),
28432787 .reg2 = tmp_reg,
28442788 .flags = 0b10,
2845 }).encode(),
2789 }),
28462790 .data = .{ .imm = 0 },
28472791 });
28482792 return;
......@@ -2856,11 +2800,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28562800 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
28572801 _ = try self.addInst(.{
28582802 .tag = .mov,
2859 .ops = (Mir.Ops(Register, Register){
2803 .ops = Mir.Inst.Ops.encode(.{
28602804 .reg1 = addr_reg.to64(),
28612805 .reg2 = tmp_reg,
28622806 .flags = 0b10,
2863 }).encode(),
2807 }),
28642808 .data = .{ .imm = 0 },
28652809 });
28662810 return;
......@@ -3017,10 +2961,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30172961 if (signedness == .signed and field_size < 8) {
30182962 _ = try self.addInst(.{
30192963 .tag = .mov_sign_extend,
3020 .ops = (Mir.Ops(Register, Register){
2964 .ops = Mir.Inst.Ops.encode(.{
30212965 .reg1 = dst_mcv.register,
30222966 .reg2 = registerAlias(dst_mcv.register, field_size),
3023 }).encode(),
2967 }),
30242968 .data = undefined,
30252969 });
30262970 }
......@@ -3048,10 +2992,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30482992 };
30492993 _ = try self.addInst(.{
30502994 .tag = .cond_set_byte_overflow,
3051 .ops = (Mir.Ops(Register, Register){
2995 .ops = Mir.Inst.Ops.encode(.{
30522996 .reg1 = dst_reg.to8(),
30532997 .flags = flags,
3054 }).encode(),
2998 }),
30552999 .data = undefined,
30563000 });
30573001 break :result MCValue{ .register = dst_reg.to8() };
......@@ -3092,10 +3036,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
30923036 1 => {
30933037 _ = try self.addInst(.{
30943038 .tag = tag,
3095 .ops = (Mir.Ops(Register, Register){
3096 .reg1 = registerAlias(reg, abi_size),
3097 .flags = 0b00,
3098 }).encode(),
3039 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }),
30993040 .data = undefined,
31003041 });
31013042 return;
......@@ -3103,10 +3044,10 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
31033044 else => {
31043045 _ = try self.addInst(.{
31053046 .tag = tag,
3106 .ops = (Mir.Ops(Register, Register){
3047 .ops = Mir.Inst.Ops.encode(.{
31073048 .reg1 = registerAlias(reg, abi_size),
31083049 .flags = 0b10,
3109 }).encode(),
3050 }),
31103051 .data = .{ .imm = @intCast(u8, imm) },
31113052 });
31123053 return;
......@@ -3124,10 +3065,10 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
31243065
31253066 _ = try self.addInst(.{
31263067 .tag = tag,
3127 .ops = (Mir.Ops(Register, Register){
3068 .ops = Mir.Inst.Ops.encode(.{
31283069 .reg1 = registerAlias(reg, abi_size),
31293070 .flags = 0b01,
3130 }).encode(),
3071 }),
31313072 .data = undefined,
31323073 });
31333074}
......@@ -3383,32 +3324,6 @@ fn genBinOp(
33833324 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()});
33843325 }
33853326
3386 if (lhs_ty.zigTypeTag() == .Float) {
3387 switch (tag) {
3388 .add => {
3389 const dst_reg: AvxRegister = blk: {
3390 const reg = try self.avx_register_manager.allocReg(null);
3391 try self.genSetAvxReg(lhs_ty, reg, lhs);
3392 break :blk reg.to128();
3393 };
3394 const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg);
3395 defer self.avx_register_manager.unlockReg(dst_lock);
3396
3397 const src_reg: AvxRegister = blk: {
3398 const reg = try self.avx_register_manager.allocReg(null);
3399 try self.genSetAvxReg(lhs_ty, reg, rhs);
3400 break :blk reg.to128();
3401 };
3402 const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg);
3403 defer self.avx_register_manager.unlockReg(src_lock);
3404
3405 try self.genBinOpMir(.add_f64, lhs_ty, .{ .avx_register = dst_reg }, .{ .avx_register = src_reg });
3406 return MCValue{ .avx_register = dst_reg };
3407 },
3408 else => unreachable,
3409 }
3410 }
3411
34123327 const is_commutative: bool = switch (tag) {
34133328 .add,
34143329 .addwrap,
......@@ -3526,25 +3441,39 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35263441 const reg = try self.copyToTmpRegister(dst_ty, src_mcv);
35273442 return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg });
35283443 },
3529 .register => |src_reg| {
3530 _ = try self.addInst(.{
3531 .tag = mir_tag,
3532 .ops = (Mir.Ops(Register, Register){
3533 .reg1 = registerAlias(dst_reg, abi_size),
3534 .reg2 = registerAlias(src_reg, abi_size),
3535 }).encode(),
3536 .data = undefined,
3537 });
3538 },
3539 .avx_register => {
3540 return self.fail("TODO genBinOp for AVX register", .{});
3444 .register => |src_reg| switch (dst_ty.zigTypeTag()) {
3445 .Float => switch (dst_ty.tag()) {
3446 .f64 => {
3447 _ = try self.addInst(.{
3448 .tag = switch (mir_tag) {
3449 .add => .add_f64,
3450 .cmp => .cmp_f64,
3451 else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
3452 },
3453 .ops = Mir.Inst.Ops.encode(.{
3454 .reg1 = dst_reg.to128(),
3455 .reg2 = src_reg.to128(),
3456 }),
3457 .data = undefined,
3458 });
3459 },
3460 else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),
3461 },
3462 else => {
3463 _ = try self.addInst(.{
3464 .tag = mir_tag,
3465 .ops = Mir.Inst.Ops.encode(.{
3466 .reg1 = registerAlias(dst_reg, abi_size),
3467 .reg2 = registerAlias(src_reg, abi_size),
3468 }),
3469 .data = undefined,
3470 });
3471 },
35413472 },
35423473 .immediate => |imm| {
35433474 _ = try self.addInst(.{
35443475 .tag = mir_tag,
3545 .ops = (Mir.Ops(Register, Register){
3546 .reg1 = registerAlias(dst_reg, abi_size),
3547 }).encode(),
3476 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }),
35483477 .data = .{ .imm = @truncate(u32, imm) },
35493478 });
35503479 },
......@@ -3567,39 +3496,16 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35673496 }
35683497 _ = try self.addInst(.{
35693498 .tag = mir_tag,
3570 .ops = (Mir.Ops(Register, Register){
3499 .ops = Mir.Inst.Ops.encode(.{
35713500 .reg1 = registerAlias(dst_reg, abi_size),
35723501 .reg2 = .rbp,
35733502 .flags = 0b01,
3574 }).encode(),
3503 }),
35753504 .data = .{ .imm = @bitCast(u32, -off) },
35763505 });
35773506 },
35783507 }
35793508 },
3580 .avx_register => |dst_reg| {
3581 switch (src_mcv) {
3582 .avx_register => |src_reg| {
3583 switch (dst_ty.zigTypeTag()) {
3584 .Float => switch (dst_ty.tag()) {
3585 .f64 => {
3586 _ = try self.addInst(.{
3587 .tag = mir_tag,
3588 .ops = (Mir.Ops(AvxRegister, AvxRegister){
3589 .reg1 = dst_reg.to128(),
3590 .reg2 = src_reg.to128(),
3591 }).encode(),
3592 .data = undefined,
3593 });
3594 },
3595 else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}),
3596 },
3597 else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}),
3598 }
3599 },
3600 else => return self.fail("TODO genBinOp for AVX register", .{}),
3601 }
3602 },
36033509 .ptr_stack_offset, .stack_offset => |off| {
36043510 if (off > math.maxInt(i32)) {
36053511 return self.fail("stack offset too large", .{});
......@@ -3617,17 +3523,14 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36173523 .register => |src_reg| {
36183524 _ = try self.addInst(.{
36193525 .tag = mir_tag,
3620 .ops = (Mir.Ops(Register, Register){
3526 .ops = Mir.Inst.Ops.encode(.{
36213527 .reg1 = .rbp,
36223528 .reg2 = registerAlias(src_reg, abi_size),
36233529 .flags = 0b10,
3624 }).encode(),
3530 }),
36253531 .data = .{ .imm = @bitCast(u32, -off) },
36263532 });
36273533 },
3628 .avx_register => {
3629 return self.fail("TODO genBinOp for AVX register", .{});
3630 },
36313534 .immediate => |imm| {
36323535 const tag: Mir.Inst.Tag = switch (mir_tag) {
36333536 .add => .add_mem_imm,
......@@ -3651,10 +3554,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36513554 });
36523555 _ = try self.addInst(.{
36533556 .tag = tag,
3654 .ops = (Mir.Ops(Register, Register){
3557 .ops = Mir.Inst.Ops.encode(.{
36553558 .reg1 = .rbp,
36563559 .flags = flags,
3657 }).encode(),
3560 }),
36583561 .data = .{ .payload = payload },
36593562 });
36603563 },
......@@ -3697,7 +3600,6 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
36973600 .ptr_stack_offset => unreachable,
36983601 .register_overflow_unsigned => unreachable,
36993602 .register_overflow_signed => unreachable,
3700 .avx_register => unreachable,
37013603 .register => |dst_reg| {
37023604 switch (src_mcv) {
37033605 .none => unreachable,
......@@ -3706,15 +3608,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37063608 .ptr_stack_offset => unreachable,
37073609 .register_overflow_unsigned => unreachable,
37083610 .register_overflow_signed => unreachable,
3709 .avx_register => unreachable,
37103611 .register => |src_reg| {
37113612 // register, register
37123613 _ = try self.addInst(.{
37133614 .tag = .imul_complex,
3714 .ops = (Mir.Ops(Register, Register){
3615 .ops = Mir.Inst.Ops.encode(.{
37153616 .reg1 = registerAlias(dst_reg, abi_size),
37163617 .reg2 = registerAlias(src_reg, abi_size),
3717 }).encode(),
3618 }),
37183619 .data = undefined,
37193620 });
37203621 },
......@@ -3724,11 +3625,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37243625 if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
37253626 _ = try self.addInst(.{
37263627 .tag = .imul_complex,
3727 .ops = (Mir.Ops(Register, Register){
3628 .ops = Mir.Inst.Ops.encode(.{
37283629 .reg1 = dst_reg.to32(),
37293630 .reg2 = dst_reg.to32(),
37303631 .flags = 0b10,
3731 }).encode(),
3632 }),
37323633 .data = .{ .imm = @truncate(u32, imm) },
37333634 });
37343635 } else {
......@@ -3740,11 +3641,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37403641 .stack_offset => |off| {
37413642 _ = try self.addInst(.{
37423643 .tag = .imul_complex,
3743 .ops = (Mir.Ops(Register, Register){
3644 .ops = Mir.Inst.Ops.encode(.{
37443645 .reg1 = registerAlias(dst_reg, abi_size),
37453646 .reg2 = .rbp,
37463647 .flags = 0b01,
3747 }).encode(),
3648 }),
37483649 .data = .{ .imm = @bitCast(u32, -off) },
37493650 });
37503651 },
......@@ -3770,7 +3671,6 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37703671 .ptr_stack_offset => unreachable,
37713672 .register_overflow_unsigned => unreachable,
37723673 .register_overflow_signed => unreachable,
3773 .avx_register => unreachable,
37743674 .register => |src_reg| {
37753675 // copy dst to a register
37763676 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
......@@ -3778,10 +3678,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37783678 // register, register
37793679 _ = try self.addInst(.{
37803680 .tag = .imul_complex,
3781 .ops = (Mir.Ops(Register, Register){
3681 .ops = Mir.Inst.Ops.encode(.{
37823682 .reg1 = registerAlias(dst_reg, abi_size),
37833683 .reg2 = registerAlias(src_reg, abi_size),
3784 }).encode(),
3684 }),
37853685 .data = undefined,
37863686 });
37873687 // copy dst_reg back out
......@@ -3888,9 +3788,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
38883788fn airBreakpoint(self: *Self) !void {
38893789 _ = try self.addInst(.{
38903790 .tag = .interrupt,
3891 .ops = (Mir.Ops{
3892 .flags = 0b00,
3893 }).encode(),
3791 .ops = Mir.Inst.Ops.encode(.{}),
38943792 .data = undefined,
38953793 });
38963794 return self.finishAirBookkeeping();
......@@ -3973,9 +3871,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39733871 .ptr_stack_offset => {
39743872 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
39753873 },
3976 .avx_register => {
3977 return self.fail("TODO implement calling with MCValue.avx_register arg", .{});
3978 },
39793874 .undef => unreachable,
39803875 .immediate => unreachable,
39813876 .unreach => unreachable,
......@@ -3994,9 +3889,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39943889 // Adjust the stack
39953890 _ = try self.addInst(.{
39963891 .tag = .sub,
3997 .ops = (Mir.Ops(Register, Register){
3998 .reg1 = .rsp,
3999 }).encode(),
3892 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
40003893 .data = .{ .imm = info.stack_byte_count },
40013894 });
40023895 }
......@@ -4020,9 +3913,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
40203913 unreachable;
40213914 _ = try self.addInst(.{
40223915 .tag = .call,
4023 .ops = (Mir.Ops(Register, Register){
4024 .flags = 0b01,
4025 }).encode(),
3916 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
40263917 .data = .{ .imm = @truncate(u32, got_addr) },
40273918 });
40283919 } else if (func_value.castTag(.extern_fn)) |_| {
......@@ -4036,10 +3927,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
40363927 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
40373928 _ = try self.addInst(.{
40383929 .tag = .call,
4039 .ops = (Mir.Ops(Register, Register){
3930 .ops = Mir.Inst.Ops.encode(.{
40403931 .reg1 = .rax,
40413932 .flags = 0b01,
4042 }).encode(),
3933 }),
40433934 .data = undefined,
40443935 });
40453936 }
......@@ -4054,10 +3945,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
40543945 // callq *%rax
40553946 _ = try self.addInst(.{
40563947 .tag = .call,
4057 .ops = (Mir.Ops(Register, Register){
3948 .ops = Mir.Inst.Ops.encode(.{
40583949 .reg1 = .rax,
40593950 .flags = 0b01,
4060 }).encode(),
3951 }),
40613952 .data = undefined,
40623953 });
40633954 } else if (func_value.castTag(.extern_fn)) |func_payload| {
......@@ -4089,10 +3980,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
40893980 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
40903981 _ = try self.addInst(.{
40913982 .tag = .call,
4092 .ops = (Mir.Ops(Register, Register){
3983 .ops = Mir.Inst.Ops.encode(.{
40933984 .reg1 = .rax,
40943985 .flags = 0b01,
4095 }).encode(),
3986 }),
40963987 .data = undefined,
40973988 });
40983989 }
......@@ -4107,9 +3998,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
41073998 const fn_got_addr = got_addr + got_index * ptr_bytes;
41083999 _ = try self.addInst(.{
41094000 .tag = .call,
4110 .ops = (Mir.Ops(Register, Register){
4111 .flags = 0b01,
4112 }).encode(),
4001 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
41134002 .data = .{ .imm = @intCast(u32, fn_got_addr) },
41144003 });
41154004 } else return self.fail("TODO implement calling extern fn on plan9", .{});
......@@ -4119,10 +4008,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
41194008 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
41204009 _ = try self.addInst(.{
41214010 .tag = .call,
4122 .ops = (Mir.Ops(Register, Register){
4011 .ops = Mir.Inst.Ops.encode(.{
41234012 .reg1 = .rax,
41244013 .flags = 0b01,
4125 }).encode(),
4014 }),
41264015 .data = undefined,
41274016 });
41284017 }
......@@ -4132,9 +4021,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
41324021 // Readjust the stack
41334022 _ = try self.addInst(.{
41344023 .tag = .add,
4135 .ops = (Mir.Ops(Register, Register){
4136 .reg1 = .rsp,
4137 }).encode(),
4024 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
41384025 .data = .{ .imm = info.stack_byte_count },
41394026 });
41404027 }
......@@ -4192,9 +4079,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
41924079 // which is available if the jump is 127 bytes or less forward.
41934080 const jmp_reloc = try self.addInst(.{
41944081 .tag = .jmp,
4195 .ops = (Mir.Ops(Register, Register){
4196 .flags = 0b00,
4197 }).encode(),
4082 .ops = Mir.Inst.Ops.encode(.{}),
41984083 .data = .{ .inst = undefined },
41994084 });
42004085 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
......@@ -4227,9 +4112,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
42274112 // which is available if the jump is 127 bytes or less forward.
42284113 const jmp_reloc = try self.addInst(.{
42294114 .tag = .jmp,
4230 .ops = (Mir.Ops(Register, Register){
4231 .flags = 0b00,
4232 }).encode(),
4115 .ops = Mir.Inst.Ops.encode(.{}),
42334116 .data = .{ .inst = undefined },
42344117 });
42354118 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
......@@ -4256,37 +4139,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
42564139 self.compare_flags_inst = inst;
42574140
42584141 const result: MCValue = result: {
4259 if (ty.zigTypeTag() == .Float) {
4260 const lhs = try self.resolveInst(bin_op.lhs);
4261 const rhs = try self.resolveInst(bin_op.rhs);
4262
4263 const dst_reg: AvxRegister = blk: {
4264 const reg = try self.avx_register_manager.allocReg(null);
4265 try self.genSetAvxReg(ty, reg, lhs);
4266 break :blk reg.to128();
4267 };
4268 const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg);
4269 defer self.avx_register_manager.unlockReg(dst_lock);
4270
4271 const src_reg: AvxRegister = blk: {
4272 const reg = try self.avx_register_manager.allocReg(null);
4273 try self.genSetAvxReg(ty, reg, rhs);
4274 break :blk reg.to128();
4275 };
4276 const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg);
4277 defer self.avx_register_manager.unlockReg(src_lock);
4278
4279 _ = try self.addInst(.{
4280 .tag = .cmp_f64,
4281 .ops = (Mir.Ops(AvxRegister, AvxRegister){
4282 .reg1 = dst_reg,
4283 .reg2 = src_reg,
4284 }).encode(),
4285 .data = undefined,
4286 });
4287
4288 break :result MCValue{ .compare_flags_unsigned = op };
4289 }
42904142 // There are 2 operands, destination and source.
42914143 // Either one, but not both, can be a memory operand.
42924144 // Source operand can be an immediate, 8 bits or 32 bits.
......@@ -4304,8 +4156,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
43044156
43054157 const dst_mcv = MCValue{ .register = dst_reg };
43064158
4159 const rhs_ty = self.air.typeOf(bin_op.rhs);
43074160 // This instruction supports only signed 32-bit immediates at most.
4308 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
4161 const src_mcv: MCValue = blk: {
4162 switch (rhs_ty.zigTypeTag()) {
4163 .Float => {
4164 const rhs = try self.resolveInst(bin_op.rhs);
4165 const rhs_lock: ?RegisterLock = switch (rhs) {
4166 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
4167 else => null,
4168 };
4169 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
4170 const src_reg = try self.copyToTmpRegister(rhs_ty, rhs);
4171 break :blk MCValue{ .register = src_reg };
4172 },
4173 else => break :blk try self.limitImmediateType(bin_op.rhs, i32),
4174 }
4175 };
4176 const src_lock: ?RegisterLock = switch (src_mcv) {
4177 .register => |reg| self.register_manager.lockReg(reg),
4178 else => null,
4179 };
4180 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
43094181
43104182 try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv);
43114183 break :result switch (signedness) {
......@@ -4504,9 +4376,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
45044376 Mir.Inst.Tag.cond_jmp_greater_less;
45054377 return self.addInst(.{
45064378 .tag = tag,
4507 .ops = (Mir.Ops(Register, Register){
4508 .flags = flags,
4509 }).encode(),
4379 .ops = Mir.Inst.Ops.encode(.{ .flags = flags }),
45104380 .data = .{ .inst = undefined },
45114381 });
45124382 },
......@@ -4514,17 +4384,12 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
45144384 try self.spillCompareFlagsIfOccupied();
45154385 _ = try self.addInst(.{
45164386 .tag = .@"test",
4517 .ops = (Mir.Ops(Register, Register){
4518 .reg1 = reg,
4519 .flags = 0b00,
4520 }).encode(),
4387 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
45214388 .data = .{ .imm = 1 },
45224389 });
45234390 return self.addInst(.{
45244391 .tag = .cond_jmp_eq_ne,
4525 .ops = (Mir.Ops(Register, Register){
4526 .flags = 0b01,
4527 }).encode(),
4392 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
45284393 .data = .{ .inst = undefined },
45294394 });
45304395 },
......@@ -4918,9 +4783,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
49184783 try self.genBody(body);
49194784 _ = try self.addInst(.{
49204785 .tag = .jmp,
4921 .ops = (Mir.Ops(Register, Register){
4922 .flags = 0b00,
4923 }).encode(),
4786 .ops = Mir.Inst.Ops.encode(.{}),
49244787 .data = .{ .inst = jmp_target },
49254788 });
49264789 return self.finishAirBookkeeping();
......@@ -4971,19 +4834,17 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
49714834 .immediate => |imm| {
49724835 _ = try self.addInst(.{
49734836 .tag = .xor,
4974 .ops = (Mir.Ops(Register, Register){
4975 .reg1 = registerAlias(cond_reg, abi_size),
4976 }).encode(),
4837 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }),
49774838 .data = .{ .imm = @intCast(u32, imm) },
49784839 });
49794840 },
49804841 .register => |reg| {
49814842 _ = try self.addInst(.{
49824843 .tag = .xor,
4983 .ops = (Mir.Ops(Register, Register){
4844 .ops = Mir.Inst.Ops.encode(.{
49844845 .reg1 = registerAlias(cond_reg, abi_size),
49854846 .reg2 = registerAlias(reg, abi_size),
4986 }).encode(),
4847 }),
49874848 .data = undefined,
49884849 });
49894850 },
......@@ -5002,17 +4863,15 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
50024863
50034864 _ = try self.addInst(.{
50044865 .tag = .@"test",
5005 .ops = (Mir.Ops(Register, Register){
4866 .ops = Mir.Inst.Ops.encode(.{
50064867 .reg1 = registerAlias(cond_reg, abi_size),
50074868 .reg2 = registerAlias(cond_reg, abi_size),
5008 }).encode(),
4869 }),
50094870 .data = undefined,
50104871 });
50114872 return self.addInst(.{
50124873 .tag = .cond_jmp_eq_ne,
5013 .ops = (Mir.Ops(Register, Register){
5014 .flags = 0b00,
5015 }).encode(),
4874 .ops = Mir.Inst.Ops.encode(.{}),
50164875 .data = .{ .inst = undefined },
50174876 });
50184877 },
......@@ -5156,17 +5015,20 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
51565015 if (block_mcv == .none) {
51575016 block_data.mcv = switch (operand_mcv) {
51585017 .none, .dead, .unreach => unreachable,
5159 .register, .stack_offset, .memory => operand_mcv,
5018 .stack_offset, .memory => operand_mcv,
51605019 .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: {
51615020 const new_mcv = try self.allocRegOrMem(block, true);
51625021 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
51635022 break :blk new_mcv;
51645023 },
5165 .avx_register => blk: {
5166 // TODO not needed; return operand_mcv ones we can transfer between XMM registers
5167 const new_mcv = try self.allocRegOrMem(block, false);
5168 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
5169 break :blk new_mcv;
5024 .register => blk: {
5025 if (self.air.typeOfIndex(block).zigTypeTag() == .Float) {
5026 // TODO not needed; return operand_mcv ones we can transfer between XMM registers
5027 const new_mcv = try self.allocRegOrMem(block, false);
5028 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
5029 break :blk new_mcv;
5030 }
5031 break :blk operand_mcv;
51705032 },
51715033 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
51725034 };
......@@ -5184,9 +5046,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
51845046 // Leave the jump offset undefined
51855047 const jmp_reloc = try self.addInst(.{
51865048 .tag = .jmp,
5187 .ops = (Mir.Ops(Register, Register){
5188 .flags = 0b00,
5189 }).encode(),
5049 .ops = Mir.Inst.Ops.encode(.{}),
51905050 .data = .{ .inst = undefined },
51915051 });
51925052 block_data.relocs.appendAssumeCapacity(jmp_reloc);
......@@ -5274,9 +5134,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52745134 };
52755135 _ = try self.addInst(.{
52765136 .tag = .push,
5277 .ops = (Mir.Ops(Register, Register){
5278 .flags = 0b10,
5279 }).encode(),
5137 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }),
52805138 .data = .{ .imm = n },
52815139 });
52825140 } else if (mem.indexOf(u8, arg, "%%")) |l| {
......@@ -5285,9 +5143,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52855143 return self.fail("unrecognized register: '{s}'", .{reg_name});
52865144 _ = try self.addInst(.{
52875145 .tag = .push,
5288 .ops = (Mir.Ops(Register, Register){
5289 .reg1 = reg,
5290 }).encode(),
5146 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
52915147 .data = undefined,
52925148 });
52935149 } else return self.fail("TODO more push operands", .{});
......@@ -5299,9 +5155,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52995155 return self.fail("unrecognized register: '{s}'", .{reg_name});
53005156 _ = try self.addInst(.{
53015157 .tag = .pop,
5302 .ops = (Mir.Ops(Register, Register){
5303 .reg1 = reg,
5304 }).encode(),
5158 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
53055159 .data = undefined,
53065160 });
53075161 } else return self.fail("TODO more pop operands", .{});
......@@ -5365,7 +5219,6 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
53655219 .none => return,
53665220 .immediate => unreachable,
53675221 .register => |reg| return self.genSetReg(ty, reg, val),
5368 .avx_register => |reg| return self.genSetAvxReg(ty, reg, val),
53695222 .stack_offset => |off| return self.genSetStack(ty, off, val, .{}),
53705223 .memory => {
53715224 return self.fail("TODO implement setRegOrMem for memory", .{});
......@@ -5396,9 +5249,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
53965249 .register_overflow_unsigned,
53975250 .register_overflow_signed,
53985251 => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
5399 .avx_register => {
5400 return self.fail("TODO genSetStackArg for AVX register", .{});
5401 },
54025252 .compare_flags_unsigned,
54035253 .compare_flags_signed,
54045254 => {
......@@ -5417,7 +5267,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
54175267 });
54185268 _ = try self.addInst(.{
54195269 .tag = .mov_mem_imm,
5420 .ops = (Mir.Ops(Register, Register){
5270 .ops = Mir.Inst.Ops.encode(.{
54215271 .reg1 = .rsp,
54225272 .flags = switch (abi_size) {
54235273 1 => 0b00,
......@@ -5425,7 +5275,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
54255275 4 => 0b10,
54265276 else => unreachable,
54275277 },
5428 }).encode(),
5278 }),
54295279 .data = .{ .payload = payload },
54305280 });
54315281 },
......@@ -5453,11 +5303,11 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
54535303 .register => |reg| {
54545304 _ = try self.addInst(.{
54555305 .tag = .mov,
5456 .ops = (Mir.Ops(Register, Register){
5306 .ops = Mir.Inst.Ops.encode(.{
54575307 .reg1 = .rsp,
54585308 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
54595309 .flags = 0b10,
5460 }).encode(),
5310 }),
54615311 .data = .{ .imm = @bitCast(u32, -stack_offset) },
54625312 });
54635313 },
......@@ -5520,10 +5370,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55205370 };
55215371 _ = try self.addInst(.{
55225372 .tag = .cond_set_byte_overflow,
5523 .ops = (Mir.Ops(Register, Register){
5373 .ops = Mir.Inst.Ops.encode(.{
55245374 .reg1 = tmp_reg.to8(),
55255375 .flags = flags,
5526 }).encode(),
5376 }),
55275377 .data = undefined,
55285378 });
55295379
......@@ -5550,7 +5400,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55505400 });
55515401 _ = try self.addInst(.{
55525402 .tag = .mov_mem_imm,
5553 .ops = (Mir.Ops(Register, Register){
5403 .ops = Mir.Inst.Ops.encode(.{
55545404 .reg1 = base_reg,
55555405 .flags = switch (abi_size) {
55565406 1 => 0b00,
......@@ -5558,7 +5408,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55585408 4 => 0b10,
55595409 else => unreachable,
55605410 },
5561 }).encode(),
5411 }),
55625412 .data = .{ .payload = payload },
55635413 });
55645414 },
......@@ -5572,10 +5422,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55725422 });
55735423 _ = try self.addInst(.{
55745424 .tag = .mov_mem_imm,
5575 .ops = (Mir.Ops(Register, Register){
5425 .ops = Mir.Inst.Ops.encode(.{
55765426 .reg1 = base_reg,
55775427 .flags = 0b10,
5578 }).encode(),
5428 }),
55795429 .data = .{ .payload = payload },
55805430 });
55815431 }
......@@ -5586,10 +5436,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55865436 });
55875437 _ = try self.addInst(.{
55885438 .tag = .mov_mem_imm,
5589 .ops = (Mir.Ops(Register, Register){
5439 .ops = Mir.Inst.Ops.encode(.{
55905440 .reg1 = base_reg,
55915441 .flags = 0b10,
5592 }).encode(),
5442 }),
55935443 .data = .{ .payload = payload },
55945444 });
55955445 }
......@@ -5605,65 +5455,64 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
56055455 }
56065456
56075457 const base_reg = opts.dest_stack_base orelse .rbp;
5608 if (!math.isPowerOfTwo(abi_size)) {
5609 const reg_lock = self.register_manager.lockReg(reg);
5610 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
5611
5612 const tmp_reg = try self.copyToTmpRegister(ty, mcv);
5613
5614 var next_offset = stack_offset;
5615 var remainder = abi_size;
5616 while (remainder > 0) {
5617 const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder));
5618
5619 _ = try self.addInst(.{
5620 .tag = .mov,
5621 .ops = (Mir.Ops(Register, Register){
5622 .reg1 = base_reg,
5623 .reg2 = registerAlias(tmp_reg, nearest_power_of_two),
5624 .flags = 0b10,
5625 }).encode(),
5626 .data = .{ .imm = @bitCast(u32, -next_offset) },
5627 });
56285458
5629 if (nearest_power_of_two > 1) {
5630 try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 });
5631 }
5632
5633 remainder -= nearest_power_of_two;
5634 next_offset -= nearest_power_of_two;
5635 }
5636 } else {
5637 _ = try self.addInst(.{
5638 .tag = .mov,
5639 .ops = (Mir.Ops(Register, Register){
5640 .reg1 = base_reg,
5641 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
5642 .flags = 0b10,
5643 }).encode(),
5644 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5645 });
5646 }
5647 },
5648 .avx_register => |reg| {
5649 const base_reg = opts.dest_stack_base orelse .rbp;
56505459 switch (ty.zigTypeTag()) {
56515460 .Float => switch (ty.tag()) {
5652 .f32 => return self.fail("TODO genSetStack for AVX register for f32", .{}),
5461 .f32 => return self.fail("TODO genSetStack for register for f32", .{}),
56535462 .f64 => {
56545463 _ = try self.addInst(.{
56555464 .tag = .mov_f64,
5656 .ops = (Mir.Ops(Register, AvxRegister){
5465 .ops = Mir.Inst.Ops.encode(.{
56575466 .reg1 = base_reg,
56585467 .reg2 = reg.to128(),
56595468 .flags = 0b01,
5660 }).encode(),
5469 }),
56615470 .data = .{ .imm = @bitCast(u32, -stack_offset) },
56625471 });
56635472 },
5664 else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}),
5473 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
5474 },
5475 else => {
5476 if (!math.isPowerOfTwo(abi_size)) {
5477 const reg_lock = self.register_manager.lockReg(reg);
5478 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
5479
5480 const tmp_reg = try self.copyToTmpRegister(ty, mcv);
5481
5482 var next_offset = stack_offset;
5483 var remainder = abi_size;
5484 while (remainder > 0) {
5485 const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder));
5486
5487 _ = try self.addInst(.{
5488 .tag = .mov,
5489 .ops = Mir.Inst.Ops.encode(.{
5490 .reg1 = base_reg,
5491 .reg2 = registerAlias(tmp_reg, nearest_power_of_two),
5492 .flags = 0b10,
5493 }),
5494 .data = .{ .imm = @bitCast(u32, -next_offset) },
5495 });
5496
5497 if (nearest_power_of_two > 1) {
5498 try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 });
5499 }
5500
5501 remainder -= nearest_power_of_two;
5502 next_offset -= nearest_power_of_two;
5503 }
5504 } else {
5505 _ = try self.addInst(.{
5506 .tag = .mov,
5507 .ops = Mir.Inst.Ops.encode(.{
5508 .reg1 = base_reg,
5509 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
5510 .flags = 0b10,
5511 }),
5512 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5513 });
5514 }
56655515 },
5666 else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}),
56675516 }
56685517 },
56695518 .memory,
......@@ -5742,20 +5591,20 @@ fn genInlineMemcpy(
57425591 .ptr_stack_offset, .stack_offset => |off| {
57435592 _ = try self.addInst(.{
57445593 .tag = .lea,
5745 .ops = (Mir.Ops(Register, Register){
5594 .ops = Mir.Inst.Ops.encode(.{
57465595 .reg1 = dst_addr_reg.to64(),
57475596 .reg2 = opts.dest_stack_base orelse .rbp,
5748 }).encode(),
5597 }),
57495598 .data = .{ .imm = @bitCast(u32, -off) },
57505599 });
57515600 },
57525601 .register => |reg| {
57535602 _ = try self.addInst(.{
57545603 .tag = .mov,
5755 .ops = (Mir.Ops(Register, Register){
5604 .ops = Mir.Inst.Ops.encode(.{
57565605 .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)),
57575606 .reg2 = reg,
5758 }).encode(),
5607 }),
57595608 .data = undefined,
57605609 });
57615610 },
......@@ -5777,20 +5626,20 @@ fn genInlineMemcpy(
57775626 .ptr_stack_offset, .stack_offset => |off| {
57785627 _ = try self.addInst(.{
57795628 .tag = .lea,
5780 .ops = (Mir.Ops(Register, Register){
5629 .ops = Mir.Inst.Ops.encode(.{
57815630 .reg1 = src_addr_reg.to64(),
57825631 .reg2 = opts.source_stack_base orelse .rbp,
5783 }).encode(),
5632 }),
57845633 .data = .{ .imm = @bitCast(u32, -off) },
57855634 });
57865635 },
57875636 .register => |reg| {
57885637 _ = try self.addInst(.{
57895638 .tag = .mov,
5790 .ops = (Mir.Ops(Register, Register){
5639 .ops = Mir.Inst.Ops.encode(.{
57915640 .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)),
57925641 .reg2 = reg,
5793 }).encode(),
5642 }),
57945643 .data = undefined,
57955644 });
57965645 },
......@@ -5810,18 +5659,14 @@ fn genInlineMemcpy(
58105659 // mov rcx, 0
58115660 _ = try self.addInst(.{
58125661 .tag = .mov,
5813 .ops = (Mir.Ops(Register, Register){
5814 .reg1 = .rcx,
5815 }).encode(),
5662 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
58165663 .data = .{ .imm = 0 },
58175664 });
58185665
58195666 // mov rax, 0
58205667 _ = try self.addInst(.{
58215668 .tag = .mov,
5822 .ops = (Mir.Ops(Register, Register){
5823 .reg1 = .rax,
5824 }).encode(),
5669 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
58255670 .data = .{ .imm = 0 },
58265671 });
58275672
......@@ -5829,70 +5674,62 @@ fn genInlineMemcpy(
58295674 // cmp count, 0
58305675 const loop_start = try self.addInst(.{
58315676 .tag = .cmp,
5832 .ops = (Mir.Ops(Register, Register){
5833 .reg1 = count_reg,
5834 }).encode(),
5677 .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }),
58355678 .data = .{ .imm = 0 },
58365679 });
58375680
58385681 // je end
58395682 const loop_reloc = try self.addInst(.{
58405683 .tag = .cond_jmp_eq_ne,
5841 .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(),
5684 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
58425685 .data = .{ .inst = undefined },
58435686 });
58445687
58455688 // mov tmp, [addr + rcx]
58465689 _ = try self.addInst(.{
58475690 .tag = .mov_scale_src,
5848 .ops = (Mir.Ops(Register, Register){
5691 .ops = Mir.Inst.Ops.encode(.{
58495692 .reg1 = tmp_reg.to8(),
58505693 .reg2 = src_addr_reg,
5851 }).encode(),
5694 }),
58525695 .data = .{ .imm = 0 },
58535696 });
58545697
58555698 // mov [stack_offset + rax], tmp
58565699 _ = try self.addInst(.{
58575700 .tag = .mov_scale_dst,
5858 .ops = (Mir.Ops(Register, Register){
5701 .ops = Mir.Inst.Ops.encode(.{
58595702 .reg1 = dst_addr_reg,
58605703 .reg2 = tmp_reg.to8(),
5861 }).encode(),
5704 }),
58625705 .data = .{ .imm = 0 },
58635706 });
58645707
58655708 // add rcx, 1
58665709 _ = try self.addInst(.{
58675710 .tag = .add,
5868 .ops = (Mir.Ops(Register, Register){
5869 .reg1 = .rcx,
5870 }).encode(),
5711 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
58715712 .data = .{ .imm = 1 },
58725713 });
58735714
58745715 // add rax, 1
58755716 _ = try self.addInst(.{
58765717 .tag = .add,
5877 .ops = (Mir.Ops(Register, Register){
5878 .reg1 = .rax,
5879 }).encode(),
5718 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
58805719 .data = .{ .imm = 1 },
58815720 });
58825721
58835722 // sub count, 1
58845723 _ = try self.addInst(.{
58855724 .tag = .sub,
5886 .ops = (Mir.Ops(Register, Register){
5887 .reg1 = count_reg,
5888 }).encode(),
5725 .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }),
58895726 .data = .{ .imm = 1 },
58905727 });
58915728
58925729 // jmp loop
58935730 _ = try self.addInst(.{
58945731 .tag = .jmp,
5895 .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(),
5732 .ops = Mir.Inst.Ops.encode(.{}),
58965733 .data = .{ .inst = loop_start },
58975734 });
58985735
......@@ -5924,20 +5761,20 @@ fn genInlineMemset(
59245761 .ptr_stack_offset, .stack_offset => |off| {
59255762 _ = try self.addInst(.{
59265763 .tag = .lea,
5927 .ops = (Mir.Ops(Register, Register){
5764 .ops = Mir.Inst.Ops.encode(.{
59285765 .reg1 = addr_reg.to64(),
59295766 .reg2 = opts.dest_stack_base orelse .rbp,
5930 }).encode(),
5767 }),
59315768 .data = .{ .imm = @bitCast(u32, -off) },
59325769 });
59335770 },
59345771 .register => |reg| {
59355772 _ = try self.addInst(.{
59365773 .tag = .mov,
5937 .ops = (Mir.Ops(Register, Register){
5774 .ops = Mir.Inst.Ops.encode(.{
59385775 .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)),
59395776 .reg2 = reg,
5940 }).encode(),
5777 }),
59415778 .data = undefined,
59425779 });
59435780 },
......@@ -5955,16 +5792,14 @@ fn genInlineMemset(
59555792 // cmp rax, -1
59565793 const loop_start = try self.addInst(.{
59575794 .tag = .cmp,
5958 .ops = (Mir.Ops(Register, Register){
5959 .reg1 = .rax,
5960 }).encode(),
5795 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
59615796 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },
59625797 });
59635798
59645799 // je end
59655800 const loop_reloc = try self.addInst(.{
59665801 .tag = .cond_jmp_eq_ne,
5967 .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(),
5802 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
59685803 .data = .{ .inst = undefined },
59695804 });
59705805
......@@ -5980,9 +5815,7 @@ fn genInlineMemset(
59805815 });
59815816 _ = try self.addInst(.{
59825817 .tag = .mov_mem_index_imm,
5983 .ops = (Mir.Ops(Register, Register){
5984 .reg1 = addr_reg,
5985 }).encode(),
5818 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),
59865819 .data = .{ .payload = payload },
59875820 });
59885821 },
......@@ -5992,16 +5825,14 @@ fn genInlineMemset(
59925825 // sub rax, 1
59935826 _ = try self.addInst(.{
59945827 .tag = .sub,
5995 .ops = (Mir.Ops(Register, Register){
5996 .reg1 = .rax,
5997 }).encode(),
5828 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
59985829 .data = .{ .imm = 1 },
59995830 });
60005831
60015832 // jmp loop
60025833 _ = try self.addInst(.{
60035834 .tag = .jmp,
6004 .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(),
5835 .ops = Mir.Inst.Ops.encode(.{}),
60055836 .data = .{ .inst = loop_start },
60065837 });
60075838
......@@ -6009,88 +5840,6 @@ fn genInlineMemset(
60095840 try self.performReloc(loop_reloc);
60105841}
60115842
6012fn genSetAvxReg(self: *Self, ty: Type, reg: AvxRegister, mcv: MCValue) InnerError!void {
6013 switch (mcv) {
6014 .dead => unreachable,
6015 .register_overflow_unsigned,
6016 .register_overflow_signed,
6017 => unreachable,
6018 .stack_offset => |off| {
6019 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
6020 return self.fail("stack offset too large", .{});
6021 }
6022
6023 switch (ty.zigTypeTag()) {
6024 .Float => {
6025 switch (ty.tag()) {
6026 .f32 => return self.fail("TODO genSetAvxReg from stack offset for f32", .{}),
6027 .f64 => {
6028 _ = try self.addInst(.{
6029 .tag = .mov_f64,
6030 .ops = (Mir.Ops(AvxRegister, Register){
6031 .reg1 = reg.to128(),
6032 .reg2 = .rbp,
6033 }).encode(),
6034 .data = .{ .imm = @bitCast(u32, -off) },
6035 });
6036 },
6037 else => return self.fail("TODO genSetAvxReg from stack offset for {}", .{ty.fmtDebug()}),
6038 }
6039 },
6040 else => return self.fail("TODO genSetAvxReg from stack offset for type {}", .{ty.fmtDebug()}),
6041 }
6042 },
6043 .avx_register => |src_reg| {
6044 switch (ty.zigTypeTag()) {
6045 .Float => {
6046 switch (ty.tag()) {
6047 .f32 => return self.fail("TODO genSetAvxReg from register for f32", .{}),
6048 .f64 => {
6049 _ = try self.addInst(.{
6050 .tag = .mov_f64,
6051 .ops = (Mir.Ops(AvxRegister, AvxRegister){
6052 .reg1 = reg.to128(),
6053 .reg2 = src_reg.to128(),
6054 .flags = 0b10,
6055 }).encode(),
6056 .data = undefined,
6057 });
6058 },
6059 else => return self.fail("TODO genSetAvxReg from register for {}", .{ty.fmtDebug()}),
6060 }
6061 },
6062 else => return self.fail("TODO genSetAvxReg from register for type {}", .{ty.fmtDebug()}),
6063 }
6064 },
6065 .memory => {
6066 switch (ty.zigTypeTag()) {
6067 .Float => {
6068 switch (ty.tag()) {
6069 .f32 => return self.fail("TODO genSetAvxReg from memory for f32", .{}),
6070 .f64 => {
6071 const base_reg = try self.register_manager.allocReg(null);
6072 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
6073 _ = try self.addInst(.{
6074 .tag = .mov_f64,
6075 .ops = (Mir.Ops(AvxRegister, Register){
6076 .reg1 = reg.to128(),
6077 .reg2 = base_reg.to64(),
6078 }).encode(),
6079 .data = .{ .imm = 0 },
6080 });
6081 },
6082 else => return self.fail("TODO genSetAvxReg from memory for {}", .{ty.fmtDebug()}),
6083 }
6084 },
6085 else => return self.fail("TODO genSetAvxReg from memory for type {}", .{ty.fmtDebug()}),
6086 }
6087 },
6088 else => |other| {
6089 return self.fail("TODO genSetAvxReg from {}", .{other});
6090 },
6091 }
6092}
6093
60945843fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
60955844 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
60965845 switch (mcv) {
......@@ -6098,17 +5847,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
60985847 .register_overflow_unsigned,
60995848 .register_overflow_signed,
61005849 => unreachable,
6101 .avx_register => unreachable,
61025850 .ptr_stack_offset => |off| {
61035851 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
61045852 return self.fail("stack offset too large", .{});
61055853 }
61065854 _ = try self.addInst(.{
61075855 .tag = .lea,
6108 .ops = (Mir.Ops(Register, Register){
5856 .ops = Mir.Inst.Ops.encode(.{
61095857 .reg1 = registerAlias(reg, abi_size),
61105858 .reg2 = .rbp,
6111 }).encode(),
5859 }),
61125860 .data = .{ .imm = @bitCast(u32, -off) },
61135861 });
61145862 },
......@@ -6145,10 +5893,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61455893 };
61465894 _ = try self.addInst(.{
61475895 .tag = tag,
6148 .ops = (Mir.Ops(Register, Register){
5896 .ops = Mir.Inst.Ops.encode(.{
61495897 .reg1 = reg.to8(),
61505898 .flags = flags,
6151 }).encode(),
5899 }),
61525900 .data = undefined,
61535901 });
61545902 },
......@@ -6158,10 +5906,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61585906 if (x == 0) {
61595907 _ = try self.addInst(.{
61605908 .tag = .xor,
6161 .ops = (Mir.Ops(Register, Register){
5909 .ops = Mir.Inst.Ops.encode(.{
61625910 .reg1 = reg.to32(),
61635911 .reg2 = reg.to32(),
6164 }).encode(),
5912 }),
61655913 .data = undefined,
61665914 });
61675915 return;
......@@ -6170,9 +5918,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61705918 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
61715919 _ = try self.addInst(.{
61725920 .tag = .mov,
6173 .ops = (Mir.Ops(Register, Register){
6174 .reg1 = registerAlias(reg, abi_size),
6175 }).encode(),
5921 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }),
61765922 .data = .{ .imm = @truncate(u32, x) },
61775923 });
61785924 return;
......@@ -6187,9 +5933,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61875933 const payload = try self.addExtra(Mir.Imm64.encode(x));
61885934 _ = try self.addInst(.{
61895935 .tag = .movabs,
6190 .ops = (Mir.Ops(Register, Register){
6191 .reg1 = reg.to64(),
6192 }).encode(),
5936 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }),
61935937 .data = .{ .payload = payload },
61945938 });
61955939 },
......@@ -6198,40 +5942,60 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61985942 if (src_reg.id() == reg.id())
61995943 return;
62005944
6201 if (ty.zigTypeTag() == .Int) blk: {
6202 switch (ty.intInfo(self.target.*).signedness) {
5945 switch (ty.zigTypeTag()) {
5946 .Int => switch (ty.intInfo(self.target.*).signedness) {
62035947 .signed => {
6204 if (abi_size > 4) break :blk;
6205 _ = try self.addInst(.{
6206 .tag = .mov_sign_extend,
6207 .ops = (Mir.Ops(Register, Register){
6208 .reg1 = reg.to64(),
6209 .reg2 = registerAlias(src_reg, abi_size),
6210 }).encode(),
6211 .data = undefined,
6212 });
5948 if (abi_size <= 4) {
5949 _ = try self.addInst(.{
5950 .tag = .mov_sign_extend,
5951 .ops = Mir.Inst.Ops.encode(.{
5952 .reg1 = reg.to64(),
5953 .reg2 = registerAlias(src_reg, abi_size),
5954 }),
5955 .data = undefined,
5956 });
5957 return;
5958 }
62135959 },
62145960 .unsigned => {
6215 if (abi_size > 2) break :blk;
5961 if (abi_size <= 2) {
5962 _ = try self.addInst(.{
5963 .tag = .mov_zero_extend,
5964 .ops = Mir.Inst.Ops.encode(.{
5965 .reg1 = reg.to64(),
5966 .reg2 = registerAlias(src_reg, abi_size),
5967 }),
5968 .data = undefined,
5969 });
5970 return;
5971 }
5972 },
5973 },
5974 .Float => switch (ty.tag()) {
5975 .f32 => return self.fail("TODO genSetReg from register for f32", .{}),
5976 .f64 => {
62165977 _ = try self.addInst(.{
6217 .tag = .mov_zero_extend,
6218 .ops = (Mir.Ops(Register, Register){
6219 .reg1 = reg.to64(),
6220 .reg2 = registerAlias(src_reg, abi_size),
6221 }).encode(),
5978 .tag = .mov_f64,
5979 .ops = Mir.Inst.Ops.encode(.{
5980 .reg1 = reg.to128(),
5981 .reg2 = src_reg.to128(),
5982 .flags = 0b10,
5983 }),
62225984 .data = undefined,
62235985 });
5986 return;
62245987 },
6225 }
6226 return;
5988 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
5989 },
5990 else => {},
62275991 }
62285992
62295993 _ = try self.addInst(.{
62305994 .tag = .mov,
6231 .ops = (Mir.Ops(Register, Register){
5995 .ops = Mir.Inst.Ops.encode(.{
62325996 .reg1 = registerAlias(reg, abi_size),
62335997 .reg2 = registerAlias(src_reg, abi_size),
6234 }).encode(),
5998 }),
62355999 .data = undefined,
62366000 });
62376001 },
......@@ -6241,107 +6005,148 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
62416005 try self.loadMemPtrIntoRegister(reg, Type.usize, mcv);
62426006 _ = try self.addInst(.{
62436007 .tag = .mov,
6244 .ops = (Mir.Ops(Register, Register){
6008 .ops = Mir.Inst.Ops.encode(.{
62456009 .reg1 = registerAlias(reg, abi_size),
62466010 .reg2 = reg.to64(),
62476011 .flags = 0b01,
6248 }).encode(),
6012 }),
62496013 .data = .{ .imm = 0 },
62506014 });
62516015 },
6252 .memory => |x| {
6253 if (x <= math.maxInt(i32)) {
6254 // mov reg, [ds:imm32]
6255 _ = try self.addInst(.{
6256 .tag = .mov,
6257 .ops = (Mir.Ops(Register, Register){
6258 .reg1 = registerAlias(reg, abi_size),
6259 .flags = 0b01,
6260 }).encode(),
6261 .data = .{ .imm = @truncate(u32, x) },
6262 });
6263 } else {
6264 // If this is RAX, we can use a direct load.
6265 // Otherwise, we need to load the address, then indirectly load the value.
6266 if (reg.id() == 0) {
6267 // movabs rax, ds:moffs64
6268 const payload = try self.addExtra(Mir.Imm64.encode(x));
6269 _ = try self.addInst(.{
6270 .tag = .movabs,
6271 .ops = (Mir.Ops(Register, Register){
6272 .reg1 = .rax,
6273 .flags = 0b01, // imm64 will become moffs64
6274 }).encode(),
6275 .data = .{ .payload = payload },
6276 });
6277 } else {
6278 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
6279 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
6280
6281 // mov reg, [reg + 0x0]
6016 .memory => |x| switch (ty.zigTypeTag()) {
6017 .Float => {
6018 switch (ty.tag()) {
6019 .f32 => return self.fail("TODO genSetReg from memory for f32", .{}),
6020 .f64 => {
6021 const base_reg = try self.register_manager.allocReg(null);
6022 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
6023 _ = try self.addInst(.{
6024 .tag = .mov_f64,
6025 .ops = Mir.Inst.Ops.encode(.{
6026 .reg1 = reg.to128(),
6027 .reg2 = base_reg.to64(),
6028 }),
6029 .data = .{ .imm = 0 },
6030 });
6031 },
6032 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
6033 }
6034 },
6035 else => {
6036 if (x <= math.maxInt(i32)) {
6037 // mov reg, [ds:imm32]
62826038 _ = try self.addInst(.{
62836039 .tag = .mov,
6284 .ops = (Mir.Ops(Register, Register){
6040 .ops = Mir.Inst.Ops.encode(.{
62856041 .reg1 = registerAlias(reg, abi_size),
6286 .reg2 = reg.to64(),
62876042 .flags = 0b01,
6288 }).encode(),
6289 .data = .{ .imm = 0 },
6043 }),
6044 .data = .{ .imm = @truncate(u32, x) },
62906045 });
6046 } else {
6047 // If this is RAX, we can use a direct load.
6048 // Otherwise, we need to load the address, then indirectly load the value.
6049 if (reg.id() == 0) {
6050 // movabs rax, ds:moffs64
6051 const payload = try self.addExtra(Mir.Imm64.encode(x));
6052 _ = try self.addInst(.{
6053 .tag = .movabs,
6054 .ops = Mir.Inst.Ops.encode(.{
6055 .reg1 = .rax,
6056 .flags = 0b01, // imm64 will become moffs64
6057 }),
6058 .data = .{ .payload = payload },
6059 });
6060 } else {
6061 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
6062 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
6063
6064 // mov reg, [reg + 0x0]
6065 _ = try self.addInst(.{
6066 .tag = .mov,
6067 .ops = Mir.Inst.Ops.encode(.{
6068 .reg1 = registerAlias(reg, abi_size),
6069 .reg2 = reg.to64(),
6070 .flags = 0b01,
6071 }),
6072 .data = .{ .imm = 0 },
6073 });
6074 }
62916075 }
6292 }
6076 },
62936077 },
62946078 .stack_offset => |off| {
62956079 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
62966080 return self.fail("stack offset too large", .{});
62976081 }
62986082
6299 if (ty.zigTypeTag() == .Int) blk: {
6300 switch (ty.intInfo(self.target.*).signedness) {
6083 switch (ty.zigTypeTag()) {
6084 .Int => switch (ty.intInfo(self.target.*).signedness) {
63016085 .signed => {
6302 const flags: u2 = switch (abi_size) {
6303 1 => 0b01,
6304 2 => 0b10,
6305 4 => 0b11,
6306 else => break :blk,
6307 };
6308 _ = try self.addInst(.{
6309 .tag = .mov_sign_extend,
6310 .ops = (Mir.Ops(Register, Register){
6311 .reg1 = reg.to64(),
6312 .reg2 = .rbp,
6313 .flags = flags,
6314 }).encode(),
6315 .data = .{ .imm = @bitCast(u32, -off) },
6316 });
6086 if (abi_size <= 4) {
6087 const flags: u2 = switch (abi_size) {
6088 1 => 0b01,
6089 2 => 0b10,
6090 4 => 0b11,
6091 else => unreachable,
6092 };
6093 _ = try self.addInst(.{
6094 .tag = .mov_sign_extend,
6095 .ops = Mir.Inst.Ops.encode(.{
6096 .reg1 = reg.to64(),
6097 .reg2 = .rbp,
6098 .flags = flags,
6099 }),
6100 .data = .{ .imm = @bitCast(u32, -off) },
6101 });
6102 return;
6103 }
63176104 },
63186105 .unsigned => {
6319 const flags: u2 = switch (abi_size) {
6320 1 => 0b01,
6321 2 => 0b10,
6322 else => break :blk,
6323 };
6106 if (abi_size <= 2) {
6107 const flags: u2 = switch (abi_size) {
6108 1 => 0b01,
6109 2 => 0b10,
6110 else => unreachable,
6111 };
6112 _ = try self.addInst(.{
6113 .tag = .mov_zero_extend,
6114 .ops = Mir.Inst.Ops.encode(.{
6115 .reg1 = reg.to64(),
6116 .reg2 = .rbp,
6117 .flags = flags,
6118 }),
6119 .data = .{ .imm = @bitCast(u32, -off) },
6120 });
6121 return;
6122 }
6123 },
6124 },
6125 .Float => switch (ty.tag()) {
6126 .f32 => return self.fail("TODO genSetReg from stack offset for f32", .{}),
6127 .f64 => {
63246128 _ = try self.addInst(.{
6325 .tag = .mov_zero_extend,
6326 .ops = (Mir.Ops(Register, Register){
6327 .reg1 = reg.to64(),
6129 .tag = .mov_f64,
6130 .ops = Mir.Inst.Ops.encode(.{
6131 .reg1 = reg.to128(),
63286132 .reg2 = .rbp,
6329 .flags = flags,
6330 }).encode(),
6133 }),
63316134 .data = .{ .imm = @bitCast(u32, -off) },
63326135 });
6136 return;
63336137 },
6334 }
6335 return;
6138 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
6139 },
6140 else => {},
63366141 }
63376142
63386143 _ = try self.addInst(.{
63396144 .tag = .mov,
6340 .ops = (Mir.Ops(Register, Register){
6145 .ops = Mir.Inst.Ops.encode(.{
63416146 .reg1 = registerAlias(reg, abi_size),
63426147 .reg2 = .rbp,
63436148 .flags = 0b01,
6344 }).encode(),
6149 }),
63456150 .data = .{ .imm = @bitCast(u32, -off) },
63466151 });
63476152 },
......@@ -6408,14 +6213,14 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
64086213 };
64096214 _ = try self.addInst(.{
64106215 .tag = .fld,
6411 .ops = (Mir.Ops(Register, Register){
6216 .ops = Mir.Inst.Ops.encode(.{
6217 .reg1 = .rbp,
64126218 .flags = switch (src_ty.abiSize(self.target.*)) {
64136219 4 => 0b01,
64146220 8 => 0b10,
64156221 else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}),
64166222 },
6417 .reg1 = .rbp,
6418 }).encode(),
6223 }),
64196224 .data = .{ .imm = @bitCast(u32, -stack_offset) },
64206225 });
64216226
......@@ -6423,15 +6228,15 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
64236228 const stack_dst = try self.allocRegOrMem(inst, false);
64246229 _ = try self.addInst(.{
64256230 .tag = .fisttp,
6426 .ops = (Mir.Ops(Register, Register){
6231 .ops = Mir.Inst.Ops.encode(.{
6232 .reg1 = .rbp,
64276233 .flags = switch (dst_ty.abiSize(self.target.*)) {
64286234 1...2 => 0b00,
64296235 3...4 => 0b01,
64306236 5...8 => 0b10,
64316237 else => |size| return self.fail("TODO convert float with abiSize={}", .{size}),
64326238 },
6433 .reg1 = .rbp,
6434 }).encode(),
6239 }),
64356240 .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) },
64366241 });
64376242
......@@ -6527,11 +6332,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
65276332 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
65286333 _ = try self.addInst(.{
65296334 .tag = .mov,
6530 .ops = (Mir.Ops(Register, Register){
6335 .ops = Mir.Inst.Ops.encode(.{
65316336 .reg1 = reg,
65326337 .reg2 = reg,
65336338 .flags = 0b01,
6534 }).encode(),
6339 }),
65356340 .data = .{ .imm = 0 },
65366341 });
65376342 break :blk MCValue{ .register = reg };
......@@ -7095,22 +6900,11 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
70956900 return reg.to32();
70966901 } else if (size_bytes <= 8) {
70976902 return reg.to64();
7098 } else {
7099 unreachable;
7100 }
7101}
7102
7103/// Returns AVX register wide enough to hold at least `size_bytes`.
7104fn avxRegisterAlias(reg: AvxRegister, size_bytes: u32) AvxRegister {
7105 if (size_bytes == 0) {
7106 unreachable; // should be comptime known
71076903 } else if (size_bytes <= 16) {
71086904 return reg.to128();
71096905 } else if (size_bytes <= 32) {
71106906 return reg.to256();
7111 } else {
7112 unreachable;
7113 }
6907 } else unreachable;
71146908}
71156909
71166910/// Truncates the value in the register in place.
src/arch/x86_64/Emit.zig+383-389
......@@ -25,9 +25,8 @@ const MCValue = @import("CodeGen.zig").MCValue;
2525const Mir = @import("Mir.zig");
2626const Module = @import("../../Module.zig");
2727const Instruction = bits.Instruction;
28const GpRegister = bits.Register;
29const AvxRegister = bits.AvxRegister;
3028const Type = @import("../../type.zig").Type;
29const Register = bits.Register;
3130
3231mir: Mir,
3332bin_file: *link.File,
......@@ -238,7 +237,7 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
238237fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
239238 const tag = emit.mir.instructions.items(.tag)[inst];
240239 assert(tag == .interrupt);
241 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
240 const ops = emit.mir.instructions.items(.ops)[inst].decode();
242241 switch (ops.flags) {
243242 0b00 => return lowerToZoEnc(.int3, emit.code),
244243 else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}),
......@@ -254,11 +253,11 @@ fn mirSyscall(emit: *Emit) InnerError!void {
254253}
255254
256255fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
257 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
256 const ops = emit.mir.instructions.items(.ops)[inst].decode();
258257 switch (ops.flags) {
259258 0b00 => {
260259 // PUSH/POP reg
261 return lowerToOEnc(tag, Register.reg(ops.reg1), emit.code);
260 return lowerToOEnc(tag, ops.reg1, emit.code);
262261 },
263262 0b01 => {
264263 // PUSH/POP r/m64
......@@ -283,7 +282,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
283282}
284283
285284fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
286 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
285 const ops = emit.mir.instructions.items(.ops)[inst].decode();
287286 const payload = emit.mir.instructions.items(.data)[inst].payload;
288287 const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data;
289288 const regs = data.regs;
......@@ -294,9 +293,9 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
294293 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
295294 .disp = @bitCast(u32, -@intCast(i32, disp)),
296295 .base = ops.reg1,
297 }), Register.reg(reg.to64()), emit.code);
296 }), reg.to64(), emit.code);
298297 } else {
299 try lowerToRmEnc(.mov, Register.reg(reg.to64()), RegisterOrMemory.mem(.qword_ptr, .{
298 try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{
300299 .disp = @bitCast(u32, -@intCast(i32, disp)),
301300 .base = ops.reg1,
302301 }), emit.code);
......@@ -306,7 +305,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
306305}
307306
308307fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
309 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
308 const ops = emit.mir.instructions.items(.ops)[inst].decode();
310309 switch (ops.flags) {
311310 0b00 => {
312311 const target = emit.mir.instructions.items(.data)[inst].inst;
......@@ -335,7 +334,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
335334 0b10 => {
336335 // JMP/CALL r/m64
337336 const imm = emit.mir.instructions.items(.data)[inst].imm;
338 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
337 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
339338 .disp = imm,
340339 .base = ops.reg1,
341340 }), emit.code);
......@@ -345,7 +344,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
345344}
346345
347346fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
348 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
347 const ops = emit.mir.instructions.items(.ops)[inst].decode();
349348 const target = emit.mir.instructions.items(.data)[inst].inst;
350349 const tag = switch (mir_tag) {
351350 .cond_jmp_greater_less => switch (ops.flags) {
......@@ -377,7 +376,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr
377376}
378377
379378fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
380 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
379 const ops = emit.mir.instructions.items(.ops)[inst].decode();
381380 const tag = switch (mir_tag) {
382381 .cond_set_byte_greater_less => switch (ops.flags) {
383382 0b00 => Tag.setge,
......@@ -407,9 +406,9 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
407406}
408407
409408fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
410 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
409 const ops = emit.mir.instructions.items(.ops)[inst].decode();
411410 if (ops.flags == 0b00) {
412 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code);
411 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
413412 }
414413 const imm = emit.mir.instructions.items(.data)[inst].imm;
415414 const ptr_size: Memory.PtrSize = switch (ops.flags) {
......@@ -418,7 +417,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
418417 0b10 => .dword_ptr,
419418 0b11 => .qword_ptr,
420419 };
421 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(ptr_size, .{
420 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{
422421 .disp = imm,
423422 .base = ops.reg2,
424423 }), emit.code);
......@@ -427,7 +426,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
427426fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
428427 const tag = emit.mir.instructions.items(.tag)[inst];
429428 assert(tag == .@"test");
430 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
429 const ops = emit.mir.instructions.items(.ops)[inst].decode();
431430 switch (ops.flags) {
432431 0b00 => {
433432 if (ops.reg2 == .none) {
......@@ -442,12 +441,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
442441 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
443442 }
444443 // TEST r/m64, r64
445 return lowerToMrEnc(
446 .@"test",
447 RegisterOrMemory.reg(ops.reg1),
448 Register.reg(ops.reg2),
449 emit.code,
450 );
444 return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code);
451445 },
452446 else => return emit.fail("TODO more TEST alternatives", .{}),
453447 }
......@@ -456,7 +450,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
456450fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
457451 const tag = emit.mir.instructions.items(.tag)[inst];
458452 assert(tag == .ret);
459 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
453 const ops = emit.mir.instructions.items(.ops)[inst].decode();
460454 switch (ops.flags) {
461455 0b00 => {
462456 // RETF imm16
......@@ -480,7 +474,7 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
480474}
481475
482476fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
483 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
477 const ops = emit.mir.instructions.items(.ops)[inst].decode();
484478 switch (ops.flags) {
485479 0b00 => {
486480 if (ops.reg2 == .none) {
......@@ -491,14 +485,14 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
491485 }
492486 // mov reg1, reg2
493487 // RM
494 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code);
488 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
495489 },
496490 0b01 => {
497491 // mov reg1, [reg2 + imm32]
498492 // RM
499493 const imm = emit.mir.instructions.items(.data)[inst].imm;
500 const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2;
501 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
494 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
495 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
502496 .disp = imm,
503497 .base = src_reg,
504498 }), emit.code);
......@@ -510,10 +504,10 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
510504 // mov [reg1 + imm32], reg2
511505 // MR
512506 const imm = emit.mir.instructions.items(.data)[inst].imm;
513 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
507 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
514508 .disp = imm,
515509 .base = ops.reg1,
516 }), Register.reg(ops.reg2), emit.code);
510 }), ops.reg2, emit.code);
517511 },
518512 0b11 => {
519513 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});
......@@ -522,7 +516,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
522516}
523517
524518fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
525 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
519 const ops = emit.mir.instructions.items(.ops)[inst].decode();
526520 assert(ops.reg2 == .none);
527521 const payload = emit.mir.instructions.items(.data)[inst].payload;
528522 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
......@@ -539,19 +533,14 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
539533}
540534
541535inline fn setRexWRegister(reg: Register) bool {
542 switch (reg) {
543 .avx_register => return false,
544 .register => |r| {
545 if (r.size() == 64) return true;
546 return switch (r) {
547 .ah, .bh, .ch, .dh => true,
548 else => false,
549 };
550 },
551 }
536 if (reg.size() == 64) return true;
537 return switch (reg) {
538 .ah, .ch, .dh, .bh => true,
539 else => false,
540 };
552541}
553542
554inline fn immOpSize(u_imm: u32) u8 {
543inline fn immOpSize(u_imm: u32) u6 {
555544 const imm = @bitCast(i32, u_imm);
556545 if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
557546 return 8;
......@@ -563,7 +552,7 @@ inline fn immOpSize(u_imm: u32) u8 {
563552}
564553
565554fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
566 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
555 const ops = emit.mir.instructions.items(.ops)[inst].decode();
567556 const scale = ops.flags;
568557 const imm = emit.mir.instructions.items(.data)[inst].imm;
569558 // OP reg1, [reg2 + scale*rcx + imm32]
......@@ -571,7 +560,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
571560 .scale = scale,
572561 .index = .rcx,
573562 };
574 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
563 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
575564 .disp = imm,
576565 .base = ops.reg2,
577566 .scale_index = scale_index,
......@@ -579,7 +568,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
579568}
580569
581570fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
582 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
571 const ops = emit.mir.instructions.items(.ops)[inst].decode();
583572 const scale = ops.flags;
584573 const imm = emit.mir.instructions.items(.data)[inst].imm;
585574 const scale_index = ScaleIndex{
......@@ -595,15 +584,15 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
595584 }), imm, emit.code);
596585 }
597586 // OP [reg1 + scale*rax + imm32], reg2
598 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{
587 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
599588 .disp = imm,
600589 .base = ops.reg1,
601590 .scale_index = scale_index,
602 }), Register.reg(ops.reg2), emit.code);
591 }), ops.reg2, emit.code);
603592}
604593
605594fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
606 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
595 const ops = emit.mir.instructions.items(.ops)[inst].decode();
607596 const scale = ops.flags;
608597 const payload = emit.mir.instructions.items(.data)[inst].payload;
609598 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
......@@ -620,7 +609,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
620609}
621610
622611fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
623 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
612 const ops = emit.mir.instructions.items(.ops)[inst].decode();
624613 assert(ops.reg2 == .none);
625614 const payload = emit.mir.instructions.items(.data)[inst].payload;
626615 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
......@@ -645,27 +634,27 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v
645634fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
646635 const mir_tag = emit.mir.instructions.items(.tag)[inst];
647636 assert(mir_tag == .mov_sign_extend);
648 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
637 const ops = emit.mir.instructions.items(.ops)[inst].decode();
649638 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
650639 switch (ops.flags) {
651640 0b00 => {
652641 const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx;
653 return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code);
642 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
654643 },
655644 0b01 => {
656 return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{
645 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
657646 .disp = imm,
658647 .base = ops.reg2,
659648 }), emit.code);
660649 },
661650 0b10 => {
662 return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{
651 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
663652 .disp = imm,
664653 .base = ops.reg2,
665654 }), emit.code);
666655 },
667656 0b11 => {
668 return lowerToRmEnc(.movsxd, Register.reg(ops.reg1), RegisterOrMemory.mem(.dword_ptr, .{
657 return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{
669658 .disp = imm,
670659 .base = ops.reg2,
671660 }), emit.code);
......@@ -676,20 +665,20 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
676665fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
677666 const mir_tag = emit.mir.instructions.items(.tag)[inst];
678667 assert(mir_tag == .mov_zero_extend);
679 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
668 const ops = emit.mir.instructions.items(.ops)[inst].decode();
680669 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
681670 switch (ops.flags) {
682671 0b00 => {
683 return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code);
672 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
684673 },
685674 0b01 => {
686 return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{
675 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
687676 .disp = imm,
688677 .base = ops.reg2,
689678 }), emit.code);
690679 },
691680 0b10 => {
692 return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{
681 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
693682 .disp = imm,
694683 .base = ops.reg2,
695684 }), emit.code);
......@@ -703,31 +692,46 @@ fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
703692fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
704693 const tag = emit.mir.instructions.items(.tag)[inst];
705694 assert(tag == .movabs);
706 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
707 const imm: u64 = if (ops.reg1.size() == 64) blk: {
708 const payload = emit.mir.instructions.items(.data)[inst].payload;
709 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
710 break :blk imm.decode();
711 } else emit.mir.instructions.items(.data)[inst].imm;
712 if (ops.flags == 0b00) {
713 // movabs reg, imm64
714 // OI
715 return lowerToOiEnc(.mov, Register.reg(ops.reg1), imm, emit.code);
716 }
717 if (ops.reg1 == .none) {
718 // movabs moffs64, rax
719 // TD
720 return lowerToTdEnc(.mov, imm, Register.reg(ops.reg2), emit.code);
695 const ops = emit.mir.instructions.items(.ops)[inst].decode();
696 switch (ops.flags) {
697 0b00 => {
698 const imm: u64 = if (ops.reg1.size() == 64) blk: {
699 const payload = emit.mir.instructions.items(.data)[inst].payload;
700 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
701 break :blk imm.decode();
702 } else emit.mir.instructions.items(.data)[inst].imm;
703 // movabs reg, imm64
704 // OI
705 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);
706 },
707 0b01 => {
708 if (ops.reg1 == .none) {
709 const imm: u64 = if (ops.reg2.size() == 64) blk: {
710 const payload = emit.mir.instructions.items(.data)[inst].payload;
711 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
712 break :blk imm.decode();
713 } else emit.mir.instructions.items(.data)[inst].imm;
714 // movabs moffs64, rax
715 // TD
716 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code);
717 }
718 const imm: u64 = if (ops.reg1.size() == 64) blk: {
719 const payload = emit.mir.instructions.items(.data)[inst].payload;
720 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
721 break :blk imm.decode();
722 } else emit.mir.instructions.items(.data)[inst].imm;
723 // movabs rax, moffs64
724 // FD
725 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
726 },
727 else => return emit.fail("TODO unused variant: movabs 0b{b}", .{ops.flags}),
721728 }
722 // movabs rax, moffs64
723 // FD
724 return lowerToFdEnc(.mov, Register.reg(ops.reg1), imm, emit.code);
725729}
726730
727731fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
728732 const tag = emit.mir.instructions.items(.tag)[inst];
729733 assert(tag == .fisttp);
730 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
734 const ops = emit.mir.instructions.items(.ops)[inst].decode();
731735
732736 // the selecting between operand sizes for this particular `fisttp` instruction
733737 // is done via opcode instead of the usual prefixes.
......@@ -749,7 +753,7 @@ fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
749753fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
750754 const tag = emit.mir.instructions.items(.tag)[inst];
751755 assert(tag == .fld);
752 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
756 const ops = emit.mir.instructions.items(.ops)[inst].decode();
753757
754758 // the selecting between operand sizes for this particular `fisttp` instruction
755759 // is done via opcode instead of the usual prefixes.
......@@ -768,7 +772,7 @@ fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
768772}
769773
770774fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
771 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
775 const ops = emit.mir.instructions.items(.ops)[inst].decode();
772776 switch (ops.flags) {
773777 0b00 => {
774778 // sal reg1, 1
......@@ -793,12 +797,11 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
793797}
794798
795799fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
796 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
800 const ops = emit.mir.instructions.items(.ops)[inst].decode();
797801 if (ops.reg1 != .none) {
798802 assert(ops.reg2 == .none);
799803 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
800804 }
801 assert(ops.reg1 == .none);
802805 assert(ops.reg2 != .none);
803806 const imm = emit.mir.instructions.items(.data)[inst].imm;
804807 const ptr_size: Memory.PtrSize = switch (ops.flags) {
......@@ -816,27 +819,27 @@ fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
816819fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
817820 const tag = emit.mir.instructions.items(.tag)[inst];
818821 assert(tag == .imul_complex);
819 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
822 const ops = emit.mir.instructions.items(.ops)[inst].decode();
820823 switch (ops.flags) {
821824 0b00 => {
822 return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code);
825 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
823826 },
824827 0b01 => {
825828 const imm = emit.mir.instructions.items(.data)[inst].imm;
826 const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2;
827 return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{
829 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
830 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
828831 .disp = imm,
829832 .base = src_reg,
830833 }), emit.code);
831834 },
832835 0b10 => {
833836 const imm = emit.mir.instructions.items(.data)[inst].imm;
834 return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), imm, emit.code);
837 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
835838 },
836839 0b11 => {
837840 const payload = emit.mir.instructions.items(.data)[inst].payload;
838841 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
839 return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{
842 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
840843 .disp = imm_pair.dest_off,
841844 .base = ops.reg2,
842845 }), imm_pair.operand, emit.code);
......@@ -845,7 +848,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
845848}
846849
847850fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
848 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
851 const ops = emit.mir.instructions.items(.ops)[inst].decode();
849852 const tag: Tag = switch (ops.flags) {
850853 0b00 => .cbw,
851854 0b01 => .cwd,
......@@ -858,17 +861,17 @@ fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
858861fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
859862 const tag = emit.mir.instructions.items(.tag)[inst];
860863 assert(tag == .lea);
861 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
864 const ops = emit.mir.instructions.items(.ops)[inst].decode();
862865 switch (ops.flags) {
863866 0b00 => {
864867 // lea reg1, [reg2 + imm32]
865868 // RM
866869 const imm = emit.mir.instructions.items(.data)[inst].imm;
867 const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2;
870 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
868871 return lowerToRmEnc(
869872 .lea,
870 Register.reg(ops.reg1),
871 RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
873 ops.reg1,
874 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
872875 .disp = imm,
873876 .base = src_reg,
874877 }),
......@@ -881,8 +884,8 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
881884 const start_offset = emit.code.items.len;
882885 try lowerToRmEnc(
883886 .lea,
884 Register.reg(ops.reg1),
885 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
887 ops.reg1,
888 RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0),
886889 emit.code,
887890 );
888891 const end_offset = emit.code.items.len;
......@@ -895,15 +898,15 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
895898 0b10 => {
896899 // lea reg, [rbp + rcx + imm32]
897900 const imm = emit.mir.instructions.items(.data)[inst].imm;
898 const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2;
901 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
899902 const scale_index = ScaleIndex{
900903 .scale = 0,
901904 .index = .rcx,
902905 };
903906 return lowerToRmEnc(
904907 .lea,
905 Register.reg(ops.reg1),
906 RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
908 ops.reg1,
909 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
907910 .disp = imm,
908911 .base = src_reg,
909912 .scale_index = scale_index,
......@@ -918,15 +921,15 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
918921fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
919922 const tag = emit.mir.instructions.items(.tag)[inst];
920923 assert(tag == .lea_pie);
921 const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]);
924 const ops = emit.mir.instructions.items(.ops)[inst].decode();
922925 const load_reloc = emit.mir.instructions.items(.data)[inst].load_reloc;
923926
924927 // lea reg1, [rip + reloc]
925928 // RM
926929 try lowerToRmEnc(
927930 .lea,
928 Register.reg(ops.reg1),
929 RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0),
931 ops.reg1,
932 RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0),
930933 emit.code,
931934 );
932935
......@@ -962,78 +965,70 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
962965fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
963966 const tag = emit.mir.instructions.items(.tag)[inst];
964967 assert(tag == .mov_f64);
965 const ops = emit.mir.instructions.items(.ops)[inst];
966 const flags = @truncate(u2, ops);
968 const ops = emit.mir.instructions.items(.ops)[inst].decode();
967969
968 switch (flags) {
970 switch (ops.flags) {
969971 0b00 => {
970 const decoded = Mir.Ops(AvxRegister, GpRegister).decode(ops);
971972 const imm = emit.mir.instructions.items(.data)[inst].imm;
972 return lowerToRmEnc(.vmovsd, Register.avxReg(decoded.reg1), RegisterOrMemory.mem(.qword_ptr, .{
973 return lowerToVmEnc(.vmovsd, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
973974 .disp = imm,
974 .base = decoded.reg2,
975 .base = ops.reg2,
975976 }), emit.code);
976977 },
977978 0b01 => {
978 const decoded = Mir.Ops(GpRegister, AvxRegister).decode(ops);
979979 const imm = emit.mir.instructions.items(.data)[inst].imm;
980 return lowerToMrEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{
980 return lowerToMvEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{
981981 .disp = imm,
982 .base = decoded.reg1,
983 }), Register.avxReg(decoded.reg2), emit.code);
982 .base = ops.reg1,
983 }), ops.reg2, emit.code);
984984 },
985985 0b10 => {
986 const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops);
987986 return lowerToRvmEnc(
988987 .vmovsd,
989 Register.avxReg(decoded.reg1),
990 Register.avxReg(decoded.reg1),
991 RegisterOrMemory.avxReg(decoded.reg2),
988 ops.reg1,
989 ops.reg1,
990 RegisterOrMemory.reg(ops.reg2),
992991 emit.code,
993992 );
994993 },
995 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}),
994 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
996995 }
997996}
998997
999998fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1000999 const tag = emit.mir.instructions.items(.tag)[inst];
10011000 assert(tag == .add_f64);
1002 const ops = emit.mir.instructions.items(.ops)[inst];
1003 const flags = @truncate(u2, ops);
1001 const ops = emit.mir.instructions.items(.ops)[inst].decode();
10041002
1005 switch (flags) {
1003 switch (ops.flags) {
10061004 0b00 => {
1007 const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops);
10081005 return lowerToRvmEnc(
10091006 .vaddsd,
1010 Register.avxReg(decoded.reg1),
1011 Register.avxReg(decoded.reg1),
1012 RegisterOrMemory.avxReg(decoded.reg2),
1007 ops.reg1,
1008 ops.reg1,
1009 RegisterOrMemory.reg(ops.reg2),
10131010 emit.code,
10141011 );
10151012 },
1016 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}),
1013 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
10171014 }
10181015}
10191016
10201017fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10211018 const tag = emit.mir.instructions.items(.tag)[inst];
10221019 assert(tag == .cmp_f64);
1023 const ops = emit.mir.instructions.items(.ops)[inst];
1024 const flags = @truncate(u2, ops);
1020 const ops = emit.mir.instructions.items(.ops)[inst].decode();
10251021
1026 switch (flags) {
1022 switch (ops.flags) {
10271023 0b00 => {
1028 const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops);
1029 return lowerToRmEnc(
1024 return lowerToVmEnc(
10301025 .vucomisd,
1031 Register.avxReg(decoded.reg1),
1032 RegisterOrMemory.avxReg(decoded.reg2),
1026 ops.reg1,
1027 RegisterOrMemory.reg(ops.reg2),
10331028 emit.code,
10341029 );
10351030 },
1036 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}),
1031 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
10371032 }
10381033}
10391034
......@@ -1277,6 +1272,18 @@ const Tag = enum {
12771272 vcmpsd,
12781273 vucomisd,
12791274
1275 fn isAvx(tag: Tag) bool {
1276 return switch (tag) {
1277 .vmovsd,
1278 .vaddsd,
1279 .vcmpsd,
1280 .vucomisd,
1281 => true,
1282
1283 else => false,
1284 };
1285 }
1286
12801287 fn isSetCC(tag: Tag) bool {
12811288 return switch (tag) {
12821289 .seto,
......@@ -1361,6 +1368,12 @@ const Encoding = enum {
13611368 /// OP r64, r/m64, imm32
13621369 rmi,
13631370
1371 /// OP xmm1, xmm2/m64
1372 vm,
1373
1374 /// OP m64, xmm1
1375 mv,
1376
13641377 /// OP xmm1, xmm2, xmm3/m64
13651378 rvm,
13661379
......@@ -1389,7 +1402,7 @@ const OpCode = union(enum) {
13891402
13901403 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {
13911404 assert(opc == .one_byte);
1392 encoder.opcode_withReg(opc.one_byte, reg.lowId());
1405 encoder.opcode_withReg(opc.one_byte, reg.lowEnc());
13931406 }
13941407};
13951408
......@@ -1536,6 +1549,15 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
15361549 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
15371550 else => null,
15381551 },
1552 .mv => return switch (tag) {
1553 .vmovsd => OpCode.oneByte(0x11),
1554 else => null,
1555 },
1556 .vm => return switch (tag) {
1557 .vmovsd => OpCode.oneByte(0x10),
1558 .vucomisd => OpCode.oneByte(0x2e),
1559 else => null,
1560 },
15391561 .rvm => return switch (tag) {
15401562 .vaddsd => OpCode.oneByte(0x58),
15411563 .vmovsd => OpCode.oneByte(0x10),
......@@ -1647,11 +1669,11 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
16471669 } = .none,
16481670 } = blk: {
16491671 switch (enc) {
1650 .mr => switch (tag) {
1672 .mv => switch (tag) {
16511673 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
16521674 else => return null,
16531675 },
1654 .rm => switch (tag) {
1676 .vm => switch (tag) {
16551677 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
16561678 .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true },
16571679 else => return null,
......@@ -1697,61 +1719,50 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
16971719 } };
16981720}
16991721
1700const ScaleIndex = struct {
1722const ScaleIndex = packed struct {
17011723 scale: u2,
1702 index: GpRegister,
1724 index: Register,
17031725};
17041726
17051727const Memory = struct {
1706 base: ?GpRegister,
1728 base: ?Register,
17071729 rip: bool = false,
17081730 disp: u32,
17091731 ptr_size: PtrSize,
17101732 scale_index: ?ScaleIndex = null,
17111733
1712 const PtrSize = enum {
1713 byte_ptr,
1714 word_ptr,
1715 dword_ptr,
1716 qword_ptr,
1734 const PtrSize = enum(u2) {
1735 byte_ptr = 0b00,
1736 word_ptr = 0b01,
1737 dword_ptr = 0b10,
1738 qword_ptr = 0b11,
17171739
1718 fn fromBits(in_bits: u64) PtrSize {
1719 return switch (in_bits) {
1720 8 => .byte_ptr,
1721 16 => .word_ptr,
1722 32 => .dword_ptr,
1723 64 => .qword_ptr,
1724 else => unreachable,
1725 };
1740 fn new(bit_size: u64) PtrSize {
1741 return @intToEnum(PtrSize, math.log2_int(u4, @intCast(u4, @divExact(bit_size, 8))));
17261742 }
17271743
17281744 /// Returns size in bits.
17291745 fn size(ptr_size: PtrSize) u64 {
1730 return switch (ptr_size) {
1731 .byte_ptr => 8,
1732 .word_ptr => 16,
1733 .dword_ptr => 32,
1734 .qword_ptr => 64,
1735 };
1746 return 8 * (math.powi(u8, 2, @enumToInt(ptr_size)) catch unreachable);
17361747 }
17371748 };
17381749
17391750 fn encode(mem_op: Memory, encoder: Encoder, operand: u3) void {
17401751 if (mem_op.base) |base| {
1741 const dst = base.lowId();
1752 const dst = base.lowEnc();
17421753 const src = operand;
17431754 if (dst == 4 or mem_op.scale_index != null) {
17441755 if (mem_op.disp == 0 and dst != 5) {
17451756 encoder.modRm_SIBDisp0(src);
17461757 if (mem_op.scale_index) |si| {
1747 encoder.sib_scaleIndexBase(si.scale, si.index.lowId(), dst);
1758 encoder.sib_scaleIndexBase(si.scale, si.index.lowEnc(), dst);
17481759 } else {
17491760 encoder.sib_base(dst);
17501761 }
17511762 } else if (immOpSize(mem_op.disp) == 8) {
17521763 encoder.modRm_SIBDisp8(src);
17531764 if (mem_op.scale_index) |si| {
1754 encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowId(), dst);
1765 encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowEnc(), dst);
17551766 } else {
17561767 encoder.sib_baseDisp8(dst);
17571768 }
......@@ -1759,7 +1770,7 @@ const Memory = struct {
17591770 } else {
17601771 encoder.modRm_SIBDisp32(src);
17611772 if (mem_op.scale_index) |si| {
1762 encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowId(), dst);
1773 encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowEnc(), dst);
17631774 } else {
17641775 encoder.sib_baseDisp32(dst);
17651776 }
......@@ -1782,7 +1793,7 @@ const Memory = struct {
17821793 } else {
17831794 encoder.modRm_SIBDisp0(operand);
17841795 if (mem_op.scale_index) |si| {
1785 encoder.sib_scaleIndexDisp32(si.scale, si.index.lowId());
1796 encoder.sib_scaleIndexDisp32(si.scale, si.index.lowEnc());
17861797 } else {
17871798 encoder.sib_disp32();
17881799 }
......@@ -1791,6 +1802,7 @@ const Memory = struct {
17911802 }
17921803 }
17931804
1805 /// Returns size in bits.
17941806 fn size(memory: Memory) u64 {
17951807 return memory.ptr_size.size();
17961808 }
......@@ -1805,55 +1817,17 @@ fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
18051817 }
18061818}
18071819
1808const Register = union(enum) {
1809 register: GpRegister,
1810 avx_register: AvxRegister,
1811
1812 fn reg(register: GpRegister) Register {
1813 return .{ .register = register };
1814 }
1815
1816 fn avxReg(register: AvxRegister) Register {
1817 return .{ .avx_register = register };
1818 }
1819
1820 fn lowId(register: Register) u3 {
1821 return switch (register) {
1822 .register => |r| r.lowId(),
1823 .avx_register => |r| r.lowId(),
1824 };
1825 }
1826
1827 fn size(register: Register) u64 {
1828 return switch (register) {
1829 .register => |r| r.size(),
1830 .avx_register => |r| r.size(),
1831 };
1832 }
1833
1834 fn isExtended(register: Register) bool {
1835 return switch (register) {
1836 .register => |r| r.isExtended(),
1837 .avx_register => |r| r.isExtended(),
1838 };
1839 }
1840};
1841
18421820const RegisterOrMemory = union(enum) {
18431821 register: Register,
18441822 memory: Memory,
18451823
1846 fn reg(register: GpRegister) RegisterOrMemory {
1847 return .{ .register = Register.reg(register) };
1848 }
1849
1850 fn avxReg(register: AvxRegister) RegisterOrMemory {
1851 return .{ .register = Register.avxReg(register) };
1824 fn reg(register: Register) RegisterOrMemory {
1825 return .{ .register = register };
18521826 }
18531827
18541828 fn mem(ptr_size: Memory.PtrSize, args: struct {
18551829 disp: u32,
1856 base: ?GpRegister = null,
1830 base: ?Register = null,
18571831 scale_index: ?ScaleIndex = null,
18581832 }) RegisterOrMemory {
18591833 return .{
......@@ -1877,6 +1851,7 @@ const RegisterOrMemory = union(enum) {
18771851 };
18781852 }
18791853
1854 /// Returns size in bits.
18801855 fn size(reg_or_mem: RegisterOrMemory) u64 {
18811856 return switch (reg_or_mem) {
18821857 .register => |reg| reg.size(),
......@@ -1886,6 +1861,7 @@ const RegisterOrMemory = union(enum) {
18861861};
18871862
18881863fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
1864 assert(!tag.isAvx());
18891865 const opc = getOpCode(tag, .zo, false).?;
18901866 const encoder = try Encoder.init(code, 2);
18911867 switch (tag) {
......@@ -1900,6 +1876,7 @@ fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
19001876}
19011877
19021878fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
1879 assert(!tag.isAvx());
19031880 if (tag == .ret_far or tag == .ret_near) {
19041881 const encoder = try Encoder.init(code, 3);
19051882 const opc = getOpCode(tag, .i, false).?;
......@@ -1917,6 +1894,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
19171894}
19181895
19191896fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
1897 assert(!tag.isAvx());
19201898 const opc = getOpCode(tag, .o, false).?;
19211899 const encoder = try Encoder.init(code, 3);
19221900 if (reg.size() == 16) {
......@@ -1930,6 +1908,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi
19301908}
19311909
19321910fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
1911 assert(!tag.isAvx());
19331912 const opc = getOpCode(tag, .d, false).?;
19341913 const encoder = try Encoder.init(code, 6);
19351914 opc.encode(encoder);
......@@ -1937,6 +1916,7 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
19371916}
19381917
19391918fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void {
1919 assert(!tag.isAvx());
19401920 const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
19411921 const modrm_ext = getModRmExt(tag).?;
19421922 switch (reg_or_mem) {
......@@ -1951,7 +1931,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st
19511931 .b = reg.isExtended(),
19521932 });
19531933 opc.encode(encoder);
1954 encoder.modRm_direct(modrm_ext, reg.lowId());
1934 encoder.modRm_direct(modrm_ext, reg.lowEnc());
19551935 },
19561936 .memory => |mem_op| {
19571937 const encoder = try Encoder.init(code, 8);
......@@ -1992,6 +1972,7 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) I
19921972}
19931973
19941974fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void {
1975 assert(!tag.isAvx());
19951976 const opc = if (td)
19961977 getOpCode(tag, .td, reg.size() == 8).?
19971978 else
......@@ -2014,6 +1995,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8),
20141995}
20151996
20161997fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void {
1998 assert(!tag.isAvx());
20171999 const opc = getOpCode(tag, .oi, reg.size() == 8).?;
20182000 const encoder = try Encoder.init(code, 10);
20192001 if (reg.size() == 16) {
......@@ -2040,6 +2022,7 @@ fn lowerToMiXEnc(
20402022 enc: Encoding,
20412023 code: *std.ArrayList(u8),
20422024) InnerError!void {
2025 assert(!tag.isAvx());
20432026 const modrm_ext = getModRmExt(tag).?;
20442027 const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
20452028 switch (reg_or_mem) {
......@@ -2056,7 +2039,7 @@ fn lowerToMiXEnc(
20562039 .b = dst_reg.isExtended(),
20572040 });
20582041 opc.encode(encoder);
2059 encoder.modRm_direct(modrm_ext, dst_reg.lowId());
2042 encoder.modRm_direct(modrm_ext, dst_reg.lowEnc());
20602043 encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size());
20612044 },
20622045 .memory => |dst_mem| {
......@@ -2095,84 +2078,43 @@ fn lowerToRmEnc(
20952078 reg_or_mem: RegisterOrMemory,
20962079 code: *std.ArrayList(u8),
20972080) InnerError!void {
2081 assert(!tag.isAvx());
20982082 const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?;
20992083 switch (reg_or_mem) {
21002084 .register => |src_reg| {
2101 const encoder: Encoder = blk: {
2102 switch (reg) {
2103 .register => {
2104 const encoder = try Encoder.init(code, 4);
2105 if (reg.size() == 16) {
2106 encoder.prefix16BitMode();
2107 }
2108 encoder.rex(.{
2109 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
2110 .r = reg.isExtended(),
2111 .b = src_reg.isExtended(),
2112 });
2113 break :blk encoder;
2114 },
2115 .avx_register => {
2116 const encoder = try Encoder.init(code, 5);
2117 var vex_prefix = getVexPrefix(tag, .rm).?;
2118 const vex = &vex_prefix.prefix;
2119 vex.rex(.{
2120 .r = reg.isExtended(),
2121 .b = src_reg.isExtended(),
2122 });
2123 encoder.vex(vex_prefix.prefix);
2124 break :blk encoder;
2125 },
2126 }
2127 };
2085 const encoder = try Encoder.init(code, 5);
2086 if (reg.size() == 16) {
2087 encoder.prefix16BitMode();
2088 }
2089 encoder.rex(.{
2090 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
2091 .r = reg.isExtended(),
2092 .b = src_reg.isExtended(),
2093 });
21282094 opc.encode(encoder);
2129 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
2095 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
21302096 },
21312097 .memory => |src_mem| {
2132 const encoder: Encoder = blk: {
2133 switch (reg) {
2134 .register => {
2135 const encoder = try Encoder.init(code, 9);
2136 if (reg.size() == 16) {
2137 encoder.prefix16BitMode();
2138 }
2139 if (src_mem.base) |base| {
2140 // TODO handle 32-bit base register - requires prefix 0x67
2141 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
2142 encoder.rex(.{
2143 .w = setRexWRegister(reg),
2144 .r = reg.isExtended(),
2145 .b = base.isExtended(),
2146 });
2147 } else {
2148 encoder.rex(.{
2149 .w = setRexWRegister(reg),
2150 .r = reg.isExtended(),
2151 });
2152 }
2153 break :blk encoder;
2154 },
2155 .avx_register => {
2156 const encoder = try Encoder.init(code, 10);
2157 var vex_prefix = getVexPrefix(tag, .rm).?;
2158 const vex = &vex_prefix.prefix;
2159 if (src_mem.base) |base| {
2160 vex.rex(.{
2161 .r = reg.isExtended(),
2162 .b = base.isExtended(),
2163 });
2164 } else {
2165 vex.rex(.{
2166 .r = reg.isExtended(),
2167 });
2168 }
2169 encoder.vex(vex_prefix.prefix);
2170 break :blk encoder;
2171 },
2172 }
2173 };
2098 const encoder = try Encoder.init(code, 9);
2099 if (reg.size() == 16) {
2100 encoder.prefix16BitMode();
2101 }
2102 if (src_mem.base) |base| {
2103 // TODO handle 32-bit base register - requires prefix 0x67
2104 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
2105 encoder.rex(.{
2106 .w = setRexWRegister(reg),
2107 .r = reg.isExtended(),
2108 .b = base.isExtended(),
2109 });
2110 } else {
2111 encoder.rex(.{
2112 .w = setRexWRegister(reg),
2113 .r = reg.isExtended(),
2114 });
2115 }
21742116 opc.encode(encoder);
2175 src_mem.encode(encoder, reg.lowId());
2117 src_mem.encode(encoder, reg.lowEnc());
21762118 },
21772119 }
21782120}
......@@ -2183,6 +2125,7 @@ fn lowerToMrEnc(
21832125 reg: Register,
21842126 code: *std.ArrayList(u8),
21852127) InnerError!void {
2128 assert(!tag.isAvx());
21862129 const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?;
21872130 switch (reg_or_mem) {
21882131 .register => |dst_reg| {
......@@ -2196,53 +2139,27 @@ fn lowerToMrEnc(
21962139 .b = dst_reg.isExtended(),
21972140 });
21982141 opc.encode(encoder);
2199 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
2142 encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc());
22002143 },
22012144 .memory => |dst_mem| {
2202 const encoder: Encoder = blk: {
2203 switch (reg) {
2204 .register => {
2205 const encoder = try Encoder.init(code, 9);
2206 if (reg.size() == 16) {
2207 encoder.prefix16BitMode();
2208 }
2209 if (dst_mem.base) |base| {
2210 encoder.rex(.{
2211 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2212 .r = reg.isExtended(),
2213 .b = base.isExtended(),
2214 });
2215 } else {
2216 encoder.rex(.{
2217 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2218 .r = reg.isExtended(),
2219 });
2220 }
2221 break :blk encoder;
2222 },
2223 .avx_register => {
2224 const encoder = try Encoder.init(code, 10);
2225 var vex_prefix = getVexPrefix(tag, .mr).?;
2226 const vex = &vex_prefix.prefix;
2227 if (dst_mem.base) |base| {
2228 vex.rex(.{
2229 .w = dst_mem.ptr_size == .qword_ptr,
2230 .r = reg.isExtended(),
2231 .b = base.isExtended(),
2232 });
2233 } else {
2234 vex.rex(.{
2235 .w = dst_mem.ptr_size == .qword_ptr,
2236 .r = reg.isExtended(),
2237 });
2238 }
2239 encoder.vex(vex_prefix.prefix);
2240 break :blk encoder;
2241 },
2242 }
2243 };
2145 const encoder = try Encoder.init(code, 9);
2146 if (reg.size() == 16) {
2147 encoder.prefix16BitMode();
2148 }
2149 if (dst_mem.base) |base| {
2150 encoder.rex(.{
2151 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2152 .r = reg.isExtended(),
2153 .b = base.isExtended(),
2154 });
2155 } else {
2156 encoder.rex(.{
2157 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2158 .r = reg.isExtended(),
2159 });
2160 }
22442161 opc.encode(encoder);
2245 dst_mem.encode(encoder, reg.lowId());
2162 dst_mem.encode(encoder, reg.lowEnc());
22462163 },
22472164 }
22482165}
......@@ -2254,6 +2171,7 @@ fn lowerToRmiEnc(
22542171 imm: u32,
22552172 code: *std.ArrayList(u8),
22562173) InnerError!void {
2174 assert(!tag.isAvx());
22572175 const opc = getOpCode(tag, .rmi, false).?;
22582176 const encoder = try Encoder.init(code, 13);
22592177 if (reg.size() == 16) {
......@@ -2267,7 +2185,7 @@ fn lowerToRmiEnc(
22672185 .b = src_reg.isExtended(),
22682186 });
22692187 opc.encode(encoder);
2270 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
2188 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
22712189 },
22722190 .memory => |src_mem| {
22732191 if (src_mem.base) |base| {
......@@ -2285,12 +2203,94 @@ fn lowerToRmiEnc(
22852203 });
22862204 }
22872205 opc.encode(encoder);
2288 src_mem.encode(encoder, reg.lowId());
2206 src_mem.encode(encoder, reg.lowEnc());
22892207 },
22902208 }
22912209 encodeImm(encoder, imm, reg.size());
22922210}
22932211
2212/// Also referred to as XM encoding in Intel manual.
2213fn lowerToVmEnc(
2214 tag: Tag,
2215 reg: Register,
2216 reg_or_mem: RegisterOrMemory,
2217 code: *std.ArrayList(u8),
2218) InnerError!void {
2219 const opc = getOpCode(tag, .vm, false).?;
2220 var vex_prefix = getVexPrefix(tag, .vm).?;
2221 const vex = &vex_prefix.prefix;
2222 switch (reg_or_mem) {
2223 .register => |src_reg| {
2224 const encoder = try Encoder.init(code, 5);
2225 vex.rex(.{
2226 .r = reg.isExtended(),
2227 .b = src_reg.isExtended(),
2228 });
2229 encoder.vex(vex_prefix.prefix);
2230 opc.encode(encoder);
2231 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
2232 },
2233 .memory => |src_mem| {
2234 assert(src_mem.ptr_size == .qword_ptr);
2235 const encoder = try Encoder.init(code, 10);
2236 if (src_mem.base) |base| {
2237 vex.rex(.{
2238 .r = reg.isExtended(),
2239 .b = base.isExtended(),
2240 });
2241 } else {
2242 vex.rex(.{
2243 .r = reg.isExtended(),
2244 });
2245 }
2246 encoder.vex(vex_prefix.prefix);
2247 opc.encode(encoder);
2248 src_mem.encode(encoder, reg.lowEnc());
2249 },
2250 }
2251}
2252
2253/// Usually referred to as MR encoding with V/V in Intel manual.
2254fn lowerToMvEnc(
2255 tag: Tag,
2256 reg_or_mem: RegisterOrMemory,
2257 reg: Register,
2258 code: *std.ArrayList(u8),
2259) InnerError!void {
2260 const opc = getOpCode(tag, .mv, false).?;
2261 var vex_prefix = getVexPrefix(tag, .mv).?;
2262 const vex = &vex_prefix.prefix;
2263 switch (reg_or_mem) {
2264 .register => |dst_reg| {
2265 const encoder = try Encoder.init(code, 4);
2266 vex.rex(.{
2267 .r = reg.isExtended(),
2268 .b = dst_reg.isExtended(),
2269 });
2270 encoder.vex(vex_prefix.prefix);
2271 opc.encode(encoder);
2272 encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc());
2273 },
2274 .memory => |dst_mem| {
2275 assert(dst_mem.ptr_size == .qword_ptr);
2276 const encoder = try Encoder.init(code, 10);
2277 if (dst_mem.base) |base| {
2278 vex.rex(.{
2279 .r = reg.isExtended(),
2280 .b = base.isExtended(),
2281 });
2282 } else {
2283 vex.rex(.{
2284 .r = reg.isExtended(),
2285 });
2286 }
2287 encoder.vex(vex_prefix.prefix);
2288 opc.encode(encoder);
2289 dst_mem.encode(encoder, reg.lowEnc());
2290 },
2291 }
2292}
2293
22942294fn lowerToRvmEnc(
22952295 tag: Tag,
22962296 reg1: Register,
......@@ -2305,7 +2305,7 @@ fn lowerToRvmEnc(
23052305 .register => |reg3| {
23062306 if (vex_prefix.reg) |vvvv| {
23072307 switch (vvvv) {
2308 .nds => vex.reg(reg2.avx_register.id()),
2308 .nds => vex.reg(reg2.enc()),
23092309 else => unreachable, // TODO
23102310 }
23112311 }
......@@ -2316,7 +2316,7 @@ fn lowerToRvmEnc(
23162316 });
23172317 encoder.vex(vex_prefix.prefix);
23182318 opc.encode(encoder);
2319 encoder.modRm_direct(reg1.lowId(), reg3.lowId());
2319 encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
23202320 },
23212321 .memory => |dst_mem| {
23222322 _ = dst_mem;
......@@ -2341,7 +2341,7 @@ fn lowerToRvmiEnc(
23412341 .register => |reg3| {
23422342 if (vex_prefix.reg) |vvvv| {
23432343 switch (vvvv) {
2344 .nds => vex.reg(reg2.avx_register.id()),
2344 .nds => vex.reg(reg2.enc()),
23452345 else => unreachable, // TODO
23462346 }
23472347 }
......@@ -2352,7 +2352,7 @@ fn lowerToRvmiEnc(
23522352 });
23532353 encoder.vex(vex_prefix.prefix);
23542354 opc.encode(encoder);
2355 encoder.modRm_direct(reg1.lowId(), reg3.lowId());
2355 encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
23562356 break :blk encoder;
23572357 },
23582358 .memory => |dst_mem| {
......@@ -2490,23 +2490,23 @@ test "lower MI encoding" {
24902490test "lower RM encoding" {
24912491 var emit = TestEmit.init();
24922492 defer emit.deinit();
2493 try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.reg(.rbx), emit.code());
2493 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code());
24942494 try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx");
2495 try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code());
2495 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code());
24962496 try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]");
2497 try lowerToRmEnc(.add, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code());
2497 try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code());
24982498 try expectEqualHexStrings(
24992499 "\x4C\x03\x1C\x25\x00\x00\x00\x10",
25002500 emit.lowered(),
25012501 "add r11, qword ptr [ds:0x10000000]",
25022502 );
2503 try lowerToRmEnc(.add, Register.reg(.r12b), RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code());
2503 try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code());
25042504 try expectEqualHexStrings(
25052505 "\x44\x02\x24\x25\x00\x00\x00\x10",
25062506 emit.lowered(),
25072507 "add r11b, byte ptr [ds:0x10000000]",
25082508 );
2509 try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{
2509 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{
25102510 .disp = 0x10000000,
25112511 .base = .r13,
25122512 }), emit.code());
......@@ -2515,7 +2515,7 @@ test "lower RM encoding" {
25152515 emit.lowered(),
25162516 "sub r11, qword ptr [r13 + 0x10000000]",
25172517 );
2518 try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{
2518 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{
25192519 .disp = 0x10000000,
25202520 .base = .r12,
25212521 }), emit.code());
......@@ -2524,14 +2524,14 @@ test "lower RM encoding" {
25242524 emit.lowered(),
25252525 "sub r11, qword ptr [r12 + 0x10000000]",
25262526 );
2527 try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{
2527 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
25282528 .disp = @bitCast(u32, @as(i32, -4)),
25292529 .base = .rbp,
25302530 }), emit.code());
25312531 try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]");
2532 try lowerToRmEnc(.lea, Register.reg(.rax), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2532 try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
25332533 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]");
2534 try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{
2534 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
25352535 .disp = @bitCast(u32, @as(i32, -8)),
25362536 .base = .rbp,
25372537 .scale_index = .{
......@@ -2540,7 +2540,7 @@ test "lower RM encoding" {
25402540 },
25412541 }), emit.code());
25422542 try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]");
2543 try lowerToRmEnc(.mov, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{
2543 try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{
25442544 .disp = @bitCast(u32, @as(i32, -4)),
25452545 .base = .rbp,
25462546 .scale_index = .{
......@@ -2549,7 +2549,7 @@ test "lower RM encoding" {
25492549 },
25502550 }), emit.code());
25512551 try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
2552 try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{
2552 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
25532553 .disp = @bitCast(u32, @as(i32, -8)),
25542554 .base = .rbp,
25552555 .scale_index = .{
......@@ -2558,7 +2558,7 @@ test "lower RM encoding" {
25582558 },
25592559 }), emit.code());
25602560 try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]");
2561 try lowerToRmEnc(.mov, Register.reg(.r8b), RegisterOrMemory.mem(.byte_ptr, .{
2561 try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{
25622562 .disp = @bitCast(u32, @as(i32, -24)),
25632563 .base = .rsi,
25642564 .scale_index = .{
......@@ -2567,7 +2567,7 @@ test "lower RM encoding" {
25672567 },
25682568 }), emit.code());
25692569 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]");
2570 try lowerToRmEnc(.lea, Register.reg(.rsi), RegisterOrMemory.mem(.qword_ptr, .{
2570 try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{
25712571 .disp = 0,
25722572 .base = .rbp,
25732573 .scale_index = .{
......@@ -2576,33 +2576,25 @@ test "lower RM encoding" {
25762576 },
25772577 }), emit.code());
25782578 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]");
2579
2580 // AVX extension tests
2581 try lowerToRmEnc(.vmovsd, Register.avxReg(.xmm1), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2582 try expectEqualHexStrings(
2583 "\xC5\xFB\x10\x0D\x10\x00\x00\x00",
2584 emit.lowered(),
2585 "vmovsd xmm1, qword ptr [rip + 0x10]",
2586 );
25872579}
25882580
25892581test "lower MR encoding" {
25902582 var emit = TestEmit.init();
25912583 defer emit.deinit();
2592 try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), Register.reg(.rbx), emit.code());
2584 try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code());
25932585 try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx");
25942586 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
25952587 .disp = @bitCast(u32, @as(i32, -4)),
25962588 .base = .rbp,
2597 }), Register.reg(.r11), emit.code());
2589 }), .r11, emit.code());
25982590 try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11");
2599 try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12b), emit.code());
2591 try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code());
26002592 try expectEqualHexStrings(
26012593 "\x44\x00\x24\x25\x00\x00\x00\x10",
26022594 emit.lowered(),
26032595 "add byte ptr [ds:0x10000000], r12b",
26042596 );
2605 try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12d), emit.code());
2597 try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code());
26062598 try expectEqualHexStrings(
26072599 "\x44\x01\x24\x25\x00\x00\x00\x10",
26082600 emit.lowered(),
......@@ -2611,61 +2603,53 @@ test "lower MR encoding" {
26112603 try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{
26122604 .disp = 0x10000000,
26132605 .base = .r11,
2614 }), Register.reg(.r12), emit.code());
2606 }), .r12, emit.code());
26152607 try expectEqualHexStrings(
26162608 "\x4D\x29\xA3\x00\x00\x00\x10",
26172609 emit.lowered(),
26182610 "sub qword ptr [r11 + 0x10000000], r12",
26192611 );
2620 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code());
2612 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code());
26212613 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12");
2622
2623 // AVX extension tests
2624 try lowerToMrEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.avxReg(.xmm1), emit.code());
2625 try expectEqualHexStrings(
2626 "\xC5\xFB\x11\x0D\x10\x00\x00\x00",
2627 emit.lowered(),
2628 "vmovsd qword ptr [rip + 0x10], xmm1",
2629 );
26302614}
26312615
26322616test "lower OI encoding" {
26332617 var emit = TestEmit.init();
26342618 defer emit.deinit();
2635 try lowerToOiEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code());
2619 try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code());
26362620 try expectEqualHexStrings(
26372621 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
26382622 emit.lowered(),
26392623 "movabs rax, 0x1000000000000000",
26402624 );
2641 try lowerToOiEnc(.mov, Register.reg(.r11), 0x1000000000000000, emit.code());
2625 try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code());
26422626 try expectEqualHexStrings(
26432627 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
26442628 emit.lowered(),
26452629 "movabs r11, 0x1000000000000000",
26462630 );
2647 try lowerToOiEnc(.mov, Register.reg(.r11d), 0x10000000, emit.code());
2631 try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code());
26482632 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000");
2649 try lowerToOiEnc(.mov, Register.reg(.r11w), 0x1000, emit.code());
2633 try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code());
26502634 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000");
2651 try lowerToOiEnc(.mov, Register.reg(.r11b), 0x10, emit.code());
2635 try lowerToOiEnc(.mov, .r11b, 0x10, emit.code());
26522636 try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10");
26532637}
26542638
26552639test "lower FD/TD encoding" {
26562640 var emit = TestEmit.init();
26572641 defer emit.deinit();
2658 try lowerToFdEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code());
2642 try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code());
26592643 try expectEqualHexStrings(
26602644 "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10",
26612645 emit.lowered(),
26622646 "mov rax, ds:0x1000000000000000",
26632647 );
2664 try lowerToFdEnc(.mov, Register.reg(.eax), 0x10000000, emit.code());
2648 try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code());
26652649 try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000");
2666 try lowerToFdEnc(.mov, Register.reg(.ax), 0x1000, emit.code());
2650 try lowerToFdEnc(.mov, .ax, 0x1000, emit.code());
26672651 try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000");
2668 try lowerToFdEnc(.mov, Register.reg(.al), 0x10, emit.code());
2652 try lowerToFdEnc(.mov, .al, 0x10, emit.code());
26692653 try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10");
26702654}
26712655
......@@ -2741,16 +2725,16 @@ test "lower M1 and MC encodings" {
27412725test "lower O encoding" {
27422726 var emit = TestEmit.init();
27432727 defer emit.deinit();
2744 try lowerToOEnc(.pop, Register.reg(.r12), emit.code());
2728 try lowerToOEnc(.pop, .r12, emit.code());
27452729 try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12");
2746 try lowerToOEnc(.push, Register.reg(.r12w), emit.code());
2730 try lowerToOEnc(.push, .r12w, emit.code());
27472731 try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w");
27482732}
27492733
27502734test "lower RMI encoding" {
27512735 var emit = TestEmit.init();
27522736 defer emit.deinit();
2753 try lowerToRmiEnc(.imul, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{
2737 try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{
27542738 .disp = @bitCast(u32, @as(i32, -8)),
27552739 .base = .rbp,
27562740 }), 0x10, emit.code());
......@@ -2759,39 +2743,49 @@ test "lower RMI encoding" {
27592743 emit.lowered(),
27602744 "imul rax, qword ptr [rbp - 8], 0x10",
27612745 );
2762 try lowerToRmiEnc(.imul, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{
2746 try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{
27632747 .disp = @bitCast(u32, @as(i32, -4)),
27642748 .base = .rbp,
27652749 }), 0x10, emit.code());
27662750 try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10");
2767 try lowerToRmiEnc(.imul, Register.reg(.ax), RegisterOrMemory.mem(.word_ptr, .{
2751 try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{
27682752 .disp = @bitCast(u32, @as(i32, -2)),
27692753 .base = .rbp,
27702754 }), 0x10, emit.code());
27712755 try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10");
2772 try lowerToRmiEnc(.imul, Register.reg(.r12), RegisterOrMemory.reg(.r12), 0x10, emit.code());
2756 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code());
27732757 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10");
2774 try lowerToRmiEnc(.imul, Register.reg(.r12w), RegisterOrMemory.reg(.r12w), 0x10, emit.code());
2758 try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code());
27752759 try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10");
27762760}
27772761
2778test "lower to RVM encoding" {
2762test "lower MV encoding" {
27792763 var emit = TestEmit.init();
27802764 defer emit.deinit();
2781 try lowerToRvmEnc(
2782 .vaddsd,
2783 Register.avxReg(.xmm0),
2784 Register.avxReg(.xmm1),
2785 RegisterOrMemory.avxReg(.xmm2),
2786 emit.code(),
2765 try lowerToMvEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), .xmm1, emit.code());
2766 try expectEqualHexStrings(
2767 "\xC5\xFB\x11\x0D\x10\x00\x00\x00",
2768 emit.lowered(),
2769 "vmovsd qword ptr [rip + 0x10], xmm1",
27872770 );
2788 try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2");
2789 try lowerToRvmEnc(
2790 .vaddsd,
2791 Register.avxReg(.xmm0),
2792 Register.avxReg(.xmm0),
2793 RegisterOrMemory.avxReg(.xmm1),
2794 emit.code(),
2771}
2772
2773test "lower VM encoding" {
2774 var emit = TestEmit.init();
2775 defer emit.deinit();
2776 try lowerToVmEnc(.vmovsd, .xmm1, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2777 try expectEqualHexStrings(
2778 "\xC5\xFB\x10\x0D\x10\x00\x00\x00",
2779 emit.lowered(),
2780 "vmovsd xmm1, qword ptr [rip + 0x10]",
27952781 );
2782}
2783
2784test "lower to RVM encoding" {
2785 var emit = TestEmit.init();
2786 defer emit.deinit();
2787 try lowerToRvmEnc(.vaddsd, .xmm0, .xmm1, RegisterOrMemory.reg(.xmm2), emit.code());
2788 try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2");
2789 try lowerToRvmEnc(.vaddsd, .xmm0, .xmm0, RegisterOrMemory.reg(.xmm1), emit.code());
27962790 try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1");
27972791}
src/arch/x86_64/Mir.zig+32-34
......@@ -14,8 +14,7 @@ const assert = std.debug.assert;
1414const bits = @import("bits.zig");
1515const Air = @import("../../Air.zig");
1616const CodeGen = @import("CodeGen.zig");
17const GpRegister = bits.Register;
18const AvxRegister = bits.AvxRegister;
17const Register = bits.Register;
1918
2019instructions: std.MultiArrayList(Inst).Slice,
2120/// The meaning of this data is determined by `Inst.Tag` value.
......@@ -23,11 +22,7 @@ extra: []const u32,
2322
2423pub const Inst = struct {
2524 tag: Tag,
26 /// This is 3 fields, and the meaning of each depends on `tag`.
27 /// reg1: Register
28 /// reg2: Register
29 /// flags: u2
30 ops: u16,
25 ops: Ops,
3126 /// The meaning of this depends on `tag` and `ops`.
3227 data: Data,
3328
......@@ -397,6 +392,36 @@ pub const Inst = struct {
397392 /// The position of an MIR instruction within the `Mir` instructions array.
398393 pub const Index = u32;
399394
395 pub const Ops = packed struct {
396 reg1: u7,
397 reg2: u7,
398 flags: u2,
399
400 pub fn encode(vals: struct {
401 reg1: Register = .none,
402 reg2: Register = .none,
403 flags: u2 = 0b00,
404 }) Ops {
405 return .{
406 .reg1 = @enumToInt(vals.reg1),
407 .reg2 = @enumToInt(vals.reg2),
408 .flags = vals.flags,
409 };
410 }
411
412 pub fn decode(ops: Ops) struct {
413 reg1: Register,
414 reg2: Register,
415 flags: u2,
416 } {
417 return .{
418 .reg1 = @intToEnum(Register, ops.reg1),
419 .reg2 = @intToEnum(Register, ops.reg2),
420 .flags = ops.flags,
421 };
422 }
423 };
424
400425 /// All instructions have a 4-byte payload, which is contained within
401426 /// this union. `Tag` determines which union field is active, as well as
402427 /// how to interpret the data within.
......@@ -466,33 +491,6 @@ pub const DbgLineColumn = struct {
466491 column: u32,
467492};
468493
469pub fn Ops(comptime Reg1: type, comptime Reg2: type) type {
470 return struct {
471 reg1: Reg1 = .none,
472 reg2: Reg2 = .none,
473 flags: u2 = 0b00,
474
475 pub fn encode(self: @This()) u16 {
476 var ops: u16 = 0;
477 ops |= @intCast(u16, @enumToInt(self.reg1)) << 9;
478 ops |= @intCast(u16, @enumToInt(self.reg2)) << 2;
479 ops |= self.flags;
480 return ops;
481 }
482
483 pub fn decode(ops: u16) @This() {
484 const reg1 = @intToEnum(Reg1, @truncate(u7, ops >> 9));
485 const reg2 = @intToEnum(Reg2, @truncate(u7, ops >> 2));
486 const flags = @truncate(u2, ops);
487 return .{
488 .reg1 = reg1,
489 .reg2 = reg2,
490 .flags = flags,
491 };
492 }
493 };
494}
495
496494pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
497495 mir.instructions.deinit(gpa);
498496 gpa.free(mir.extra);
src/arch/x86_64/abi.zig+11-6
......@@ -3,7 +3,6 @@ const Type = @import("../../type.zig").Type;
33const Target = std.Target;
44const assert = std.debug.assert;
55const Register = @import("bits.zig").Register;
6const AvxRegister = @import("bits.zig").AvxRegister;
76
87pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
98
......@@ -379,11 +378,17 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
379378/// the caller relinquishes control to a subroutine via call instruction (or similar).
380379/// In other words, these registers are free to use by the callee.
381380pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
382pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
383pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
384pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
385
386pub const avx_regs = [_]AvxRegister{
381pub const avx_regs = [_]Register{
387382 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
388383 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
389384};
385pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs;
386
387// Masks for register manager
388const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len);
389// TODO
390pub const gp_mask: FreeRegInt = 0x3fff;
391pub const avx_mask: FreeRegInt = 0x3fff_c000;
392
393pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
394pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
src/arch/x86_64/bits.zig+38-62
......@@ -43,17 +43,36 @@ pub const Register = enum(u7) {
4343 al, cl, dl, bl, ah, ch, dh, bh,
4444 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
4545
46 // Pseudo, used only for MIR to signify that the
47 // operand is not a register but an immediate, etc.
46 // 64-79, 256-bit registers.
47 // id is int value - 64.
48 ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
49 ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
50
51 // 80-95, 128-bit registers.
52 // id is int value - 80.
53 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
54 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
55
56 // Pseudo-value for MIR instructions.
4857 none,
4958
59 pub fn id(self: Register) u5 {
60 return switch (@enumToInt(self)) {
61 0...63 => @as(u5, @truncate(u4, @enumToInt(self))),
62 64...79 => @truncate(u5, @enumToInt(self)),
63 else => unreachable,
64 };
65 }
66
5067 /// Returns the bit-width of the register.
51 pub fn size(self: Register) u7 {
68 pub fn size(self: Register) u9 {
5269 return switch (@enumToInt(self)) {
5370 0...15 => 64,
5471 16...31 => 32,
5572 32...47 => 16,
56 48...64 => 8,
73 48...63 => 8,
74 64...79 => 256,
75 80...95 => 128,
5776 else => unreachable,
5877 };
5978 }
......@@ -72,15 +91,23 @@ pub const Register = enum(u7) {
7291 /// an instruction (@see isExtended), and requires special handling. The
7392 /// lower three bits are often embedded directly in instructions (such as
7493 /// the B8 variant of moves), or used in R/M bytes.
75 pub fn id(self: Register) u4 {
94 pub fn enc(self: Register) u4 {
7695 return @truncate(u4, @enumToInt(self));
7796 }
7897
79 /// Like id, but only returns the lower 3 bits.
80 pub fn lowId(self: Register) u3 {
98 /// Like enc, but only returns the lower 3 bits.
99 pub fn lowEnc(self: Register) u3 {
81100 return @truncate(u3, @enumToInt(self));
82101 }
83102
103 pub fn to256(self: Register) Register {
104 return @intToEnum(Register, @as(u8, self.id()) + 64);
105 }
106
107 pub fn to128(self: Register) Register {
108 return @intToEnum(Register, @as(u8, self.id()) + 80);
109 }
110
84111 /// Convert from any register to its 64 bit alias.
85112 pub fn to64(self: Register) Register {
86113 return @intToEnum(Register, self.id());
......@@ -126,57 +153,6 @@ pub const Register = enum(u7) {
126153 }
127154};
128155
129/// AVX registers.
130/// TODO missing dwarfLocOp implementation.
131/// TODO add support for AVX-512
132pub const AvxRegister = enum(u6) {
133 // 256-bit registers
134 ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
135 ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
136
137 // 128-bit registers
138 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
139 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
140
141 // Pseudo, used only for MIR to signify that the
142 // operand is not a register but an immediate, etc.
143 none,
144
145 /// Returns the bit-width of the register.
146 pub fn size(self: AvxRegister) u9 {
147 return switch (@enumToInt(self)) {
148 0...15 => 256,
149 16...31 => 128,
150 else => unreachable,
151 };
152 }
153
154 /// Returns whether the register is *extended*.
155 pub fn isExtended(self: AvxRegister) bool {
156 return @enumToInt(self) & 0x08 != 0;
157 }
158
159 /// This returns the 4-bit register ID.
160 pub fn id(self: AvxRegister) u4 {
161 return @truncate(u4, @enumToInt(self));
162 }
163
164 /// Like id, but only returns the lower 3 bits.
165 pub fn lowId(self: AvxRegister) u3 {
166 return @truncate(u3, @enumToInt(self));
167 }
168
169 /// Convert from any register to its 256 bit alias.
170 pub fn to256(self: AvxRegister) AvxRegister {
171 return @intToEnum(AvxRegister, self.id());
172 }
173
174 /// Convert from any register to its 128 bit alias.
175 pub fn to128(self: AvxRegister) AvxRegister {
176 return @intToEnum(AvxRegister, @as(u8, self.id()) + 16);
177 }
178};
179
180156// zig fmt: on
181157
182158/// Encoding helper functions for x86_64 instructions
......@@ -792,7 +768,7 @@ test "Encoder helpers - Vex prefix" {
792768 {
793769 stream.reset();
794770 var vex_prefix = Encoder.Vex{};
795 vex_prefix.reg(AvxRegister.xmm15.id());
771 vex_prefix.reg(Register.xmm15.id());
796772 const nwritten = vex_prefix.write(writer);
797773 try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]);
798774 }
......@@ -832,7 +808,7 @@ test "Encoder helpers - Vex prefix" {
832808 vex.simd_prefix_66();
833809 encoder.vex(vex); // use 64 bit operation
834810 encoder.opcode_1byte(0x28);
835 encoder.modRm_direct(0, AvxRegister.xmm1.lowId());
811 encoder.modRm_direct(0, Register.xmm1.lowId());
836812 try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items);
837813 }
838814
......@@ -846,10 +822,10 @@ test "Encoder helpers - Vex prefix" {
846822 vex.simd_prefix_66();
847823 vex.lead_opc_0f();
848824 vex.rex(.{ .r = true });
849 vex.reg(AvxRegister.xmm1.id());
825 vex.reg(Register.xmm1.id());
850826 encoder.vex(vex);
851827 encoder.opcode_1byte(0x16);
852 encoder.modRm_RIPDisp32(AvxRegister.xmm13.lowId());
828 encoder.modRm_RIPDisp32(Register.xmm13.lowId());
853829 encoder.disp32(0);
854830 try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items);
855831 }
src/codegen.zig+12-12
......@@ -78,13 +78,13 @@ pub fn generateFunction(
7878 debug_output: DebugInfoOutput,
7979) GenerateSymbolError!FnResult {
8080 switch (bin_file.options.target.cpu.arch) {
81 // .arm,
82 // .armeb,
83 // => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
84 // .aarch64,
85 // .aarch64_be,
86 // .aarch64_32,
87 // => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
81 .arm,
82 .armeb,
83 => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
84 .aarch64,
85 .aarch64_be,
86 .aarch64_32,
87 => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
8888 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
8989 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9090 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
......@@ -101,9 +101,9 @@ pub fn generateFunction(
101101 //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
102102 //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
103103 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
104 // .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
104 .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
105105 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
106 // .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
106 .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
107107 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
108108 //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
109109 //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
......@@ -129,9 +129,9 @@ pub fn generateFunction(
129129 //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
130130 //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
131131 //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
132 // .wasm32,
133 // .wasm64,
134 // => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
132 .wasm32,
133 .wasm64,
134 => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
135135 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
136136 }
137137}
src/register_manager.zig+255-261
......@@ -23,15 +23,10 @@ pub const AllocateRegistersError = error{
2323 CodegenFail,
2424};
2525
26pub fn SpillFn(comptime Function: type, comptime Register: type) type {
27 return fn (*Function, Register, Air.Inst.Index) anyerror!void;
28}
29
3026pub fn RegisterManager(
3127 comptime Function: type,
3228 comptime Register: type,
3329 comptime tracked_registers: []const Register,
34 comptime spill_fn: SpillFn(Function, Register),
3530) type {
3631 // architectures which do not have a concept of registers should
3732 // refrain from using RegisterManager
......@@ -52,7 +47,6 @@ pub fn RegisterManager(
5247 allocated_registers: FreeRegInt = 0,
5348 /// Tracks registers which are locked from being allocated
5449 locked_registers: FreeRegInt = 0,
55 function: *Function,
5650
5751 const Self = @This();
5852
......@@ -61,8 +55,8 @@ pub fn RegisterManager(
6155 const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len);
6256 const ShiftInt = math.Log2Int(FreeRegInt);
6357
64 fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) AllocateRegistersError!void {
65 return try spill_fn(self.function, reg, inst);
58 fn getFunction(self: *Self) *Function {
59 return @fieldParentPtr(Function, "register_manager", self);
6660 }
6761
6862 fn getRegisterMask(reg: Register) ?FreeRegInt {
......@@ -257,14 +251,14 @@ pub fn RegisterManager(
257251 self.markRegUsed(reg);
258252 } else {
259253 const spilled_inst = self.registers[index];
260 try self.spillInstruction(reg, spilled_inst);
254 try self.getFunction().spillInstruction(reg, spilled_inst);
261255 }
262256 self.registers[index] = inst;
263257 } else {
264258 // Don't track the register
265259 if (!self.isRegFree(reg)) {
266260 const spilled_inst = self.registers[index];
267 try self.spillInstruction(reg, spilled_inst);
261 try self.getFunction().spillInstruction(reg, spilled_inst);
268262 self.freeReg(reg);
269263 }
270264 }
......@@ -299,7 +293,7 @@ pub fn RegisterManager(
299293 // stack allocation.
300294 const spilled_inst = self.registers[index];
301295 self.registers[index] = tracked_inst;
302 try self.spillInstruction(reg, spilled_inst);
296 try self.getFunction().spillInstruction(reg, spilled_inst);
303297 } else {
304298 self.getRegAssumeFree(reg, tracked_inst);
305299 }
......@@ -308,7 +302,7 @@ pub fn RegisterManager(
308302 // Move the instruction that was previously there to a
309303 // stack allocation.
310304 const spilled_inst = self.registers[index];
311 try self.spillInstruction(reg, spilled_inst);
305 try self.getFunction().spillInstruction(reg, spilled_inst);
312306 self.freeReg(reg);
313307 }
314308 }
......@@ -338,253 +332,253 @@ pub fn RegisterManager(
338332 };
339333}
340334
341//const MockRegister1 = enum(u2) {
342// r0,
343// r1,
344// r2,
345// r3,
346
347// pub fn id(reg: MockRegister1) u2 {
348// return @enumToInt(reg);
349// }
335const MockRegister1 = enum(u2) {
336 r0,
337 r1,
338 r2,
339 r3,
340
341 pub fn id(reg: MockRegister1) u2 {
342 return @enumToInt(reg);
343 }
344
345 const allocatable_registers = [_]MockRegister1{ .r2, .r3 };
346};
347
348const MockRegister2 = enum(u2) {
349 r0,
350 r1,
351 r2,
352 r3,
353
354 pub fn id(reg: MockRegister2) u2 {
355 return @enumToInt(reg);
356 }
357
358 const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
359};
360
361fn MockFunction(comptime Register: type) type {
362 return struct {
363 allocator: Allocator,
364 register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{},
365 spilled: std.ArrayListUnmanaged(Register) = .{},
366
367 const Self = @This();
368
369 pub fn deinit(self: *Self) void {
370 self.spilled.deinit(self.allocator);
371 }
372
373 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
374 _ = inst;
375 try self.spilled.append(self.allocator, reg);
376 }
377
378 pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void {
379 _ = self;
380 _ = res;
381 _ = lhs;
382 _ = rhs;
383 }
384 };
385}
386
387const MockFunction1 = MockFunction(MockRegister1);
388const MockFunction2 = MockFunction(MockRegister2);
389
390test "default state" {
391 const allocator = std.testing.allocator;
392
393 var function = MockFunction1{
394 .allocator = allocator,
395 };
396 defer function.deinit();
397
398 try expect(!function.register_manager.isRegAllocated(.r2));
399 try expect(!function.register_manager.isRegAllocated(.r3));
400 try expect(function.register_manager.isRegFree(.r2));
401 try expect(function.register_manager.isRegFree(.r3));
402}
403
404test "tryAllocReg: no spilling" {
405 const allocator = std.testing.allocator;
406
407 var function = MockFunction1{
408 .allocator = allocator,
409 };
410 defer function.deinit();
411
412 const mock_instruction: Air.Inst.Index = 1;
413
414 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction));
415 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction));
416 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction));
417
418 try expect(function.register_manager.isRegAllocated(.r2));
419 try expect(function.register_manager.isRegAllocated(.r3));
420 try expect(!function.register_manager.isRegFree(.r2));
421 try expect(!function.register_manager.isRegFree(.r3));
422
423 function.register_manager.freeReg(.r2);
424 function.register_manager.freeReg(.r3);
425
426 try expect(function.register_manager.isRegAllocated(.r2));
427 try expect(function.register_manager.isRegAllocated(.r3));
428 try expect(function.register_manager.isRegFree(.r2));
429 try expect(function.register_manager.isRegFree(.r3));
430}
431
432test "allocReg: spilling" {
433 const allocator = std.testing.allocator;
350434
351// const allocatable_registers = [_]MockRegister1{ .r2, .r3 };
352//};
353
354//const MockRegister2 = enum(u2) {
355// r0,
356// r1,
357// r2,
358// r3,
359
360// pub fn id(reg: MockRegister2) u2 {
361// return @enumToInt(reg);
362// }
363
364// const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
365//};
366
367//fn MockFunction(comptime Register: type) type {
368// return struct {
369// allocator: Allocator,
370// register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{},
371// spilled: std.ArrayListUnmanaged(Register) = .{},
372
373// const Self = @This();
374
375// pub fn deinit(self: *Self) void {
376// self.spilled.deinit(self.allocator);
377// }
378
379// pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
380// _ = inst;
381// try self.spilled.append(self.allocator, reg);
382// }
383
384// pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void {
385// _ = self;
386// _ = res;
387// _ = lhs;
388// _ = rhs;
389// }
390// };
391//}
392
393//const MockFunction1 = MockFunction(MockRegister1);
394//const MockFunction2 = MockFunction(MockRegister2);
395
396//test "default state" {
397// const allocator = std.testing.allocator;
398
399// var function = MockFunction1{
400// .allocator = allocator,
401// };
402// defer function.deinit();
403
404// try expect(!function.register_manager.isRegAllocated(.r2));
405// try expect(!function.register_manager.isRegAllocated(.r3));
406// try expect(function.register_manager.isRegFree(.r2));
407// try expect(function.register_manager.isRegFree(.r3));
408//}
409
410//test "tryAllocReg: no spilling" {
411// const allocator = std.testing.allocator;
412
413// var function = MockFunction1{
414// .allocator = allocator,
415// };
416// defer function.deinit();
417
418// const mock_instruction: Air.Inst.Index = 1;
419
420// try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction));
421// try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction));
422// try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction));
423
424// try expect(function.register_manager.isRegAllocated(.r2));
425// try expect(function.register_manager.isRegAllocated(.r3));
426// try expect(!function.register_manager.isRegFree(.r2));
427// try expect(!function.register_manager.isRegFree(.r3));
428
429// function.register_manager.freeReg(.r2);
430// function.register_manager.freeReg(.r3);
431
432// try expect(function.register_manager.isRegAllocated(.r2));
433// try expect(function.register_manager.isRegAllocated(.r3));
434// try expect(function.register_manager.isRegFree(.r2));
435// try expect(function.register_manager.isRegFree(.r3));
436//}
437
438//test "allocReg: spilling" {
439// const allocator = std.testing.allocator;
440
441// var function = MockFunction1{
442// .allocator = allocator,
443// };
444// defer function.deinit();
445
446// const mock_instruction: Air.Inst.Index = 1;
447
448// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
449// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
450
451// // Spill a register
452// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
453// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
454
455// // No spilling necessary
456// function.register_manager.freeReg(.r3);
457// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
458// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
459
460// // Locked registers
461// function.register_manager.freeReg(.r3);
462// {
463// const lock = function.register_manager.lockReg(.r2);
464// defer if (lock) |reg| function.register_manager.unlockReg(reg);
465
466// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
467// }
468// try expect(!function.register_manager.lockedRegsExist());
469//}
470
471//test "tryAllocRegs" {
472// const allocator = std.testing.allocator;
473
474// var function = MockFunction2{
475// .allocator = allocator,
476// };
477// defer function.deinit();
478
479// try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
480
481// try expect(function.register_manager.isRegAllocated(.r0));
482// try expect(function.register_manager.isRegAllocated(.r1));
483// try expect(function.register_manager.isRegAllocated(.r2));
484// try expect(!function.register_manager.isRegAllocated(.r3));
485
486// // Locked registers
487// function.register_manager.freeReg(.r0);
488// function.register_manager.freeReg(.r2);
489// function.register_manager.freeReg(.r3);
490// {
491// const lock = function.register_manager.lockReg(.r1);
492// defer if (lock) |reg| function.register_manager.unlockReg(reg);
493
494// try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
495// }
496// try expect(!function.register_manager.lockedRegsExist());
497
498// try expect(function.register_manager.isRegAllocated(.r0));
499// try expect(function.register_manager.isRegAllocated(.r1));
500// try expect(function.register_manager.isRegAllocated(.r2));
501// try expect(function.register_manager.isRegAllocated(.r3));
502//}
503
504//test "allocRegs: normal usage" {
505// // TODO: convert this into a decltest once that is supported
506
507// const allocator = std.testing.allocator;
508
509// var function = MockFunction2{
510// .allocator = allocator,
511// };
512// defer function.deinit();
513
514// {
515// const result_reg: MockRegister2 = .r1;
516
517// // The result register is known and fixed at this point, we
518// // don't want to accidentally allocate lhs or rhs to the
519// // result register, this is why we lock it.
520// //
521// // Using defer unlock right after lock is a good idea in
522// // most cases as you probably are using the locked registers
523// // in the remainder of this scope and don't need to use it
524// // after the end of this scope. However, in some situations,
525// // it may make sense to manually unlock registers before the
526// // end of the scope when you are certain that they don't
527// // contain any valuable data anymore and can be reused. For an
528// // example of that, see `selectively reducing register
529// // pressure`.
530// const lock = function.register_manager.lockReg(result_reg);
531// defer if (lock) |reg| function.register_manager.unlockReg(reg);
532
533// const regs = try function.register_manager.allocRegs(2, .{ null, null });
534// try function.genAdd(result_reg, regs[0], regs[1]);
535// }
536//}
537
538//test "allocRegs: selectively reducing register pressure" {
539// // TODO: convert this into a decltest once that is supported
540
541// const allocator = std.testing.allocator;
542
543// var function = MockFunction2{
544// .allocator = allocator,
545// };
546// defer function.deinit();
547
548// {
549// const result_reg: MockRegister2 = .r1;
550
551// const lock = function.register_manager.lockReg(result_reg);
552
553// // Here, we don't defer unlock because we manually unlock
554// // after genAdd
555// const regs = try function.register_manager.allocRegs(2, .{ null, null });
556
557// try function.genAdd(result_reg, regs[0], regs[1]);
558// function.register_manager.unlockReg(lock.?);
559
560// const extra_summand_reg = try function.register_manager.allocReg(null);
561// try function.genAdd(result_reg, result_reg, extra_summand_reg);
562// }
563//}
564
565//test "getReg" {
566// const allocator = std.testing.allocator;
567
568// var function = MockFunction1{
569// .allocator = allocator,
570// };
571// defer function.deinit();
572
573// const mock_instruction: Air.Inst.Index = 1;
574
575// try function.register_manager.getReg(.r3, mock_instruction);
576
577// try expect(!function.register_manager.isRegAllocated(.r2));
578// try expect(function.register_manager.isRegAllocated(.r3));
579// try expect(function.register_manager.isRegFree(.r2));
580// try expect(!function.register_manager.isRegFree(.r3));
581
582// // Spill r3
583// try function.register_manager.getReg(.r3, mock_instruction);
584
585// try expect(!function.register_manager.isRegAllocated(.r2));
586// try expect(function.register_manager.isRegAllocated(.r3));
587// try expect(function.register_manager.isRegFree(.r2));
588// try expect(!function.register_manager.isRegFree(.r3));
589// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items);
590//}
435 var function = MockFunction1{
436 .allocator = allocator,
437 };
438 defer function.deinit();
439
440 const mock_instruction: Air.Inst.Index = 1;
441
442 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
443 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
444
445 // Spill a register
446 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
447 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
448
449 // No spilling necessary
450 function.register_manager.freeReg(.r3);
451 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
452 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
453
454 // Locked registers
455 function.register_manager.freeReg(.r3);
456 {
457 const lock = function.register_manager.lockReg(.r2);
458 defer if (lock) |reg| function.register_manager.unlockReg(reg);
459
460 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
461 }
462 try expect(!function.register_manager.lockedRegsExist());
463}
464
465test "tryAllocRegs" {
466 const allocator = std.testing.allocator;
467
468 var function = MockFunction2{
469 .allocator = allocator,
470 };
471 defer function.deinit();
472
473 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
474
475 try expect(function.register_manager.isRegAllocated(.r0));
476 try expect(function.register_manager.isRegAllocated(.r1));
477 try expect(function.register_manager.isRegAllocated(.r2));
478 try expect(!function.register_manager.isRegAllocated(.r3));
479
480 // Locked registers
481 function.register_manager.freeReg(.r0);
482 function.register_manager.freeReg(.r2);
483 function.register_manager.freeReg(.r3);
484 {
485 const lock = function.register_manager.lockReg(.r1);
486 defer if (lock) |reg| function.register_manager.unlockReg(reg);
487
488 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
489 }
490 try expect(!function.register_manager.lockedRegsExist());
491
492 try expect(function.register_manager.isRegAllocated(.r0));
493 try expect(function.register_manager.isRegAllocated(.r1));
494 try expect(function.register_manager.isRegAllocated(.r2));
495 try expect(function.register_manager.isRegAllocated(.r3));
496}
497
498test "allocRegs: normal usage" {
499 // TODO: convert this into a decltest once that is supported
500
501 const allocator = std.testing.allocator;
502
503 var function = MockFunction2{
504 .allocator = allocator,
505 };
506 defer function.deinit();
507
508 {
509 const result_reg: MockRegister2 = .r1;
510
511 // The result register is known and fixed at this point, we
512 // don't want to accidentally allocate lhs or rhs to the
513 // result register, this is why we lock it.
514 //
515 // Using defer unlock right after lock is a good idea in
516 // most cases as you probably are using the locked registers
517 // in the remainder of this scope and don't need to use it
518 // after the end of this scope. However, in some situations,
519 // it may make sense to manually unlock registers before the
520 // end of the scope when you are certain that they don't
521 // contain any valuable data anymore and can be reused. For an
522 // example of that, see `selectively reducing register
523 // pressure`.
524 const lock = function.register_manager.lockReg(result_reg);
525 defer if (lock) |reg| function.register_manager.unlockReg(reg);
526
527 const regs = try function.register_manager.allocRegs(2, .{ null, null });
528 try function.genAdd(result_reg, regs[0], regs[1]);
529 }
530}
531
532test "allocRegs: selectively reducing register pressure" {
533 // TODO: convert this into a decltest once that is supported
534
535 const allocator = std.testing.allocator;
536
537 var function = MockFunction2{
538 .allocator = allocator,
539 };
540 defer function.deinit();
541
542 {
543 const result_reg: MockRegister2 = .r1;
544
545 const lock = function.register_manager.lockReg(result_reg);
546
547 // Here, we don't defer unlock because we manually unlock
548 // after genAdd
549 const regs = try function.register_manager.allocRegs(2, .{ null, null });
550
551 try function.genAdd(result_reg, regs[0], regs[1]);
552 function.register_manager.unlockReg(lock.?);
553
554 const extra_summand_reg = try function.register_manager.allocReg(null);
555 try function.genAdd(result_reg, result_reg, extra_summand_reg);
556 }
557}
558
559test "getReg" {
560 const allocator = std.testing.allocator;
561
562 var function = MockFunction1{
563 .allocator = allocator,
564 };
565 defer function.deinit();
566
567 const mock_instruction: Air.Inst.Index = 1;
568
569 try function.register_manager.getReg(.r3, mock_instruction);
570
571 try expect(!function.register_manager.isRegAllocated(.r2));
572 try expect(function.register_manager.isRegAllocated(.r3));
573 try expect(function.register_manager.isRegFree(.r2));
574 try expect(!function.register_manager.isRegFree(.r3));
575
576 // Spill r3
577 try function.register_manager.getReg(.r3, mock_instruction);
578
579 try expect(!function.register_manager.isRegAllocated(.r2));
580 try expect(function.register_manager.isRegAllocated(.r3));
581 try expect(function.register_manager.isRegFree(.r2));
582 try expect(!function.register_manager.isRegFree(.r3));
583 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items);
584}