Reducing value count. IVM could take advantage of this.
This commit is contained in:
parent
05432242ff
commit
c4d6c7a526
|
@ -126,12 +126,14 @@ uint32_t AllocLabel(TranslationContext& ctx, Label* label) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AllocDynamicRegister(TranslationContext& ctx, Value* value) {
|
uint32_t AllocDynamicRegister(TranslationContext& ctx, Value* value) {
|
||||||
int32_t reg = (int32_t)value->tag - 1;
|
if (value->flags & VALUE_IS_ALLOCATED) {
|
||||||
if (reg == -1) {
|
return (uint32_t)value->tag;
|
||||||
reg = ctx.register_count++;
|
} else {
|
||||||
value->tag = (void*)(reg + 1);
|
value->flags |= VALUE_IS_ALLOCATED;
|
||||||
|
auto reg = ctx.register_count++;
|
||||||
|
value->tag = (void*)reg;
|
||||||
|
return (uint32_t)reg;
|
||||||
}
|
}
|
||||||
return (uint32_t)reg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AllocOpRegister(
|
uint32_t AllocOpRegister(
|
||||||
|
|
|
@ -202,8 +202,23 @@ void X64Emitter::FindFreeRegs(
|
||||||
Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
Value* v0, uint32_t& v0_idx, uint32_t v0_flags,
|
||||||
Value* v1, uint32_t& v1_idx, uint32_t v1_flags) {
|
Value* v1, uint32_t& v1_idx, uint32_t v1_flags) {
|
||||||
// TODO(benvanik): support REG_DEST reuse/etc.
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
FindFreeRegs(v0, v0_idx, v0_flags);
|
// Grab all already-present registers first.
|
||||||
FindFreeRegs(v1, v1_idx, v1_flags);
|
// This way we won't spill them trying to get new registers.
|
||||||
|
bool need_v0 = v0->reg != -1;
|
||||||
|
bool need_v1 = v1->reg != -1;
|
||||||
|
if (!need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (!need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
// Grab any registers we still need. These calls may evict.
|
||||||
|
if (need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::FindFreeRegs(
|
void X64Emitter::FindFreeRegs(
|
||||||
|
@ -211,9 +226,30 @@ void X64Emitter::FindFreeRegs(
|
||||||
Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
Value* v1, uint32_t& v1_idx, uint32_t v1_flags,
|
||||||
Value* v2, uint32_t& v2_idx, uint32_t v2_flags) {
|
Value* v2, uint32_t& v2_idx, uint32_t v2_flags) {
|
||||||
// TODO(benvanik): support REG_DEST reuse/etc.
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
FindFreeRegs(v0, v0_idx, v0_flags);
|
// Grab all already-present registers first.
|
||||||
FindFreeRegs(v1, v1_idx, v1_flags);
|
// This way we won't spill them trying to get new registers.
|
||||||
FindFreeRegs(v2, v2_idx, v2_flags);
|
bool need_v0 = v0->reg != -1;
|
||||||
|
bool need_v1 = v1->reg != -1;
|
||||||
|
bool need_v2 = v2->reg != -1;
|
||||||
|
if (!need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (!need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
if (!need_v2) {
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
}
|
||||||
|
// Grab any registers we still need. These calls may evict.
|
||||||
|
if (need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
if (need_v2) {
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::FindFreeRegs(
|
void X64Emitter::FindFreeRegs(
|
||||||
|
@ -222,10 +258,37 @@ void X64Emitter::FindFreeRegs(
|
||||||
Value* v2, uint32_t& v2_idx, uint32_t v2_flags,
|
Value* v2, uint32_t& v2_idx, uint32_t v2_flags,
|
||||||
Value* v3, uint32_t& v3_idx, uint32_t v3_flags) {
|
Value* v3, uint32_t& v3_idx, uint32_t v3_flags) {
|
||||||
// TODO(benvanik): support REG_DEST reuse/etc.
|
// TODO(benvanik): support REG_DEST reuse/etc.
|
||||||
FindFreeRegs(v0, v0_idx, v0_flags);
|
// Grab all already-present registers first.
|
||||||
FindFreeRegs(v1, v1_idx, v1_flags);
|
// This way we won't spill them trying to get new registers.
|
||||||
FindFreeRegs(v2, v2_idx, v2_flags);
|
bool need_v0 = v0->reg != -1;
|
||||||
FindFreeRegs(v3, v3_idx, v3_flags);
|
bool need_v1 = v1->reg != -1;
|
||||||
|
bool need_v2 = v2->reg != -1;
|
||||||
|
bool need_v3 = v3->reg != -1;
|
||||||
|
if (!need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (!need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
if (!need_v2) {
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
}
|
||||||
|
if (!need_v3) {
|
||||||
|
FindFreeRegs(v3, v3_idx, v3_flags);
|
||||||
|
}
|
||||||
|
// Grab any registers we still need. These calls may evict.
|
||||||
|
if (need_v0) {
|
||||||
|
FindFreeRegs(v0, v0_idx, v0_flags);
|
||||||
|
}
|
||||||
|
if (need_v1) {
|
||||||
|
FindFreeRegs(v1, v1_idx, v1_flags);
|
||||||
|
}
|
||||||
|
if (need_v2) {
|
||||||
|
FindFreeRegs(v2, v2_idx, v2_flags);
|
||||||
|
}
|
||||||
|
if (need_v3) {
|
||||||
|
FindFreeRegs(v3, v3_idx, v3_flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::MarkSourceOffset(Instr* i) {
|
void X64Emitter::MarkSourceOffset(Instr* i) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <alloy/compiler/passes/finalization_pass.h>
|
#include <alloy/compiler/passes/finalization_pass.h>
|
||||||
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
||||||
#include <alloy/compiler/passes/simplification_pass.h>
|
#include <alloy/compiler/passes/simplification_pass.h>
|
||||||
|
#include <alloy/compiler/passes/value_reduction_pass.h>
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - mark_use/mark_set
|
// - mark_use/mark_set
|
||||||
|
|
|
@ -63,14 +63,6 @@ int FinalizationPass::Run(HIRBuilder* builder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renumber all instructions to make liveness tracking easier.
|
|
||||||
uint32_t instr_ordinal = 0;
|
|
||||||
auto instr = block->instr_head;
|
|
||||||
while (instr) {
|
|
||||||
instr->ordinal = instr_ordinal++;
|
|
||||||
instr = instr->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
block = block->next;
|
block = block->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,7 @@
|
||||||
#'dead_store_elimination_pass.h',
|
#'dead_store_elimination_pass.h',
|
||||||
'simplification_pass.cc',
|
'simplification_pass.cc',
|
||||||
'simplification_pass.h',
|
'simplification_pass.h',
|
||||||
|
'value_reduction_pass.cc',
|
||||||
|
'value_reduction_pass.h',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <alloy/compiler/passes/value_reduction_pass.h>
|
||||||
|
|
||||||
|
#include <alloy/backend/backend.h>
|
||||||
|
#include <alloy/compiler/compiler.h>
|
||||||
|
#include <alloy/runtime/runtime.h>
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
using namespace alloy;
|
||||||
|
using namespace alloy::backend;
|
||||||
|
using namespace alloy::compiler;
|
||||||
|
using namespace alloy::compiler::passes;
|
||||||
|
using namespace alloy::frontend;
|
||||||
|
using namespace alloy::hir;
|
||||||
|
using namespace alloy::runtime;
|
||||||
|
|
||||||
|
|
||||||
|
ValueReductionPass::ValueReductionPass() :
|
||||||
|
CompilerPass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueReductionPass::~ValueReductionPass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueReductionPass::ComputeLastUse(Value* value) {
|
||||||
|
uint32_t max_ordinal = 0;
|
||||||
|
Value::Use* last_use = NULL;
|
||||||
|
auto use = value->use_head;
|
||||||
|
while (use) {
|
||||||
|
if (!last_use || use->instr->ordinal >= max_ordinal) {
|
||||||
|
last_use = use;
|
||||||
|
max_ordinal = use->instr->ordinal;
|
||||||
|
}
|
||||||
|
use = use->next;
|
||||||
|
}
|
||||||
|
value->tag = last_use->instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ValueReductionPass::Run(HIRBuilder* builder) {
|
||||||
|
// Walk each block and reuse variable ordinals as much as possible.
|
||||||
|
|
||||||
|
// Let's hope this is enough.
|
||||||
|
std::bitset<1024> ordinals;
|
||||||
|
|
||||||
|
auto block = builder->first_block();
|
||||||
|
while (block) {
|
||||||
|
// Reset used ordinals.
|
||||||
|
ordinals.reset();
|
||||||
|
|
||||||
|
// Renumber all instructions to make liveness tracking easier.
|
||||||
|
uint32_t instr_ordinal = 0;
|
||||||
|
auto instr = block->instr_head;
|
||||||
|
while (instr) {
|
||||||
|
instr->ordinal = instr_ordinal++;
|
||||||
|
instr = instr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
instr = block->instr_head;
|
||||||
|
while (instr) {
|
||||||
|
const OpcodeInfo* info = instr->opcode;
|
||||||
|
OpcodeSignatureType dest_type = GET_OPCODE_SIG_TYPE_DEST(info->signature);
|
||||||
|
OpcodeSignatureType src1_type = GET_OPCODE_SIG_TYPE_SRC1(info->signature);
|
||||||
|
OpcodeSignatureType src2_type = GET_OPCODE_SIG_TYPE_SRC2(info->signature);
|
||||||
|
OpcodeSignatureType src3_type = GET_OPCODE_SIG_TYPE_SRC3(info->signature);
|
||||||
|
if (src1_type == OPCODE_SIG_TYPE_V && !instr->src1.value->IsConstant()) {
|
||||||
|
auto v = instr->src1.value;
|
||||||
|
if (!v->tag) {
|
||||||
|
ComputeLastUse(v);
|
||||||
|
}
|
||||||
|
if (v->tag == instr) {
|
||||||
|
// Available.
|
||||||
|
ordinals.set(v->ordinal, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (src2_type == OPCODE_SIG_TYPE_V && !instr->src2.value->IsConstant()) {
|
||||||
|
auto v = instr->src2.value;
|
||||||
|
if (!v->tag) {
|
||||||
|
ComputeLastUse(v);
|
||||||
|
}
|
||||||
|
if (v->tag == instr) {
|
||||||
|
// Available.
|
||||||
|
ordinals.set(v->ordinal, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (src3_type == OPCODE_SIG_TYPE_V && !instr->src3.value->IsConstant()) {
|
||||||
|
auto v = instr->src3.value;
|
||||||
|
if (!v->tag) {
|
||||||
|
ComputeLastUse(v);
|
||||||
|
}
|
||||||
|
if (v->tag == instr) {
|
||||||
|
// Available.
|
||||||
|
ordinals.set(v->ordinal, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dest_type == OPCODE_SIG_TYPE_V) {
|
||||||
|
// Dest values are processed last, as they may be able to reuse a
|
||||||
|
// source value ordinal.
|
||||||
|
auto v = instr->dest;
|
||||||
|
// Find a lower ordinal.
|
||||||
|
for (auto n = 0; n < ordinals.size(); n++) {
|
||||||
|
if (!ordinals.test(n)) {
|
||||||
|
ordinals.set(n);
|
||||||
|
v->ordinal = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instr = instr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
block = block->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ALLOY_COMPILER_PASSES_VALUE_REDUCTION_PASS_H_
|
||||||
|
#define ALLOY_COMPILER_PASSES_VALUE_REDUCTION_PASS_H_
|
||||||
|
|
||||||
|
#include <alloy/compiler/compiler_pass.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace alloy {
|
||||||
|
namespace compiler {
|
||||||
|
namespace passes {
|
||||||
|
|
||||||
|
|
||||||
|
class ValueReductionPass : public CompilerPass {
|
||||||
|
public:
|
||||||
|
ValueReductionPass();
|
||||||
|
virtual ~ValueReductionPass();
|
||||||
|
|
||||||
|
virtual int Run(hir::HIRBuilder* builder);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ComputeLastUse(hir::Value* value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace passes
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ALLOY_COMPILER_PASSES_VALUE_REDUCTION_PASS_H_
|
|
@ -50,6 +50,9 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) :
|
||||||
//compiler_->AddPass(new passes::DeadStoreEliminationPass());
|
//compiler_->AddPass(new passes::DeadStoreEliminationPass());
|
||||||
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
compiler_->AddPass(new passes::DeadCodeEliminationPass());
|
||||||
|
|
||||||
|
// Removes all unneeded variables. Try not to add new ones after this.
|
||||||
|
compiler_->AddPass(new passes::ValueReductionPass());
|
||||||
|
|
||||||
// Must come last. The HIR is not really HIR after this.
|
// Must come last. The HIR is not really HIR after this.
|
||||||
compiler_->AddPass(new passes::FinalizationPass());
|
compiler_->AddPass(new passes::FinalizationPass());
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,8 @@ static bool IsIntType(TypeName type_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ValueFlags {
|
enum ValueFlags {
|
||||||
VALUE_IS_CONSTANT = (1 << 1),
|
VALUE_IS_CONSTANT = (1 << 1),
|
||||||
|
VALUE_IS_ALLOCATED = (1 << 2), // Used by backends. Do not set.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue