/** ****************************************************************************** * 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 #if XE_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4244) #pragma warning(disable : 4267) #include #pragma warning(pop) #else #include #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