mirror of
https://github.com/ziglang/zig.git
synced 2024-11-30 17:12:31 +00:00
stage2 ARM: spill insts currently in compare flags if necessary
This commit is contained in:
parent
5b29b4ffa6
commit
9892684d35
@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
|
||||
register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
|
||||
/// Maps offset to what is stored there.
|
||||
stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
|
||||
/// Tracks the current instruction allocated to the compare flags
|
||||
compare_flags_inst: ?Air.Inst.Index = null,
|
||||
|
||||
/// Offset from the stack base, representing the end of the stack frame.
|
||||
max_end_stack: u32 = 0,
|
||||
@ -647,6 +649,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
|
||||
.register => |reg| {
|
||||
self.register_manager.freeReg(reg);
|
||||
},
|
||||
.compare_flags_signed, .compare_flags_unsigned => {
|
||||
self.compare_flags_inst = null;
|
||||
},
|
||||
else => {}, // TODO process stack allocation death
|
||||
}
|
||||
}
|
||||
@ -779,6 +784,28 @@ 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);
|
||||
}
|
||||
|
||||
/// Save the current instruction stored in the compare flags if
|
||||
/// occupied
|
||||
fn spillCompareFlagsIfOccupied(self: *Self) !void {
|
||||
if (self.compare_flags_inst) |inst_to_save| {
|
||||
const mcv = self.getResolvedInstValue(inst_to_save);
|
||||
assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);
|
||||
|
||||
const new_mcv = try self.allocRegOrMem(inst_to_save, true);
|
||||
switch (new_mcv) {
|
||||
.register => |reg| try self.genSetReg(self.air.typeOfIndex(inst_to_save), reg, mcv),
|
||||
.stack_offset => |offset| try self.genSetStack(self.air.typeOfIndex(inst_to_save), offset, mcv),
|
||||
else => unreachable,
|
||||
}
|
||||
log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies a value to a register without tracking the register. The register is not considered
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
@ -890,10 +917,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
break :result r;
|
||||
},
|
||||
else => {},
|
||||
else => {
|
||||
break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
|
||||
},
|
||||
}
|
||||
|
||||
break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
@ -1831,6 +1858,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
|
||||
var info = try self.resolveCallingConventionValues(fn_ty);
|
||||
defer info.deinit(self);
|
||||
|
||||
// According to the Procedure Call Standard for the ARM
|
||||
// Architecture, compare flags are not preserved across
|
||||
// calls. Therefore, if some value is currently stored there, we
|
||||
// need to save it.
|
||||
//
|
||||
// TODO once caller-saved registers are implemented, save them
|
||||
// here too, but crucially *after* we save the compare flags as
|
||||
// saving compare flags may require a new caller-saved register
|
||||
try self.spillCompareFlagsIfOccupied();
|
||||
|
||||
// Make space for the arguments passed via the stack
|
||||
self.max_end_stack += info.stack_byte_count;
|
||||
|
||||
@ -1984,6 +2021,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
if (ty.zigTypeTag() == .ErrorSet)
|
||||
return self.fail("TODO implement cmp for errors", .{});
|
||||
|
||||
try self.spillCompareFlagsIfOccupied();
|
||||
self.compare_flags_inst = inst;
|
||||
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const result: MCValue = result: {
|
||||
@ -2094,12 +2134,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
});
|
||||
};
|
||||
|
||||
// If the condition dies here in this condbr instruction, process
|
||||
// that death now instead of later as this has an effect on
|
||||
// whether it needs to be spilled in the branches
|
||||
if (self.liveness.operandDies(inst, 0)) {
|
||||
const op_int = @enumToInt(pl_op.operand);
|
||||
if (op_int >= Air.Inst.Ref.typed_value_map.len) {
|
||||
const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
|
||||
self.processDeath(op_index);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
var parent_stack = try self.stack.clone(self.gpa);
|
||||
defer parent_stack.deinit(self.gpa);
|
||||
const parent_registers = self.register_manager.registers;
|
||||
const parent_compare_flags_inst = self.compare_flags_inst;
|
||||
|
||||
try self.branch_stack.append(.{});
|
||||
|
||||
@ -2115,6 +2167,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.stack.deinit(self.gpa);
|
||||
self.stack = parent_stack;
|
||||
@ -2206,7 +2259,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
|
||||
self.branch_stack.pop().deinit(self.gpa);
|
||||
|
||||
return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
|
||||
// We already took care of pl_op.operand earlier, so we're going
|
||||
// to pass .none here
|
||||
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn isNull(self: *Self, operand: MCValue) !MCValue {
|
||||
|
@ -547,4 +547,25 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("save compare flags", linux_arm);
|
||||
case.addCompareOutput(
|
||||
\\pub fn main() void {
|
||||
\\ foo(2, 1);
|
||||
\\}
|
||||
\\
|
||||
\\fn foo(x: u32, y: u32) void {
|
||||
\\ const b = x > y;
|
||||
\\ assert(b);
|
||||
\\ assert(b);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user