A bunch of fixes for division logic:

"turns out theres a lot of quirks with the div instructions we havent been covering
if the denom is 0, we jump to the end and mov eax/rax to dst, which is correct because ppc raises no exceptions for divide by 0 unlike x86
except we don't initialize eax before that jump, so whatever garbage from the previous sequence that has been left in eax/rax is what the result of the instruction will be
and then in our constant folding, we don't do the same zero check in Value::Div, so if we constant folded the denom to 0 we will host crash
the ppc manual says the result for a division by 0 is undefined, but in reality it seems it is always 0
there are a few posts i saw from googling about it, and tests on my rgh gave me 0, but then another issue came up
and that is that we dont check for signed overflow in our division, so we raise an exception if guest code ever does (1<<signbit_pos) / -1
signed overflow in division also produces 0 on ppc
the last thing is that if src2 is constant we skip the 0 check for division
without checking if its nonzero
all weird, likely very rare edge cases, except for maybe the signed overflow division
chrispy — Today at 9:51 AM
oh yeah, and because the int members of constantvalue are all signed ints, we were actually doing signed division always with constant folding"

fixed an earlier mistake by me with the precision of fresx
made some optimization disableable

implemented vkpkx
fixed possible bugs with vsr/vsl constant folding
disabled the nice imul code for now, there was a bug with int64 version and i dont have time to check
started on multiplication/addition/subtraction/division identities
Removed optimized VSL implementation, it's going to have to be rewritten anyway
Added ppc_ctx_t to xboxkrnl shim for direct context access
started working on KeSaveFloatingPointState, re'ed most of it
Exposed some more state/functionality to the kernel for implementing lower level routines like the save/restore ones
Add cvar to re-enable incorrect mxcsr behavior if a user doesnt care and wants better cpu performance
Stubbed out more impossible sequences, replace mul_hi_i32 with a 64 bit multiply
This commit is contained in:
chss95cs@gmail.com
2022-08-07 10:41:26 -07:00
parent f45e9e5e9a
commit 324a8eb818
19 changed files with 512 additions and 477 deletions

View File

@@ -20,7 +20,9 @@
DEFINE_bool(inline_mmio_access, true, "Inline constant MMIO loads and stores.",
"CPU");
DEFINE_bool(permit_float_constant_evaluation, false, "Allow float constant evaluation, may produce incorrect results and break games math",
DEFINE_bool(permit_float_constant_evaluation, false,
"Allow float constant evaluation, may produce incorrect results "
"and break games math",
"CPU");
namespace xe {
@@ -85,8 +87,8 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
if (i->dest) {
might_be_floatop |= i->dest->MaybeFloaty();
}
bool should_skip_because_of_float =
bool should_skip_because_of_float =
might_be_floatop && !cvars::permit_float_constant_evaluation;
auto v = i->dest;
@@ -557,6 +559,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
v->Div(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
i->Remove();
result = true;
} else if (!i->src2.value->MaybeFloaty() &&
i->src2.value->IsConstantZero()) {
// division by 0 == 0 every time,
v->set_zero(i->src2.value->type);
i->Remove();
result = true;
} else if (i->src2.value->IsConstant()) {
// Division by one = no-op.
Value* src1 = i->src1.value;
@@ -672,29 +680,33 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
}
break;
case OPCODE_SHL:
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
v->set_from(i->src1.value);
v->Shl(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
if (i->dest->type != VEC128_TYPE) {
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
v->set_from(i->src1.value);
v->Shl(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
}
}
break;
case OPCODE_SHR:
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
v->set_from(i->src1.value);
v->Shr(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
if (i->dest->type != VEC128_TYPE) {
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
v->set_from(i->src1.value);
v->Shr(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
}
}
break;
case OPCODE_SHA:
@@ -729,7 +741,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
result = true;
}
break;
#if 1
case OPCODE_PERMUTE: {
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
i->src3.value->IsConstant() &&
@@ -756,6 +768,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
break;
}
#endif
case OPCODE_INSERT:
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
i->src3.value->IsConstant()) {

View File

@@ -83,6 +83,7 @@ bool SimplificationPass::Run(HIRBuilder* builder, bool& result) {
iter_result |= SimplifyBitArith(builder);
iter_result |= EliminateConversions(builder);
iter_result |= SimplifyAssignments(builder);
iter_result |= SimplifyBasicArith(builder);
result |= iter_result;
} while (iter_result);
@@ -1228,6 +1229,91 @@ Value* SimplificationPass::CheckValue(Value* value, bool& result) {
return value;
}
bool SimplificationPass::SimplifyAddArith(hir::Instr* i,
hir::HIRBuilder* builder) {
/*
example: (x <<1 ) + x == (x*3)
*/
auto [shlinsn, addend] =
i->BinaryValueArrangeByDefiningOpcode(&OPCODE_SHL_info);
if (!shlinsn) {
return false;
}
Instr* shift_insn = shlinsn->def;
Value* shift = shift_insn->src2.value;
// if not a constant shift, we cant combine to a multiply
if (!shift->IsConstant()) {
return false;
}
Value* shouldbeaddend = shift_insn->src1.value;
if (!shouldbeaddend->IsEqual(addend)) {
return false;
}
uint64_t multiplier = 1ULL << shift->constant.u8;
multiplier++;
hir::Value* oldvalue = shouldbeaddend;
i->Replace(&OPCODE_MUL_info, ARITHMETIC_UNSIGNED);
i->set_src1(oldvalue);
// this sequence needs to be broken out into some kind of LoadConstant(type,
// raw_value) method of hirbuilder
auto constmul = builder->AllocValue(oldvalue->type);
// could cause problems on big endian targets...
constmul->flags |= VALUE_IS_CONSTANT;
constmul->constant.u64 = multiplier;
i->set_src2(constmul);
return true;
}
bool SimplificationPass::SimplifySubArith(hir::Instr* i,
hir::HIRBuilder* builder) {
return false;
}
bool SimplificationPass::SimplifyBasicArith(hir::Instr* i,
hir::HIRBuilder* builder) {
if (!i->dest) {
return false;
}
if (i->dest->MaybeFloaty()) {
return false;
}
hir::Opcode op = i->GetOpcodeNum();
switch (op) {
case OPCODE_ADD: {
return SimplifyAddArith(i, builder);
}
case OPCODE_SUB: {
return SimplifySubArith(i, builder);
}
}
return false;
}
bool SimplificationPass::SimplifyBasicArith(hir::HIRBuilder* builder) {
bool result = false;
auto block = builder->first_block();
while (block) {
auto i = block->instr_head;
while (i) {
result |= SimplifyBasicArith(i, builder);
i = i->next;
}
block = block->next;
}
return result;
}
} // namespace passes
} // namespace compiler
} // namespace cpu

View File

@@ -32,6 +32,13 @@ class SimplificationPass : public ConditionalGroupSubpass {
bool SimplifyAssignments(hir::HIRBuilder* builder);
hir::Value* CheckValue(hir::Value* value, bool& result);
bool SimplifyBitArith(hir::HIRBuilder* builder);
// handles simple multiplication/addition rules
bool SimplifyBasicArith(hir::HIRBuilder* builder);
bool SimplifyBasicArith(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifyAddArith(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifySubArith(hir::Instr* i, hir::HIRBuilder* builder);
// handle either or or xor with 0
bool CheckOrXorZero(hir::Instr* i);
bool CheckOr(hir::Instr* i, hir::HIRBuilder* builder);