| ... | ... | @@ -544,7 +544,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 544 | 544 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 545 | 545 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 546 | 546 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 547 | | .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), |
| 547 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 548 | 548 | |
| 549 | 549 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 550 | 550 | |
| ... | ... | @@ -2022,6 +2022,96 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2022 | 2022 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 2023 | 2023 | } |
| 2024 | 2024 | |
| 2025 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2026 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2027 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2028 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2029 | const lhs = try self.resolveInst(extra.lhs); |
| 2030 | const rhs = try self.resolveInst(extra.rhs); |
| 2031 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2032 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2033 | |
| 2034 | switch (lhs_ty.zigTypeTag()) { |
| 2035 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 2036 | .Int => { |
| 2037 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2038 | if (int_info.bits <= 64) { |
| 2039 | try self.spillConditionFlagsIfOccupied(); |
| 2040 | self.condition_flags_inst = inst; |
| 2041 | |
| 2042 | const lhs_lock: ?RegisterLock = if (lhs == .register) |
| 2043 | self.register_manager.lockRegAssumeUnused(lhs.register) |
| 2044 | else |
| 2045 | null; |
| 2046 | // TODO this currently crashes stage1 |
| 2047 | // defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2048 | |
| 2049 | // Increase shift amount (i.e, rhs) by shamt_bits - int_info.bits |
| 2050 | // e.g if shifting a i48 then use sr*x (shamt_bits == 64) but increase rhs by 16 |
| 2051 | // and if shifting a i24 then use sr* (shamt_bits == 32) but increase rhs by 8 |
| 2052 | const new_rhs = switch (int_info.bits) { |
| 2053 | 1...31 => if (rhs == .immediate) MCValue{ |
| 2054 | .immediate = rhs.immediate + 32 - int_info.bits, |
| 2055 | } else try self.binOp(.add, rhs, .{ .immediate = 32 - int_info.bits }, rhs_ty, rhs_ty, null), |
| 2056 | 33...63 => if (rhs == .immediate) MCValue{ |
| 2057 | .immediate = rhs.immediate + 64 - int_info.bits, |
| 2058 | } else try self.binOp(.add, rhs, .{ .immediate = 64 - int_info.bits }, rhs_ty, rhs_ty, null), |
| 2059 | 32, 64 => rhs, |
| 2060 | else => unreachable, |
| 2061 | }; |
| 2062 | |
| 2063 | const new_rhs_lock: ?RegisterLock = if (new_rhs == .register) |
| 2064 | self.register_manager.lockRegAssumeUnused(new_rhs.register) |
| 2065 | else |
| 2066 | null; |
| 2067 | // TODO this currently crashes stage1 |
| 2068 | // defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2069 | |
| 2070 | const dest = try self.binOp(.shl, lhs, new_rhs, lhs_ty, rhs_ty, null); |
| 2071 | const dest_reg = dest.register; |
| 2072 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2073 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2074 | |
| 2075 | const shr = try self.binOp(.shr, dest, new_rhs, lhs_ty, rhs_ty, null); |
| 2076 | |
| 2077 | _ = try self.addInst(.{ |
| 2078 | .tag = .cmp, |
| 2079 | .data = .{ .arithmetic_2op = .{ |
| 2080 | .is_imm = false, |
| 2081 | .rs1 = dest_reg, |
| 2082 | .rs2_or_imm = .{ .rs2 = shr.register }, |
| 2083 | } }, |
| 2084 | }); |
| 2085 | |
| 2086 | const cond = Instruction.ICondition.ne; |
| 2087 | const ccr = switch (int_info.bits) { |
| 2088 | 1...32 => Instruction.CCR.icc, |
| 2089 | 33...64 => Instruction.CCR.xcc, |
| 2090 | else => unreachable, |
| 2091 | }; |
| 2092 | |
| 2093 | // TODO Those should really be written as defers, however stage1 currently |
| 2094 | // panics when those are turned into defer statements so those are |
| 2095 | // written here at the end as ordinary statements. |
| 2096 | // Because of that, on failure, the lock on those registers wouldn't be |
| 2097 | // released. |
| 2098 | if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2099 | if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2100 | |
| 2101 | break :result MCValue{ .register_with_overflow = .{ |
| 2102 | .reg = dest_reg, |
| 2103 | .flag = .{ .cond = cond, .ccr = ccr }, |
| 2104 | } }; |
| 2105 | } else { |
| 2106 | return self.fail("TODO overflow operations on other integer sizes", .{}); |
| 2107 | } |
| 2108 | }, |
| 2109 | else => unreachable, |
| 2110 | } |
| 2111 | }; |
| 2112 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2113 | } |
| 2114 | |
| 2025 | 2115 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 2026 | 2116 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2027 | 2117 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |