aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-05-25 20:33:43 +0200
committerGitHub <noreply@github.com>2022-05-25 20:33:43 +0200
commit39ebfedd2bea7ebb8630f8593ae59538969bfea7 (patch)
treeeb11df8c0125d4afd99fd4ecf43ac3f1ce13e9d6 /src
parente0be22b6c06530d56308853723f982c492e768fd (diff)
parent7000395a7d002a6d242d717de60614516f4e29af (diff)
downloadzig-39ebfedd2bea7ebb8630f8593ae59538969bfea7.tar.gz
zig-39ebfedd2bea7ebb8630f8593ae59538969bfea7.zip
Merge pull request #11715 from ziglang/stage2-x64-cond-codes
Diffstat (limited to 'src')
-rw-r--r--src/arch/x86_64/CodeGen.zig447
-rw-r--r--src/arch/x86_64/Emit.zig296
-rw-r--r--src/arch/x86_64/Mir.zig49
-rw-r--r--src/arch/x86_64/bits.zig129
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.