From b02ce5e95e499982ea75ee67a5b1730a2aa41319 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Wed, 6 Aug 2014 14:19:42 -0700 Subject: [PATCH] Adding control flow simplification pass to compensate for lack of phi. --- src/alloy/compiler/compiler_passes.h | 3 +- .../control_flow_simplification_pass.cc | 60 ++++++++++++++ .../passes/control_flow_simplification_pass.h | 33 ++++++++ .../passes/register_allocation_pass.cc | 3 +- src/alloy/compiler/passes/sources.gypi | 2 + src/alloy/frontend/ppc/ppc_translator.cc | 5 +- src/alloy/hir/hir_builder.cc | 82 +++++++++++++++++++ src/alloy/hir/hir_builder.h | 1 + src/alloy/hir/opcodes.inl | 4 +- src/xenia/kernel/xboxkrnl_rtl.cc | 2 - 10 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 src/alloy/compiler/passes/control_flow_simplification_pass.cc create mode 100644 src/alloy/compiler/passes/control_flow_simplification_pass.h diff --git a/src/alloy/compiler/compiler_passes.h b/src/alloy/compiler/compiler_passes.h index a01896e44..935b8397b 100644 --- a/src/alloy/compiler/compiler_passes.h +++ b/src/alloy/compiler/compiler_passes.h @@ -11,8 +11,9 @@ #define ALLOY_COMPILER_COMPILER_PASSES_H_ #include -#include #include +#include +#include #include #include //#include diff --git a/src/alloy/compiler/passes/control_flow_simplification_pass.cc b/src/alloy/compiler/passes/control_flow_simplification_pass.cc new file mode 100644 index 000000000..1891f2d7b --- /dev/null +++ b/src/alloy/compiler/passes/control_flow_simplification_pass.cc @@ -0,0 +1,60 @@ +/** + ****************************************************************************** + * 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 + +namespace alloy { +namespace compiler { +namespace passes { + +// TODO(benvanik): remove when enums redefined. +using namespace alloy::hir; + +using alloy::hir::Edge; +using alloy::hir::HIRBuilder; + +ControlFlowSimplificationPass::ControlFlowSimplificationPass() + : CompilerPass() {} + +ControlFlowSimplificationPass::~ControlFlowSimplificationPass() {} + +int ControlFlowSimplificationPass::Run(HIRBuilder* builder) { + SCOPE_profile_cpu_f("alloy"); + + // Walk backwards and merge blocks if possible. + bool merged_any = false; + auto block = builder->last_block(); + while (block) { + auto prev_block = block->prev; + const uint32_t expected = Edge::DOMINATES | Edge::UNCONDITIONAL; + if (block->incoming_edge_head && + (block->incoming_edge_head->flags & expected) == expected) { + // Dominated by the incoming block. + // If that block comes immediately before us then we can merge the + // two blocks (assuming it's not a volatile instruction like Trap). + if (block->prev == block->incoming_edge_head->src && + block->prev->instr_tail && + !(block->prev->instr_tail->opcode->flags & OPCODE_FLAG_VOLATILE)) { + builder->MergeAdjacentBlocks(block->prev, block); + merged_any = true; + } + } + block = prev_block; + } + + return 0; +} + +} // namespace passes +} // namespace compiler +} // namespace alloy diff --git a/src/alloy/compiler/passes/control_flow_simplification_pass.h b/src/alloy/compiler/passes/control_flow_simplification_pass.h new file mode 100644 index 000000000..350ed4d9d --- /dev/null +++ b/src/alloy/compiler/passes/control_flow_simplification_pass.h @@ -0,0 +1,33 @@ +/** + ****************************************************************************** + * 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_CONTROL_FLOW_SIMPLIFICATION_PASS_H_ +#define ALLOY_COMPILER_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_ + +#include + +namespace alloy { +namespace compiler { +namespace passes { + +class ControlFlowSimplificationPass : public CompilerPass { + public: + ControlFlowSimplificationPass(); + ~ControlFlowSimplificationPass() override; + + int Run(hir::HIRBuilder* builder) override; + + private: +}; + +} // namespace passes +} // namespace compiler +} // namespace alloy + +#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_ diff --git a/src/alloy/compiler/passes/register_allocation_pass.cc b/src/alloy/compiler/passes/register_allocation_pass.cc index 171610744..2175765c2 100644 --- a/src/alloy/compiler/passes/register_allocation_pass.cc +++ b/src/alloy/compiler/passes/register_allocation_pass.cc @@ -320,8 +320,7 @@ bool RegisterAllocationPass::TryAllocateRegister(Value* value) { return false; } -bool RegisterAllocationPass::SpillOneRegister(HIRBuilder* builder, - Block* block, +bool RegisterAllocationPass::SpillOneRegister(HIRBuilder* builder, Block* block, TypeName required_type) { // Get the set that we will be picking from. RegisterSetUsage* usage_set; diff --git a/src/alloy/compiler/passes/sources.gypi b/src/alloy/compiler/passes/sources.gypi index ed16920ad..12f16e695 100644 --- a/src/alloy/compiler/passes/sources.gypi +++ b/src/alloy/compiler/passes/sources.gypi @@ -7,6 +7,8 @@ 'context_promotion_pass.h', 'control_flow_analysis_pass.cc', 'control_flow_analysis_pass.h', + 'control_flow_simplification_pass.cc', + 'control_flow_simplification_pass.h', 'data_flow_analysis_pass.cc', 'data_flow_analysis_pass.h', 'dead_code_elimination_pass.cc', diff --git a/src/alloy/frontend/ppc/ppc_translator.cc b/src/alloy/frontend/ppc/ppc_translator.cc index ff6145256..e1720f523 100644 --- a/src/alloy/frontend/ppc/ppc_translator.cc +++ b/src/alloy/frontend/ppc/ppc_translator.cc @@ -43,7 +43,10 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { bool validate = FLAGS_validate_hir; - // Build the CFG first. + // Merge blocks early. This will let us use more context in other passes. + // The CFG is required for simplification and dirtied by it. + compiler_->AddPass(std::make_unique()); + compiler_->AddPass(std::make_unique()); compiler_->AddPass(std::make_unique()); // Passes are executed in the order they are added. Multiple of the same diff --git a/src/alloy/hir/hir_builder.cc b/src/alloy/hir/hir_builder.cc index 29d404af1..87d9f75f9 100644 --- a/src/alloy/hir/hir_builder.cc +++ b/src/alloy/hir/hir_builder.cc @@ -436,6 +436,88 @@ void HIRBuilder::AddEdge(Block* src, Block* dest, uint32_t flags) { dest->incoming_edge_head = edge; } +void HIRBuilder::MergeAdjacentBlocks(Block* left, Block* right) { + assert_true(left->next == right && right->prev == left); + assert_true(!right->incoming_edge_head || + right->incoming_edge_head->flags & Edge::DOMINATES); + + // If the left block ends with a branch to the right block, drop it. + if (left->instr_tail && + left->instr_tail->opcode->flags & OPCODE_FLAG_BRANCH) { + auto sig = left->instr_tail->opcode->signature; + if (GET_OPCODE_SIG_TYPE_SRC1(sig) == OPCODE_SIG_TYPE_L) { + if (left->instr_tail->src1.label->block == right) { + left->instr_tail->Remove(); + } + } + if (GET_OPCODE_SIG_TYPE_SRC2(sig) == OPCODE_SIG_TYPE_L) { + if (left->instr_tail->src2.label->block == right) { + left->instr_tail->Remove(); + } + } + } + + // Walk through the right instructions and shift each one back into the left. + while (right->instr_head) { + auto instr = right->instr_head; + auto next = instr->next; + + // Link into block list. + instr->next = nullptr; + instr->prev = left->instr_tail; + if (left->instr_tail) { + left->instr_tail->next = instr; + } else { + left->instr_head = left->instr_tail = instr; + } + left->instr_tail = instr; + + // Unlink from old block list; + right->instr_head = next; + if (right->instr_tail == instr) { + right->instr_tail = nullptr; + } + if (next) { + next->prev = nullptr; + } + + // Update state. + instr->block = left; + } + + // Move/remove labels. + // We only need to preserve named labels. + while (right->label_head) { + auto label = right->label_head; + if (label->name) { + // Label is named - move it. + label->block = left; + label->prev = left->label_tail; + if (left->label_tail) { + left->label_tail->next = label; + } + left->label_tail = label; + if (!left->label_head) { + left->label_head = label; + } + } + right->label_head = label->next; + if (right->label_tail == label) { + right->label_tail = nullptr; + } + label->next = nullptr; + } + + // Remove the right block from the block list. + left->next = right->next; + if (right->next) { + right->next->prev = left; + } + if (block_tail_ == right) { + block_tail_ = left; + } +} + Block* HIRBuilder::AppendBlock() { Block* block = arena_->Alloc(); block->arena = arena_; diff --git a/src/alloy/hir/hir_builder.h b/src/alloy/hir/hir_builder.h index c4440e6b5..a6ec2ef4d 100644 --- a/src/alloy/hir/hir_builder.h +++ b/src/alloy/hir/hir_builder.h @@ -61,6 +61,7 @@ class HIRBuilder { void ResetLabelTags(); void AddEdge(Block* src, Block* dest, uint32_t flags); + void MergeAdjacentBlocks(Block* left, Block* right); // static allocations: // Value* AllocStatic(size_t length); diff --git a/src/alloy/hir/opcodes.inl b/src/alloy/hir/opcodes.inl index a5c22c89b..77c51bb5a 100644 --- a/src/alloy/hir/opcodes.inl +++ b/src/alloy/hir/opcodes.inl @@ -108,13 +108,13 @@ DEFINE_OPCODE( OPCODE_BRANCH_TRUE, "branch_true", OPCODE_SIG_X_V_L, - OPCODE_FLAG_BRANCH) + OPCODE_FLAG_BRANCH | OPCODE_FLAG_VOLATILE) DEFINE_OPCODE( OPCODE_BRANCH_FALSE, "branch_false", OPCODE_SIG_X_V_L, - OPCODE_FLAG_BRANCH) + OPCODE_FLAG_BRANCH | OPCODE_FLAG_VOLATILE) DEFINE_OPCODE( OPCODE_ASSIGN, diff --git a/src/xenia/kernel/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl_rtl.cc index 97fe9efc5..3173229ca 100644 --- a/src/xenia/kernel/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl_rtl.cc @@ -663,8 +663,6 @@ spin: SHIM_CALL RtlEnterCriticalSection_shim( PPCContext* ppc_state, KernelState* state) { - SCOPE_profile_cpu_f("kernel"); - uint32_t cs_ptr = SHIM_GET_ARG_32(0); // XELOGD("RtlEnterCriticalSection(%.8X)", cs_ptr);