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:
Dani Messerman 2015-05-03 21:20:25 +03:00
parent cf4914e4fc
commit 1b87113184
8 changed files with 142 additions and 21 deletions

View File

@ -18,9 +18,10 @@
using namespace llvm; 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_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() void CodeGen::GenerateModule()
{ {
moduleGenerator = std::make_unique<ModuleGen>(module.get()); moduleGenerator = std::make_unique<ModuleGen>(module.get(), verify);
moduleGenerator->Run(); moduleGenerator->Run();
} }

View File

@ -15,7 +15,7 @@ class ModuleGen;
class CodeGen class CodeGen
{ {
public: 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(); ~CodeGen();
void Run(); void Run();
@ -27,6 +27,7 @@ public:
private: private:
const char *output_object_filename; const char *output_object_filename;
const char *output_debug_filename; const char *output_debug_filename;
bool verify;
std::unique_ptr<ModuleGen> moduleGenerator; std::unique_ptr<ModuleGen> moduleGenerator;

View File

@ -14,8 +14,9 @@
using namespace llvm; using namespace llvm;
ModuleGen::ModuleGen(llvm::Module* module) ModuleGen::ModuleGen(llvm::Module* module, bool verify)
: module(module) : module(module),
verify(verify)
{ {
ir_builder = make_unique<IRBuilder<>>(getGlobalContext()); ir_builder = make_unique<IRBuilder<>>(getGlobalContext());
machine = make_unique<MachineState>(this); machine = make_unique<MachineState>(this);
@ -47,25 +48,41 @@ void ModuleGen::Run()
void ModuleGen::BranchReadPC() void ModuleGen::BranchReadPC()
{ {
auto call = ir_builder->CreateCall(run_function); if (verify)
call->setTailCall(); {
ir_builder->CreateRetVoid(); ir_builder->CreateRetVoid();
}
else
{
auto call = ir_builder->CreateCall(run_function);
call->setTailCall();
ir_builder->CreateRetVoid();
}
} }
void ModuleGen::BranchWritePCConst(InstructionBlock *current, u32 pc) void ModuleGen::BranchWritePCConst(InstructionBlock *current, u32 pc)
{ {
auto i = instruction_blocks_by_pc.find(pc); if (verify)
if (i != instruction_blocks_by_pc.end())
{ {
// Found instruction, jump to it // Just write PC and exit on verify
ir_builder->CreateBr(i->second->GetEntryBasicBlock()); machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
InstructionBlock::Link(i->second, current); ir_builder->CreateRetVoid();
} }
else else
{ {
// Didn't find instruction, write PC and exit auto i = instruction_blocks_by_pc.find(pc);
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc)); if (i != instruction_blocks_by_pc.end())
ir_builder->CreateRetVoid(); {
// 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_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"); 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() void ModuleGen::GenerateBlockAddressArray()

View File

@ -18,7 +18,11 @@ namespace llvm
class ModuleGen class ModuleGen
{ {
public: 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(); ~ModuleGen();
void Run(); void Run();
@ -50,11 +54,13 @@ private:
// inter block jumps // inter block jumps
void ColorBlocks(); void ColorBlocks();
llvm::Module *module;
bool verify;
std::unique_ptr<MachineState> machine; std::unique_ptr<MachineState> machine;
std::unique_ptr<TBAA> tbaa; std::unique_ptr<TBAA> tbaa;
std::unique_ptr<llvm::IRBuilder<>> ir_builder; std::unique_ptr<llvm::IRBuilder<>> ir_builder;
llvm::Module *module;
size_t block_address_array_base; size_t block_address_array_base;
size_t block_address_array_size; size_t block_address_array_size;

View File

@ -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> 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> 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<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) 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 input_rom = InputFilename.c_str();
auto output_object = OutputFilename.c_str(); auto output_object = OutputFilename.c_str();
auto output_debug = DebugFilename.getNumOccurrences() ? DebugFilename.c_str() : nullptr; auto output_debug = DebugFilename.getNumOccurrences() ? DebugFilename.c_str() : nullptr;
bool verify = Verify;
Core::Init(); Core::Init();
Memory::Init(); Memory::Init();
@ -50,6 +52,6 @@ int main(int argc, const char *const *argv)
return -1; return -1;
} }
CodeGen code_generator(output_object, output_debug); CodeGen code_generator(output_object, output_debug, verify);
code_generator.Run(); code_generator.Run();
} }

View File

@ -3717,6 +3717,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
#define GOTO_NEXT_INST \ #define GOTO_NEXT_INST \
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
num_instrs++; \ num_instrs++; \
BinaryTranslationLoader::VerifyCallback(); \
switch(inst_base->idx) { \ switch(inst_base->idx) { \
case 0: goto VMLA_INST; \ case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \ case 1: goto VMLS_INST; \

View File

@ -10,6 +10,7 @@
using namespace llvm; using namespace llvm;
bool g_enabled = false; bool g_enabled = false;
bool g_verify = false;
std::unique_ptr<SectionMemoryManager> g_memory_manager; std::unique_ptr<SectionMemoryManager> g_memory_manager;
std::unique_ptr<RuntimeDyld> g_dyld; std::unique_ptr<RuntimeDyld> g_dyld;
@ -17,6 +18,15 @@ std::unique_ptr<RuntimeDyld::LoadedObjectInfo> g_loaded_object_info;
void (*g_run_function)(); 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) void BinaryTranslationLoader::Load(FileUtil::IOFile& file)
{ {
if (offsetof(ARMul_State, ZFlag) - offsetof(ARMul_State, NFlag) != 4 || 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_memory_manager->finalizeMemory();
g_run_function = static_cast<decltype(g_run_function)>(g_dyld->getSymbolAddress("Run")); 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; g_enabled = true;
} }
@ -74,11 +86,88 @@ void BinaryTranslationLoader::SetCpuState(ARMul_State* state)
*regs_ptr = state->Reg; *regs_ptr = state->Reg;
*flags_ptr = &state->NFlag; *flags_ptr = &state->NFlag;
g_have_saved_state = false;
g_state = state;
} }
void BinaryTranslationLoader::Run() void BinaryTranslationLoader::Run()
{ {
if (!g_enabled) return; // If verify is enabled, it will run opcodes
if (!g_enabled || g_verify) return;
g_run_function(); 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;
}
} }

View File

@ -11,4 +11,5 @@ public:
static void SetCpuState(ARMul_State *state); static void SetCpuState(ARMul_State *state);
// Runs the state provided at SetCpuState. // Runs the state provided at SetCpuState.
static void Run(); static void Run();
static void VerifyCallback();
}; };