Skip to content

Commit

Permalink
Merge pull request #136 from DrXiao/master
Browse files Browse the repository at this point in the history
Improve the division emulation in the Arm backend
  • Loading branch information
jserv authored Jul 3, 2024
2 parents 6cdd7e2 + 4f2cb0c commit 0a830cc
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 76 deletions.
146 changes: 71 additions & 75 deletions src/arm-codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,16 @@ void update_elf_offset(ph2_ir_t *ph2_ir)
elf_offset += 4;
return;
case OP_div:
if (hard_mul_div) {
elf_offset += 4;
} else {
elf_offset += 104;
}
return;
case OP_mod:
if (hard_mul_div) {
elf_offset += 12;
} else {
elf_offset += 104;
if (ph2_ir->op == OP_div)
elf_offset += 4;
else
elf_offset += 12;
return;
}
/* div/mod emulation's offset */
elf_offset += 116;
return;
case OP_load_data_address:
elf_offset += 8;
Expand Down Expand Up @@ -193,6 +191,11 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
int rm = ph2_ir->src1;
int ofs;

/* Prepare this variable to reuse the same code for
* the instruction sequence of division and modulo.
*/
arm_reg soft_div_rd = __r8;

switch (ph2_ir->op) {
case OP_define:
emit(__sw(__AL, __lr, __sp, -4));
Expand Down Expand Up @@ -335,76 +338,69 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
emit(__mul(__AL, rd, rn, rm));
return;
case OP_div:
if (hard_mul_div) {
emit(__div(__AL, rd, rm, rn));
} else {
/* Obtain absolute values of dividend and divisor */
emit(__srl_amt(__AL, 0, arith_rs, __r8, rn, 31));
emit(__add_r(__AL, rn, rn, __r8));
emit(__eor_r(__AL, rn, rn, __r8));
emit(__srl_amt(__AL, 0, arith_rs, __r9, rm, 31));
emit(__add_r(__AL, rm, rm, __r9));
emit(__eor_r(__AL, rm, rm, __r9));
emit(__eor_r(__AL, __r10, __r8, __r9));
/* Unsigned integer division */
emit(__zero(__r9));
emit(__mov_i(__AL, __r8, 1));
emit(__cmp_i(__AL, rm, 0));
emit(__b(__EQ, 52));
emit(__cmp_i(__AL, rn, 0));
emit(__b(__EQ, 44));
emit(__cmp_r(__AL, rm, rn));
emit(__sll_amt(__CC, 0, logic_ls, rm, rm, 1));
emit(__sll_amt(__CC, 0, logic_ls, __r8, __r8, 1));
emit(__b(__CC, -12));
emit(__cmp_r(__AL, rn, rm));
emit(__sub_r(__CS, rn, rn, rm));
emit(__add_r(__CS, __r9, __r9, __r8));
emit(__srl_amt(__AL, 1, logic_rs, __r8, __r8, 1));
emit(__srl_amt(__CC, 0, logic_rs, rm, rm, 1));
emit(__b(__CC, -20));
emit(__mov_r(__AL, rd, __r9));
/* Handle the correct sign for quotient */
emit(__cmp_i(__AL, __r10, 0));
emit(__rsb_i(__NE, rd, 0, rd));
}
return;
case OP_mod:
if (hard_mul_div) {
emit(__div(__AL, __r8, rm, rn));
emit(__mul(__AL, __r8, rm, __r8));
emit(__sub_r(__AL, rd, rn, __r8));
} else {
/* Obtain absolute values of dividend and divisor */
emit(__srl_amt(__AL, 0, arith_rs, __r8, rn, 31));
emit(__add_r(__AL, rn, rn, __r8));
emit(__eor_r(__AL, rn, rn, __r8));
emit(__srl_amt(__AL, 0, arith_rs, __r9, rm, 31));
emit(__add_r(__AL, rm, rm, __r9));
emit(__eor_r(__AL, rm, rm, __r9));
if (ph2_ir->op == OP_div)
emit(__div(__AL, rd, rm, rn));
else {
emit(__div(__AL, __r8, rm, rn));
emit(__mul(__AL, __r8, rm, __r8));
emit(__sub_r(__AL, rd, rn, __r8));
}
return;
}
/* div/mod emulation */
/* Preserve the values of the dividend and divisor */
emit(__stmdb(__AL, 1, __sp, (1 << rn) | (1 << rm)));
/* Obtain absolute values of the dividend and divisor */
emit(__srl_amt(__AL, 0, arith_rs, __r8, rn, 31));
emit(__add_r(__AL, rn, rn, __r8));
emit(__eor_r(__AL, rn, rn, __r8));
emit(__srl_amt(__AL, 0, arith_rs, __r9, rm, 31));
emit(__add_r(__AL, rm, rm, __r9));
emit(__eor_r(__AL, rm, rm, __r9));
if (ph2_ir->op == OP_div)
emit(__eor_r(__AL, __r10, __r8, __r9));
else {
/* If the requested operation is modulo, the result will be stored
* in __r9. The sign of the divisor is irrelevant for determining
* the result's sign.
*/
soft_div_rd = __r9;
emit(__mov_r(__AL, __r10, __r8));
/* Unsigned integer division */
emit(__zero(__r9));
emit(__mov_i(__AL, __r8, 1));
emit(__cmp_i(__AL, rm, 0));
emit(__b(__EQ, 52));
emit(__cmp_i(__AL, rn, 0));
emit(__b(__EQ, 44));
emit(__cmp_r(__AL, rm, rn));
emit(__sll_amt(__CC, 0, logic_ls, rm, rm, 1));
emit(__sll_amt(__CC, 0, logic_ls, __r8, __r8, 1));
emit(__b(__CC, -12));
emit(__cmp_r(__AL, rn, rm));
emit(__sub_r(__CS, rn, rn, rm));
emit(__add_r(__CS, __r9, __r9, __r8));
emit(__srl_amt(__AL, 1, logic_rs, __r8, __r8, 1));
emit(__srl_amt(__CC, 0, logic_rs, rm, rm, 1));
emit(__b(__CC, -20));
emit(__mov_r(__AL, rd, rn));
/* Handle the correct sign for remainder */
emit(__cmp_i(__AL, __r10, 0));
emit(__rsb_i(__NE, rd, 0, rd));
}
/* Unsigned integer division */
emit(__zero(__r8));
emit(__mov_i(__AL, __r9, 1));
emit(__cmp_i(__AL, rm, 0));
emit(__b(__EQ, 52));
emit(__cmp_i(__AL, rn, 0));
emit(__b(__EQ, 44));
emit(__cmp_r(__AL, rm, rn));
emit(__sll_amt(__CC, 0, logic_ls, rm, rm, 1));
emit(__sll_amt(__CC, 0, logic_ls, __r9, __r9, 1));
emit(__b(__CC, -12));
emit(__cmp_r(__AL, rn, rm));
emit(__sub_r(__CS, rn, rn, rm));
emit(__add_r(__CS, __r8, __r8, __r9));
emit(__srl_amt(__AL, 1, logic_rs, __r9, __r9, 1));
emit(__srl_amt(__CC, 0, logic_rs, rm, rm, 1));
emit(__b(__CC, -20));
/* After completing the emulation, the quotient and remainder
* will be stored in __r8 and __r9, respectively.
*
* The original values of the dividend and divisor will be
* restored in rn and rm.
*
* Finally, the result (quotient or remainder) will be stored
* in rd.
*/
emit(__mov_r(__AL, __r9, rn));
emit(__ldm(__AL, 1, __sp, (1 << rn) | (1 << rm)));
emit(__mov_r(__AL, rd, soft_div_rd));
/* Handle the correct sign for the quotient or remainder */
emit(__cmp_i(__AL, __r10, 0));
emit(__rsb_i(__NE, rd, 0, rd));
return;
case OP_lshift:
emit(__sll(__AL, rd, rn, rm));
Expand Down
14 changes: 13 additions & 1 deletion src/arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ typedef enum {
arm_sub = 2,
arm_rsb = 3,
arm_add = 4,
arm_ldm = 9,
arm_teq = 9,
arm_cmp = 10,
arm_orr = 12,
arm_mov = 13,
arm_mvn = 15
arm_mvn = 15,
arm_stmdb = 16
} arm_op_t;

/* Condition code
Expand Down Expand Up @@ -283,6 +285,16 @@ int __sb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
return arm_transfer(cond, 0, 1, rn, rd, ofs);
}

int __stmdb(arm_cond_t cond, int w, arm_reg rn, int reg_list)
{
return arm_encode(cond, arm_stmdb + (0x2 << 6) + (w << 1), rn, 0, reg_list);
}

int __ldm(arm_cond_t cond, int w, arm_reg rn, int reg_list)
{
return arm_encode(cond, arm_ldm + (0x2 << 6) + (w << 1), rn, 0, reg_list);
}

int __b(arm_cond_t cond, int ofs)
{
int o = (ofs - 8) >> 2;
Expand Down

0 comments on commit 0a830cc

Please sign in to comment.