xenia/src/alloy/compiler/passes/value_reduction_pass.cc
Ben Vanik 19b6e90b63 Alloy now builds on clang.
Hopefully not just bouncing back to MSVC failures…
2014-07-17 20:43:30 -07:00

147 lines
4.1 KiB
C++

/**
******************************************************************************
* 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>
#if XE_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4267)
#include <llvm/ADT/BitVector.h>
#pragma warning(pop)
#else
#include <llvm/ADT/BitVector.h>
#endif // XE_COMPILER_MSVC
namespace alloy {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using alloy::hir::HIRBuilder;
using alloy::hir::OpcodeInfo;
using alloy::hir::Value;
ValueReductionPass::ValueReductionPass() : CompilerPass() {}
ValueReductionPass::~ValueReductionPass() {}
void ValueReductionPass::ComputeLastUse(Value* value) {
// TODO(benvanik): compute during construction?
// Note that this list isn't sorted (unfortunately), so we have to scan
// them all.
uint32_t max_ordinal = 0;
Value::Use* last_use = nullptr;
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->last_use = last_use ? last_use->instr : nullptr;
}
int ValueReductionPass::Run(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
// Walk each block and reuse variable ordinals as much as possible.
llvm::BitVector ordinals(builder->max_value_ordinal());
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) {
auto v = instr->src1.value;
if (!v->last_use) {
ComputeLastUse(v);
}
if (v->last_use == instr) {
// Available.
if (!instr->src1.value->IsConstant()) {
ordinals.reset(v->ordinal);
}
}
}
if (src2_type == OPCODE_SIG_TYPE_V) {
auto v = instr->src2.value;
if (!v->last_use) {
ComputeLastUse(v);
}
if (v->last_use == instr) {
// Available.
if (!instr->src2.value->IsConstant()) {
ordinals.reset(v->ordinal);
}
}
}
if (src3_type == OPCODE_SIG_TYPE_V) {
auto v = instr->src3.value;
if (!v->last_use) {
ComputeLastUse(v);
}
if (v->last_use == instr) {
// Available.
if (!instr->src3.value->IsConstant()) {
ordinals.reset(v->ordinal);
}
}
}
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 = 0u; n < ordinals.size(); n++) {
if (!ordinals.test(n)) {
ordinals.set(n);
v->ordinal = n;
break;
}
}
}
instr = instr->next;
}
block = block->next;
}
return 0;
}
} // namespace passes
} // namespace compiler
} // namespace alloy