xenia/src/alloy/runtime/runtime.cc

263 lines
7.2 KiB
C++

/**
******************************************************************************
* 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 <alloy/runtime/runtime.h>
#include <gflags/gflags.h>
#include <alloy/runtime/module.h>
#include <alloy/runtime/tracing.h>
// TODO(benvanik): based on compiler support
#include <alloy/backend/ivm/ivm_backend.h>
#include <alloy/backend/x64/x64_backend.h>
DEFINE_string(runtime_backend, "any", "Runtime backend [any, ivm, x64].");
namespace alloy {
namespace runtime {
using alloy::backend::Backend;
using alloy::frontend::Frontend;
Runtime::Runtime(Memory* memory)
: memory_(memory), debugger_(0), frontend_(0), backend_(0) {
tracing::Initialize();
}
Runtime::~Runtime() {
{
std::lock_guard<std::mutex> guard(modules_lock_);
for (ModuleList::iterator it = modules_.begin(); it != modules_.end();
++it) {
Module* module = *it;
delete module;
}
}
delete frontend_;
delete backend_;
delete debugger_;
tracing::Flush();
}
int Runtime::Initialize(Frontend* frontend, Backend* backend) {
// Must be initialized by subclass before calling into this.
XEASSERTNOTNULL(memory_);
// Create debugger first. Other types hook up to it.
debugger_ = new Debugger(this);
if (frontend_ || backend_) {
return 1;
}
if (!backend) {
#if defined(ALLOY_HAS_X64_BACKEND) && ALLOY_HAS_X64_BACKEND
if (FLAGS_runtime_backend == "x64") {
backend = new alloy::backend::x64::X64Backend(this);
}
#endif // ALLOY_HAS_X64_BACKEND
#if defined(ALLOY_HAS_IVM_BACKEND) && ALLOY_HAS_IVM_BACKEND
if (FLAGS_runtime_backend == "ivm") {
backend = new alloy::backend::ivm::IVMBackend(this);
}
#endif // ALLOY_HAS_IVM_BACKEND
if (FLAGS_runtime_backend == "any") {
#if defined(ALLOY_HAS_X64_BACKEND) && ALLOY_HAS_X64_BACKEND
if (!backend) {
backend = new alloy::backend::x64::X64Backend(this);
}
#endif // ALLOY_HAS_X64_BACKEND
#if defined(ALLOY_HAS_IVM_BACKEND) && ALLOY_HAS_IVM_BACKEND
if (!backend) {
backend = new alloy::backend::ivm::IVMBackend(this);
}
#endif // ALLOY_HAS_IVM_BACKEND
}
}
if (!backend) {
return 1;
}
backend_ = backend;
frontend_ = frontend;
int result = backend_->Initialize();
if (result) {
return result;
}
result = frontend_->Initialize();
if (result) {
return result;
}
return 0;
}
int Runtime::AddModule(Module* module) {
std::lock_guard<std::mutex> guard(modules_lock_);
modules_.push_back(module);
return 0;
}
Module* Runtime::GetModule(const char* name) {
std::lock_guard<std::mutex> guard(modules_lock_);
Module* result = NULL;
for (ModuleList::iterator it = modules_.begin(); it != modules_.end(); ++it) {
Module* module = *it;
if (xestrcmpa(module->name(), name) == 0) {
result = module;
break;
}
}
return result;
}
Runtime::ModuleList Runtime::GetModules() {
std::lock_guard<std::mutex> guard(modules_lock_);
ModuleList clone = modules_;
return clone;
}
std::vector<Function*> Runtime::FindFunctionsWithAddress(uint64_t address) {
return entry_table_.FindWithAddress(address);
}
int Runtime::ResolveFunction(uint64_t address, Function** out_function) {
SCOPE_profile_cpu_f("alloy");
*out_function = NULL;
Entry* entry;
Entry::Status status = entry_table_.GetOrCreate(address, &entry);
if (status == Entry::STATUS_NEW) {
// Needs to be generated. We have the 'lock' on it and must do so now.
// Grab symbol declaration.
FunctionInfo* symbol_info;
int result = LookupFunctionInfo(address, &symbol_info);
if (result) {
return result;
}
result = DemandFunction(symbol_info, &entry->function);
if (result) {
entry->status = Entry::STATUS_FAILED;
return result;
}
entry->end_address = symbol_info->end_address();
status = entry->status = Entry::STATUS_READY;
}
if (status == Entry::STATUS_READY) {
// Ready to use.
*out_function = entry->function;
return 0;
} else {
// Failed or bad state.
return 1;
}
}
int Runtime::LookupFunctionInfo(uint64_t address,
FunctionInfo** out_symbol_info) {
SCOPE_profile_cpu_f("alloy");
*out_symbol_info = NULL;
// TODO(benvanik): fast reject invalid addresses/log errors.
// Find the module that contains the address.
Module* code_module = NULL;
{
std::lock_guard<std::mutex> guard(modules_lock_);
// TODO(benvanik): sort by code address (if contiguous) so can bsearch.
// TODO(benvanik): cache last module low/high, as likely to be in there.
for (ModuleList::const_iterator it = modules_.begin(); it != modules_.end();
++it) {
Module* module = *it;
if (module->ContainsAddress(address)) {
code_module = module;
break;
}
}
}
if (!code_module) {
// No module found that could contain the address.
return 1;
}
return LookupFunctionInfo(code_module, address, out_symbol_info);
}
int Runtime::LookupFunctionInfo(Module* module, uint64_t address,
FunctionInfo** out_symbol_info) {
SCOPE_profile_cpu_f("alloy");
// Atomic create/lookup symbol in module.
// If we get back the NEW flag we must declare it now.
FunctionInfo* symbol_info = NULL;
SymbolInfo::Status symbol_status =
module->DeclareFunction(address, &symbol_info);
if (symbol_status == SymbolInfo::STATUS_NEW) {
// Symbol is undeclared, so declare now.
int result = frontend_->DeclareFunction(symbol_info);
if (result) {
symbol_info->set_status(SymbolInfo::STATUS_FAILED);
return 1;
}
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
}
*out_symbol_info = symbol_info;
return 0;
}
int Runtime::DemandFunction(FunctionInfo* symbol_info,
Function** out_function) {
SCOPE_profile_cpu_f("alloy");
*out_function = NULL;
// Lock function for generation. If it's already being generated
// by another thread this will block and return DECLARED.
Module* module = symbol_info->module();
SymbolInfo::Status symbol_status = module->DefineFunction(symbol_info);
if (symbol_status == SymbolInfo::STATUS_NEW) {
// Symbol is undefined, so define now.
Function* function = NULL;
int result =
frontend_->DefineFunction(symbol_info, DEBUG_INFO_DEFAULT, &function);
if (result) {
symbol_info->set_status(SymbolInfo::STATUS_FAILED);
return result;
}
symbol_info->set_function(function);
// Before we give the symbol back to the rest, let the debugger know.
debugger_->OnFunctionDefined(symbol_info, function);
symbol_info->set_status(SymbolInfo::STATUS_DEFINED);
symbol_status = symbol_info->status();
}
if (symbol_status == SymbolInfo::STATUS_FAILED) {
// Symbol likely failed.
return 1;
}
*out_function = symbol_info->function();
return 0;
}
} // namespace runtime
} // namespace alloy