/** ****************************************************************************** * 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/base/assert.h" #include "xenia/base/byte_order.h" #include "xenia/base/memory.h" #include "xenia/base/reset_scope.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/processor.h" #include "xenia/debug/debugger.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->processor()->backend(); scanner_.reset(new PPCScanner(frontend)); builder_.reset(new PPCHIRBuilder(frontend)); compiler_.reset(new Compiler(frontend->processor())); 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; bool PPCTranslator::Translate(FunctionInfo* symbol_info, uint32_t debug_info_flags, Function** out_function) { SCOPE_profile_cpu_f("cpu"); // Reset() all caching when we leave. xe::make_reset_scope(builder_); xe::make_reset_scope(compiler_); xe::make_reset_scope(assembler_); xe::make_reset_scope(&string_buffer_); // NOTE: we only want to do this when required, as it's expensive to build. if (FLAGS_always_disasm) { debug_info_flags |= DebugInfoFlags::kDebugInfoAllDisasm; } if (FLAGS_trace_functions) { debug_info_flags |= DebugInfoFlags::kDebugInfoTraceFunctions; } if (FLAGS_trace_function_coverage) { debug_info_flags |= DebugInfoFlags::kDebugInfoTraceFunctionCoverage; } if (FLAGS_trace_function_references) { debug_info_flags |= DebugInfoFlags::kDebugInfoTraceFunctionReferences; } if (FLAGS_trace_function_data) { debug_info_flags |= DebugInfoFlags::kDebugInfoTraceFunctionData; } std::unique_ptr debug_info; if (debug_info_flags) { debug_info.reset(new DebugInfo()); } // Scan the function to find its extents and gather debug data. if (!scanner_->Scan(symbol_info, debug_info.get())) { return false; } // Setup trace data, if needed. if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctions) { // Base trace data. size_t trace_data_size = debug::FunctionTraceData::SizeOfHeader(); if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) { // Additional space for instruction coverage counts. trace_data_size += debug::FunctionTraceData::SizeOfInstructionCounts( symbol_info->address(), symbol_info->end_address()); } uint8_t* trace_data = frontend_->processor()->debugger()->AllocateTraceFunctionData( trace_data_size); debug_info->trace_data().Reset(trace_data, trace_data_size, symbol_info->address(), symbol_info->end_address()); } // Stash source. if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) { 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 (!builder_->Emit(symbol_info, emit_flags)) { return false; } // Stash raw HIR. if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmRawHir) { builder_->Dump(&string_buffer_); debug_info->set_raw_hir_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } // Compile/optimize/etc. if (!compiler_->Compile(builder_.get())) { return false; } // Stash optimized HIR. if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmHir) { builder_->Dump(&string_buffer_); debug_info->set_hir_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } // Assemble to backend machine code. if (!assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, std::move(debug_info), out_function)) { return false; } return true; }; void PPCTranslator::DumpSource(FunctionInfo* symbol_info, 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 = xe::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