/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/cpu/frontend/ppc_translator.h" #include "xenia/cpu/compiler/compiler_passes.h" #include "xenia/cpu/cpu-private.h" #include "xenia/cpu/frontend/ppc_disasm.h" #include "xenia/cpu/frontend/ppc_frontend.h" #include "xenia/cpu/frontend/ppc_hir_builder.h" #include "xenia/cpu/frontend/ppc_instr.h" #include "xenia/cpu/frontend/ppc_scanner.h" #include "xenia/cpu/runtime.h" #include "poly/reset_scope.h" #include "xenia/profiling.h" namespace xe { namespace cpu { namespace frontend { // TODO(benvanik): remove when enums redefined. using namespace xe::cpu; using xe::cpu::backend::Backend; using xe::cpu::compiler::Compiler; namespace passes = xe::cpu::compiler::passes; PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { Backend* backend = frontend->runtime()->backend(); scanner_.reset(new PPCScanner(frontend)); builder_.reset(new PPCHIRBuilder(frontend)); compiler_.reset(new Compiler(frontend->runtime())); assembler_ = std::move(backend->CreateAssembler()); assembler_->Initialize(); bool validate = FLAGS_validate_hir; // 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 // pass type may be used. 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()); 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()); //// Removes all unneeded variables. Try not to add new ones after this. // compiler_->AddPass(new passes::ValueReductionPass()); // if (validate) compiler_->AddPass(new passes::ValidationPass()); // Register allocation for the target backend. // Will modify the HIR to add loads/stores. // This should be the last pass before finalization, as after this all // registers are assigned and ready to be emitted. compiler_->AddPass(std::make_unique( backend->machine_info())); if (validate) compiler_->AddPass(std::make_unique()); // Must come last. The HIR is not really HIR after this. compiler_->AddPass(std::make_unique()); } PPCTranslator::~PPCTranslator() = default; int PPCTranslator::Translate(FunctionInfo* symbol_info, uint32_t debug_info_flags, uint32_t trace_flags, Function** out_function) { SCOPE_profile_cpu_f("cpu"); // Reset() all caching when we leave. poly::make_reset_scope(builder_); poly::make_reset_scope(compiler_); poly::make_reset_scope(assembler_); poly::make_reset_scope(&string_buffer_); // Scan the function to find its extents. We only need to do this if we // haven't already been provided with them from some other source. if (!symbol_info->has_end_address()) { // TODO(benvanik): find a way to remove the need for the scan. A fixup // scheme acting on branches could go back and modify calls to branches // if they are within the extents. int result = scanner_->FindExtents(symbol_info); if (result) { return result; } } // NOTE: we only want to do this when required, as it's expensive to build. if (FLAGS_always_disasm) { debug_info_flags |= DEBUG_INFO_ALL_DISASM; } std::unique_ptr debug_info; if (debug_info_flags) { debug_info.reset(new DebugInfo()); } // Stash source. if (debug_info_flags & DEBUG_INFO_SOURCE_DISASM) { DumpSource(symbol_info, &string_buffer_); debug_info->set_source_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } if (false) { xe::cpu::frontend::DumpAllInstrCounts(); } // Emit function. uint32_t emit_flags = 0; if (debug_info) { emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS; } if (trace_flags & TRACE_SOURCE_VALUES) { emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE_VALUES; } else if (trace_flags & TRACE_SOURCE) { emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE; } int result = builder_->Emit(symbol_info, emit_flags); if (result) { return result; } // Stash raw HIR. if (debug_info_flags & DEBUG_INFO_RAW_HIR_DISASM) { builder_->Dump(&string_buffer_); debug_info->set_raw_hir_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } // Compile/optimize/etc. result = compiler_->Compile(builder_.get()); if (result) { return result; } // Stash optimized HIR. if (debug_info_flags & DEBUG_INFO_HIR_DISASM) { builder_->Dump(&string_buffer_); debug_info->set_hir_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } // Assemble to backend machine code. result = assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, std::move(debug_info), trace_flags, out_function); if (result) { return result; } return 0; }; void PPCTranslator::DumpSource(FunctionInfo* symbol_info, poly::StringBuffer* string_buffer) { Memory* memory = frontend_->memory(); string_buffer->Append("%s fn %.8X-%.8X %s\n", symbol_info->module()->name().c_str(), symbol_info->address(), symbol_info->end_address(), symbol_info->name().c_str()); auto blocks = scanner_->FindBlocks(symbol_info); uint32_t start_address = symbol_info->address(); uint32_t end_address = symbol_info->end_address(); InstrData i; auto block_it = blocks.begin(); for (uint32_t address = start_address, offset = 0; address <= end_address; address += 4, offset++) { i.address = address; i.code = poly::load_and_swap(memory->TranslateVirtual(address)); // TODO(benvanik): find a way to avoid using the opcode tables. i.type = GetInstrType(i.code); // Check labels. if (block_it != blocks.end() && block_it->start_address == address) { string_buffer->Append("%.8X loc_%.8X:\n", address, address); ++block_it; } string_buffer->Append("%.8X %.8X ", address, i.code); DisasmPPC(i, string_buffer); string_buffer->Append("\n"); } } } // namespace frontend } // namespace cpu } // namespace xe