mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 09:21:05 +00:00
Added an option to verify the output. This is done by stopping after each instruction and checking what the interpreter produced vs. the binary translation.
This commit is contained in:
parent
cf4914e4fc
commit
1b87113184
@ -18,9 +18,10 @@
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
CodeGen::CodeGen(const char* output_object_filename, const char* output_debug_filename)
|
||||
CodeGen::CodeGen(const char* output_object_filename, const char* output_debug_filename, bool verify)
|
||||
: output_object_filename(output_object_filename),
|
||||
output_debug_filename(output_debug_filename)
|
||||
output_debug_filename(output_debug_filename),
|
||||
verify(verify)
|
||||
{
|
||||
}
|
||||
|
||||
@ -68,7 +69,7 @@ void CodeGen::IntializeLLVM()
|
||||
|
||||
void CodeGen::GenerateModule()
|
||||
{
|
||||
moduleGenerator = std::make_unique<ModuleGen>(module.get());
|
||||
moduleGenerator = std::make_unique<ModuleGen>(module.get(), verify);
|
||||
moduleGenerator->Run();
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ class ModuleGen;
|
||||
class CodeGen
|
||||
{
|
||||
public:
|
||||
CodeGen(const char *output_object_filename, const char *output_debug_filename);
|
||||
CodeGen(const char *output_object_filename, const char *output_debug_filename, bool verify);
|
||||
~CodeGen();
|
||||
|
||||
void Run();
|
||||
@ -27,6 +27,7 @@ public:
|
||||
private:
|
||||
const char *output_object_filename;
|
||||
const char *output_debug_filename;
|
||||
bool verify;
|
||||
|
||||
std::unique_ptr<ModuleGen> moduleGenerator;
|
||||
|
||||
|
@ -14,8 +14,9 @@
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
ModuleGen::ModuleGen(llvm::Module* module)
|
||||
: module(module)
|
||||
ModuleGen::ModuleGen(llvm::Module* module, bool verify)
|
||||
: module(module),
|
||||
verify(verify)
|
||||
{
|
||||
ir_builder = make_unique<IRBuilder<>>(getGlobalContext());
|
||||
machine = make_unique<MachineState>(this);
|
||||
@ -47,25 +48,41 @@ void ModuleGen::Run()
|
||||
|
||||
void ModuleGen::BranchReadPC()
|
||||
{
|
||||
auto call = ir_builder->CreateCall(run_function);
|
||||
call->setTailCall();
|
||||
ir_builder->CreateRetVoid();
|
||||
if (verify)
|
||||
{
|
||||
ir_builder->CreateRetVoid();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto call = ir_builder->CreateCall(run_function);
|
||||
call->setTailCall();
|
||||
ir_builder->CreateRetVoid();
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleGen::BranchWritePCConst(InstructionBlock *current, u32 pc)
|
||||
{
|
||||
auto i = instruction_blocks_by_pc.find(pc);
|
||||
if (i != instruction_blocks_by_pc.end())
|
||||
if (verify)
|
||||
{
|
||||
// Found instruction, jump to it
|
||||
ir_builder->CreateBr(i->second->GetEntryBasicBlock());
|
||||
InstructionBlock::Link(i->second, current);
|
||||
// Just write PC and exit on verify
|
||||
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
|
||||
ir_builder->CreateRetVoid();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Didn't find instruction, write PC and exit
|
||||
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
|
||||
ir_builder->CreateRetVoid();
|
||||
auto i = instruction_blocks_by_pc.find(pc);
|
||||
if (i != instruction_blocks_by_pc.end())
|
||||
{
|
||||
// Found instruction, jump to it
|
||||
ir_builder->CreateBr(i->second->GetEntryBasicBlock());
|
||||
InstructionBlock::Link(i->second, current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Didn't find instruction, write PC and exit
|
||||
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
|
||||
ir_builder->CreateRetVoid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +108,9 @@ void ModuleGen::GenerateGlobals()
|
||||
|
||||
block_address_array_type = ArrayType::get(block_address_type, block_address_array_size);
|
||||
block_address_array = new GlobalVariable(*module, block_address_array_type, true, GlobalValue::ExternalLinkage, nullptr, "BlockAddressArray");
|
||||
|
||||
// bool Verify - contains the value of verify for citra usage
|
||||
new GlobalVariable(*module, ir_builder->getInt1Ty(), true, GlobalValue::ExternalLinkage, ir_builder->getInt1(verify), "Verify");
|
||||
}
|
||||
|
||||
void ModuleGen::GenerateBlockAddressArray()
|
||||
|
@ -18,7 +18,11 @@ namespace llvm
|
||||
class ModuleGen
|
||||
{
|
||||
public:
|
||||
explicit ModuleGen(llvm::Module *module);
|
||||
/*
|
||||
* Verify - produce a code that can be verified
|
||||
* this is done by returning after every opcode
|
||||
*/
|
||||
explicit ModuleGen(llvm::Module *module, bool verify);
|
||||
~ModuleGen();
|
||||
|
||||
void Run();
|
||||
@ -50,11 +54,13 @@ private:
|
||||
// inter block jumps
|
||||
void ColorBlocks();
|
||||
|
||||
llvm::Module *module;
|
||||
bool verify;
|
||||
|
||||
std::unique_ptr<MachineState> machine;
|
||||
std::unique_ptr<TBAA> tbaa;
|
||||
|
||||
std::unique_ptr<llvm::IRBuilder<>> ir_builder;
|
||||
llvm::Module *module;
|
||||
|
||||
size_t block_address_array_base;
|
||||
size_t block_address_array_size;
|
||||
|
@ -12,6 +12,7 @@ namespace cl = llvm::cl;
|
||||
cl::opt<std::string> InputFilename(cl::Positional, cl::Required, cl::desc("<input rom filename>"));
|
||||
cl::opt<std::string> OutputFilename(cl::Positional, cl::Required, cl::desc("<output object filename>"));
|
||||
cl::opt<std::string> DebugFilename(cl::Positional, cl::Optional, cl::desc("<debug filename>"));
|
||||
cl::opt<bool> Verify("verify", cl::desc("<verify>"), cl::init(false));
|
||||
|
||||
int main(int argc, const char *const *argv)
|
||||
{
|
||||
@ -39,6 +40,7 @@ int main(int argc, const char *const *argv)
|
||||
auto input_rom = InputFilename.c_str();
|
||||
auto output_object = OutputFilename.c_str();
|
||||
auto output_debug = DebugFilename.getNumOccurrences() ? DebugFilename.c_str() : nullptr;
|
||||
bool verify = Verify;
|
||||
|
||||
Core::Init();
|
||||
Memory::Init();
|
||||
@ -50,6 +52,6 @@ int main(int argc, const char *const *argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
CodeGen code_generator(output_object, output_debug);
|
||||
CodeGen code_generator(output_object, output_debug, verify);
|
||||
code_generator.Run();
|
||||
}
|
@ -3717,6 +3717,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
#define GOTO_NEXT_INST \
|
||||
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
|
||||
num_instrs++; \
|
||||
BinaryTranslationLoader::VerifyCallback(); \
|
||||
switch(inst_base->idx) { \
|
||||
case 0: goto VMLA_INST; \
|
||||
case 1: goto VMLS_INST; \
|
||||
|
@ -10,6 +10,7 @@
|
||||
using namespace llvm;
|
||||
|
||||
bool g_enabled = false;
|
||||
bool g_verify = false;
|
||||
|
||||
std::unique_ptr<SectionMemoryManager> g_memory_manager;
|
||||
std::unique_ptr<RuntimeDyld> g_dyld;
|
||||
@ -17,6 +18,15 @@ std::unique_ptr<RuntimeDyld::LoadedObjectInfo> g_loaded_object_info;
|
||||
|
||||
void (*g_run_function)();
|
||||
|
||||
// Used by the verifier
|
||||
bool (*g_can_run_function)();
|
||||
bool g_have_saved_state; // Whether there is a copied state
|
||||
ARMul_State *g_state;
|
||||
u32 g_regs_copy[16];
|
||||
u32 g_flags_copy[4];
|
||||
u32 g_regs_copy_before[16];
|
||||
u32 g_flags_copy_before[4];
|
||||
|
||||
void BinaryTranslationLoader::Load(FileUtil::IOFile& file)
|
||||
{
|
||||
if (offsetof(ARMul_State, ZFlag) - offsetof(ARMul_State, NFlag) != 4 ||
|
||||
@ -61,6 +71,8 @@ void BinaryTranslationLoader::Load(FileUtil::IOFile& file)
|
||||
g_memory_manager->finalizeMemory();
|
||||
|
||||
g_run_function = static_cast<decltype(g_run_function)>(g_dyld->getSymbolAddress("Run"));
|
||||
g_can_run_function = static_cast<decltype(g_can_run_function)>(g_dyld->getSymbolAddress("CanRun"));
|
||||
g_verify = *static_cast<bool*>(g_dyld->getSymbolAddress("Verify"));
|
||||
|
||||
g_enabled = true;
|
||||
}
|
||||
@ -74,11 +86,88 @@ void BinaryTranslationLoader::SetCpuState(ARMul_State* state)
|
||||
|
||||
*regs_ptr = state->Reg;
|
||||
*flags_ptr = &state->NFlag;
|
||||
g_have_saved_state = false;
|
||||
g_state = state;
|
||||
}
|
||||
|
||||
void BinaryTranslationLoader::Run()
|
||||
{
|
||||
if (!g_enabled) return;
|
||||
// If verify is enabled, it will run opcodes
|
||||
if (!g_enabled || g_verify) return;
|
||||
|
||||
g_run_function();
|
||||
}
|
||||
|
||||
void Swap(void *a, void *b, size_t size)
|
||||
{
|
||||
auto a_char = (char *)a;
|
||||
auto b_char = (char *)b;
|
||||
for (auto i = 0; i < size; ++i)
|
||||
{
|
||||
std::swap(a_char[i], b_char[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowRegs(u32 *regs, u32 *flags)
|
||||
{
|
||||
LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x",
|
||||
regs[0], regs[1], regs[2], regs[3],
|
||||
regs[4], regs[5], regs[6], regs[7]);
|
||||
LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x",
|
||||
regs[8], regs[9], regs[10], regs[11],
|
||||
regs[12], regs[13], regs[14], regs[15]);
|
||||
LOG_ERROR(BinaryTranslator, "%01x %01x %01x %01x", flags[0], flags[1], flags[2], flags[3]);
|
||||
}
|
||||
|
||||
void BinaryTranslationLoader::VerifyCallback()
|
||||
{
|
||||
if (!g_enabled || !g_verify) return;
|
||||
|
||||
// Swap the PC to the old state before checking if it can run
|
||||
std::swap(g_regs_copy[15], g_state->Reg[15]);
|
||||
auto can_run = g_can_run_function();
|
||||
std::swap(g_regs_copy[15], g_state->Reg[15]);
|
||||
|
||||
if (g_have_saved_state && can_run)
|
||||
{
|
||||
// An opcode is finished, simulate it
|
||||
|
||||
// Copy the state before
|
||||
memcpy(g_regs_copy_before, g_regs_copy, sizeof(g_regs_copy));
|
||||
memcpy(g_flags_copy_before, &g_flags_copy, sizeof(g_flags_copy));
|
||||
|
||||
// Swap to the state before the opcode
|
||||
Swap(g_state->Reg, g_regs_copy, sizeof(g_regs_copy));
|
||||
Swap(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy));
|
||||
|
||||
// Run the opcode
|
||||
g_run_function();
|
||||
|
||||
// Test
|
||||
if (memcmp(g_state->Reg, g_regs_copy, sizeof(g_regs_copy)) || memcmp(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy)))
|
||||
{
|
||||
LOG_ERROR(BinaryTranslator, "Verify failed");
|
||||
LOG_ERROR(BinaryTranslator, "Regs Before");
|
||||
ShowRegs(g_regs_copy_before, g_flags_copy_before);
|
||||
LOG_ERROR(BinaryTranslator, "Regs OK");
|
||||
ShowRegs(g_regs_copy, g_flags_copy);
|
||||
LOG_ERROR(BinaryTranslator, "Regs not OK");
|
||||
ShowRegs(g_state->Reg, &g_state->NFlag);
|
||||
|
||||
// Don't spam
|
||||
g_enabled = false;
|
||||
|
||||
// Make sure it has a valid state to continue to run
|
||||
Swap(g_state->Reg, g_regs_copy, sizeof(g_regs_copy));
|
||||
Swap(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this opcode is not translated or there is no saved state, just save the state and continue
|
||||
memcpy(g_regs_copy, g_state->Reg, sizeof(g_regs_copy));
|
||||
memcpy(g_flags_copy, &g_state->NFlag, sizeof(g_flags_copy));
|
||||
|
||||
g_have_saved_state = true;
|
||||
}
|
||||
}
|
@ -11,4 +11,5 @@ public:
|
||||
static void SetCpuState(ARMul_State *state);
|
||||
// Runs the state provided at SetCpuState.
|
||||
static void Run();
|
||||
static void VerifyCallback();
|
||||
};
|
Loading…
Reference in New Issue
Block a user