diff --git a/src/alloy/backend/ivm/ivm_intcode.cc b/src/alloy/backend/ivm/ivm_intcode.cc index 5c5b741a6..a987c60e0 100644 --- a/src/alloy/backend/ivm/ivm_intcode.cc +++ b/src/alloy/backend/ivm/ivm_intcode.cc @@ -126,12 +126,14 @@ uint32_t AllocLabel(TranslationContext& ctx, Label* label) { } uint32_t AllocDynamicRegister(TranslationContext& ctx, Value* value) { - int32_t reg = (int32_t)value->tag - 1; - if (reg == -1) { - reg = ctx.register_count++; - value->tag = (void*)(reg + 1); + if (value->flags & VALUE_IS_ALLOCATED) { + return (uint32_t)value->tag; + } else { + 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( diff --git a/src/alloy/backend/x64/x64_emitter.cc b/src/alloy/backend/x64/x64_emitter.cc index c527e62a5..5b7e94147 100644 --- a/src/alloy/backend/x64/x64_emitter.cc +++ b/src/alloy/backend/x64/x64_emitter.cc @@ -202,8 +202,23 @@ void X64Emitter::FindFreeRegs( Value* v0, uint32_t& v0_idx, uint32_t v0_flags, Value* v1, uint32_t& v1_idx, uint32_t v1_flags) { // TODO(benvanik): support REG_DEST reuse/etc. - FindFreeRegs(v0, v0_idx, v0_flags); - FindFreeRegs(v1, v1_idx, v1_flags); + // Grab all already-present registers first. + // 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( @@ -211,9 +226,30 @@ void X64Emitter::FindFreeRegs( Value* v1, uint32_t& v1_idx, uint32_t v1_flags, Value* v2, uint32_t& v2_idx, uint32_t v2_flags) { // TODO(benvanik): support REG_DEST reuse/etc. - FindFreeRegs(v0, v0_idx, v0_flags); - FindFreeRegs(v1, v1_idx, v1_flags); - FindFreeRegs(v2, v2_idx, v2_flags); + // Grab all already-present registers first. + // This way we won't spill them trying to get new registers. + 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( @@ -222,10 +258,37 @@ void X64Emitter::FindFreeRegs( Value* v2, uint32_t& v2_idx, uint32_t v2_flags, Value* v3, uint32_t& v3_idx, uint32_t v3_flags) { // TODO(benvanik): support REG_DEST reuse/etc. - FindFreeRegs(v0, v0_idx, v0_flags); - FindFreeRegs(v1, v1_idx, v1_flags); - FindFreeRegs(v2, v2_idx, v2_flags); - FindFreeRegs(v3, v3_idx, v3_flags); + // Grab all already-present registers first. + // This way we won't spill them trying to get new registers. + bool need_v0 = v0->reg != -1; + 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) { diff --git a/src/alloy/compiler/compiler_passes.h b/src/alloy/compiler/compiler_passes.h index d22758980..200159ac2 100644 --- a/src/alloy/compiler/compiler_passes.h +++ b/src/alloy/compiler/compiler_passes.h @@ -16,6 +16,7 @@ #include //#include #include +#include // TODO: // - mark_use/mark_set diff --git a/src/alloy/compiler/passes/finalization_pass.cc b/src/alloy/compiler/passes/finalization_pass.cc index 4784eee9e..3fa3fc1b6 100644 --- a/src/alloy/compiler/passes/finalization_pass.cc +++ b/src/alloy/compiler/passes/finalization_pass.cc @@ -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; } diff --git a/src/alloy/compiler/passes/sources.gypi b/src/alloy/compiler/passes/sources.gypi index b8866f72c..251e6350a 100644 --- a/src/alloy/compiler/passes/sources.gypi +++ b/src/alloy/compiler/passes/sources.gypi @@ -13,5 +13,7 @@ #'dead_store_elimination_pass.h', 'simplification_pass.cc', 'simplification_pass.h', + 'value_reduction_pass.cc', + 'value_reduction_pass.h', ], } diff --git a/src/alloy/compiler/passes/value_reduction_pass.cc b/src/alloy/compiler/passes/value_reduction_pass.cc new file mode 100644 index 000000000..8e299a308 --- /dev/null +++ b/src/alloy/compiler/passes/value_reduction_pass.cc @@ -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 + +#include +#include +#include + +#include + +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; +} diff --git a/src/alloy/compiler/passes/value_reduction_pass.h b/src/alloy/compiler/passes/value_reduction_pass.h new file mode 100644 index 000000000..3c6bc72af --- /dev/null +++ b/src/alloy/compiler/passes/value_reduction_pass.h @@ -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 + + +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_ diff --git a/src/alloy/frontend/ppc/ppc_translator.cc b/src/alloy/frontend/ppc/ppc_translator.cc index 21ba0941f..9f82c9827 100644 --- a/src/alloy/frontend/ppc/ppc_translator.cc +++ b/src/alloy/frontend/ppc/ppc_translator.cc @@ -50,6 +50,9 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : //compiler_->AddPass(new passes::DeadStoreEliminationPass()); 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. compiler_->AddPass(new passes::FinalizationPass()); } diff --git a/src/alloy/hir/value.h b/src/alloy/hir/value.h index ba0db526a..51957da5f 100644 --- a/src/alloy/hir/value.h +++ b/src/alloy/hir/value.h @@ -38,7 +38,8 @@ static bool IsIntType(TypeName type_name) { } enum ValueFlags { - VALUE_IS_CONSTANT = (1 << 1), + VALUE_IS_CONSTANT = (1 << 1), + VALUE_IS_ALLOCATED = (1 << 2), // Used by backends. Do not set. };