Skip to content

Commit

Permalink
jit: Fix overflow handling in function muldivmod
Browse files Browse the repository at this point in the history
The behavior of the host instructions div and mod differs from that of
RISC-V. Additional checks are required to align with RISC-V's DIV[U] and
REM[U] behavior, particularly when handling division by zero and
overflow scenarios.
  • Loading branch information
qwe661234 committed Jun 7, 2024
1 parent 486b4b9 commit da17bd0
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 35 deletions.
73 changes: 40 additions & 33 deletions src/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1041,27 +1041,13 @@ static void muldivmod(struct jit_state *state,
uint8_t opcode,
int src,
int dst,
int32_t imm UNUSED)
bool sign)
{
#if defined(__x86_64__)
bool mul = (opcode & JIT_ALU_OP_MASK) == (JIT_OP_MUL_IMM & JIT_ALU_OP_MASK);
bool div = (opcode & JIT_ALU_OP_MASK) == (JIT_OP_DIV_IMM & JIT_ALU_OP_MASK);
bool mod = (opcode & JIT_ALU_OP_MASK) == (JIT_OP_MOD_IMM & JIT_ALU_OP_MASK);
bool is64 = (opcode & JIT_CLS_MASK) == JIT_CLS_ALU64;
bool reg = (opcode & JIT_SRC_REG) == JIT_SRC_REG;

/* Short circuit for imm == 0 */
if (!reg && imm == 0) {
assert(NULL);
if (div || mul) {
/* For division and multiplication, set result to zero. */
emit_alu32(state, 0x31, dst, dst);
} else {
/* For modulo, set result to dividend. */
emit_mov(state, dst, dst);
}
return;
}

/* Record the mapping status before the registers are used for other
* purposes, and restore the status after popping the registers.
Expand All @@ -1080,34 +1066,32 @@ static void muldivmod(struct jit_state *state,
}

/* Load the divisor into RCX */
if (imm)
emit_load_imm(state, RCX, imm);
else
emit_mov(state, src, RCX);
emit_mov(state, src, RCX);

/* Load the dividend into RAX */
emit_mov(state, dst, RAX);

/* The JIT employs two different semantics for division and modulus
* operations. In the case of division, if the divisor is zero, the result
* is set to zero. For modulus operations, if the divisor is zero, the
* is set to -1. For modulus operations, if the divisor is zero, the
* result becomes the dividend. To manage this, we first set the divisor to
* 1 if it is initially zero. Then, we adjust the result accordingly: for
* division, we set it to zero if the original divisor was zero; for
* division, we set it to -1 if the original divisor was zero; for
* modulus, we set it to the dividend under the same condition.
*/

if (div || mod) {
/* Check if divisor is zero */
if (is64)
emit_alu64(state, 0x85, RCX, RCX);
else
emit_alu32(state, 0x85, RCX, RCX);

/* Save the dividend for the modulo case */
if (mod)
if (sign) {
emit_load_imm(state, RDX, -1);
/* compare divisor with -1 for overflow checking */
emit_cmp32(state, RDX, RCX);
/* Save the result of the comparision */
emit1(state, 0x9c); /* pushfq */
}
if (mod || (div && sign))
emit_push(state, RAX); /* Save dividend */

emit_alu32(state, 0x85, RCX, RCX);
/* Save the result of the test */
emit1(state, 0x9c); /* pushfq */

Expand All @@ -1117,14 +1101,12 @@ static void muldivmod(struct jit_state *state,
emit1(state, 0x0f);
emit1(state, 0x44);
emit1(state, 0xca); /* cmove rcx, rdx */

/* xor %edx,%edx */
emit_alu32(state, 0x31, RDX, RDX);
}

if (is64)
emit_rex(state, 1, 0, 0, 0);

/* Multiply or divide */
emit_alu32(state, 0xf7, mul ? 4 : 6, RCX);

Expand All @@ -1139,24 +1121,49 @@ static void muldivmod(struct jit_state *state,

if (div) {
/* Set the dividend to zero if the divisor was zero. */
emit_load_imm(state, RCX, 0);
emit_load_imm(state, RCX, -1);

/* Store 0 in RAX if the divisor was zero. */
/* Use conditional move to avoid a branch. */
emit1(state, 0x48);
emit1(state, 0x0f);
emit1(state, 0x44);
emit1(state, 0xc1); /* cmove rax, rcx */
if (sign) {
emit_pop(state, RCX);
/* handle DIV overflow */
emit1(state, 0x9d); /* popfq */
uint32_t jump_loc = state->offset;
emit_jcc_offset(state, 0x85);
emit_cmp_imm32(state, RCX, 0x80000000);
emit1(state, 0x48);
emit1(state, 0x0f);
emit1(state, 0x44);
emit1(state, 0xc1); /* cmove rax, rcx */
emit_jump_target_offset(state, JUMP_LOC, state->offset);
}
} else {
/* Restore dividend to RCX */
emit_pop(state, RCX);

/* Store the dividend in RAX if the divisor was zero. */
/* Use conditional move to avoid a branch. */
emit1(state, 0x48);
emit1(state, 0x0f);
emit1(state, 0x44);
emit1(state, 0xd1); /* cmove rdx, rcx */
if (sign) {
/* handle REM overflow */
emit1(state, 0x9d); /* popfq */
uint32_t jump_loc = state->offset;
emit_jcc_offset(state, 0x85);
emit_cmp_imm32(state, RCX, 0x80000000);
emit_load_imm(state, RCX, 0);
emit1(state, 0x48);
emit1(state, 0x0f);
emit1(state, 0x44);
emit1(state, 0xd1); /* cmove rdx, rcx */
emit_jump_target_offset(state, JUMP_LOC, state->offset);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/rv32_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ RVOP(
map, VR2, rd;
mov, VR1, TMP;
mov, VR0, VR2;
div, 0x38, TMP, VR2, 0;
div, 0x38, TMP, VR2, 1;
/* FIXME: handle overflow */
}))

Expand Down Expand Up @@ -1260,7 +1260,7 @@ GEN({
map, VR2, rd;
mov, VR1, TMP;
mov, VR0, VR2;
mod, 0x98, TMP, VR2, 0;
mod, 0x98, TMP, VR2, 1;
/* FIXME: handle overflow */
}))

Expand Down

0 comments on commit da17bd0

Please sign in to comment.