Huge set of performance improvements, combined with an architecture specific build and clang-cl users have reported absurd gains over master for some gains, in the range 50%-90%

But for normal msvc builds i would put it at around 30-50%
Added per-xexmodule caching of information per instruction, can be used to remember what code needs compiling at start up
Record what guest addresses wrote mmio and backpropagate that to future runs, eliminating dependence on exception trapping. this makes many games like h3 actually tolerable to run under a debugger
fixed a number of errors where temporaries were being passed by reference/pointer
Can now be compiled with clang-cl 14.0.1, requires -Werror off though and some other solution/project changes.
Added macros wrapping compiler extensions like noinline, forceinline, __expect, and cold.
Removed the "global lock" in guest code completely. It does not properly emulate the behavior of mfmsrd/mtmsr and it seriously cripples amd cpus. Removing this yielded around a 3x speedup in Halo Reach for me.
Disabled the microprofiler for now. The microprofiler has a huge performance cost associated with it. Developers can re-enable it in the base/profiling header if they really need it
Disable the trace writer in release builds. despite just returning after checking if the file was open the trace functions were consuming about 0.60% cpu time total
Add IsValidReg, GetRegisterInfo is a huge (about 45k) branching function and using that to check if a register was valid consumed a significant chunk of time
Optimized RingBuffer::ReadAndSwap and RingBuffer::read_count. This gave us the largest overall boost in performance. The memcpies were unnecessary and one of them was always a no-op
Added simplification rules for multiplicative patterns like (x+x), (x<<1)+x
For the most frequently called win32 functions i added code to call their underlying NT implementations, which lets us skip a lot of MS code we don't care about/isnt relevant to our usecases
^this can be toggled off in the platform_win header
handle indirect call true with constant function pointer, was occurring in h3
lookup host format swizzle in denser array
by default, don't check if a gpu register is unknown, instead just check if its out of range. controlled by a cvar
^looking up whether its known or not took approx 0.3% cpu time
Changed some things in /cpu to make the project UNITYBUILD friendly
The timer thread was spinning way too much and consuming a ton of cpu, changed it to use a blocking wait instead
tagged some conditions as XE_UNLIKELY/LIKELY based on profiler feedback (will only affect clang builds)
Shifted around some code in CommandProcessor::WriteRegister based on how frequently it was executed
added support for docdecaduple precision floating point so that we can represent our performance gains numerically
tons of other stuff im probably forgetting
This commit is contained in:
chss95cs@gmail.com
2022-08-13 12:59:00 -07:00
parent 2f59487bf3
commit cb85fe401c
49 changed files with 1462 additions and 483 deletions

View File

@@ -149,7 +149,20 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
i->Remove();
}
result = true;
} else if (i->src2.value->IsConstant()) { // chrispy: fix h3 bug from
// const indirect call true
auto function = processor_->LookupFunction(
uint32_t(i->src2.value->constant.i32));
if (!function) {
break;
}
// i->Replace(&OPCODE_CALL_TRUE_info, i->flags);
i->opcode = &OPCODE_CALL_TRUE_info;
i->set_src2(nullptr);
i->src2.symbol = function;
result = true;
}
break;
case OPCODE_BRANCH_TRUE:

View File

@@ -796,10 +796,13 @@ bool SimplificationPass::CheckScalarConstCmp(hir::Instr* i,
if (var_definition) {
var_definition = var_definition->GetDestDefSkipAssigns();
if (var_definition != NULL)
{
def_opcode = var_definition->opcode->num;
if (!var_definition) {
return false;
}
def_opcode = var_definition->opcode->num;
}
if (!var_definition) {
return false;
}
// x == 0 -> !x
if (cmpop == OPCODE_COMPARE_EQ && constant_unpacked == 0) {
@@ -1231,13 +1234,12 @@ Value* SimplificationPass::CheckValue(Value* value, bool& result) {
result = false;
return value;
}
bool SimplificationPass::SimplifyAddArith(hir::Instr* i,
hir::HIRBuilder* builder) {
bool SimplificationPass::SimplifyAddWithSHL(hir::Instr* i,
hir::HIRBuilder* builder) {
/*
example: (x <<1 ) + x == (x*3)
example: (x <<1 ) + x == (x*3)
*/
*/
auto [shlinsn, addend] =
i->BinaryValueArrangeByDefiningOpcode(&OPCODE_SHL_info);
if (!shlinsn) {
@@ -1278,11 +1280,81 @@ bool SimplificationPass::SimplifyAddArith(hir::Instr* i,
return true;
}
bool SimplificationPass::SimplifyAddToSelf(hir::Instr* i,
hir::HIRBuilder* builder) {
/*
heres a super easy one
*/
if (i->src1.value != i->src2.value) {
return false;
}
i->opcode = &OPCODE_SHL_info;
i->set_src2(builder->LoadConstantUint8(1));
return true;
}
bool SimplificationPass::SimplifyAddArith(hir::Instr* i,
hir::HIRBuilder* builder) {
if (SimplifyAddWithSHL(i, builder)) {
return true;
}
if (SimplifyAddToSelf(i, builder)) {
return true;
}
return false;
}
bool SimplificationPass::SimplifySubArith(hir::Instr* i,
hir::HIRBuilder* builder) {
/*
todo: handle expressions like (x*8) - (x*5) == (x*3)...if these can even
happen of course */
return false;
}
bool SimplificationPass::SimplifySHLArith(hir::Instr* i,
hir::HIRBuilder* builder) {
Value* sh = i->src2.value;
Value* shifted = i->src1.value;
if (!sh->IsConstant()) {
return false;
}
hir::Instr* definition = shifted->GetDefSkipAssigns();
if (!definition) {
return false;
}
if (definition->GetOpcodeNum() != OPCODE_MUL) {
return false;
}
if (definition->flags != ARITHMETIC_UNSIGNED) {
return false;
}
auto [mulconst, mulnonconst] = definition->BinaryValueArrangeAsConstAndVar();
if (!mulconst) {
return false;
}
auto newmul = builder->AllocValue(mulconst->type);
newmul->set_from(mulconst);
newmul->Shl(sh);
i->Replace(&OPCODE_MUL_info, ARITHMETIC_UNSIGNED);
i->set_src1(mulnonconst);
i->set_src2(newmul);
return true;
}
bool SimplificationPass::SimplifyBasicArith(hir::Instr* i,
hir::HIRBuilder* builder) {
if (!i->dest) {
@@ -1301,6 +1373,9 @@ bool SimplificationPass::SimplifyBasicArith(hir::Instr* i,
case OPCODE_SUB: {
return SimplifySubArith(i, builder);
}
case OPCODE_SHL: {
return SimplifySHLArith(i, builder);
}
}
return false;
}
@@ -1317,6 +1392,97 @@ bool SimplificationPass::SimplifyBasicArith(hir::HIRBuilder* builder) {
}
return result;
}
/*
todo: add load-store simplification pass
do things like load-store byteswap elimination, for instance,
if a value is loaded, ored with a constant mask, and then stored, we
simply have to byteswap the mask it will be ored with and then we can
eliminate the two byteswaps
the same can be done for and, or, xor, andn with constant masks
this can also be done for comparisons with 0 for equality and not equal
another optimization: with ppc you cannot move a floating point register
directly to a gp one, a gp one directly to a floating point register, or a
vmx one to either. so guest code will store the result to the stack, and then
load it to the register it needs in HIR we can sidestep this. we will still
need to byteswap and store the result for correctness, but we can eliminate
the load and byteswap by grabbing the original value from the store
skyth's sanic idb, 0x824D7724
lis r11,
lfs f0, flt_8200CBCC@l(r11)
fmuls f0, time, f0
fctidz f0, f0 # vcvttss2si
stfd f0, 0x190+var_138(r1)
lwz r30, 0x190+var_138+4(r1)
cmplwi cr6, r30, 0x63 # 'c'
ble cr6, counter_op
*/
/*
todo: simple loop unrolling
skyth sanic 0x831D9908
mr r30, r4
mr r29, r5
mr r11, r7
li r31, 0
loc_831D9928:
slwi r9, r11, 1
addi r10, r11, 1
addi r8, r1, 0xD0+var_80
clrlwi r11, r10, 16
cmplwi cr6, r11, 0x10
sthx r31, r9, r8
ble cr6, loc_831D9928
v5 = 1;
do
{
v6 = 2 * v5;
v5 = (unsigned __int16)(v5 + 1);
*(_WORD *)&v24[v6] = 0;
}
while ( v5 <= 0x10 );
v7 = 0;
do
{
v8 = __ROL4__(*(unsigned __int8 *)(v7 + a2), 1);
v7 = (unsigned __int16)(v7 + 1);
++*(_WORD *)&v24[v8];
}
while ( v7 < 8 );
v9 = 1;
v25[0] = 0;
do
{
v10 = 2 * v9;
v11 = 16 - v9;
v9 = (unsigned __int16)(v9 + 1);
v25[v10 / 2] = (*(_WORD *)&v24[v10] << v11) + *(_WORD
*)&v24[v10 + 48];
}
while ( v9 <= 0x10 );
skyth sanic:
sub_831BBAE0
sub_831A41A8
*/
} // namespace passes
} // namespace compiler
} // namespace cpu

View File

@@ -36,9 +36,11 @@ class SimplificationPass : public ConditionalGroupSubpass {
// handles simple multiplication/addition rules
bool SimplifyBasicArith(hir::HIRBuilder* builder);
bool SimplifyBasicArith(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifyAddWithSHL(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifyAddToSelf(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifyAddArith(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifySubArith(hir::Instr* i, hir::HIRBuilder* builder);
bool SimplifySHLArith(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);