Getting ppc tests building again (big surprise: they are failing).
This commit is contained in:
parent
437ec45d66
commit
a337ce33ed
|
@ -10,10 +10,10 @@ os:
|
|||
# Run setup to build ninja/gyp/etc and actually build xenia.
|
||||
before_script:
|
||||
- travis_retry ./xenia-build.py setup
|
||||
#- ./xenia-build.py build --debug
|
||||
- ./third_party/ninja/ninja -C build/xenia/Debug alloy-test
|
||||
- ./xenia-build.py build --debug alloy-test
|
||||
- ./xenia-build.py build --debug alloy-ppc-test
|
||||
|
||||
# Run test suite.
|
||||
script:
|
||||
- ./build/xenia/Debug/alloy-test
|
||||
#- ./test/run-tests.sh
|
||||
- ./build/xenia/Debug/alloy-ppc-test
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC --abort_before_entry=true "$@"
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC "$@"
|
||||
|
||||
|
||||
# TODO(benvanik): add --valgrind and --leaks
|
||||
# xbb --debug && rm valgrind.txt && valgrind --log-file=valgrind.txt --dsymutil=yes build/xenia/debug/xenia-run "$@"
|
||||
# --track-origins=yes --leak-check=full
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-test
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC "$@"
|
|
@ -24,4 +24,7 @@
|
|||
'ppc_translator.cc',
|
||||
'ppc_translator.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
BINUTILS=../../build/binutils/
|
||||
PPC_AS=$(BINUTILS)/gas/as-new
|
||||
PPC_LD=$(BINUTILS)/ld/ld-new
|
||||
PPC_OBJDUMP=$(BINUTILS)/binutils/objdump
|
||||
BINUTILS=../../../third_party/binutils/bin/
|
||||
PPC_AS=$(BINUTILS)/powerpc-none-elf-as
|
||||
PPC_LD=$(BINUTILS)/powerpc-none-elf-ld
|
||||
PPC_OBJDUMP=$(BINUTILS)/powerpc-none-elf-objdump
|
||||
|
||||
SRCS=$(wildcard *.s)
|
||||
BINS=$(SRCS:.s=.bin)
|
||||
|
@ -15,12 +15,13 @@ DISASMS=$(SRCS:.s=.dis)
|
|||
-mpower7 \
|
||||
-maltivec \
|
||||
-mvsx \
|
||||
-mvmx128 \
|
||||
-R \
|
||||
-o $@ \
|
||||
$<
|
||||
|
||||
%.dis: %.o
|
||||
$(PPC_OBJDUMP) --adjust-vma=0x82010000 -Mpower7 -D -EB $< > $@
|
||||
$(PPC_OBJDUMP) --adjust-vma=0x100000 -Mpower7 -Mvmx128 -D -EB $< > $@
|
||||
|
||||
%.bin: %.o
|
||||
$(PPC_LD) \
|
||||
|
@ -29,8 +30,8 @@ DISASMS=$(SRCS:.s=.dis)
|
|||
-EB \
|
||||
-nostdlib \
|
||||
--oformat binary \
|
||||
-Ttext 0x82010000 \
|
||||
-e 0x82010000 \
|
||||
-Ttext 0x100000 \
|
||||
-e 0x100000 \
|
||||
-o $@ \
|
||||
$<
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/alloy.h>
|
||||
#include <alloy/backend/ivm/ivm_backend.h>
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include <alloy/frontend/ppc/ppc_context.h>
|
||||
#include <alloy/frontend/ppc/ppc_frontend.h>
|
||||
#include <alloy/runtime/raw_module.h>
|
||||
#include <poly/main.h>
|
||||
#include <poly/poly.h>
|
||||
|
||||
#if !XE_LIKE_WIN32
|
||||
#include <dirent.h>
|
||||
#endif // !WIN32
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_string(test_path, "src/alloy/frontend/ppc/test/",
|
||||
"Directory scanned for test files.");
|
||||
|
||||
namespace alloy {
|
||||
namespace test {
|
||||
|
||||
using alloy::frontend::ppc::PPCContext;
|
||||
using alloy::runtime::Runtime;
|
||||
|
||||
typedef std::vector<std::pair<std::string, std::string>> AnnotationList;
|
||||
|
||||
const uint32_t START_ADDRESS = 0x100000;
|
||||
|
||||
class ThreadState : public alloy::runtime::ThreadState {
|
||||
public:
|
||||
ThreadState(Runtime* runtime, uint32_t thread_id, uint64_t stack_address,
|
||||
size_t stack_size, uint64_t thread_state_address)
|
||||
: alloy::runtime::ThreadState(runtime, thread_id),
|
||||
stack_address_(stack_address),
|
||||
stack_size_(stack_size),
|
||||
thread_state_address_(thread_state_address) {
|
||||
memset(memory_->Translate(stack_address_), 0, stack_size_);
|
||||
|
||||
// Allocate with 64b alignment.
|
||||
context_ = (PPCContext*)calloc(1, sizeof(PPCContext));
|
||||
assert_true((reinterpret_cast<uint64_t>(context_) & 0xF) == 0);
|
||||
|
||||
// Stash pointers to common structures that callbacks may need.
|
||||
context_->reserve_address = memory_->reserve_address();
|
||||
context_->membase = memory_->membase();
|
||||
context_->runtime = runtime;
|
||||
context_->thread_state = this;
|
||||
|
||||
// Set initial registers.
|
||||
context_->r[1] = stack_address_ + stack_size;
|
||||
context_->r[13] = thread_state_address_;
|
||||
|
||||
// Pad out stack a bit, as some games seem to overwrite the caller by about
|
||||
// 16 to 32b.
|
||||
context_->r[1] -= 64;
|
||||
|
||||
raw_context_ = context_;
|
||||
|
||||
runtime_->debugger()->OnThreadCreated(this);
|
||||
}
|
||||
~ThreadState() override {
|
||||
runtime_->debugger()->OnThreadDestroyed(this);
|
||||
free(context_);
|
||||
}
|
||||
|
||||
PPCContext* context() const { return context_; }
|
||||
|
||||
private:
|
||||
uint64_t stack_address_;
|
||||
size_t stack_size_;
|
||||
uint64_t thread_state_address_;
|
||||
|
||||
// NOTE: must be 64b aligned for SSE ops.
|
||||
PPCContext* context_;
|
||||
};
|
||||
|
||||
bool ReadAnnotations(std::wstring& src_file_path, AnnotationList& annotations) {
|
||||
// TODO(benvanik): use PAL instead of this
|
||||
FILE* f = fopen(poly::to_string(src_file_path).c_str(), "r");
|
||||
char line_buffer[BUFSIZ];
|
||||
while (fgets(line_buffer, sizeof(line_buffer), f)) {
|
||||
if (strlen(line_buffer) > 3 && line_buffer[0] == '#' &&
|
||||
line_buffer[1] == ' ') {
|
||||
// Comment - check if formed like an annotation.
|
||||
// We don't actually verify anything here.
|
||||
char* next_space = strchr(line_buffer + 3, ' ');
|
||||
if (next_space) {
|
||||
// Looks legit.
|
||||
std::string key(line_buffer + 2, next_space);
|
||||
std::string value(next_space + 1);
|
||||
while (value.find_last_of(" \t\n") == value.size() - 1) {
|
||||
value.erase(value.end() - 1);
|
||||
}
|
||||
annotations.emplace_back(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
public:
|
||||
TestRunner() {
|
||||
memory_size = 64 * 1024 * 1024;
|
||||
memory.reset(new SimpleMemory(memory_size));
|
||||
|
||||
runtime.reset(new Runtime(memory.get()));
|
||||
auto frontend =
|
||||
std::make_unique<alloy::frontend::ppc::PPCFrontend>(runtime.get());
|
||||
runtime->Initialize(std::move(frontend), nullptr);
|
||||
}
|
||||
|
||||
~TestRunner() {
|
||||
thread_state.reset();
|
||||
runtime.reset();
|
||||
memory.reset();
|
||||
}
|
||||
|
||||
bool Setup(std::wstring& src_file_path) {
|
||||
// test.s -> test.bin
|
||||
std::wstring bin_file_path;
|
||||
size_t dot = src_file_path.find_last_of(L".s");
|
||||
bin_file_path = src_file_path;
|
||||
bin_file_path.replace(dot - 1, 2, L".bin");
|
||||
|
||||
// Read annotations so we can setup state/etc.
|
||||
if (!ReadAnnotations(src_file_path, annotations)) {
|
||||
PLOGE("Unable to read annotations for test %ls", src_file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the binary module.
|
||||
auto module = std::make_unique<alloy::runtime::RawModule>(runtime.get());
|
||||
if (module->LoadFile(START_ADDRESS, bin_file_path)) {
|
||||
PLOGE("Unable to load test binary %ls", bin_file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
runtime->AddModule(std::move(module));
|
||||
|
||||
// Simulate a thread.
|
||||
uint64_t stack_size = 64 * 1024;
|
||||
uint64_t stack_address = START_ADDRESS - stack_size;
|
||||
uint64_t thread_state_address = stack_address - 0x1000;
|
||||
thread_state.reset(new ThreadState(runtime.get(), 0x100, stack_address,
|
||||
stack_size, thread_state_address));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Run() {
|
||||
// Setup test state from annotations.
|
||||
if (!SetupTestState()) {
|
||||
PLOGE("Test setup failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute test.
|
||||
alloy::runtime::Function* fn;
|
||||
runtime->ResolveFunction(START_ADDRESS, &fn);
|
||||
if (!fn) {
|
||||
PLOGE("Entry function not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = thread_state->context();
|
||||
ctx->lr = 0xBEBEBEBE;
|
||||
fn->Call(thread_state.get(), ctx->lr);
|
||||
|
||||
// Assert test state expectations.
|
||||
bool result = CheckTestResults();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SetupTestState() {
|
||||
auto ppc_state = thread_state->context();
|
||||
|
||||
for (AnnotationList::iterator it = annotations.begin();
|
||||
it != annotations.end(); ++it) {
|
||||
if (it->first == "REGISTER_IN") {
|
||||
size_t space_pos = it->second.find(" ");
|
||||
auto reg_name = it->second.substr(0, space_pos);
|
||||
auto reg_value = it->second.substr(space_pos + 1);
|
||||
ppc_state->SetRegFromString(reg_name.c_str(), reg_value.c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckTestResults() {
|
||||
auto ppc_state = thread_state->context();
|
||||
|
||||
char actual_value[2048];
|
||||
|
||||
bool any_failed = false;
|
||||
for (AnnotationList::iterator it = annotations.begin();
|
||||
it != annotations.end(); ++it) {
|
||||
if (it->first == "REGISTER_OUT") {
|
||||
size_t space_pos = it->second.find(" ");
|
||||
auto reg_name = it->second.substr(0, space_pos);
|
||||
auto reg_value = it->second.substr(space_pos + 1);
|
||||
if (!ppc_state->CompareRegWithString(reg_name.c_str(),
|
||||
reg_value.c_str(), actual_value,
|
||||
poly::countof(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 !any_failed;
|
||||
}
|
||||
|
||||
size_t memory_size;
|
||||
std::unique_ptr<Memory> memory;
|
||||
std::unique_ptr<Runtime> runtime;
|
||||
std::unique_ptr<ThreadState> thread_state;
|
||||
AnnotationList annotations;
|
||||
};
|
||||
|
||||
bool DiscoverTests(std::wstring& test_path,
|
||||
std::vector<std::wstring>& test_files) {
|
||||
// TODO(benvanik): use PAL instead of this.
|
||||
#if XE_LIKE_WIN32
|
||||
std::wstring search_path = test_path;
|
||||
search_path.append(L"\\*.s");
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFile(search_path.c_str(), &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
PLOGE("Unable to find test path %s", test_path.c_str());
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
std::wstring file_name(ffd.cFileName);
|
||||
std::wstring file_path = test_path;
|
||||
if (*(test_path.end() - 1) != '\\') {
|
||||
file_path += '\\';
|
||||
}
|
||||
file_path += file_name;
|
||||
test_files.push_back(file_path);
|
||||
}
|
||||
} while (FindNextFile(hFind, &ffd));
|
||||
FindClose(hFind);
|
||||
#else
|
||||
DIR* d = opendir(test_path.c_str());
|
||||
if (!d) {
|
||||
PLOGE("Unable to find test path %s", test_path.c_str());
|
||||
return false;
|
||||
}
|
||||
struct dirent* dir;
|
||||
while ((dir = readdir(d))) {
|
||||
if (dir->d_type == DT_REG) {
|
||||
// Only return .s files.
|
||||
string file_name = string(dir->d_name);
|
||||
if (file_name.rfind(".s") != string::npos) {
|
||||
string file_path = test_path;
|
||||
if (*(test_path.end() - 1) != '/') {
|
||||
file_path += "/";
|
||||
}
|
||||
file_path += file_name;
|
||||
test_files.push_back(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
#endif // WIN32
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunTests(const std::wstring& test_name) {
|
||||
int result_code = 1;
|
||||
int failed_count = 0;
|
||||
int passed_count = 0;
|
||||
|
||||
auto test_path = poly::fix_path_separators(poly::to_wstring(FLAGS_test_path));
|
||||
std::vector<std::wstring> test_files;
|
||||
if (!DiscoverTests(test_path, test_files)) {
|
||||
return false;
|
||||
}
|
||||
if (!test_files.size()) {
|
||||
PLOGE("No tests discovered - invalid path?");
|
||||
return false;
|
||||
}
|
||||
PLOGI("%d tests discovered.", (int)test_files.size());
|
||||
PLOGI("");
|
||||
|
||||
for (auto& test_path : test_files) {
|
||||
if (!test_name.empty() && test_path != test_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PLOGI("Running %ls...", test_path.c_str());
|
||||
TestRunner runner;
|
||||
if (!runner.Setup(test_path)) {
|
||||
PLOGE("TEST FAILED SETUP");
|
||||
++failed_count;
|
||||
}
|
||||
if (runner.Run()) {
|
||||
PLOGI("Passed");
|
||||
++passed_count;
|
||||
} else {
|
||||
PLOGE("TEST FAILED");
|
||||
++failed_count;
|
||||
}
|
||||
}
|
||||
|
||||
PLOGI("");
|
||||
PLOGI("Total tests: %d", failed_count + passed_count);
|
||||
PLOGI("Passed: %d", passed_count);
|
||||
PLOGI("Failed: %d", failed_count);
|
||||
|
||||
return failed_count ? false : true;
|
||||
}
|
||||
|
||||
int main(std::vector<std::wstring>& args) {
|
||||
// Grab test name, if present.
|
||||
std::wstring test_name;
|
||||
if (args.size() >= 2) {
|
||||
test_name = args[1];
|
||||
}
|
||||
|
||||
return RunTests(test_name) ? 0 : 1;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace alloy
|
||||
|
||||
DEFINE_ENTRY_POINT(L"alloy-ppc-test", L"alloy-ppc-test [test name]",
|
||||
alloy::test::main);
|
0
test/codegen/instr_add.bin → src/alloy/frontend/ppc/test/instr_add.bin
Executable file → Normal file
0
test/codegen/instr_add.bin → src/alloy/frontend/ppc/test/instr_add.bin
Executable file → Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
instr_add.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 7d 65 ca 14 add r11,r5,r25
|
||||
100004: 4e 80 00 20 blr
|
0
test/codegen/instr_extrwi.bin → src/alloy/frontend/ppc/test/instr_extrwi.bin
Executable file → Normal file
0
test/codegen/instr_extrwi.bin → src/alloy/frontend/ppc/test/instr_extrwi.bin
Executable file → Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
instr_extrwi.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 54 a7 ef 3e rlwinm r7,r5,29,28,31
|
||||
100004: 4e 80 00 20 blr
|
0
test/codegen/instr_ori.bin → src/alloy/frontend/ppc/test/instr_ori.bin
Executable file → Normal file
0
test/codegen/instr_ori.bin → src/alloy/frontend/ppc/test/instr_ori.bin
Executable file → Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
instr_ori.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 60 83 fe dc ori r3,r4,65244
|
||||
100004: 4e 80 00 20 blr
|
0
test/codegen/instr_rlwimi.bin → src/alloy/frontend/ppc/test/instr_rlwimi.bin
Executable file → Normal file
0
test/codegen/instr_rlwimi.bin → src/alloy/frontend/ppc/test/instr_rlwimi.bin
Executable file → Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
instr_rlwimi.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 50 86 10 3a rlwimi r6,r4,2,0,29
|
||||
100004: 4e 80 00 20 blr
|
0
test/codegen/instr_subfe.bin → src/alloy/frontend/ppc/test/instr_subfe.bin
Executable file → Normal file
0
test/codegen/instr_subfe.bin → src/alloy/frontend/ppc/test/instr_subfe.bin
Executable file → Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
instr_subfe.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 7c 6a 59 10 subfe r3,r10,r11
|
||||
100004: 4e 80 00 20 blr
|
Binary file not shown.
|
@ -0,0 +1,10 @@
|
|||
|
||||
instr_x.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <.text>:
|
||||
100000: 10 01 12 46 vcmpgtuh v0,v1,v2
|
||||
100004: 10 03 20 83 lvewx128 v0,r3,r4
|
||||
100008: 4e 80 00 20 blr
|
|
@ -0,0 +1,4 @@
|
|||
vcmpgtuh v0, v1, v2
|
||||
lvewx128 v0, r3, r4
|
||||
|
||||
blr
|
|
@ -1,8 +1,8 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
# Copyright 2014 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'xenia-test',
|
||||
'target_name': 'alloy-ppc-test',
|
||||
'type': 'executable',
|
||||
|
||||
'msvs_settings': {
|
||||
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
|
||||
'dependencies': [
|
||||
'alloy',
|
||||
'xenia',
|
||||
],
|
||||
|
||||
|
@ -20,7 +21,7 @@
|
|||
],
|
||||
|
||||
'sources': [
|
||||
'xenia-test.cc',
|
||||
'alloy-ppc-test.cc',
|
||||
],
|
||||
},
|
||||
],
|
|
@ -1,5 +1,31 @@
|
|||
# Copyright 2014 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'alloy-sandbox',
|
||||
'type': 'executable',
|
||||
|
||||
'msvs_settings': {
|
||||
'VCLinkerTool': {
|
||||
'SubSystem': '1'
|
||||
},
|
||||
},
|
||||
|
||||
'dependencies': [
|
||||
'alloy',
|
||||
'xenia',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
'.',
|
||||
],
|
||||
|
||||
'sources': [
|
||||
'alloy-sandbox.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'alloy-test',
|
|
@ -137,7 +137,7 @@ class TestFunction {
|
|||
uint64_t stack_address = memory_size - stack_size;
|
||||
uint64_t thread_state_address = stack_address - 0x1000;
|
||||
auto thread_state = std::make_unique<ThreadState>(
|
||||
runtime.get(), 100, stack_address, stack_size, thread_state_address);
|
||||
runtime.get(), 0x100, stack_address, stack_size, thread_state_address);
|
||||
auto ctx = thread_state->context();
|
||||
ctx->lr = 0xBEBEBEBE;
|
||||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#include <xenia/emulator.h>
|
||||
#include <xenia/export_resolver.h>
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/xenon_runtime.h>
|
||||
|
@ -45,10 +44,10 @@ void InitializeIfNeeded() {
|
|||
void CleanupOnShutdown() {}
|
||||
}
|
||||
|
||||
Processor::Processor(Emulator* emulator)
|
||||
: export_resolver_(emulator->export_resolver()),
|
||||
Processor::Processor(xe::Memory* memory, ExportResolver* export_resolver)
|
||||
: export_resolver_(export_resolver),
|
||||
runtime_(0),
|
||||
memory_(emulator->memory()),
|
||||
memory_(memory),
|
||||
interrupt_thread_state_(NULL),
|
||||
interrupt_thread_block_(0) {
|
||||
InitializeIfNeeded();
|
||||
|
|
|
@ -14,12 +14,11 @@
|
|||
#include <vector>
|
||||
|
||||
#include <xenia/core.h>
|
||||
#include <xenia/emulator.h>
|
||||
#include <xenia/export_resolver.h>
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class XenonMemory;
|
||||
class XenonRuntime;
|
||||
class XenonThreadState;
|
||||
class XexModule;
|
||||
|
@ -33,7 +32,7 @@ enum class Irql : uint32_t {
|
|||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(Emulator* emulator);
|
||||
Processor(Memory* memory, ExportResolver* export_resolver);
|
||||
~Processor();
|
||||
|
||||
ExportResolver* export_resolver() const { return export_resolver_; }
|
||||
|
|
|
@ -89,7 +89,8 @@ X_STATUS Emulator::Setup() {
|
|||
export_resolver_ = std::make_unique<ExportResolver>();
|
||||
|
||||
// Initialize the CPU.
|
||||
processor_ = std::make_unique<Processor>(this);
|
||||
processor_ =
|
||||
std::make_unique<Processor>(memory_.get(), export_resolver_.get());
|
||||
|
||||
// Initialize the APU.
|
||||
audio_system_ = std::move(xe::apu::Create(this));
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
add.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000082010000 <.text>:
|
||||
82010000: 7d 65 ca 14 add r11,r5,r25
|
||||
82010004: 4e 80 00 20 blr
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
extrwi.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000082010000 <.text>:
|
||||
82010000: 54 a7 ef 3e rlwinm r7,r5,29,28,31
|
||||
82010004: 4e 80 00 20 blr
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
ori.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000082010000 <.text>:
|
||||
82010000: 60 83 fe dc ori r3,r4,65244
|
||||
82010004: 4e 80 00 20 blr
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
rlwimi.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000082010000 <.text>:
|
||||
82010000: 50 86 10 3a rlwimi r6,r4,2,0,29
|
||||
82010004: 4e 80 00 20 blr
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
subfe.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000082010000 <.text>:
|
||||
82010000: 7c 6a 59 10 subfe r3,r10,r11
|
||||
82010004: 4e 80 00 20 blr
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# TODO(benvanik): tests :)
|
||||
exit 0
|
|
@ -1,28 +0,0 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'alloy-sandbox',
|
||||
'type': 'executable',
|
||||
|
||||
'msvs_settings': {
|
||||
'VCLinkerTool': {
|
||||
'SubSystem': '1'
|
||||
},
|
||||
},
|
||||
|
||||
'dependencies': [
|
||||
'alloy',
|
||||
'xenia',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
'.',
|
||||
],
|
||||
|
||||
'sources': [
|
||||
'alloy-sandbox.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
|
@ -1,11 +1,8 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'includes': [
|
||||
'alloy-sandbox/alloy-sandbox.gypi',
|
||||
'alloy-test/alloy-test.gypi',
|
||||
'xenia-compare/xenia-compare.gypi',
|
||||
'xenia-debug/xenia-debug.gypi',
|
||||
'xenia-run/xenia-run.gypi',
|
||||
#'xenia-test/xenia-test.gypi',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,292 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/xenia.h>
|
||||
|
||||
#if !XE_LIKE(WIN32)
|
||||
#include <dirent.h>
|
||||
#endif // !WIN32
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <poly/main.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
#if XE_LIKE(WIN32)
|
||||
DEFINE_string(test_path, "test\\codegen\\",
|
||||
"Directory scanned for test files.");
|
||||
#else
|
||||
DEFINE_string(test_path, "test/codegen/",
|
||||
"Directory scanned for test files.");
|
||||
#endif // WIN32
|
||||
|
||||
|
||||
typedef vector<pair<string, string> > annotations_list_t;
|
||||
|
||||
|
||||
int read_annotations(string& src_file_path, annotations_list_t& annotations) {
|
||||
// TODO(benvanik): use PAL instead of this
|
||||
FILE* f = fopen(src_file_path.c_str(), "r");
|
||||
char line_buffer[BUFSIZ];
|
||||
while (fgets(line_buffer, sizeof(line_buffer), f)) {
|
||||
if (strlen(line_buffer) > 3 &&
|
||||
line_buffer[0] == '#' &&
|
||||
line_buffer[1] == ' ') {
|
||||
// Comment - check if formed like an annotation.
|
||||
// We don't actually verify anything here.
|
||||
char* next_space = strchr(line_buffer + 3, ' ');
|
||||
if (next_space) {
|
||||
// Looks legit.
|
||||
string key = string(line_buffer + 2, next_space);
|
||||
string value = string(next_space + 1);
|
||||
while (value.find_last_of(" \t\n") == value.size() - 1) {
|
||||
value.erase(value.end() - 1);
|
||||
}
|
||||
annotations.push_back(pair<string, string>(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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") {
|
||||
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") {
|
||||
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 any_failed;
|
||||
}
|
||||
|
||||
int run_test(string& src_file_path) {
|
||||
int result_code = 1;
|
||||
|
||||
// test.s -> test.bin
|
||||
string bin_file_path;
|
||||
size_t dot = src_file_path.find_last_of(".s");
|
||||
bin_file_path = src_file_path;
|
||||
bin_file_path.replace(dot - 1, 2, ".bin");
|
||||
|
||||
xe_memory_ref memory = NULL;
|
||||
shared_ptr<Backend> backend;
|
||||
shared_ptr<Processor> processor;
|
||||
shared_ptr<Runtime> runtime;
|
||||
annotations_list_t annotations;
|
||||
ThreadState* thread_state = NULL;
|
||||
|
||||
XEEXPECTZERO(read_annotations(src_file_path, annotations));
|
||||
|
||||
while (!memory) {
|
||||
xe_memory_options_t memory_options;
|
||||
xe_zero_struct(&memory_options, sizeof(memory_options));
|
||||
memory = xe_memory_create(memory_options);
|
||||
}
|
||||
|
||||
backend = shared_ptr<Backend>(new xe::cpu::x64::X64Backend());
|
||||
|
||||
processor = shared_ptr<Processor>(new Processor(memory, backend));
|
||||
XEEXPECTZERO(processor->Setup());
|
||||
|
||||
runtime = shared_ptr<Runtime>(new Runtime(processor, XT("")));
|
||||
|
||||
// Load the binary module.
|
||||
#if XE_WCHAR
|
||||
xechar_t bin_file_path_str[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrwiden(bin_file_path_str, XECOUNT(bin_file_path_str),
|
||||
bin_file_path.c_str()));
|
||||
#else
|
||||
const xechar_t* bin_file_path_str = bin_file_path.c_str();
|
||||
#endif // XE_CHAR
|
||||
XEEXPECTZERO(processor->LoadRawBinary(bin_file_path_str, 0x82010000));
|
||||
|
||||
// Simulate a thread.
|
||||
thread_state = processor->AllocThread(256 * 1024, 0, 100);
|
||||
|
||||
// Setup test state from annotations.
|
||||
XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state,
|
||||
annotations));
|
||||
|
||||
// Execute test.
|
||||
XEEXPECTZERO(processor->Execute(thread_state, 0x82010000));
|
||||
|
||||
// Assert test state expectations.
|
||||
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);
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int discover_tests(string& test_path,
|
||||
vector<string>& test_files) {
|
||||
// TODO(benvanik): use PAL instead of this.
|
||||
#if XE_LIKE(WIN32)
|
||||
string search_path = test_path;
|
||||
search_path.append("\\*.s");
|
||||
WIN32_FIND_DATAA ffd;
|
||||
HANDLE hFind = FindFirstFileA(search_path.c_str(), &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
XELOGE("Unable to find test path %s", test_path.c_str());
|
||||
return 1;
|
||||
}
|
||||
do {
|
||||
if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
string file_name = string(ffd.cFileName);
|
||||
string file_path = test_path;
|
||||
if (*(test_path.end() - 1) != '\\') {
|
||||
file_path += "\\";
|
||||
}
|
||||
file_path += file_name;
|
||||
test_files.push_back(file_path);
|
||||
}
|
||||
} while (FindNextFileA(hFind, &ffd));
|
||||
FindClose(hFind);
|
||||
#else
|
||||
DIR* d = opendir(test_path.c_str());
|
||||
if (!d) {
|
||||
XELOGE("Unable to find test path %s", test_path.c_str());
|
||||
return 1;
|
||||
}
|
||||
struct dirent* dir;
|
||||
while ((dir = readdir(d))) {
|
||||
if (dir->d_type == DT_REG) {
|
||||
// Only return .s files.
|
||||
string file_name = string(dir->d_name);
|
||||
if (file_name.rfind(".s") != string::npos) {
|
||||
string file_path = test_path;
|
||||
if (*(test_path.end() - 1) != '/') {
|
||||
file_path += "/";
|
||||
}
|
||||
file_path += file_name;
|
||||
test_files.push_back(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
#endif // WIN32
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_tests(std::string& test_name) {
|
||||
int result_code = 1;
|
||||
int failed_count = 0;
|
||||
int passed_count = 0;
|
||||
|
||||
vector<string> test_files;
|
||||
|
||||
XEEXPECTZERO(discover_tests(FLAGS_test_path, test_files));
|
||||
if (!test_files.size()) {
|
||||
printf("No tests discovered - invalid path?\n");
|
||||
XEFAIL();
|
||||
}
|
||||
printf("%d tests discovered.\n", (int)test_files.size());
|
||||
printf("\n");
|
||||
|
||||
for (vector<string>::iterator it = test_files.begin();
|
||||
it != test_files.end(); ++it) {
|
||||
if (test_name.length() && *it != test_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Running %s...\n", (*it).c_str());
|
||||
if (run_test(*it)) {
|
||||
printf("TEST FAILED\n");
|
||||
failed_count++;
|
||||
} else {
|
||||
printf("Passed\n");
|
||||
passed_count++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("Total tests: %d\n", failed_count + passed_count);
|
||||
printf("Passed: %d\n", passed_count);
|
||||
printf("Failed: %d\n", failed_count);
|
||||
|
||||
result_code = failed_count ? 1 : 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int xenia_test(int argc, xechar_t **argv) {
|
||||
int result_code = 1;
|
||||
|
||||
// Grab test name, if present.
|
||||
const xechar_t* test_name = NULL;
|
||||
if (argc >= 2) {
|
||||
test_name = argv[1];
|
||||
}
|
||||
|
||||
string test_name_str;
|
||||
if (test_name) {
|
||||
#if XE_WCHAR
|
||||
char test_name_buffer[XE_MAX_PATH];
|
||||
XEIGNORE(xestrnarrow(test_name_buffer, XECOUNT(test_name_buffer),
|
||||
test_name));
|
||||
test_name_str = test_name_buffer;
|
||||
#else
|
||||
test_name_str = test_name;
|
||||
#endif // XE_WCHAR
|
||||
}
|
||||
|
||||
result_code = run_tests(test_name_str);
|
||||
|
||||
return result_code;
|
||||
}
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-test", L"xenia-test some.xex", xenia_test);
|
|
@ -459,18 +459,30 @@ class TestCommand(Command):
|
|||
print('Testing...')
|
||||
print('')
|
||||
|
||||
# First run make and update all of the test files.
|
||||
# TOOD(benvanik): disable on Windows
|
||||
print('Updating test files...')
|
||||
result = shell_call('make -C test/codegen/')
|
||||
# Run base alloy tests.
|
||||
print('Launching alloy-test runner...')
|
||||
result = shell_call('"build/xenia/Debug/alloy-test"')
|
||||
print('')
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
# First run make and update all of the test files.
|
||||
if sys.platform == 'win32':
|
||||
# TODO(benvanik): use cygwin/vagrant/whatever
|
||||
print('WARNING: test files not updated!');
|
||||
else:
|
||||
print('Updating test files...')
|
||||
result = shell_call('make -C src/alloy/frontend/ppc/')
|
||||
print('')
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
# Start the test runner.
|
||||
print('Launching test runner...')
|
||||
result = shell_call('bin/xenia-test')
|
||||
print('Launching alloy-ppc-test runner...')
|
||||
result = shell_call('"build/xenia/Debug/alloy-ppc-test"')
|
||||
print('')
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
return result
|
||||
|
||||
|
|
Loading…
Reference in New Issue