diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 447 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 296 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 49 | ||||
| -rw-r--r-- | src/arch/x86_64/bits.zig | 129 |
4 files changed, 552 insertions, 369 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c69f78724..8adad94e24 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -37,6 +37,7 @@ const caller_preserved_regs = abi.caller_preserved_regs; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; +const Condition = bits.Condition; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -65,7 +66,7 @@ arg_index: u32, src_loc: Module.SrcLoc, stack_align: u32, -compare_flags_inst: ?Air.Inst.Index = null, +eflags_inst: ?Air.Inst.Index = null, /// MIR Instructions mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, @@ -127,12 +128,8 @@ pub const MCValue = union(enum) { immediate: u64, /// The value is in a GP register. register: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, - /// and the operation is an unsigned operation. - register_overflow_unsigned: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, - /// and the operation is a signed operation. - register_overflow_signed: Register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register. + register_overflow: struct { reg: Register, eflags: Condition }, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -149,12 +146,8 @@ pub const MCValue = union(enum) { stack_offset: i32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: i32, - /// The value is in the compare flags assuming an unsigned operation, - /// with this operator applied on top of it. - compare_flags_unsigned: math.CompareOperator, - /// The value is in the compare flags assuming a signed operation, - /// with this operator applied on top of it. - compare_flags_signed: math.CompareOperator, + /// The value resides in the EFLAGS register. + eflags: Condition, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -183,12 +176,10 @@ pub const MCValue = union(enum) { .immediate, .memory, - .compare_flags_unsigned, - .compare_flags_signed, + .eflags, .ptr_stack_offset, .undef, - .register_overflow_unsigned, - .register_overflow_signed, + .register_overflow, => false, .register, @@ -778,12 +769,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg.to64()); }, - .register_overflow_signed, .register_overflow_unsigned => |reg| { - self.register_manager.freeReg(reg.to64()); - self.compare_flags_inst = null; + .register_overflow => |ro| { + self.register_manager.freeReg(ro.reg.to64()); + self.eflags_inst = null; }, - .compare_flags_signed, .compare_flags_unsigned => { - self.compare_flags_inst = null; + .eflags => { + self.eflags_inst = null; }, else => {}, // TODO process stack allocation death } @@ -813,20 +804,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. switch (result) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. + .register => |reg| { if (self.register_manager.isRegFree(reg)) { self.register_manager.getRegAssumeFree(reg, inst); } }, + .register_overflow => |ro| { + if (self.register_manager.isRegFree(ro.reg)) { + self.register_manager.getRegAssumeFree(ro.reg, inst); + } + }, else => {}, } } @@ -916,12 +909,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); switch (reg_mcv) { - .register, - .register_overflow_unsigned, - .register_overflow_signed, - => |other| { + .register => |other| { assert(reg.to64() == other.to64()); }, + .register_overflow => |ro| { + assert(reg.to64() == ro.reg.to64()); + }, else => {}, } const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -929,16 +922,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); } -pub fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { +pub fn spillEflagsIfOccupied(self: *Self) !void { + if (self.eflags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { - .register_overflow_signed, - .register_overflow_unsigned, - => try self.allocRegOrMem(inst_to_save, false), - .compare_flags_signed, - .compare_flags_unsigned, - => try self.allocRegOrMem(inst_to_save, true), + .register_overflow => try self.allocRegOrMem(inst_to_save, false), + .eflags => try self.allocRegOrMem(inst_to_save, true), else => unreachable, }; @@ -948,14 +937,12 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - self.compare_flags_inst = null; + self.eflags_inst = null; // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! switch (mcv) { - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| self.register_manager.freeReg(reg), + .register_overflow => |ro| self.register_manager.freeReg(ro.reg), else => {}, } } @@ -1123,31 +1110,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { switch (operand) { .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => |op| { - const r = MCValue{ - .compare_flags_unsigned = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, - .compare_flags_signed => |op| { - const r = MCValue{ - .compare_flags_signed = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; + .eflags => |cc| { + break :result MCValue{ .eflags = cc.negate() }; }, else => {}, } @@ -1213,13 +1177,17 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); + const cc: Condition = switch (signedness) { + .unsigned => .b, + .signed => .l, + }; _ = try self.addInst(.{ - .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below, + .tag = .cond_mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_mcv.register, .reg2 = lhs_reg, }), - .data = undefined, + .data = .{ .cc = cc }, }); break :result dst_mcv; @@ -1341,7 +1309,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); } - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (tag == .shl_with_overflow) { try self.spillRegisters(1, .{.rcx}); @@ -1362,16 +1330,19 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const int_info = ty.intInfo(self.target.*); if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { - self.compare_flags_inst = inst; + self.eflags_inst = inst; - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, + const cc: Condition = switch (int_info.signedness) { + .unsigned => .c, + .signed => .o, }; - break :result result; + break :result MCValue{ .register_overflow = .{ + .reg = partial.register, + .eflags = cc, + } }; } - self.compare_flags_inst = null; + self.eflags_inst = null; const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); @@ -1413,17 +1384,16 @@ fn genSetStackTruncatedOverflowCompare( }; const overflow_reg = temp_regs[0]; - const flags: u2 = switch (int_info.signedness) { - .signed => 0b00, - .unsigned => 0b10, + const cc: Condition = switch (int_info.signedness) { + .signed => .o, + .unsigned => .c, }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = overflow_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); const scratch_reg = temp_regs[1]; @@ -1438,9 +1408,9 @@ fn genSetStackTruncatedOverflowCompare( const eq_reg = temp_regs[2]; _ = try self.addInst(.{ - .tag = .cond_set_byte_eq_ne, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), - .data = undefined, + .data = .{ .cc = .ne }, }); try self.genBinOpMir( @@ -1477,8 +1447,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const int_info = ty.intInfo(self.target.*); if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; try self.spillRegisters(2, .{ .rax, .rdx }); @@ -1486,14 +1456,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs = try self.resolveInst(bin_op.rhs); const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); - break :result switch (int_info.signedness) { - .signed => MCValue{ .register_overflow_signed = partial.register }, - .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, + const cc: Condition = switch (int_info.signedness) { + .unsigned => .c, + .signed => .o, }; + break :result MCValue{ .register_overflow = .{ + .reg = partial.register, + .eflags = cc, + } }; } - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + try self.spillEflagsIfOccupied(); + self.eflags_inst = null; const dst_reg: Register = dst_reg: { switch (int_info.signedness) { @@ -1686,12 +1660,12 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .data = undefined, }); _ = try self.addInst(.{ - .tag = .cond_mov_eq, + .tag = .cond_mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = divisor.to64(), .reg2 = .rdx, }), - .data = undefined, + .data = .{ .cc = .e }, }); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; @@ -2511,10 +2485,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .eflags => unreachable, + .register_overflow => unreachable, .immediate => |imm| { try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); }, @@ -2532,8 +2504,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo switch (dst_mcv) { .dead => unreachable, .undef => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .register => |dst_reg| { // mov dst_reg, [reg] _ = try self.addInst(.{ @@ -2637,10 +2608,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .eflags => unreachable, + .register_overflow => unreachable, .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, @@ -2660,8 +2629,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .immediate => |imm| { switch (abi_size) { 1, 2, 4 => { @@ -3027,32 +2995,24 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result dst_mcv; }, - .register_overflow_unsigned, - .register_overflow_signed, - => |reg| { + .register_overflow => |ro| { switch (index) { 0 => { // Get wrapped value for overflow operation. - break :result MCValue{ .register = reg }; + break :result MCValue{ .register = ro.reg }; }, 1 => { // Get overflow bit. - const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + const reg_lock = self.register_manager.lockRegAssumeUnused(ro.reg); defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - const flags: u2 = switch (mcv) { - .register_overflow_unsigned => 0b10, - .register_overflow_signed => 0b00, - else => unreachable, - }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = ro.eflags }, }); break :result MCValue{ .register = dst_reg.to8() }; }, @@ -3479,17 +3439,14 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .none => unreachable, .undef => unreachable, .dead, .unreach, .immediate => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .eflags => unreachable, + .register_overflow => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .ptr_stack_offset => { const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); @@ -3559,8 +3516,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .memory, .got_load, .direct_load, - .compare_flags_signed, - .compare_flags_unsigned, + .eflags, => { assert(abi_size <= 8); const dst_reg_lock = self.register_manager.lockReg(dst_reg); @@ -3597,8 +3553,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, @@ -3649,11 +3604,8 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 ADD/SUB/CMP source eflags", .{}); }, } }, @@ -3674,19 +3626,16 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .none => unreachable, .undef => unreachable, .dead, .unreach, .immediate => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ @@ -3734,11 +3683,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 multiply source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 multiply source eflags", .{}); }, } }, @@ -3748,8 +3694,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3782,11 +3727,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 multiply source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 multiply source eflags", .{}); }, } }, @@ -3905,7 +3847,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); for (caller_preserved_regs) |reg| { try self.register_manager.getReg(reg, null); @@ -3957,10 +3899,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .memory => unreachable, .got_load => unreachable, .direct_load => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .register_overflow_signed => unreachable, - .register_overflow_unsigned => unreachable, + .eflags => unreachable, + .register_overflow => unreachable, } } @@ -4220,8 +4160,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { break :blk ty.intInfo(self.target.*).signedness; }; - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const result: MCValue = result: { // There are 2 operands, destination and source. @@ -4265,9 +4205,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { defer if (src_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); + break :result switch (signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, + .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) }, + .unsigned => MCValue{ .eflags = Condition.fromCompareOperatorUnsigned(op) }, }; }; @@ -4440,47 +4381,39 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { - .compare_flags_unsigned, - .compare_flags_signed, - => |cmp_op| { - // Here we map the opposites since the jump is to the false branch. - const flags: u2 = switch (cmp_op) { - .gte => 0b10, - .gt => 0b11, - .neq => 0b01, - .lt => 0b00, - .lte => 0b01, - .eq => 0b00, - }; - const tag: Mir.Inst.Tag = if (cmp_op == .neq or cmp_op == .eq) - .cond_jmp_eq_ne - else if (mcv == .compare_flags_unsigned) - Mir.Inst.Tag.cond_jmp_above_below - else - Mir.Inst.Tag.cond_jmp_greater_less; + .eflags => |cc| { return self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ .flags = flags }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ + .inst_cc = .{ + .inst = undefined, + // Here we map the opposites since the jump is to the false branch. + .cc = cc.negate(), + }, + }, }); }, .register => |reg| { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); _ = try self.addInst(.{ .tag = .@"test", .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = .{ .imm = 1 }, }); return self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); }, .immediate, .stack_offset, => { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genCondBrMir(ty, .{ .register = reg }); @@ -4516,7 +4449,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const parent_next_stack_offset = self.next_stack_offset; const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_eflags_inst = self.eflags_inst; var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; @@ -4538,7 +4471,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { defer saved_then_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.eflags_inst = parent_eflags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; @@ -4640,8 +4573,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: { var buf: Type.Payload.ElemType = undefined; @@ -4651,13 +4584,13 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); - return MCValue{ .compare_flags_unsigned = .eq }; + return MCValue{ .eflags = .e }; } fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const is_null_res = try self.isNull(inst, ty, operand); - assert(is_null_res.compare_flags_unsigned == .eq); - return MCValue{ .compare_flags_unsigned = .neq }; + assert(is_null_res.eflags == .e); + return MCValue{ .eflags = is_null_res.eflags.negate() }; } fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { @@ -4667,8 +4600,8 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue return MCValue{ .immediate = 0 }; // always false } - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); switch (operand) { @@ -4691,15 +4624,15 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue else => return self.fail("TODO implement isErr for {}", .{operand}), } - return MCValue{ .compare_flags_unsigned = .gt }; + return MCValue{ .eflags = .a }; } fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const is_err_res = try self.isErr(inst, ty, operand); switch (is_err_res) { - .compare_flags_unsigned => |op| { - assert(op == .gt); - return MCValue{ .compare_flags_unsigned = .lte }; + .eflags => |cc| { + assert(cc == .a); + return MCValue{ .eflags = cc.negate() }; }, .immediate => |imm| { assert(imm == 0); @@ -4914,10 +4847,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, + .eflags => unreachable, .register => |cond_reg| { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); const cond_reg_lock = self.register_manager.lockReg(cond_reg); defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock); @@ -4965,13 +4897,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .data = undefined, }); return self.addInst(.{ - .tag = .cond_jmp_eq_ne, + .tag = .cond_jmp, .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = undefined }, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .ne, + } }, }); }, .stack_offset => { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, condition); @@ -5030,7 +4965,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const parent_next_stack_offset = self.next_stack_offset; const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_eflags_inst = self.eflags_inst; var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; @@ -5052,7 +4987,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { defer saved_case_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.eflags_inst = parent_eflags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; parent_stack = .{}; @@ -5092,7 +5027,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { const next_inst = @intCast(u32, self.mir_instructions.len); - self.mir_instructions.items(.data)[reloc].inst = next_inst; + switch (self.mir_instructions.items(.tag)[reloc]) { + .cond_jmp => { + self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; + }, + .jmp => { + self.mir_instructions.items(.data)[reloc].inst = next_inst; + }, + else => unreachable, + } } fn airBr(self: *Self, inst: Air.Inst.Index) !void { @@ -5111,7 +5054,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, .register, .stack_offset, .memory => operand_mcv, - .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: { + .eflags, .immediate => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; @@ -5332,12 +5275,8 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .{ .dest_stack_base = .rsp }, ); }, - .register_overflow_unsigned, - .register_overflow_signed, - => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), - .compare_flags_unsigned, - .compare_flags_signed, - => { + .register_overflow => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), + .eflags => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); }, @@ -5472,30 +5411,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl ), } }, - .register_overflow_unsigned, - .register_overflow_signed, - => |reg| { - const reg_lock = self.register_manager.lockReg(reg); + .register_overflow => |ro| { + const reg_lock = self.register_manager.lockReg(ro.reg); defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); const wrapped_ty = ty.structFieldType(0); - try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = ro.reg }, .{}); const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - const flags: u2 = switch (mcv) { - .register_overflow_unsigned => 0b10, - .register_overflow_signed => 0b00, - else => unreachable, - }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = ro.eflags }, }); return self.genSetStack( @@ -5505,9 +5436,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .{}, ); }, - .compare_flags_unsigned, - .compare_flags_signed, - => { + .eflags => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); }, @@ -5832,9 +5761,12 @@ fn genInlineMemcpy( // je end const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); // mov tmp, [addr + rcx] @@ -5950,9 +5882,12 @@ fn genInlineMemset( // je end const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); switch (value) { @@ -5996,9 +5931,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, - .register_overflow_unsigned, - .register_overflow_signed, - => unreachable, + .register_overflow => unreachable, .ptr_stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); @@ -6025,31 +5958,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, } }, - .compare_flags_unsigned, - .compare_flags_signed, - => |op| { - const tag: Mir.Inst.Tag = switch (op) { - .gte, .gt, .lt, .lte => if (mcv == .compare_flags_unsigned) - Mir.Inst.Tag.cond_set_byte_above_below - else - Mir.Inst.Tag.cond_set_byte_greater_less, - .eq, .neq => .cond_set_byte_eq_ne, - }; - const flags: u2 = switch (op) { - .gte => 0b00, - .gt => 0b01, - .lt => 0b10, - .lte => 0b11, - .eq => 0b01, - .neq => 0b00, - }; + .eflags => |cc| { _ = try self.addInst(.{ - .tag = tag, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); }, .immediate => |x| { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 84955a8aac..654775cc21 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -158,20 +158,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .jmp => try emit.mirJmpCall(.jmp_near, inst), .call => try emit.mirJmpCall(.call_near, inst), - .cond_jmp_greater_less, - .cond_jmp_above_below, - .cond_jmp_eq_ne, - => try emit.mirCondJmp(tag, inst), - - .cond_set_byte_greater_less, - .cond_set_byte_above_below, - .cond_set_byte_eq_ne, - .cond_set_byte_overflow, - => try emit.mirCondSetByte(tag, inst), - - .cond_mov_eq => try emit.mirCondMov(.cmove, inst), - .cond_mov_lt => try emit.mirCondMov(.cmovl, inst), - .cond_mov_below => try emit.mirCondMov(.cmovb, inst), + .cond_jmp => try emit.mirCondJmp(inst), + .cond_set_byte => try emit.mirCondSetByte(inst), + .cond_mov => try emit.mirCondMov(inst), .ret => try emit.mirRet(inst), @@ -356,70 +345,130 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const target = emit.mir.instructions.items(.data)[inst].inst; - const tag = switch (mir_tag) { - .cond_jmp_greater_less => switch (ops.flags) { - 0b00 => Tag.jge, - 0b01 => Tag.jg, - 0b10 => Tag.jl, - 0b11 => Tag.jle, - }, - .cond_jmp_above_below => switch (ops.flags) { - 0b00 => Tag.jae, - 0b01 => Tag.ja, - 0b10 => Tag.jb, - 0b11 => Tag.jbe, - }, - .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) { - 0b0 => Tag.jne, - 0b1 => Tag.je, - }, - else => unreachable, +fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_jmp); + const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; + const tag: Tag = switch (inst_cc.cc) { + .a => .ja, + .ae => .jae, + .b => .jb, + .be => .jbe, + .c => .jc, + .e => .je, + .g => .jg, + .ge => .jge, + .l => .jl, + .le => .jle, + .na => .jna, + .nae => .jnae, + .nb => .jnb, + .nbe => .jnbe, + .nc => .jnc, + .ne => .jne, + .ng => .jng, + .nge => .jnge, + .nl => .jnl, + .nle => .jnle, + .no => .jno, + .np => .jnp, + .ns => .jns, + .nz => .jnz, + .o => .jo, + .p => .jp, + .pe => .jpe, + .po => .jpo, + .s => .js, + .z => .jz, }; const source = emit.code.items.len; try lowerToDEnc(tag, 0, emit.code); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, - .target = target, + .target = inst_cc.inst, .offset = emit.code.items.len - 4, .length = 6, }); } -fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_set_byte); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const tag = switch (mir_tag) { - .cond_set_byte_greater_less => switch (ops.flags) { - 0b00 => Tag.setge, - 0b01 => Tag.setg, - 0b10 => Tag.setl, - 0b11 => Tag.setle, - }, - .cond_set_byte_above_below => switch (ops.flags) { - 0b00 => Tag.setae, - 0b01 => Tag.seta, - 0b10 => Tag.setb, - 0b11 => Tag.setbe, - }, - .cond_set_byte_eq_ne => switch (@truncate(u1, ops.flags)) { - 0b0 => Tag.setne, - 0b1 => Tag.sete, - }, - .cond_set_byte_overflow => switch (ops.flags) { - 0b00 => Tag.seto, - 0b01 => Tag.setno, - 0b10 => Tag.setc, - 0b11 => Tag.setnc, - }, - else => unreachable, + const cc = emit.mir.instructions.items(.data)[inst].cc; + const tag: Tag = switch (cc) { + .a => .seta, + .ae => .setae, + .b => .setb, + .be => .setbe, + .c => .setc, + .e => .sete, + .g => .setg, + .ge => .setge, + .l => .setl, + .le => .setle, + .na => .setna, + .nae => .setnae, + .nb => .setnb, + .nbe => .setnbe, + .nc => .setnc, + .ne => .setne, + .ng => .setng, + .nge => .setnge, + .nl => .setnl, + .nle => .setnle, + .no => .setno, + .np => .setnp, + .ns => .setns, + .nz => .setnz, + .o => .seto, + .p => .setp, + .pe => .setpe, + .po => .setpo, + .s => .sets, + .z => .setz, }; return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); } -fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_mov); const ops = emit.mir.instructions.items(.ops)[inst].decode(); + const cc = emit.mir.instructions.items(.data)[inst].cc; + const tag: Tag = switch (cc) { + .a => .cmova, + .ae => .cmovae, + .b => .cmovb, + .be => .cmovbe, + .c => .cmovc, + .e => .cmove, + .g => .cmovg, + .ge => .cmovge, + .l => .cmovl, + .le => .cmovle, + .na => .cmovna, + .nae => .cmovnae, + .nb => .cmovnb, + .nbe => .cmovnbe, + .nc => .cmovnc, + .ne => .cmovne, + .ng => .cmovng, + .nge => .cmovnge, + .nl => .cmovnl, + .nle => .cmovnle, + .no => .cmovno, + .np => .cmovnp, + .ns => .cmovns, + .nz => .cmovnz, + .o => .cmovo, + .p => .cmovp, + .pe => .cmovpe, + .po => .cmovpo, + .s => .cmovs, + .z => .cmovz, + }; + if (ops.flags == 0b00) { return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); } @@ -1277,7 +1326,7 @@ const Tag = enum { setp, setpe, setnp, - setop, + setpo, setl, setnge, setnl, @@ -1286,6 +1335,36 @@ const Tag = enum { setng, setnle, setg, + cmovo, + cmovno, + cmovb, + cmovc, + cmovnae, + cmovnb, + cmovnc, + cmovae, + cmove, + cmovz, + cmovne, + cmovnz, + cmovbe, + cmovna, + cmova, + cmovnbe, + cmovs, + cmovns, + cmovp, + cmovpe, + cmovnp, + cmovpo, + cmovl, + cmovnge, + cmovnl, + cmovge, + cmovle, + cmovng, + cmovnle, + cmovg, shl, sal, shr, @@ -1294,12 +1373,6 @@ const Tag = enum { cwd, cdq, cqo, - cmove, - cmovz, - cmovl, - cmovng, - cmovb, - cmovnae, movsd, movss, addsd, @@ -1372,7 +1445,7 @@ const Tag = enum { .setp, .setpe, .setnp, - .setop, + .setpo, .setl, .setnge, .setnl, @@ -1492,77 +1565,110 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { .d => return switch (tag) { .jmp_near => OpCode.init(&.{0xe9}), .call_near => OpCode.init(&.{0xe8}), + .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}), + .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}), + .jb, .jc, .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}), + .jnb, .jnc, .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}), + .je, .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}), + .jne, .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}), + .jna, .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}), + .jnbe, .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}), + .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}), + .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}), + .jpe, .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}), + .jpo, .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}), + .jnge, .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}), + .jge, .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}), + .jle, .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}), + .jg, .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}), + else => unreachable, }, .m => return switch (tag) { .jmp_near, .call_near, .push => OpCode.init(&.{0xff}), + .pop => OpCode.init(&.{0x8f}), .seto => OpCode.init(&.{0x0f,0x90}), .setno => OpCode.init(&.{0x0f,0x91}), + .setb, .setc, .setnae => OpCode.init(&.{0x0f,0x92}), + .setnb, .setnc, .setae => OpCode.init(&.{0x0f,0x93}), + .sete, .setz => OpCode.init(&.{0x0f,0x94}), + .setne, .setnz => OpCode.init(&.{0x0f,0x95}), + .setbe, .setna => OpCode.init(&.{0x0f,0x96}), + .seta, .setnbe => OpCode.init(&.{0x0f,0x97}), + .sets => OpCode.init(&.{0x0f,0x98}), .setns => OpCode.init(&.{0x0f,0x99}), + .setp, .setpe => OpCode.init(&.{0x0f,0x9a}), + .setnp, - .setop => OpCode.init(&.{0x0f,0x9b}), + .setpo => OpCode.init(&.{0x0f,0x9b}), + .setl, .setnge => OpCode.init(&.{0x0f,0x9c}), + .setnl, .setge => OpCode.init(&.{0x0f,0x9d}), + .setle, .setng => OpCode.init(&.{0x0f,0x9e}), + .setnle, .setg => OpCode.init(&.{0x0f,0x9f}), + .idiv, .div, .imul, .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), + .fisttp16 => OpCode.init(&.{0xdf}), .fisttp32 => OpCode.init(&.{0xdb}), .fisttp64 => OpCode.init(&.{0xdd}), @@ -1640,12 +1746,52 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}), .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}), .imul => OpCode.init(&.{0x0f,0xaf}), - .cmove, - .cmovz => OpCode.init(&.{0x0f,0x44}), + + .cmova, + .cmovnbe, => OpCode.init(&.{0x0f,0x47}), + + .cmovae, + .cmovnb, => OpCode.init(&.{0x0f,0x43}), + .cmovb, + .cmovc, .cmovnae => OpCode.init(&.{0x0f,0x42}), + + .cmovbe, + .cmovna, => OpCode.init(&.{0x0f,0x46}), + + .cmove, + .cmovz, => OpCode.init(&.{0x0f,0x44}), + + .cmovg, + .cmovnle, => OpCode.init(&.{0x0f,0x4f}), + + .cmovge, + .cmovnl, => OpCode.init(&.{0x0f,0x4d}), + .cmovl, - .cmovng => OpCode.init(&.{0x0f,0x4c}), + .cmovnge, => OpCode.init(&.{0x0f,0x4c}), + + .cmovle, + .cmovng, => OpCode.init(&.{0x0f,0x4e}), + + .cmovne, + .cmovnz, => OpCode.init(&.{0x0f,0x45}), + + .cmovno => OpCode.init(&.{0x0f,0x41}), + + .cmovnp, + .cmovpo, => OpCode.init(&.{0x0f,0x4b}), + + .cmovns => OpCode.init(&.{0x0f,0x49}), + + .cmovo => OpCode.init(&.{0x0f,0x40}), + + .cmovp, + .cmovpe, => OpCode.init(&.{0x0f,0x4a}), + + .cmovs => OpCode.init(&.{0x0f,0x48}), + .movsd => OpCode.init(&.{0xf2,0x0f,0x10}), .movss => OpCode.init(&.{0xf3,0x0f,0x10}), .addsd => OpCode.init(&.{0xf2,0x0f,0x58}), @@ -1735,7 +1881,7 @@ inline fn getModRmExt(tag: Tag) u3 { .setp, .setpe, .setnp, - .setop, + .setpo, .setl, .setnge, .setnl, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index a35231a9b8..0918d67f3c 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -275,42 +275,25 @@ pub const Inst = struct { call, /// ops flags: - /// 0b00 gte - /// 0b01 gt - /// 0b10 lt - /// 0b11 lte - cond_jmp_greater_less, - cond_set_byte_greater_less, - - /// ops flags: - /// 0b00 above or equal - /// 0b01 above - /// 0b10 below - /// 0b11 below or equal - cond_jmp_above_below, - cond_set_byte_above_below, + /// unused + /// Notes: + /// * uses `inst_cc` in Data. + cond_jmp, /// ops flags: - /// 0bX0 ne - /// 0bX1 eq - cond_jmp_eq_ne, - cond_set_byte_eq_ne, + /// 0b00 reg1 + /// Notes: + /// * uses condition code (CC) stored as part of data + cond_set_byte, /// ops flags: /// 0b00 reg1, reg2, /// 0b01 reg1, word ptr [reg2 + imm] /// 0b10 reg1, dword ptr [reg2 + imm] /// 0b11 reg1, qword ptr [reg2 + imm] - cond_mov_eq, - cond_mov_lt, - cond_mov_below, - - /// ops flags: - /// 0b00 reg1 if OF = 1 - /// 0b01 reg1 if OF = 0 - /// 0b10 reg1 if CF = 1 - /// 0b11 reg1 if CF = 0 - cond_set_byte_overflow, + /// Notes: + /// * uses condition code (CC) stored as part of data + cond_mov, /// ops flags: form: /// 0b00 reg1 @@ -451,6 +434,16 @@ pub const Inst = struct { inst: Index, /// A 32-bit immediate value. imm: u32, + /// A condition code for use with EFLAGS register. + cc: bits.Condition, + /// Another instruction with condition code. + /// Used by `cond_jmp`. + inst_cc: struct { + /// Another instruction. + inst: Index, + /// A condition code for use with EFLAGS register. + cc: bits.Condition, + }, /// An extern function. extern_fn: struct { /// Index of the containing atom. diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 6429781516..a2d523e84c 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -6,6 +6,135 @@ const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; const DW = std.dwarf; +/// EFLAGS condition codes +pub const Condition = enum(u5) { + /// above + a, + /// above or equal + ae, + /// below + b, + /// below or equal + be, + /// carry + c, + /// equal + e, + /// greater + g, + /// greater or equal + ge, + /// less + l, + /// less or equal + le, + /// not above + na, + /// not above or equal + nae, + /// not below + nb, + /// not below or equal + nbe, + /// not carry + nc, + /// not equal + ne, + /// not greater + ng, + /// not greater or equal + nge, + /// not less + nl, + /// not less or equal + nle, + /// not overflow + no, + /// not parity + np, + /// not sign + ns, + /// not zero + nz, + /// overflow + o, + /// parity + p, + /// parity even + pe, + /// parity odd + po, + /// sign + s, + /// zero + z, + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes signed comparison + pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { + return switch (op) { + .gte => .ge, + .gt => .g, + .neq => .ne, + .lt => .l, + .lte => .le, + .eq => .e, + }; + } + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes unsigned comparison + pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { + return switch (op) { + .gte => .ae, + .gt => .a, + .neq => .ne, + .lt => .b, + .lte => .be, + .eq => .e, + }; + } + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists) + pub fn negate(cond: Condition) Condition { + return switch (cond) { + .a => .na, + .ae => .nae, + .b => .nb, + .be => .nbe, + .c => .nc, + .e => .ne, + .g => .ng, + .ge => .nge, + .l => .nl, + .le => .nle, + .na => .a, + .nae => .ae, + .nb => .b, + .nbe => .be, + .nc => .c, + .ne => .e, + .ng => .g, + .nge => .ge, + .nl => .l, + .nle => .le, + .no => .o, + .np => .p, + .ns => .s, + .nz => .z, + .o => .no, + .p => .np, + .pe => unreachable, + .po => unreachable, + .s => .ns, + .z => .nz, + }; + } +}; + // zig fmt: off /// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. |
