PPCRec: Fix division by zero and signed overflow

This commit is contained in:
Crementif authored and Exzap committed 2026-10-04 03:08:42 +02:00
1 parent fe6720c5c4
commit 9cbb00a274
2 files changed
+54 -3

No files matched your search

@@ -748,7 +748,26 @@ bool AArch64GenContext_t::r_r_r(IMLInstruction* imlInstruction)
}
else if (imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED)
{
// sdiv returns 0 for a zero divisor and 0x80000000 on overflow, divw gives the dividend sign and -1
Label divisorZero;
Label overflow;
Label divide;
Label done;
cbz(regOperand2, divisorZero);
cmn(regOperand2, 1);
bne(divide);
mov(TEMP_GPR1.WReg, 0x80000000);
cmp(regOperand1, TEMP_GPR1.WReg);
beq(overflow);
L(divide);
sdiv(regResult, regOperand1, regOperand2);
b(done);
L(divisorZero);
asr(regResult, regOperand1, 31);
b(done);
L(overflow);
mov(regResult, 0xFFFFFFFF);
L(done);
}
else if (imlInstruction->operation == PPCREC_IML_OP_DIVIDE_UNSIGNED)
{
@@ -843,14 +843,46 @@ bool PPCRecompilerX64Gen_imlInstruction_r_r_r(PPCRecFunction_t* PPCRecFunction,
x64Gen_cdq(x64GenContext);
else
x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, X86_REG_EDX, X86_REG_EDX);
// make sure we avoid division by zero
// handle PPC division edge cases without raising x86 #DE
bool isSignedDivide = imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED;
x64Gen_test_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP);
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 3);
sint32 jumpDivisorZero = x64GenContext->emitter->GetWriteIndex();
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0);
sint32 jumpOverflow = 0;
if( isSignedDivide )
{
x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, -1);
sint32 jumpNoOverflow = x64GenContext->emitter->GetWriteIndex();
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NOT_EQUAL, 0);
x64Gen_cmp_reg64Low32_imm32(x64GenContext, X86_REG_EAX, (sint32)0x80000000);
jumpOverflow = x64GenContext->emitter->GetWriteIndex();
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0);
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpNoOverflow, x64GenContext->emitter->GetWriteIndex());
}
// divide
if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED )
if( isSignedDivide )
x64Gen_idiv_reg64Low32(x64GenContext, REG_RESV_TEMP);
else
x64Gen_div_reg64Low32(x64GenContext, REG_RESV_TEMP);
sint32 jumpEnd = x64GenContext->emitter->GetWriteIndex();
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0);
// divisor is zero: divw yields the dividend sign, divwu yields zero
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpDivisorZero, x64GenContext->emitter->GetWriteIndex());
if( isSignedDivide )
{
x64Gen_sar_reg64Low32_imm8(x64GenContext, X86_REG_EAX, 31);
sint32 jumpEndFromZero = x64GenContext->emitter->GetWriteIndex();
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0);
// signed overflow yields -1 for divw
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOverflow, x64GenContext->emitter->GetWriteIndex());
x64Gen_mov_reg64Low32_imm32(x64GenContext, X86_REG_EAX, 0xFFFFFFFF);
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpEndFromZero, x64GenContext->emitter->GetWriteIndex());
}
else
{
x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, X86_REG_EAX, X86_REG_EAX);
}
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpEnd, x64GenContext->emitter->GetWriteIndex());
// result of division is now stored in EAX, move it to result register
if( rRegResult != X86_REG_EAX )
x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, X86_REG_EAX);