diff --git a/.gitmodules b/.gitmodules index 65213c155..c2d9714c4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "third_party/binutils"] path = third_party/binutils url = http://sourceware.org/git/binutils.git +[submodule "third_party/sparsehash"] + path = third_party/sparsehash + url = https://github.com/benvanik/sparsehash.git diff --git a/include/xenia/cpu/codegen/module_generator.h b/include/xenia/cpu/codegen/module_generator.h index 2ed8af158..beea1e266 100644 --- a/include/xenia/cpu/codegen/module_generator.h +++ b/include/xenia/cpu/codegen/module_generator.h @@ -10,6 +10,11 @@ #ifndef XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_ #define XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_ +#include +#include + +#include + #include #include #include @@ -17,11 +22,11 @@ namespace llvm { + class DIBuilder; + class Function; + class FunctionType; class LLVMContext; class Module; - class FunctionType; - class Function; - class DIBuilder; class MDNode; } @@ -42,6 +47,9 @@ public: int Generate(); + void AddFunctionsToMap( + std::tr1::unordered_map& map); + private: class CodegenFunction { public: diff --git a/include/xenia/cpu/exec_module.h b/include/xenia/cpu/exec_module.h index 955279f80..752665f1a 100644 --- a/include/xenia/cpu/exec_module.h +++ b/include/xenia/cpu/exec_module.h @@ -13,15 +13,18 @@ #include #include +#include + #include #include #include namespace llvm { + class ExecutionEngine; + class Function; class LLVMContext; class Module; - class ExecutionEngine; } namespace xe { @@ -37,6 +40,9 @@ namespace xe { namespace cpu { +typedef std::tr1::unordered_map FunctionMap; + + class ExecModule { public: ExecModule( @@ -48,6 +54,8 @@ public: int PrepareUserModule(kernel::UserModule* user_module); int PrepareRawBinary(uint32_t start_address, uint32_t end_address); + void AddFunctionsToMap(FunctionMap& map); + void Dump(); private: @@ -65,6 +73,8 @@ private: shared_ptr context_; shared_ptr gen_module_; auto_ptr codegen_; + + FunctionMap fns_; }; diff --git a/include/xenia/cpu/ppc/state.h b/include/xenia/cpu/ppc/state.h index a559fdfe9..5262e2a9b 100644 --- a/include/xenia/cpu/ppc/state.h +++ b/include/xenia/cpu/ppc/state.h @@ -143,7 +143,13 @@ typedef struct XECACHEALIGN64 xe_ppc_state { // Runtime-specific data pointer. Used on callbacks to get access to the // current runtime and its data. - void* runtime_data; + void* processor; + void* thread_state; + void* runtime; + + void SetRegFromString(const char* name, const char* value); + bool CompareRegWithString(const char* name, const char* value, + char* out_value, size_t out_value_size); } xe_ppc_state_t; diff --git a/include/xenia/cpu/processor.h b/include/xenia/cpu/processor.h index 312bc7229..d4c78757a 100644 --- a/include/xenia/cpu/processor.h +++ b/include/xenia/cpu/processor.h @@ -12,15 +12,18 @@ #include +#include #include #include +#include #include #include namespace llvm { class ExecutionEngine; + class Function; } @@ -44,10 +47,15 @@ public: int PrepareModule(kernel::UserModule* user_module, shared_ptr export_resolver); - int Execute(uint32_t address); uint32_t CreateCallback(void (*callback)(void* data), void* data); + ThreadState* AllocThread(uint32_t stack_address, uint32_t stack_size); + void DeallocThread(ThreadState* thread_state); + int Execute(ThreadState* thread_state, uint32_t address); + private: + llvm::Function* GetFunction(uint32_t address); + xe_pal_ref pal_; xe_memory_ref memory_; shared_ptr engine_; @@ -55,6 +63,8 @@ private: auto_ptr dummy_context_; std::vector modules_; + + FunctionMap all_fns_; }; diff --git a/include/xenia/cpu/sdb.h b/include/xenia/cpu/sdb.h index b302a806b..fe9e70ec2 100644 --- a/include/xenia/cpu/sdb.h +++ b/include/xenia/cpu/sdb.h @@ -77,7 +77,7 @@ public: uint32_t start_address; uint32_t end_address; - vector incoming_blocks; + std::vector incoming_blocks; TargetType outgoing_type; uint32_t outgoing_address; @@ -114,11 +114,11 @@ public: kernel::KernelExport* kernel_export; ExceptionEntrySymbol* ee; - vector incoming_calls; - vector outgoing_calls; - vector variable_accesses; + std::vector incoming_calls; + std::vector outgoing_calls; + std::vector variable_accesses; - map blocks; + std::map blocks; }; class VariableSymbol : public Symbol { @@ -154,7 +154,7 @@ public: VariableSymbol* GetVariable(uint32_t address); Symbol* GetSymbol(uint32_t address); - int GetAllFunctions(vector& functions); + int GetAllFunctions(std::vector& functions); void Write(const char* file_name); void Dump(); diff --git a/include/xenia/cpu/thread_state.h b/include/xenia/cpu/thread_state.h new file mode 100644 index 000000000..efe71befe --- /dev/null +++ b/include/xenia/cpu/thread_state.h @@ -0,0 +1,45 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_CPU_THREAD_STATE_H_ +#define XENIA_CPU_THREAD_STATE_H_ + +#include + +#include + + +namespace xe { +namespace cpu { + + +class Processor; + + +class ThreadState { +public: + ThreadState(Processor* processor, + uint32_t stack_address, uint32_t stack_size); + ~ThreadState(); + + xe_ppc_state_t* ppc_state(); + +private: + uint32_t stack_address_; + uint32_t stack_size_; + + xe_ppc_state_t ppc_state_; +}; + + +} // namespace cpu +} // namespace xe + + +#endif // XENIA_CPU_THREAD_STATE_H_ diff --git a/include/xenia/types.h b/include/xenia/types.h index b6f7b463c..e2cb4c46e 100644 --- a/include/xenia/types.h +++ b/include/xenia/types.h @@ -13,12 +13,11 @@ #include #include - #include namespace xe { // TODO(benvanik): support other compilers/etc -using namespace std; -using namespace std::tr1; +using std::auto_ptr; +using std::tr1::shared_ptr; } // namespace xe diff --git a/src/cpu/codegen/function_generator.cc b/src/cpu/codegen/function_generator.cc index 4863c3a26..498a40e7c 100644 --- a/src/cpu/codegen/function_generator.cc +++ b/src/cpu/codegen/function_generator.cc @@ -413,8 +413,8 @@ Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type, IRBuilder<>& b = *builder_; PointerType* pointerTy = PointerType::getUnqual(type); Function::arg_iterator args = gen_fn_->arg_begin(); - Value* statePtr = args; - Value* address = b.CreateConstInBoundsGEP1_64(statePtr, offset); + Value* state_ptr = args; + Value* address = b.CreateInBoundsGEP(state_ptr, b.getInt32(offset)); Value* ptr = b.CreatePointerCast(address, pointerTy); return b.CreateLoad(ptr, name); } @@ -424,8 +424,8 @@ void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type, IRBuilder<>& b = *builder_; PointerType* pointerTy = PointerType::getUnqual(type); Function::arg_iterator args = gen_fn_->arg_begin(); - Value* statePtr = args; - Value* address = b.CreateConstInBoundsGEP1_64(statePtr, offset); + Value* state_ptr = args; + Value* address = b.CreateInBoundsGEP(state_ptr, b.getInt32(offset)); Value* ptr = b.CreatePointerCast(address, pointerTy); b.CreateStore(value, ptr); } @@ -861,7 +861,8 @@ Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) { } PointerType* pointerTy = PointerType::getUnqual(dataTy); - Value* address = b.CreateInBoundsGEP(GetMembase(), addr); + Value* offset_addr = b.CreateAdd(addr, b.getInt64(size)); + Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr); Value* ptr = b.CreatePointerCast(address, pointerTy); return b.CreateLoad(ptr); } @@ -889,7 +890,8 @@ void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) { } PointerType* pointerTy = PointerType::getUnqual(dataTy); - Value* address = b.CreateInBoundsGEP(GetMembase(), addr); + Value* offset_addr = b.CreateAdd(addr, b.getInt64(size)); + Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr); Value* ptr = b.CreatePointerCast(address, pointerTy); // Truncate, if required. diff --git a/src/cpu/codegen/module_generator.cc b/src/cpu/codegen/module_generator.cc index bce0fdaf4..78bfed1e4 100644 --- a/src/cpu/codegen/module_generator.cc +++ b/src/cpu/codegen/module_generator.cc @@ -126,6 +126,14 @@ int ModuleGenerator::Generate() { return 0; } +void ModuleGenerator::AddFunctionsToMap( + std::tr1::unordered_map& map) { + for (std::map::iterator it = functions_.begin(); + it != functions_.end(); ++it) { + map.insert(std::pair(it->first, it->second->function)); + } +} + ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction( uint32_t address) { std::map::iterator it = functions_.find(address); diff --git a/src/cpu/exec_module.cc b/src/cpu/exec_module.cc index 74bacc35b..a98d9f435 100644 --- a/src/cpu/exec_module.cc +++ b/src/cpu/exec_module.cc @@ -219,6 +219,10 @@ XECLEANUP: return result_code; } +void ExecModule::AddFunctionsToMap(FunctionMap& map) { + codegen_->AddFunctionsToMap(map); +} + void XeTrap(xe_ppc_state_t* state, uint32_t cia) { printf("TRAP"); XEASSERTALWAYS(); diff --git a/src/cpu/ppc/sources.gypi b/src/cpu/ppc/sources.gypi index 4acb10332..86abd4a97 100644 --- a/src/cpu/ppc/sources.gypi +++ b/src/cpu/ppc/sources.gypi @@ -2,5 +2,6 @@ { 'sources': [ 'instr.cc', + 'state.cc', ], } diff --git a/src/cpu/ppc/state.cc b/src/cpu/ppc/state.cc new file mode 100644 index 000000000..9d4779644 --- /dev/null +++ b/src/cpu/ppc/state.cc @@ -0,0 +1,47 @@ +/** + ****************************************************************************** + * 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 +#include +#include + + +namespace { + +uint64_t ParseInt64(const char* value) { + return strtoull(value, NULL, 0); +} + +} + +void xe_ppc_state::SetRegFromString(const char* name, const char* value) { + int n; + if (sscanf(name, "r%d", &n) == 1) { + this->r[n] = ParseInt64(value); + } else { + printf("Unrecognized register name: %s\n", name); + } +} + +bool xe_ppc_state::CompareRegWithString( + const char* name, const char* value, + char* out_value, size_t out_value_size) { + int n; + if (sscanf(name, "r%d", &n) == 1) { + uint64_t expected = ParseInt64(value); + if (this->r[n] != expected) { + xesnprintfa(out_value, out_value_size, "%016llX", this->r[n]); + return false; + } + return true; + } else { + printf("Unrecognized register name: %s\n", name); + return false; + } +} diff --git a/src/cpu/processor.cc b/src/cpu/processor.cc index e7a44c554..a627a452b 100644 --- a/src/cpu/processor.cc +++ b/src/cpu/processor.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -97,6 +98,7 @@ int Processor::PrepareModule( return 1; } + exec_module->AddFunctionsToMap(all_fns_); modules_.push_back(exec_module); exec_module->Dump(); @@ -115,6 +117,7 @@ int Processor::PrepareModule(UserModule* user_module, return 1; } + exec_module->AddFunctionsToMap(all_fns_); modules_.push_back(exec_module); //user_module->Dump(export_resolver.get()); @@ -123,12 +126,58 @@ int Processor::PrepareModule(UserModule* user_module, return 0; } -int Processor::Execute(uint32_t address) { - // TODO(benvanik): implement execute. - return 0; -} - uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) { // TODO(benvanik): implement callback creation. return 0; } + +ThreadState* Processor::AllocThread(uint32_t stack_address, + uint32_t stack_size) { + ThreadState* thread_state = new ThreadState( + this, stack_address, stack_size); + return thread_state; +} + +void Processor::DeallocThread(ThreadState* thread_state) { + delete thread_state; +} + +int Processor::Execute(ThreadState* thread_state, uint32_t address) { + // Find the function to execute. + Function* f = GetFunction(address); + if (!f) { + XELOGCPU("Failed to find function %.8X to execute.\n", address); + return 1; + } + + xe_ppc_state_t* ppc_state = thread_state->ppc_state(); + + // This could be set to anything to give us a unique identifier to track + // re-entrancy/etc. + uint32_t lr = 0xBEBEBEBE; + + // Setup registers. + ppc_state->lr = 0xBEBEBEBE; + + // Args: + // - i8* state + // - i64 lr + std::vector args; + args.push_back(PTOGV(ppc_state)); + GenericValue lr_arg; + lr_arg.IntVal = APInt(64, lr); + args.push_back(lr_arg); + + GenericValue ret = engine_->runFunction(f, args); + + //return (uint32_t)ret.IntVal.getSExtValue(); + return 0; +} + +Function* Processor::GetFunction(uint32_t address) { + FunctionMap::iterator it = all_fns_.find(address); + if (it != all_fns_.end()) { + return it->second; + } + return NULL; +} diff --git a/src/cpu/sdb.cc b/src/cpu/sdb.cc index 3726a546a..4e499d527 100644 --- a/src/cpu/sdb.cc +++ b/src/cpu/sdb.cc @@ -15,6 +15,7 @@ #include +using namespace std; using namespace xe; using namespace xe::cpu; using namespace xe::cpu::ppc; diff --git a/src/cpu/sources.gypi b/src/cpu/sources.gypi index 6aa30e68d..ac90e3708 100644 --- a/src/cpu/sources.gypi +++ b/src/cpu/sources.gypi @@ -5,6 +5,7 @@ 'exec_module.cc', 'processor.cc', 'sdb.cc', + 'thread_state.cc', ], 'includes': [ diff --git a/src/cpu/thread_state.cc b/src/cpu/thread_state.cc new file mode 100644 index 000000000..6c1ac0469 --- /dev/null +++ b/src/cpu/thread_state.cc @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * 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 + + +using namespace xe; +using namespace xe::cpu; + + +ThreadState::ThreadState( + Processor* processor, + uint32_t stack_address, uint32_t stack_size) { + stack_address_ = stack_address; + stack_size_ = stack_size; + + xe_zero_struct(&ppc_state_, sizeof(ppc_state_)); + + // Stash pointers to common structures that callbacks may need. + ppc_state_.processor = processor; + ppc_state_.thread_state = this; + + // Set initial registers. + ppc_state_.r[1] = stack_address_; +} + +ThreadState::~ThreadState() { +} + +xe_ppc_state_t* ThreadState::ppc_state() { + return &ppc_state_; +} diff --git a/test/codegen/ori.bin b/test/codegen/ori.bin index dffec1c24..a95f2547a 100755 Binary files a/test/codegen/ori.bin and b/test/codegen/ori.bin differ diff --git a/test/codegen/ori.dis b/test/codegen/ori.dis index 40cb4d543..c93421a54 100644 --- a/test/codegen/ori.dis +++ b/test/codegen/ori.dis @@ -5,5 +5,5 @@ ori.o: file format elf64-powerpc Disassembly of section .text: 0000000082010000 <.text>: - 82010000: 60 83 ff ff ori r3,r4,65535 + 82010000: 60 83 fe dc ori r3,r4,65244 82010004: 4e 80 00 20 blr diff --git a/test/codegen/ori.s b/test/codegen/ori.s index 7375a1e08..130aec7bc 100644 --- a/test/codegen/ori.s +++ b/test/codegen/ori.s @@ -1,6 +1,7 @@ -# REGISTER_IN r4 0xDEADBEEFCAFEBABE +# REGISTER_IN r4 0xDEADBEEF00000000 -ori r3, r4, 0xFFFF +ori r3, r4, 0xFEDC blr -# REGISTER_OUT r3 0xBABE +# REGISTER_OUT r3 0xDEADBEEF0000FEDC +# REGISTER_OUT r4 0xDEADBEEF00000000 diff --git a/third_party/sparsehash b/third_party/sparsehash new file mode 160000 index 000000000..09af64be1 --- /dev/null +++ b/third_party/sparsehash @@ -0,0 +1 @@ +Subproject commit 09af64be1e18018f5d7a1ff337bed0f32e8067f2 diff --git a/third_party/sparsehash.gypi b/third_party/sparsehash.gypi new file mode 100644 index 000000000..b9f124813 --- /dev/null +++ b/third_party/sparsehash.gypi @@ -0,0 +1,30 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'targets': [ + { + 'target_name': 'sparsehash', + 'type': 'static_library', + + 'direct_dependent_settings': { + 'include_dirs': [ + 'sparsehash/src/', + ], + }, + + 'include_dirs': [ + 'sparsehash/src/', + ], + + 'sources': [ + ], + + 'conditions': [ + ['OS == "win"', { + 'sources!': [ + 'sparsehash/src/windows/port.cc', + ], + }], + ], + } + ] +} diff --git a/tools/xenia-test/xenia-test.cc b/tools/xenia-test/xenia-test.cc index 7753b14b1..c5ff80905 100644 --- a/tools/xenia-test/xenia-test.cc +++ b/tools/xenia-test/xenia-test.cc @@ -55,25 +55,47 @@ int read_annotations(xe_pal_ref pal, string& src_file_path, } int setup_test_state(xe_memory_ref memory, Processor* processor, + ThreadState* thread_state, annotations_list_t& annotations) { + xe_ppc_state_t* ppc_state = thread_state->ppc_state(); + for (annotations_list_t::iterator it = annotations.begin(); it != annotations.end(); ++it) { if (it->first == "REGISTER_IN") { - printf("REGISTER_IN : %s\n", it->second.c_str()); + size_t space_pos = it->second.find(" "); + string reg_name = it->second.substr(0, space_pos); + string reg_value = it->second.substr(space_pos + 1); + ppc_state->SetRegFromString(reg_name.c_str(), reg_value.c_str()); } } return 0; } int check_test_results(xe_memory_ref memory, Processor* processor, + ThreadState* thread_state, annotations_list_t& annotations) { + xe_ppc_state_t* ppc_state = thread_state->ppc_state(); + + char actual_value[2048]; + + bool any_failed = false; for (annotations_list_t::iterator it = annotations.begin(); it != annotations.end(); ++it) { if (it->first == "REGISTER_OUT") { - printf("REGISTER_OUT : %s\n", it->second.c_str()); + size_t space_pos = it->second.find(" "); + string reg_name = it->second.substr(0, space_pos); + string reg_value = it->second.substr(space_pos + 1); + if (!ppc_state->CompareRegWithString( + reg_name.c_str(), reg_value.c_str(), + actual_value, XECOUNT(actual_value))) { + any_failed = true; + printf("Register %s assert failed:\n", reg_name.c_str()); + printf(" Expected: %s == %s\n", reg_name.c_str(), reg_value.c_str()); + printf(" Actual: %s == %s\n", reg_name.c_str(), actual_value); + } } } - return 0; + return any_failed; } int run_test(xe_pal_ref pal, string& src_file_path) { @@ -89,6 +111,7 @@ int run_test(xe_pal_ref pal, string& src_file_path) { shared_ptr processor; shared_ptr runtime; annotations_list_t annotations; + ThreadState* thread_state = NULL; XEEXPECTZERO(read_annotations(pal, src_file_path, annotations)); @@ -105,17 +128,25 @@ int run_test(xe_pal_ref pal, string& src_file_path) { // Load the binary module. XEEXPECTZERO(runtime->LoadBinaryModule(bin_file_path.c_str(), 0x82010000)); + // Simulate a thread. + thread_state = processor->AllocThread(0x80000000, 256 * 1024 * 1024); + // Setup test state from annotations. - XEEXPECTZERO(setup_test_state(memory, processor.get(), annotations)); + XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state, + annotations)); // Execute test. - XEEXPECTZERO(processor->Execute(0x82010000)); + XEEXPECTZERO(processor->Execute(thread_state, 0x82010000)); // Assert test state expectations. - XEEXPECTZERO(check_test_results(memory, processor.get(), annotations)); + XEEXPECTZERO(check_test_results(memory, processor.get(), thread_state, + annotations)); result_code = 0; XECLEANUP: + if (processor && thread_state) { + processor->DeallocThread(thread_state); + } runtime.reset(); processor.reset(); xe_memory_release(memory); @@ -172,12 +203,12 @@ int run_tests(const xechar_t* test_name) { continue; } - printf("Running %s... ", (*it).c_str()); + printf("Running %s...\n", (*it).c_str()); if (run_test(pal, *it)) { - printf("FAILED\n"); + printf("TEST FAILED\n"); failed_count++; } else { - printf("PASSED\n"); + printf("Passed\n"); passed_count++; } } diff --git a/xenia.gyp b/xenia.gyp index 66ba61203..b2a36eb4b 100644 --- a/xenia.gyp +++ b/xenia.gyp @@ -4,6 +4,7 @@ 'common.gypi', 'tools/tools.gypi', 'third_party/gflags.gypi', + 'third_party/sparsehash.gypi', ], 'targets': [