diff --git a/libxenia.vcxproj b/libxenia.vcxproj
index c7a93d683..9bfcc742a 100644
--- a/libxenia.vcxproj
+++ b/libxenia.vcxproj
@@ -56,6 +56,7 @@
+
@@ -317,6 +318,7 @@
+
diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters
index 851054935..1c4a2eac9 100644
--- a/libxenia.vcxproj.filters
+++ b/libxenia.vcxproj.filters
@@ -772,6 +772,9 @@
src\xenia\gpu
+
+ src\xenia\cpu\compiler\passes
+
@@ -1491,6 +1494,9 @@
third_party\capstone
+
+ src\xenia\cpu\compiler\passes
+
diff --git a/src/xenia/cpu/compiler/compiler_passes.h b/src/xenia/cpu/compiler/compiler_passes.h
index dfd714bf7..90196c7a4 100644
--- a/src/xenia/cpu/compiler/compiler_passes.h
+++ b/src/xenia/cpu/compiler/compiler_passes.h
@@ -18,6 +18,7 @@
#include "xenia/cpu/compiler/passes/dead_code_elimination_pass.h"
//#include "xenia/cpu/compiler/passes/dead_store_elimination_pass.h"
#include "xenia/cpu/compiler/passes/finalization_pass.h"
+#include "xenia/cpu/compiler/passes/memory_sequence_combination_pass.h"
#include "xenia/cpu/compiler/passes/register_allocation_pass.h"
#include "xenia/cpu/compiler/passes/simplification_pass.h"
#include "xenia/cpu/compiler/passes/validation_pass.h"
diff --git a/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.cc b/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.cc
new file mode 100644
index 000000000..11ba9561a
--- /dev/null
+++ b/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.cc
@@ -0,0 +1,146 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/cpu/compiler/passes/memory_sequence_combination_pass.h"
+
+#include "xenia/profiling.h"
+
+namespace xe {
+namespace cpu {
+namespace compiler {
+namespace passes {
+
+// TODO(benvanik): remove when enums redefined.
+using namespace xe::cpu::hir;
+
+using xe::cpu::hir::HIRBuilder;
+using xe::cpu::hir::Instr;
+using xe::cpu::hir::Value;
+
+MemorySequenceCombinationPass::MemorySequenceCombinationPass()
+ : CompilerPass() {}
+
+MemorySequenceCombinationPass::~MemorySequenceCombinationPass() = default;
+
+bool MemorySequenceCombinationPass::Run(HIRBuilder* builder) {
+ // Run over all loads and stores and see if we can collapse sequences into the
+ // fat opcodes. See the respective utility functions for examples.
+ auto block = builder->first_block();
+ while (block) {
+ auto i = block->instr_head;
+ while (i) {
+ if (i->opcode == &OPCODE_LOAD_info) {
+ CombineLoadSequence(i);
+ } else if (i->opcode == &OPCODE_STORE_info) {
+ CombineStoreSequence(i);
+ }
+ i = i->next;
+ }
+ block = block->next;
+ }
+ return true;
+}
+
+void MemorySequenceCombinationPass::CombineLoadSequence(Instr* i) {
+ // Load with swap:
+ // v1.i32 = load v0
+ // v2.i32 = byte_swap v1.i32
+ // becomes:
+ // v1.i32 = load v0, [swap]
+ //
+ // Load with swap and extend:
+ // v1.i32 = load v0
+ // v2.i32 = byte_swap v1.i32
+ // v3.i64 = zero_extend v2.i32
+ // becomes:
+ // v1.i64 = load_convert v0, [swap|i32->i64,zero]
+
+ if (!i->dest->use_head) {
+ // No uses of the load result - ignore. Will be killed by DCE.
+ return;
+ }
+
+ // Ensure all uses of the load result are BYTE_SWAP - if it's mixed we
+ // shouldn't transform as we'd have to introduce new swaps!
+ auto use = i->dest->use_head;
+ while (use) {
+ if (use->instr->opcode != &OPCODE_BYTE_SWAP_info) {
+ // Not a swap.
+ return;
+ }
+ // TODO(benvanik): allow uses by STORE (we can make that swap).
+ use = use->next;
+ }
+
+ // Merge byte swap into load.
+ // Note that we may have already been a swapped operation - this inverts that.
+ i->flags ^= LoadStoreFlags::LOAD_STORE_BYTE_SWAP;
+
+ // Replace use of byte swap value with loaded value.
+ // It's byte_swap vN -> assign vN, so not much to do.
+ use = i->dest->use_head;
+ while (use) {
+ auto next_use = use->next;
+ use->instr->opcode = &OPCODE_ASSIGN_info;
+ use->instr->flags = 0;
+ use = next_use;
+ }
+
+ // TODO(benvanik): merge in extend/truncate.
+}
+
+void MemorySequenceCombinationPass::CombineStoreSequence(Instr* i) {
+ // Store with swap:
+ // v1.i32 = ...
+ // v2.i32 = byte_swap v1.i32
+ // store v0, v2.i32
+ // becomes:
+ // store v0, v1.i32, [swap]
+ //
+ // Store with truncate and swap:
+ // v1.i64 = ...
+ // v2.i32 = truncate v1.i64
+ // v3.i32 = byte_swap v2.i32
+ // store v0, v3.i32
+ // becomes:
+ // store_convert v0, v1.i64, [swap|i64->i32,trunc]
+
+ auto src = i->src2.value;
+ if (src->IsConstant()) {
+ // Constant value write - ignore.
+ return;
+ }
+
+ // Find source and ensure it is a byte swap.
+ auto def = src->def;
+ while (def && def->opcode == &OPCODE_ASSIGN_info) {
+ // Skip asignments.
+ def = def->src1.value->def;
+ }
+ if (!def || def->opcode != &OPCODE_BYTE_SWAP_info) {
+ // Not a swap/not defined?
+ return;
+ }
+
+ // Merge byte swap into store.
+ // Note that we may have already been a swapped operation - this inverts
+ // that.
+ i->flags ^= LoadStoreFlags::LOAD_STORE_BYTE_SWAP;
+
+ // Pull the original value (from before the byte swap).
+ // The byte swap itself will go away in DCE.
+ i->set_src2(def->src1.value);
+
+ // TODO(benvanik): extend/truncate.
+}
+
+} // namespace passes
+} // namespace compiler
+} // namespace cpu
+} // namespace xe
diff --git a/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.h b/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.h
new file mode 100644
index 000000000..9d1f47495
--- /dev/null
+++ b/src/xenia/cpu/compiler/passes/memory_sequence_combination_pass.h
@@ -0,0 +1,38 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_COMPILER_PASSES_MEMORY_SEQUENCE_COMBINATION_PASS_H_
+#define XENIA_COMPILER_PASSES_MEMORY_SEQUENCE_COMBINATION_PASS_H_
+
+#include "xenia/cpu/compiler/compiler_pass.h"
+
+namespace xe {
+namespace cpu {
+namespace compiler {
+namespace passes {
+
+class MemorySequenceCombinationPass : public CompilerPass {
+ public:
+ MemorySequenceCombinationPass();
+ ~MemorySequenceCombinationPass() override;
+
+ bool Run(hir::HIRBuilder* builder) override;
+
+ private:
+ void CombineMemorySequences(hir::HIRBuilder* builder);
+ void CombineLoadSequence(hir::Instr* i);
+ void CombineStoreSequence(hir::Instr* i);
+};
+
+} // namespace passes
+} // namespace compiler
+} // namespace cpu
+} // namespace xe
+
+#endif // XENIA_COMPILER_PASSES_MEMORY_SEQUENCE_COMBINATION_PASS_H_
diff --git a/src/xenia/cpu/frontend/ppc_translator.cc b/src/xenia/cpu/frontend/ppc_translator.cc
index a606e8227..e962839ef 100644
--- a/src/xenia/cpu/frontend/ppc_translator.cc
+++ b/src/xenia/cpu/frontend/ppc_translator.cc
@@ -60,6 +60,8 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
if (validate) compiler_->AddPass(std::make_unique());
compiler_->AddPass(std::make_unique());
if (validate) compiler_->AddPass(std::make_unique());
+ compiler_->AddPass(std::make_unique());
+ if (validate) compiler_->AddPass(std::make_unique());
compiler_->AddPass(std::make_unique());
if (validate) compiler_->AddPass(std::make_unique());
// compiler_->AddPass(std::make_unique());