+ }
+
+ reg_lo = get_mfhi_mflo_reg(block, i + 1, NULL, 0, false, true, false);
+ if (reg_lo == 0) {
+ pr_debug("Mark MULT(U)/DIV(U) opcode at offset 0x%x as"
+ " not writing LO\n", i << 2);
+ list->flags |= LIGHTREC_NO_LO;
+ }
+
+ reg_hi = get_mfhi_mflo_reg(block, i + 1, NULL, 0, false, false, false);
+ if (reg_hi == 0) {
+ pr_debug("Mark MULT(U)/DIV(U) opcode at offset 0x%x as"
+ " not writing HI\n", i << 2);
+ list->flags |= LIGHTREC_NO_HI;
+ }
+
+ if (!reg_lo && !reg_hi) {
+ pr_debug("Both LO/HI unused in this block, they will "
+ "probably be used in parent block - removing "
+ "flags.\n");
+ list->flags &= ~(LIGHTREC_NO_LO | LIGHTREC_NO_HI);
+ }
+
+ if (reg_lo > 0 && reg_lo != REG_LO) {
+ pr_debug("Found register %s to hold LO (rs = %u, rt = %u)\n",
+ lightrec_reg_name(reg_lo), list->r.rs, list->r.rt);
+
+ lightrec_replace_lo_hi(block, i + 1, block->nb_ops, true);
+ list->r.rd = reg_lo;
+ } else {
+ list->r.rd = 0;
+ }
+
+ if (reg_hi > 0 && reg_hi != REG_HI) {
+ pr_debug("Found register %s to hold HI (rs = %u, rt = %u)\n",
+ lightrec_reg_name(reg_hi), list->r.rs, list->r.rt);
+
+ lightrec_replace_lo_hi(block, i + 1, block->nb_ops, false);
+ list->r.imm = reg_hi;
+ } else {
+ list->r.imm = 0;
+ }
+ }
+
+ return 0;
+}
+
+static bool remove_div_sequence(struct block *block, unsigned int offset)
+{
+ struct opcode *op;
+ unsigned int i, found = 0;
+
+ /*
+ * Scan for the zero-checking sequence that GCC automatically introduced
+ * after most DIV/DIVU opcodes. This sequence checks the value of the
+ * divisor, and if zero, executes a BREAK opcode, causing the BIOS
+ * handler to crash the PS1.
+ *
+ * For DIV opcodes, this sequence additionally checks that the signed
+ * operation does not overflow.
+ *
+ * With the assumption that the games never crashed the PS1, we can
+ * therefore assume that the games never divided by zero or overflowed,
+ * and these sequences can be removed.
+ */
+
+ for (i = offset; i < block->nb_ops; i++) {
+ op = &block->opcode_list[i];
+
+ if (!found) {
+ if (op->i.op == OP_SPECIAL &&
+ (op->r.op == OP_SPECIAL_DIV || op->r.op == OP_SPECIAL_DIVU))
+ break;
+
+ if ((op->opcode & 0xfc1fffff) == 0x14000002) {
+ /* BNE ???, zero, +8 */
+ found++;
+ } else {
+ offset++;
+ }
+ } else if (found == 1 && !op->opcode) {
+ /* NOP */
+ found++;
+ } else if (found == 2 && op->opcode == 0x0007000d) {
+ /* BREAK 0x1c00 */
+ found++;
+ } else if (found == 3 && op->opcode == 0x2401ffff) {
+ /* LI at, -1 */
+ found++;
+ } else if (found == 4 && (op->opcode & 0xfc1fffff) == 0x14010004) {
+ /* BNE ???, at, +16 */
+ found++;
+ } else if (found == 5 && op->opcode == 0x3c018000) {
+ /* LUI at, 0x8000 */
+ found++;
+ } else if (found == 6 && (op->opcode & 0x141fffff) == 0x14010002) {
+ /* BNE ???, at, +16 */
+ found++;
+ } else if (found == 7 && !op->opcode) {
+ /* NOP */
+ found++;
+ } else if (found == 8 && op->opcode == 0x0006000d) {
+ /* BREAK 0x1800 */
+ found++;
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (found >= 3) {
+ if (found != 9)
+ found = 3;
+
+ pr_debug("Removing DIV%s sequence at offset 0x%x\n",
+ found == 9 ? "" : "U", offset << 2);
+
+ for (i = 0; i < found; i++)
+ block->opcode_list[offset + i].opcode = 0;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lightrec_remove_div_by_zero_check_sequence(struct lightrec_state *state,
+ struct block *block)
+{
+ struct opcode *op;
+ unsigned int i;
+
+ for (i = 0; i < block->nb_ops; i++) {
+ op = &block->opcode_list[i];