Skip to content

Commit 97816e3

Browse files
committed
aarch64: check lo/cc flag for unsigned sub_with_overflow
With this change, we are now correctly lowering `sub_with_overflow` for signed and unsigned integers of register-sized integers (32- or 64-bit precisely). We also match LLVM's behavior and so, the condition flags we now set are: * unsigned: - `add_with_overflow`: `hs`/`cs` (carry set) - `sub_with_overflow`: `lo`/`cc` (carry clear) * signed: - `add_with_overflow`/`sub_with_overflow`: `vs` (overflow)
1 parent 39ebfed commit 97816e3

File tree

1 file changed

+31
-62
lines changed

1 file changed

+31
-62
lines changed

src/arch/aarch64/CodeGen.zig

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,11 @@ const MCValue = union(enum) {
128128
register: Register,
129129
/// The value is a tuple { wrapped: u32, overflow: u1 } where
130130
/// wrapped is stored in the register and the overflow bit is
131-
/// stored in the C flag of the CPSR.
131+
/// stored in the C (signed) or V (unsigned) flag of the CPSR.
132132
///
133133
/// This MCValue is only generated by a add_with_overflow or
134-
/// sub_with_overflow instruction operating on u32.
135-
register_c_flag: Register,
136-
/// The value is a tuple { wrapped: i32, overflow: u1 } where
137-
/// wrapped is stored in the register and the overflow bit is
138-
/// stored in the V flag of the CPSR.
139-
///
140-
/// This MCValue is only generated by a add_with_overflow or
141-
/// sub_with_overflow instruction operating on i32.
142-
register_v_flag: Register,
134+
/// sub_with_overflow instruction operating on 32- or 64-bit values.
135+
register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition },
143136
/// The value is in memory at a hard-coded address.
144137
///
145138
/// If the type is a pointer, it means the pointer address is at
@@ -760,10 +753,8 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
760753
.register => |reg| {
761754
self.register_manager.freeReg(reg);
762755
},
763-
.register_c_flag,
764-
.register_v_flag,
765-
=> |reg| {
766-
self.register_manager.freeReg(reg);
756+
.register_with_overflow => |rwo| {
757+
self.register_manager.freeReg(rwo.reg);
767758
self.compare_flags_inst = null;
768759
},
769760
.compare_flags_signed, .compare_flags_unsigned => {
@@ -905,10 +896,8 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
905896
log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
906897
const reg_mcv = self.getResolvedInstValue(inst);
907898
switch (reg_mcv) {
908-
.register,
909-
.register_c_flag,
910-
.register_v_flag,
911-
=> |r| assert(reg.id() == r.id()),
899+
.register => |r| assert(reg.id() == r.id()),
900+
.register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),
912901
else => unreachable, // not a register
913902
}
914903
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -925,9 +914,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
925914
.compare_flags_signed,
926915
.compare_flags_unsigned,
927916
=> try self.allocRegOrMem(inst_to_save, true),
928-
.register_c_flag,
929-
.register_v_flag,
930-
=> try self.allocRegOrMem(inst_to_save, false),
917+
.register_with_overflow => try self.allocRegOrMem(inst_to_save, false),
931918
else => unreachable, // mcv doesn't occupy the compare flags
932919
};
933920

@@ -942,9 +929,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
942929
// TODO consolidate with register manager and spillInstruction
943930
// this call should really belong in the register manager!
944931
switch (mcv) {
945-
.register_c_flag,
946-
.register_v_flag,
947-
=> |reg| self.register_manager.freeReg(reg),
932+
.register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg),
948933
else => {},
949934
}
950935
}
@@ -1932,14 +1917,18 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
19321917
}
19331918
};
19341919

1935-
if (tag == .sub_with_overflow) {
1936-
break :result MCValue{ .register_v_flag = dest.register };
1937-
}
1938-
1939-
switch (int_info.signedness) {
1940-
.unsigned => break :result MCValue{ .register_c_flag = dest.register },
1941-
.signed => break :result MCValue{ .register_v_flag = dest.register },
1942-
}
1920+
const flag: bits.Instruction.Condition = switch (int_info.signedness) {
1921+
.unsigned => switch (tag) {
1922+
.add_with_overflow => bits.Instruction.Condition.cs,
1923+
.sub_with_overflow => bits.Instruction.Condition.cc,
1924+
else => unreachable,
1925+
},
1926+
.signed => .vs,
1927+
};
1928+
break :result MCValue{ .register_with_overflow = .{
1929+
.reg = dest.register,
1930+
.flag = flag,
1931+
} };
19431932
},
19441933
else => return self.fail("TODO overflow operations on integers > u32/i32", .{}),
19451934
}
@@ -2648,8 +2637,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
26482637
.dead => unreachable,
26492638
.compare_flags_unsigned,
26502639
.compare_flags_signed,
2651-
.register_c_flag,
2652-
.register_v_flag,
2640+
.register_with_overflow,
26532641
=> unreachable, // cannot hold an address
26542642
.immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
26552643
.ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
@@ -2871,8 +2859,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28712859
.dead => unreachable,
28722860
.compare_flags_unsigned,
28732861
.compare_flags_signed,
2874-
.register_c_flag,
2875-
.register_v_flag,
2862+
.register_with_overflow,
28762863
=> unreachable, // cannot hold an address
28772864
.immediate => |imm| {
28782865
try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
@@ -2993,13 +2980,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
29932980
.memory => |addr| {
29942981
break :result MCValue{ .memory = addr + struct_field_offset };
29952982
},
2996-
.register_c_flag,
2997-
.register_v_flag,
2998-
=> |reg| {
2983+
.register_with_overflow => |rwo| {
29992984
switch (index) {
30002985
0 => {
30012986
// get wrapped value: return register
3002-
break :result MCValue{ .register = reg };
2987+
break :result MCValue{ .register = rwo.reg };
30032988
},
30042989
1 => {
30052990
// TODO return special MCValue condition flags
@@ -3008,17 +2993,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30082993
const raw_dest_reg = try self.register_manager.allocReg(null, gp);
30092994
const dest_reg = raw_dest_reg.to32();
30102995

3011-
// C flag: cset reg, cs
3012-
// V flag: cset reg, vs
30132996
_ = try self.addInst(.{
30142997
.tag = .cset,
30152998
.data = .{ .r_cond = .{
30162999
.rd = dest_reg,
3017-
.cond = switch (mcv) {
3018-
.register_c_flag => .cs,
3019-
.register_v_flag => .vs,
3020-
else => unreachable,
3021-
},
3000+
.cond = rwo.flag,
30223001
} },
30233002
});
30243003

@@ -4073,14 +4052,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
40734052
else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
40744053
}
40754054
},
4076-
.register_c_flag,
4077-
.register_v_flag,
4078-
=> |reg| {
4079-
const reg_lock = self.register_manager.lockReg(reg);
4055+
.register_with_overflow => |rwo| {
4056+
const reg_lock = self.register_manager.lockReg(rwo.reg);
40804057
defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
40814058

40824059
const wrapped_ty = ty.structFieldType(0);
4083-
try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg });
4060+
try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg });
40844061

40854062
const overflow_bit_ty = ty.structFieldType(1);
40864063
const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
@@ -4090,17 +4067,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
40904067
@intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
40914068
);
40924069

4093-
// C flag: cset reg, cs
4094-
// V flag: cset reg, vs
40954070
_ = try self.addInst(.{
40964071
.tag = .cset,
40974072
.data = .{ .r_cond = .{
40984073
.rd = cond_reg,
4099-
.cond = switch (mcv) {
4100-
.register_c_flag => .cs,
4101-
.register_v_flag => .vs,
4102-
else => unreachable,
4103-
},
4074+
.cond = rwo.flag,
41044075
} },
41054076
});
41064077

@@ -4270,9 +4241,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42704241
.data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
42714242
});
42724243
},
4273-
.register_c_flag,
4274-
.register_v_flag,
4275-
=> unreachable, // doesn't fit into a register
4244+
.register_with_overflow => unreachable, // doesn't fit into a register
42764245
.got_load,
42774246
.direct_load,
42784247
=> |sym_index| {

0 commit comments

Comments
 (0)