authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-04-25 21:04:39+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-05 21:43:35+02:00
logf267e7a8b45d343eb1c3cf5f0662746912af8c99
tree406157432c21e05edeca95b40a651feeb0334f3b
parentc2d2307d09017e97a9e9ce59d29754ff2becdd54

stage2 AArch64: implement {add,sub}_with_overflow for all ints < 64


3 files changed, 193 insertions(+), 40 deletions(-)

src/arch/aarch64/CodeGen.zig+172-39
......@@ -102,9 +102,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
102102const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
103103
104104const MCValue = union(enum) {
105 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
106 /// TODO Look into deleting this tag and using `dead` instead, since every use
107 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
105 /// No runtime bits. `void` types, empty structs, u0, enums with 1
106 /// tag, etc.
107 ///
108 /// TODO Look into deleting this tag and using `dead` instead,
109 /// since every use of MCValue.none should be instead looking at
110 /// the type and noticing it is 0 bits.
108111 none,
109112 /// Control flow will not allow this value to be observed.
110113 unreach,
......@@ -113,28 +116,56 @@ const MCValue = union(enum) {
113116 /// The value is undefined.
114117 undef,
115118 /// A pointer-sized integer that fits in a register.
116 /// If the type is a pointer, this is the pointer address in virtual address space.
119 ///
120 /// If the type is a pointer, this is the pointer address in
121 /// virtual address space.
117122 immediate: u64,
118123 /// The value is in a target-specific register.
119124 register: Register,
125 /// The value is a tuple { wrapped: u32, overflow: u1 } where
126 /// wrapped is stored in the register and the overflow bit is
127 /// stored in the C flag of the CPSR.
128 ///
129 /// This MCValue is only generated by a add_with_overflow or
130 /// sub_with_overflow instruction operating on u32.
131 register_c_flag: Register,
132 /// The value is a tuple { wrapped: i32, overflow: u1 } where
133 /// wrapped is stored in the register and the overflow bit is
134 /// stored in the V flag of the CPSR.
135 ///
136 /// This MCValue is only generated by a add_with_overflow or
137 /// sub_with_overflow instruction operating on i32.
138 register_v_flag: Register,
120139 /// The value is in memory at a hard-coded address.
121 /// If the type is a pointer, it means the pointer address is at this memory location.
140 ///
141 /// If the type is a pointer, it means the pointer address is at
142 /// this memory location.
122143 memory: u64,
123 /// The value is in memory referenced indirectly via a GOT entry index.
124 /// If the type is a pointer, it means the pointer is referenced indirectly via GOT.
125 /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12.
144 /// The value is in memory referenced indirectly via a GOT entry
145 /// index.
146 ///
147 /// If the type is a pointer, it means the pointer is referenced
148 /// indirectly via GOT. When lowered, linker will emit
149 /// relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and
150 /// ARM64_RELOC_GOT_LOAD_PAGEOFF12.
126151 got_load: u32,
127152 /// The value is in memory referenced directly via symbol index.
128 /// If the type is a pointer, it means the pointer is referenced directly via symbol index.
129 /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12.
153 ///
154 /// If the type is a pointer, it means the pointer is referenced
155 /// directly via symbol index. When lowered, linker will emit a
156 /// relocation of type ARM64_RELOC_PAGE21 and
157 /// ARM64_RELOC_PAGEOFF12.
130158 direct_load: u32,
131159 /// The value is one of the stack variables.
132 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
160 ///
161 /// If the type is a pointer, it means the pointer address is in
162 /// the stack at this offset.
133163 stack_offset: u32,
134 /// The value is a pointer to one of the stack variables (payload is stack offset).
164 /// The value is a pointer to one of the stack variables (payload
165 /// is stack offset).
135166 ptr_stack_offset: u32,
136 /// The value is in the compare flags assuming an unsigned operation,
137 /// with this operator applied on top of it.
167 /// The value is in the compare flags assuming an unsigned
168 /// operation, with this operator applied on top of it.
138169 compare_flags_unsigned: math.CompareOperator,
139170 /// The value is in the compare flags assuming a signed operation,
140171 /// with this operator applied on top of it.
......@@ -716,8 +747,13 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
716747 branch.inst_table.putAssumeCapacity(inst, .dead);
717748 switch (prev_value) {
718749 .register => |reg| {
719 const canon_reg = toCanonicalReg(reg);
720 self.register_manager.freeReg(canon_reg);
750 self.register_manager.freeReg(reg);
751 },
752 .register_c_flag,
753 .register_v_flag,
754 => |reg| {
755 self.register_manager.freeReg(reg);
756 self.compare_flags_inst = null;
721757 },
722758 .compare_flags_signed, .compare_flags_unsigned => {
723759 self.compare_flags_inst = null;
......@@ -857,7 +893,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
857893 const stack_mcv = try self.allocRegOrMem(inst, false);
858894 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
859895 const reg_mcv = self.getResolvedInstValue(inst);
860 assert(reg == toCanonicalReg(reg_mcv.register));
896 switch (reg_mcv) {
897 .register,
898 .register_c_flag,
899 .register_v_flag,
900 => |r| assert(reg.id() == r.id()),
901 else => unreachable, // not a register
902 }
861903 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
862904 try branch.inst_table.put(self.gpa, inst, stack_mcv);
863905 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
......@@ -868,7 +910,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
868910fn spillCompareFlagsIfOccupied(self: *Self) !void {
869911 if (self.compare_flags_inst) |inst_to_save| {
870912 const mcv = self.getResolvedInstValue(inst_to_save);
871 assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);
913 switch (mcv) {
914 .compare_flags_signed,
915 .compare_flags_unsigned,
916 .register_c_flag,
917 .register_v_flag,
918 => {},
919 else => unreachable, // mcv doesn't occupy the compare flags
920 }
872921
873922 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
874923 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
......@@ -1269,7 +1318,9 @@ fn binOpRegister(
12691318
12701319 const mir_data: Mir.Inst.Data = switch (mir_tag) {
12711320 .add_shifted_register,
1321 .adds_shifted_register,
12721322 .sub_shifted_register,
1323 .subs_shifted_register,
12731324 => .{ .rrr_imm6_shift = .{
12741325 .rd = dest_reg,
12751326 .rn = lhs_reg,
......@@ -1384,7 +1435,9 @@ fn binOpImmediate(
13841435
13851436 const mir_data: Mir.Inst.Data = switch (mir_tag) {
13861437 .add_immediate,
1438 .adds_immediate,
13871439 .sub_immediate,
1440 .subs_immediate,
13881441 => .{ .rr_imm12_sh = .{
13891442 .rd = dest_reg,
13901443 .rn = lhs_reg,
......@@ -1774,7 +1827,52 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
17741827
17751828 break :result MCValue{ .stack_offset = stack_offset };
17761829 },
1777 32, 64 => return self.fail("TODO overflow operations on integers u32/i32 and u64/i64", .{}),
1830 32, 64 => {
1831 // Only say yes if the operation is
1832 // commutative, i.e. we can swap both of the
1833 // operands
1834 const lhs_immediate_ok = switch (tag) {
1835 .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
1836 .sub_with_overflow => false,
1837 else => unreachable,
1838 };
1839 const rhs_immediate_ok = switch (tag) {
1840 .add_with_overflow,
1841 .sub_with_overflow,
1842 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
1843 else => unreachable,
1844 };
1845
1846 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1847 .add_with_overflow => .adds_shifted_register,
1848 .sub_with_overflow => .subs_shifted_register,
1849 else => unreachable,
1850 };
1851 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1852 .add_with_overflow => .adds_immediate,
1853 .sub_with_overflow => .subs_immediate,
1854 else => unreachable,
1855 };
1856
1857 try self.spillCompareFlagsIfOccupied();
1858 self.compare_flags_inst = inst;
1859
1860 const dest = blk: {
1861 if (rhs_immediate_ok) {
1862 break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false);
1863 } else if (lhs_immediate_ok) {
1864 // swap lhs and rhs
1865 break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true);
1866 } else {
1867 break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty);
1868 }
1869 };
1870
1871 switch (int_info.signedness) {
1872 .unsigned => break :result MCValue{ .register_c_flag = dest.register },
1873 .signed => break :result MCValue{ .register_v_flag = dest.register },
1874 }
1875 },
17781876 else => return self.fail("TODO overflow operations on integers > u32/i32", .{}),
17791877 }
17801878 },
......@@ -2148,8 +2246,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
21482246 .undef => unreachable,
21492247 .unreach => unreachable,
21502248 .dead => unreachable,
2151 .compare_flags_unsigned => unreachable,
2152 .compare_flags_signed => unreachable,
2249 .compare_flags_unsigned,
2250 .compare_flags_signed,
2251 .register_c_flag,
2252 .register_v_flag,
2253 => unreachable, // cannot hold an address
21532254 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
21542255 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
21552256 .register => |addr_reg| {
......@@ -2366,8 +2467,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
23662467 .undef => unreachable,
23672468 .unreach => unreachable,
23682469 .dead => unreachable,
2369 .compare_flags_unsigned => unreachable,
2370 .compare_flags_signed => unreachable,
2470 .compare_flags_unsigned,
2471 .compare_flags_signed,
2472 .register_c_flag,
2473 .register_v_flag,
2474 => unreachable, // cannot hold an address
23712475 .immediate => |imm| {
23722476 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
23732477 },
......@@ -2487,6 +2591,40 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
24872591 .memory => |addr| {
24882592 break :result MCValue{ .memory = addr + struct_field_offset };
24892593 },
2594 .register_c_flag,
2595 .register_v_flag,
2596 => |reg| {
2597 switch (index) {
2598 0 => {
2599 // get wrapped value: return register
2600 break :result MCValue{ .register = reg };
2601 },
2602 1 => {
2603 // TODO return special MCValue condition flags
2604 // get overflow bit: set register to C flag
2605 // resp. V flag
2606 const raw_dest_reg = try self.register_manager.allocReg(null);
2607 const dest_reg = raw_dest_reg.to32();
2608
2609 // C flag: cset reg, cs
2610 // V flag: cset reg, vs
2611 _ = try self.addInst(.{
2612 .tag = .cset,
2613 .data = .{ .r_cond = .{
2614 .rd = dest_reg,
2615 .cond = switch (mcv) {
2616 .register_c_flag => .cs,
2617 .register_v_flag => .vs,
2618 else => unreachable,
2619 },
2620 } },
2621 });
2622
2623 break :result MCValue{ .register = dest_reg };
2624 },
2625 else => unreachable,
2626 }
2627 },
24902628 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
24912629 }
24922630 };
......@@ -2531,7 +2669,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
25312669
25322670 switch (mcv) {
25332671 .register => |reg| {
2534 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst);
2672 self.register_manager.getRegAssumeFree(reg, inst);
25352673 },
25362674 else => {},
25372675 }
......@@ -2596,15 +2734,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
25962734
25972735 switch (mc_arg) {
25982736 .none => continue,
2599 .undef => unreachable,
2600 .immediate => unreachable,
2601 .unreach => unreachable,
2602 .dead => unreachable,
2603 .memory => unreachable,
2604 .compare_flags_signed => unreachable,
2605 .compare_flags_unsigned => unreachable,
2606 .got_load => unreachable,
2607 .direct_load => unreachable,
26082737 .register => |reg| {
26092738 try self.register_manager.getReg(reg, null);
26102739 try self.genSetReg(arg_ty, reg, arg_mcv);
......@@ -2615,6 +2744,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
26152744 .ptr_stack_offset => {
26162745 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
26172746 },
2747 else => unreachable,
26182748 }
26192749 }
26202750
......@@ -3518,6 +3648,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
35183648 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
35193649 }
35203650 },
3651 .register_c_flag,
3652 .register_v_flag,
3653 => {
3654 return self.fail("TODO implement genSetStack {}", .{mcv});
3655 },
35213656 .got_load,
35223657 .direct_load,
35233658 .memory,
......@@ -3635,7 +3770,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
36353770 .tag = .cset,
36363771 .data = .{ .r_cond = .{
36373772 .rd = reg,
3638 .cond = condition.negate(),
3773 .cond = condition,
36393774 } },
36403775 });
36413776 },
......@@ -3678,6 +3813,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
36783813 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
36793814 });
36803815 },
3816 .register_c_flag,
3817 .register_v_flag,
3818 => unreachable, // doesn't fit into a register
36813819 .got_load,
36823820 .direct_load,
36833821 => |sym_index| {
......@@ -4279,8 +4417,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register {
42794417 unreachable; // TODO handle floating-point registers
42804418 }
42814419}
4282
4283/// Resolves any aliased registers to the 64-bit wide ones.
4284fn toCanonicalReg(reg: Register) Register {
4285 return reg.to64();
4286}
src/arch/aarch64/Emit.zig+13-1
......@@ -77,8 +77,10 @@ pub fn emitMir(
7777 const inst = @intCast(u32, index);
7878 switch (tag) {
7979 .add_immediate => try emit.mirAddSubtractImmediate(inst),
80 .adds_immediate => try emit.mirAddSubtractImmediate(inst),
8081 .cmp_immediate => try emit.mirAddSubtractImmediate(inst),
8182 .sub_immediate => try emit.mirAddSubtractImmediate(inst),
83 .subs_immediate => try emit.mirAddSubtractImmediate(inst),
8284
8385 .asr_register => try emit.mirShiftRegister(inst),
8486 .lsl_register => try emit.mirShiftRegister(inst),
......@@ -106,8 +108,10 @@ pub fn emitMir(
106108 .eor_immediate => try emit.mirLogicalImmediate(inst),
107109
108110 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
111 .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
109112 .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
110113 .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
114 .subs_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
111115
112116 .cset => try emit.mirConditionalSelect(inst),
113117
......@@ -454,7 +458,9 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
454458 const tag = emit.mir.instructions.items(.tag)[inst];
455459 switch (tag) {
456460 .add_immediate,
461 .adds_immediate,
457462 .sub_immediate,
463 .subs_immediate,
458464 => {
459465 const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh;
460466 const rd = rr_imm12_sh.rd;
......@@ -464,7 +470,9 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
464470
465471 switch (tag) {
466472 .add_immediate => try emit.writeInstruction(Instruction.add(rd, rn, imm12, sh)),
473 .adds_immediate => try emit.writeInstruction(Instruction.adds(rd, rn, imm12, sh)),
467474 .sub_immediate => try emit.writeInstruction(Instruction.sub(rd, rn, imm12, sh)),
475 .subs_immediate => try emit.writeInstruction(Instruction.subs(rd, rn, imm12, sh)),
468476 else => unreachable,
469477 }
470478 },
......@@ -674,7 +682,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
674682 const tag = emit.mir.instructions.items(.tag)[inst];
675683 switch (tag) {
676684 .add_shifted_register,
685 .adds_shifted_register,
677686 .sub_shifted_register,
687 .subs_shifted_register,
678688 => {
679689 const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift;
680690 const rd = rrr_imm6_shift.rd;
......@@ -685,7 +695,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
685695
686696 switch (tag) {
687697 .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)),
698 .adds_shifted_register => try emit.writeInstruction(Instruction.addsShiftedRegister(rd, rn, rm, shift, imm6)),
688699 .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)),
700 .subs_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)),
689701 else => unreachable,
690702 }
691703 },
......@@ -717,7 +729,7 @@ fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void {
717729 64 => .xzr,
718730 else => unreachable,
719731 };
720 try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond));
732 try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond.negate()));
721733 },
722734 else => unreachable,
723735 }
src/arch/aarch64/Mir.zig+8
......@@ -26,8 +26,12 @@ pub const Inst = struct {
2626 pub const Tag = enum(u16) {
2727 /// Add (immediate)
2828 add_immediate,
29 /// Add, update condition flags (immediate)
30 adds_immediate,
2931 /// Add (shifted register)
3032 add_shifted_register,
33 /// Add, update condition flags (shifted register)
34 adds_shifted_register,
3135 /// Bitwise AND (shifted register)
3236 and_shifted_register,
3337 /// Arithmetic Shift Right (immediate)
......@@ -170,8 +174,12 @@ pub const Inst = struct {
170174 strh_register,
171175 /// Subtract (immediate)
172176 sub_immediate,
177 /// Subtract, update condition flags (immediate)
178 subs_immediate,
173179 /// Subtract (shifted register)
174180 sub_shifted_register,
181 /// Subtract, update condition flags (shifted register)
182 subs_shifted_register,
175183 /// Supervisor Call
176184 svc,
177185 /// Unsigned bitfield extract