authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-24 20:29:15+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-25 23:43:45+02:00
log97816e3cb826b64df578baa72781caba93252fd0
treea113dc2423916bd14066156855a2af6ecad2b7f3
parent39ebfedd2bea7ebb8630f8593ae59538969bfea7

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 files changed, 31 insertions(+), 62 deletions(-)

src/arch/aarch64/CodeGen.zig+31-62
...@@ -128,18 +128,11 @@ const MCValue = union(enum) {...@@ -128,18 +128,11 @@ const MCValue = union(enum) {
128 register: Register,128 register: Register,
129 /// The value is a tuple { wrapped: u32, overflow: u1 } where129 /// The value is a tuple { wrapped: u32, overflow: u1 } where
130 /// wrapped is stored in the register and the overflow bit is130 /// 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.
132 ///132 ///
133 /// This MCValue is only generated by a add_with_overflow or133 /// This MCValue is only generated by a add_with_overflow or
134 /// sub_with_overflow instruction operating on u32.134 /// sub_with_overflow instruction operating on 32- or 64-bit values.
135 register_c_flag: Register,135 register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition },
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,
143 /// The value is in memory at a hard-coded address.136 /// The value is in memory at a hard-coded address.
144 ///137 ///
145 /// If the type is a pointer, it means the pointer address is at138 /// 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 {...@@ -760,10 +753,8 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
760 .register => |reg| {753 .register => |reg| {
761 self.register_manager.freeReg(reg);754 self.register_manager.freeReg(reg);
762 },755 },
763 .register_c_flag,756 .register_with_overflow => |rwo| {
764 .register_v_flag,757 self.register_manager.freeReg(rwo.reg);
765 => |reg| {
766 self.register_manager.freeReg(reg);
767 self.compare_flags_inst = null;758 self.compare_flags_inst = null;
768 },759 },
769 .compare_flags_signed, .compare_flags_unsigned => {760 .compare_flags_signed, .compare_flags_unsigned => {
...@@ -905,10 +896,8 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -905,10 +896,8 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
905 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });896 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
906 const reg_mcv = self.getResolvedInstValue(inst);897 const reg_mcv = self.getResolvedInstValue(inst);
907 switch (reg_mcv) {898 switch (reg_mcv) {
908 .register,899 .register => |r| assert(reg.id() == r.id()),
909 .register_c_flag,900 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),
910 .register_v_flag,
911 => |r| assert(reg.id() == r.id()),
912 else => unreachable, // not a register901 else => unreachable, // not a register
913 }902 }
914 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];903 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -925,9 +914,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -925,9 +914,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
925 .compare_flags_signed,914 .compare_flags_signed,
926 .compare_flags_unsigned,915 .compare_flags_unsigned,
927 => try self.allocRegOrMem(inst_to_save, true),916 => try self.allocRegOrMem(inst_to_save, true),
928 .register_c_flag,917 .register_with_overflow => try self.allocRegOrMem(inst_to_save, false),
929 .register_v_flag,
930 => try self.allocRegOrMem(inst_to_save, false),
931 else => unreachable, // mcv doesn't occupy the compare flags918 else => unreachable, // mcv doesn't occupy the compare flags
932 };919 };
933920
...@@ -942,9 +929,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -942,9 +929,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
942 // TODO consolidate with register manager and spillInstruction929 // TODO consolidate with register manager and spillInstruction
943 // this call should really belong in the register manager!930 // this call should really belong in the register manager!
944 switch (mcv) {931 switch (mcv) {
945 .register_c_flag,932 .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg),
946 .register_v_flag,
947 => |reg| self.register_manager.freeReg(reg),
948 else => {},933 else => {},
949 }934 }
950 }935 }
...@@ -1932,14 +1917,18 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1932,14 +1917,18 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1932 }1917 }
1933 };1918 };
19341919
1935 if (tag == .sub_with_overflow) {1920 const flag: bits.Instruction.Condition = switch (int_info.signedness) {
1936 break :result MCValue{ .register_v_flag = dest.register };1921 .unsigned => switch (tag) {
1937 }1922 .add_with_overflow => bits.Instruction.Condition.cs,
19381923 .sub_with_overflow => bits.Instruction.Condition.cc,
1939 switch (int_info.signedness) {1924 else => unreachable,
1940 .unsigned => break :result MCValue{ .register_c_flag = dest.register },1925 },
1941 .signed => break :result MCValue{ .register_v_flag = dest.register },1926 .signed => .vs,
1942 }1927 };
1928 break :result MCValue{ .register_with_overflow = .{
1929 .reg = dest.register,
1930 .flag = flag,
1931 } };
1943 },1932 },
1944 else => return self.fail("TODO overflow operations on integers > u32/i32", .{}),1933 else => return self.fail("TODO overflow operations on integers > u32/i32", .{}),
1945 }1934 }
...@@ -2648,8 +2637,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -2648,8 +2637,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2648 .dead => unreachable,2637 .dead => unreachable,
2649 .compare_flags_unsigned,2638 .compare_flags_unsigned,
2650 .compare_flags_signed,2639 .compare_flags_signed,
2651 .register_c_flag,2640 .register_with_overflow,
2652 .register_v_flag,
2653 => unreachable, // cannot hold an address2641 => unreachable, // cannot hold an address
2654 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),2642 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
2655 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),2643 .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...@@ -2871,8 +2859,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2871 .dead => unreachable,2859 .dead => unreachable,
2872 .compare_flags_unsigned,2860 .compare_flags_unsigned,
2873 .compare_flags_signed,2861 .compare_flags_signed,
2874 .register_c_flag,2862 .register_with_overflow,
2875 .register_v_flag,
2876 => unreachable, // cannot hold an address2863 => unreachable, // cannot hold an address
2877 .immediate => |imm| {2864 .immediate => |imm| {
2878 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);2865 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
...@@ -2993,13 +2980,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2993,13 +2980,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2993 .memory => |addr| {2980 .memory => |addr| {
2994 break :result MCValue{ .memory = addr + struct_field_offset };2981 break :result MCValue{ .memory = addr + struct_field_offset };
2995 },2982 },
2996 .register_c_flag,2983 .register_with_overflow => |rwo| {
2997 .register_v_flag,
2998 => |reg| {
2999 switch (index) {2984 switch (index) {
3000 0 => {2985 0 => {
3001 // get wrapped value: return register2986 // get wrapped value: return register
3002 break :result MCValue{ .register = reg };2987 break :result MCValue{ .register = rwo.reg };
3003 },2988 },
3004 1 => {2989 1 => {
3005 // TODO return special MCValue condition flags2990 // TODO return special MCValue condition flags
...@@ -3008,17 +2993,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3008,17 +2993,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3008 const raw_dest_reg = try self.register_manager.allocReg(null, gp);2993 const raw_dest_reg = try self.register_manager.allocReg(null, gp);
3009 const dest_reg = raw_dest_reg.to32();2994 const dest_reg = raw_dest_reg.to32();
30102995
3011 // C flag: cset reg, cs
3012 // V flag: cset reg, vs
3013 _ = try self.addInst(.{2996 _ = try self.addInst(.{
3014 .tag = .cset,2997 .tag = .cset,
3015 .data = .{ .r_cond = .{2998 .data = .{ .r_cond = .{
3016 .rd = dest_reg,2999 .rd = dest_reg,
3017 .cond = switch (mcv) {3000 .cond = rwo.flag,
3018 .register_c_flag => .cs,
3019 .register_v_flag => .vs,
3020 else => unreachable,
3021 },
3022 } },3001 } },
3023 });3002 });
30243003
...@@ -4073,14 +4052,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4073,14 +4052,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4073 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),4052 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
4074 }4053 }
4075 },4054 },
4076 .register_c_flag,4055 .register_with_overflow => |rwo| {
4077 .register_v_flag,4056 const reg_lock = self.register_manager.lockReg(rwo.reg);
4078 => |reg| {
4079 const reg_lock = self.register_manager.lockReg(reg);
4080 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);4057 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
40814058
4082 const wrapped_ty = ty.structFieldType(0);4059 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
4085 const overflow_bit_ty = ty.structFieldType(1);4062 const overflow_bit_ty = ty.structFieldType(1);
4086 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));4063 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...@@ -4090,17 +4067,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4090 @intCast(u32, overflow_bit_ty.abiSize(self.target.*)),4067 @intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
4091 );4068 );
40924069
4093 // C flag: cset reg, cs
4094 // V flag: cset reg, vs
4095 _ = try self.addInst(.{4070 _ = try self.addInst(.{
4096 .tag = .cset,4071 .tag = .cset,
4097 .data = .{ .r_cond = .{4072 .data = .{ .r_cond = .{
4098 .rd = cond_reg,4073 .rd = cond_reg,
4099 .cond = switch (mcv) {4074 .cond = rwo.flag,
4100 .register_c_flag => .cs,
4101 .register_v_flag => .vs,
4102 else => unreachable,
4103 },
4104 } },4075 } },
4105 });4076 });
41064077
...@@ -4270,9 +4241,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4270,9 +4241,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4270 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },4241 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
4271 });4242 });
4272 },4243 },
4273 .register_c_flag,4244 .register_with_overflow => unreachable, // doesn't fit into a register
4274 .register_v_flag,
4275 => unreachable, // doesn't fit into a register
4276 .got_load,4245 .got_load,
4277 .direct_load,4246 .direct_load,
4278 => |sym_index| {4247 => |sym_index| {