| ... | ... | @@ -35,6 +35,7 @@ const RegisterManager = abi.RegisterManager; |
| 35 | 35 | const RegisterLock = RegisterManager.RegisterLock; |
| 36 | 36 | const Register = bits.Register; |
| 37 | 37 | const Instruction = bits.Instruction; |
| 38 | const Condition = bits.Instruction.Condition; |
| 38 | 39 | const callee_preserved_regs = abi.callee_preserved_regs; |
| 39 | 40 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 40 | 41 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; |
| ... | ... | @@ -90,7 +91,7 @@ register_manager: RegisterManager = .{}, |
| 90 | 91 | /// Maps offset to what is stored there. |
| 91 | 92 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 92 | 93 | /// Tracks the current instruction allocated to the compare flags |
| 93 | | compare_flags_inst: ?Air.Inst.Index = null, |
| 94 | condition_flags_inst: ?Air.Inst.Index = null, |
| 94 | 95 | |
| 95 | 96 | /// Offset from the stack base, representing the end of the stack frame. |
| 96 | 97 | max_end_stack: u32 = 0, |
| ... | ... | @@ -161,12 +162,10 @@ const MCValue = union(enum) { |
| 161 | 162 | /// The value is a pointer to one of the stack variables (payload |
| 162 | 163 | /// is stack offset). |
| 163 | 164 | ptr_stack_offset: u32, |
| 164 | | /// The value is in the compare flags assuming an unsigned |
| 165 | | /// operation, with this operator applied on top of it. |
| 166 | | compare_flags_unsigned: math.CompareOperator, |
| 167 | | /// The value is in the compare flags assuming a signed operation, |
| 168 | | /// with this operator applied on top of it. |
| 169 | | compare_flags_signed: math.CompareOperator, |
| 165 | /// The value resides in the N, Z, C, V flags. The value is 1 (if |
| 166 | /// the type is u1) or true (if the type in bool) iff the |
| 167 | /// specified condition is true. |
| 168 | condition_flags: Condition, |
| 170 | 169 | |
| 171 | 170 | fn isMemory(mcv: MCValue) bool { |
| 172 | 171 | return switch (mcv) { |
| ... | ... | @@ -190,8 +189,7 @@ const MCValue = union(enum) { |
| 190 | 189 | |
| 191 | 190 | .immediate, |
| 192 | 191 | .memory, |
| 193 | | .compare_flags_unsigned, |
| 194 | | .compare_flags_signed, |
| 192 | .condition_flags, |
| 195 | 193 | .ptr_stack_offset, |
| 196 | 194 | .undef, |
| 197 | 195 | => false, |
| ... | ... | @@ -758,10 +756,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 758 | 756 | }, |
| 759 | 757 | .register_with_overflow => |rwo| { |
| 760 | 758 | self.register_manager.freeReg(rwo.reg); |
| 761 | | self.compare_flags_inst = null; |
| 759 | self.condition_flags_inst = null; |
| 762 | 760 | }, |
| 763 | | .compare_flags_signed, .compare_flags_unsigned => { |
| 764 | | self.compare_flags_inst = null; |
| 761 | .condition_flags => { |
| 762 | self.condition_flags_inst = null; |
| 765 | 763 | }, |
| 766 | 764 | else => {}, // TODO process stack allocation death |
| 767 | 765 | } |
| ... | ... | @@ -911,12 +909,10 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 911 | 909 | /// Save the current instruction stored in the compare flags if |
| 912 | 910 | /// occupied |
| 913 | 911 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 914 | | if (self.compare_flags_inst) |inst_to_save| { |
| 912 | if (self.condition_flags_inst) |inst_to_save| { |
| 915 | 913 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 916 | 914 | const new_mcv = switch (mcv) { |
| 917 | | .compare_flags_signed, |
| 918 | | .compare_flags_unsigned, |
| 919 | | => try self.allocRegOrMem(inst_to_save, true), |
| 915 | .condition_flags => try self.allocRegOrMem(inst_to_save, true), |
| 920 | 916 | .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 921 | 917 | else => unreachable, // mcv doesn't occupy the compare flags |
| 922 | 918 | }; |
| ... | ... | @@ -927,7 +923,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 927 | 923 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 928 | 924 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 929 | 925 | |
| 930 | | self.compare_flags_inst = null; |
| 926 | self.condition_flags_inst = null; |
| 931 | 927 | |
| 932 | 928 | // TODO consolidate with register manager and spillInstruction |
| 933 | 929 | // this call should really belong in the register manager! |
| ... | ... | @@ -1109,32 +1105,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1109 | 1105 | switch (operand) { |
| 1110 | 1106 | .dead => unreachable, |
| 1111 | 1107 | .unreach => unreachable, |
| 1112 | | .compare_flags_unsigned => |op| { |
| 1113 | | const r = MCValue{ |
| 1114 | | .compare_flags_unsigned = switch (op) { |
| 1115 | | .gte => .lt, |
| 1116 | | .gt => .lte, |
| 1117 | | .neq => .eq, |
| 1118 | | .lt => .gte, |
| 1119 | | .lte => .gt, |
| 1120 | | .eq => .neq, |
| 1121 | | }, |
| 1122 | | }; |
| 1123 | | break :result r; |
| 1124 | | }, |
| 1125 | | .compare_flags_signed => |op| { |
| 1126 | | const r = MCValue{ |
| 1127 | | .compare_flags_signed = switch (op) { |
| 1128 | | .gte => .lt, |
| 1129 | | .gt => .lte, |
| 1130 | | .neq => .eq, |
| 1131 | | .lt => .gte, |
| 1132 | | .lte => .gt, |
| 1133 | | .eq => .neq, |
| 1134 | | }, |
| 1135 | | }; |
| 1136 | | break :result r; |
| 1137 | | }, |
| 1108 | .condition_flags => |cond| break :result MCValue{ .condition_flags = cond.negate() }, |
| 1138 | 1109 | else => { |
| 1139 | 1110 | switch (operand_ty.zigTypeTag()) { |
| 1140 | 1111 | .Bool => { |
| ... | ... | @@ -1851,7 +1822,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1851 | 1822 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 1852 | 1823 | |
| 1853 | 1824 | try self.spillCompareFlagsIfOccupied(); |
| 1854 | | self.compare_flags_inst = null; |
| 1825 | self.condition_flags_inst = null; |
| 1855 | 1826 | |
| 1856 | 1827 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1857 | 1828 | .add_with_overflow => .add, |
| ... | ... | @@ -1875,7 +1846,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1875 | 1846 | _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); |
| 1876 | 1847 | |
| 1877 | 1848 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1878 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| 1849 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 1879 | 1850 | |
| 1880 | 1851 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1881 | 1852 | }, |
| ... | ... | @@ -1907,7 +1878,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1907 | 1878 | }; |
| 1908 | 1879 | |
| 1909 | 1880 | try self.spillCompareFlagsIfOccupied(); |
| 1910 | | self.compare_flags_inst = inst; |
| 1881 | self.condition_flags_inst = inst; |
| 1911 | 1882 | |
| 1912 | 1883 | const dest = blk: { |
| 1913 | 1884 | if (rhs_immediate_ok) { |
| ... | ... | @@ -1966,7 +1937,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1966 | 1937 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 1967 | 1938 | |
| 1968 | 1939 | try self.spillCompareFlagsIfOccupied(); |
| 1969 | | self.compare_flags_inst = null; |
| 1940 | self.condition_flags_inst = null; |
| 1970 | 1941 | |
| 1971 | 1942 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 1972 | 1943 | .signed => .smull, |
| ... | ... | @@ -2015,16 +1986,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2015 | 1986 | } |
| 2016 | 1987 | |
| 2017 | 1988 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2018 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 2019 | | .compare_flags_unsigned = .neq, |
| 2020 | | }); |
| 1989 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2021 | 1990 | |
| 2022 | 1991 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2023 | 1992 | } else if (int_info.bits <= 64) { |
| 2024 | 1993 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2025 | 1994 | |
| 2026 | 1995 | try self.spillCompareFlagsIfOccupied(); |
| 2027 | | self.compare_flags_inst = null; |
| 1996 | self.condition_flags_inst = null; |
| 2028 | 1997 | |
| 2029 | 1998 | // TODO this should really be put in a helper similar to `binOpRegister` |
| 2030 | 1999 | const lhs_is_register = lhs == .register; |
| ... | ... | @@ -2194,9 +2163,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2194 | 2163 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2195 | 2164 | |
| 2196 | 2165 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2197 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 2198 | | .compare_flags_unsigned = .neq, |
| 2199 | | }); |
| 2166 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2200 | 2167 | |
| 2201 | 2168 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2202 | 2169 | } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); |
| ... | ... | @@ -2236,7 +2203,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2236 | 2203 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2237 | 2204 | |
| 2238 | 2205 | try self.spillCompareFlagsIfOccupied(); |
| 2239 | | self.compare_flags_inst = null; |
| 2206 | self.condition_flags_inst = null; |
| 2240 | 2207 | |
| 2241 | 2208 | // lsl dest, lhs, rhs |
| 2242 | 2209 | const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); |
| ... | ... | @@ -2251,9 +2218,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2251 | 2218 | _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); |
| 2252 | 2219 | |
| 2253 | 2220 | try self.genSetStack(lhs_ty, stack_offset, dest); |
| 2254 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 2255 | | .compare_flags_unsigned = .neq, |
| 2256 | | }); |
| 2221 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2257 | 2222 | |
| 2258 | 2223 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2259 | 2224 | } else { |
| ... | ... | @@ -2681,8 +2646,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2681 | 2646 | .undef => unreachable, |
| 2682 | 2647 | .unreach => unreachable, |
| 2683 | 2648 | .dead => unreachable, |
| 2684 | | .compare_flags_unsigned, |
| 2685 | | .compare_flags_signed, |
| 2649 | .condition_flags, |
| 2686 | 2650 | .register_with_overflow, |
| 2687 | 2651 | => unreachable, // cannot hold an address |
| 2688 | 2652 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| ... | ... | @@ -2694,7 +2658,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2694 | 2658 | switch (dst_mcv) { |
| 2695 | 2659 | .dead => unreachable, |
| 2696 | 2660 | .undef => unreachable, |
| 2697 | | .compare_flags_signed, .compare_flags_unsigned => unreachable, |
| 2661 | .condition_flags => unreachable, |
| 2698 | 2662 | .register => |dst_reg| { |
| 2699 | 2663 | try self.genLdrRegister(dst_reg, addr_reg, elem_ty); |
| 2700 | 2664 | }, |
| ... | ... | @@ -2903,8 +2867,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2903 | 2867 | .undef => unreachable, |
| 2904 | 2868 | .unreach => unreachable, |
| 2905 | 2869 | .dead => unreachable, |
| 2906 | | .compare_flags_unsigned, |
| 2907 | | .compare_flags_signed, |
| 2870 | .condition_flags, |
| 2908 | 2871 | .register_with_overflow, |
| 2909 | 2872 | => unreachable, // cannot hold an address |
| 2910 | 2873 | .immediate => |imm| { |
| ... | ... | @@ -3370,11 +3333,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3370 | 3333 | }); |
| 3371 | 3334 | |
| 3372 | 3335 | try self.spillCompareFlagsIfOccupied(); |
| 3373 | | self.compare_flags_inst = inst; |
| 3336 | self.condition_flags_inst = inst; |
| 3374 | 3337 | |
| 3375 | 3338 | break :result switch (int_info.signedness) { |
| 3376 | | .signed => MCValue{ .compare_flags_signed = op }, |
| 3377 | | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 3339 | .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, |
| 3340 | .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| 3378 | 3341 | }; |
| 3379 | 3342 | } else { |
| 3380 | 3343 | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| ... | ... | @@ -3434,26 +3397,13 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 3434 | 3397 | |
| 3435 | 3398 | fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { |
| 3436 | 3399 | switch (condition) { |
| 3437 | | .compare_flags_signed, |
| 3438 | | .compare_flags_unsigned, |
| 3439 | | => return try self.addInst(.{ |
| 3400 | .condition_flags => |cond| return try self.addInst(.{ |
| 3440 | 3401 | .tag = .b_cond, |
| 3441 | 3402 | .data = .{ |
| 3442 | 3403 | .inst_cond = .{ |
| 3443 | 3404 | .inst = undefined, // populated later through performReloc |
| 3444 | | .cond = switch (condition) { |
| 3445 | | .compare_flags_signed => |cmp_op| blk: { |
| 3446 | | // Here we map to the opposite condition because the jump is to the false branch. |
| 3447 | | const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op); |
| 3448 | | break :blk condition_code.negate(); |
| 3449 | | }, |
| 3450 | | .compare_flags_unsigned => |cmp_op| blk: { |
| 3451 | | // Here we map to the opposite condition because the jump is to the false branch. |
| 3452 | | const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); |
| 3453 | | break :blk condition_code.negate(); |
| 3454 | | }, |
| 3455 | | else => unreachable, |
| 3456 | | }, |
| 3405 | // Here we map to the opposite condition because the jump is to the false branch. |
| 3406 | .cond = cond.negate(), |
| 3457 | 3407 | }, |
| 3458 | 3408 | }, |
| 3459 | 3409 | }), |
| ... | ... | @@ -3503,7 +3453,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3503 | 3453 | var parent_stack = try self.stack.clone(self.gpa); |
| 3504 | 3454 | defer parent_stack.deinit(self.gpa); |
| 3505 | 3455 | const parent_registers = self.register_manager.registers; |
| 3506 | | const parent_compare_flags_inst = self.compare_flags_inst; |
| 3456 | const parent_condition_flags_inst = self.condition_flags_inst; |
| 3507 | 3457 | |
| 3508 | 3458 | try self.branch_stack.append(.{}); |
| 3509 | 3459 | errdefer { |
| ... | ... | @@ -3522,7 +3472,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3522 | 3472 | defer saved_then_branch.deinit(self.gpa); |
| 3523 | 3473 | |
| 3524 | 3474 | self.register_manager.registers = parent_registers; |
| 3525 | | self.compare_flags_inst = parent_compare_flags_inst; |
| 3475 | self.condition_flags_inst = parent_condition_flags_inst; |
| 3526 | 3476 | |
| 3527 | 3477 | self.stack.deinit(self.gpa); |
| 3528 | 3478 | self.stack = parent_stack; |
| ... | ... | @@ -3672,15 +3622,15 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3672 | 3622 | else => return self.fail("TODO implement isErr for {}", .{operand}), |
| 3673 | 3623 | } |
| 3674 | 3624 | |
| 3675 | | return MCValue{ .compare_flags_unsigned = .gt }; |
| 3625 | return MCValue{ .condition_flags = .hi }; |
| 3676 | 3626 | } |
| 3677 | 3627 | |
| 3678 | 3628 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3679 | 3629 | const is_err_result = try self.isErr(ty, operand); |
| 3680 | 3630 | switch (is_err_result) { |
| 3681 | | .compare_flags_unsigned => |op| { |
| 3682 | | assert(op == .gt); |
| 3683 | | return MCValue{ .compare_flags_unsigned = .lte }; |
| 3631 | .condition_flags => |cond| { |
| 3632 | assert(cond == .hi); |
| 3633 | return MCValue{ .condition_flags = cond.negate() }; |
| 3684 | 3634 | }, |
| 3685 | 3635 | .immediate => |imm| { |
| 3686 | 3636 | assert(imm == 0); |
| ... | ... | @@ -3889,7 +3839,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 3889 | 3839 | block_data.mcv = switch (operand_mcv) { |
| 3890 | 3840 | .none, .dead, .unreach => unreachable, |
| 3891 | 3841 | .register, .stack_offset, .memory => operand_mcv, |
| 3892 | | .immediate => blk: { |
| 3842 | .immediate, .condition_flags => blk: { |
| 3893 | 3843 | const new_mcv = try self.allocRegOrMem(block, true); |
| 3894 | 3844 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 3895 | 3845 | break :blk new_mcv; |
| ... | ... | @@ -4072,8 +4022,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4072 | 4022 | else => return self.fail("TODO implement memset", .{}), |
| 4073 | 4023 | } |
| 4074 | 4024 | }, |
| 4075 | | .compare_flags_unsigned, |
| 4076 | | .compare_flags_signed, |
| 4025 | .condition_flags, |
| 4077 | 4026 | .immediate, |
| 4078 | 4027 | .ptr_stack_offset, |
| 4079 | 4028 | => { |
| ... | ... | @@ -4235,15 +4184,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4235 | 4184 | } }, |
| 4236 | 4185 | }); |
| 4237 | 4186 | }, |
| 4238 | | .compare_flags_unsigned, |
| 4239 | | .compare_flags_signed, |
| 4240 | | => |op| { |
| 4241 | | const condition = switch (mcv) { |
| 4242 | | .compare_flags_unsigned => Instruction.Condition.fromCompareOperatorUnsigned(op), |
| 4243 | | .compare_flags_signed => Instruction.Condition.fromCompareOperatorSigned(op), |
| 4244 | | else => unreachable, |
| 4245 | | }; |
| 4246 | | |
| 4187 | .condition_flags => |condition| { |
| 4247 | 4188 | _ = try self.addInst(.{ |
| 4248 | 4189 | .tag = .cset, |
| 4249 | 4190 | .data = .{ .r_cond = .{ |