Files
revng-revng/codegenerator.cpp
T
Alessandro Di Federico 6338f3b0b8 Link with helpers and adjust their CPU state usage
* Move initialization and management of the structure describing the CPU
  state (CPUStateType) into variablemanager.cpp.
* Support parts of CPU state outside "env" (e.g. the MIPSCPU
  structure). Now "env" has an offset into the possibly larger CPU state
  which we have to take into account where appropriate (see
  VariableManager::envOffset).
* Link the helpers module into the generated module, including only what
  is needed.
* Create some "no-op" or "abort" function corresponding to QEMU functions
  not included in the helper module (e.g. logging and abort functions).
* Implement the CorrectCPUStateUsagePass pass, which starts from the
  "env" global variable and looks for all its usages recursively, keeping
  track of where pointers are pointing into the CPU state data structure,
  and replaces all the load/stores with the global variable corresponding
  to that specific field of the CPU state.
* After the linking phase, run SROA, the pass to adjust the CPU usage and
  DCE.
* Let global variables have common linkage.
2015-12-04 23:44:46 +01:00

256 lines
8.4 KiB
C++

/// \file
/// \brief This file handles the whole translation process from the input
/// assembly to LLVM IR.
// Standard includes
#include <cstdint>
#include <sstream>
#include <vector>
#include <fstream>
// LLVM includes
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Scalar.h"
// Local includes
#include "codegenerator.h"
#include "debughelper.h"
#include "instructiontranslator.h"
#include "ir-helpers.h"
#include "jumptargetmanager.h"
#include "ptcinterface.h"
#include "variablemanager.h"
using namespace llvm;
template<typename T, typename... Args>
inline std::array<T, sizeof...(Args)>
make_array(Args&&... args)
{ return { std::forward<Args>(args)... }; }
// Outline the destructor for the sake of privacy in the header
CodeGenerator::~CodeGenerator() = default;
CodeGenerator::CodeGenerator(Architecture& Source,
Architecture& Target,
std::string Output,
std::string Helpers,
DebugInfoType DebugInfo,
std::string Debug) :
SourceArchitecture(Source),
TargetArchitecture(Target),
Context(getGlobalContext()),
TheModule((new Module("top", Context))),
OutputPath(Output),
Debug(new DebugHelper(Output, Debug, TheModule.get(), DebugInfo))
{
OriginalInstrMDKind = Context.getMDKindID("oi");
PTCInstrMDKind = Context.getMDKindID("pi");
DbgMDKind = Context.getMDKindID("dbg");
SMDiagnostic Errors;
HelpersModule = parseIRFile(Helpers, Errors, Context);
}
void CodeGenerator::translate(size_t LoadAddress,
ArrayRef<uint8_t> Code,
size_t VirtualAddress,
std::string Name) {
const uint8_t *CodePointer = Code.data();
const uint8_t *CodeEnd = CodePointer + Code.size();
IRBuilder<> Builder(Context);
// Create main function
auto *MainType = FunctionType::get(Builder.getVoidTy(), false);
auto *MainFunction = Function::Create(MainType,
Function::ExternalLinkage,
Name,
TheModule.get());
Debug->newFunction(MainFunction);
// Create the first basic block and create a placeholder for variable
// allocations
BasicBlock *Entry = BasicBlock::Create(Context,
"entrypoint",
MainFunction);
Builder.SetInsertPoint(Entry);
Instruction *Delimiter = Builder.CreateUnreachable();
// Instantiate helpers
VariableManager Variables(*TheModule,
*HelpersModule);
GlobalVariable *PCReg = Variables.getByEnvOffset(ptc.get_pc(), "pc");
JumpTargetManager JumpTargets(*TheModule, PCReg, MainFunction);
std::map<std::string, BasicBlock *> LabeledBasicBlocks;
std::vector<BasicBlock *> Blocks;
InstructionTranslator Translator(Builder,
Variables,
JumpTargets,
LabeledBasicBlocks,
Blocks,
*TheModule,
MainFunction,
SourceArchitecture,
TargetArchitecture);
ptc.mmap(LoadAddress, Code.data(), Code.size());
while (Entry != nullptr) {
Builder.SetInsertPoint(Entry);
LabeledBasicBlocks.clear();
// TODO: rename this type
PTCInstructionListPtr InstructionList(new PTCInstructionList);
size_t ConsumedSize = 0;
assert(CodeEnd > CodePointer);
ConsumedSize = ptc.translate(VirtualAddress,
InstructionList.get());
dumpTranslation(std::cerr, InstructionList.get());
Variables.newFunction(Delimiter, InstructionList.get());
unsigned j = 0;
MDNode* MDOriginalInstr = nullptr;
bool StopTranslation = false;
// Handle the first PTC_INSTRUCTION_op_debug_insn_start
{
PTCInstruction *Instruction = &InstructionList->instructions[j];
auto Result = Translator.newInstruction(Instruction, true);
std::tie(StopTranslation, MDOriginalInstr) = Result;
j++;
}
for (; j < InstructionList->instruction_count && !StopTranslation; j++) {
PTCInstruction Instruction = InstructionList->instructions[j];
PTCOpcode Opcode = Instruction.opc;
Blocks.clear();
Blocks.push_back(Builder.GetInsertBlock());
switch(Opcode) {
case PTC_INSTRUCTION_op_discard:
// Instructions we don't even consider
break;
case PTC_INSTRUCTION_op_debug_insn_start:
{
std::tie(StopTranslation,
MDOriginalInstr) = Translator.newInstruction(&Instruction,
false);
break;
}
case PTC_INSTRUCTION_op_call:
Translator.translateCall(&Instruction);
break;
default:
Translator.translate(&Instruction);
}
// Create a new metadata referencing the PTC instruction we have just
// translated
std::stringstream PTCStringStream;
dumpInstruction(PTCStringStream, InstructionList.get(), j);
std::string PTCString = PTCStringStream.str() + "\n";
MDString *MDPTCString = MDString::get(Context, PTCString);
MDNode* MDPTCInstr = MDNode::getDistinct(Context, MDPTCString);
// Set metadata for all the new instructions
for (BasicBlock *Block : Blocks) {
BasicBlock::iterator I = Block->end();
while (I != Block->begin() && !(--I)->hasMetadata()) {
I->setMetadata(OriginalInstrMDKind, MDOriginalInstr);
I->setMetadata(PTCInstrMDKind, MDPTCInstr);
}
}
} // End loop over instructions
Translator.closeLastInstruction(VirtualAddress + ConsumedSize);
// Before looking for writes to the PC, give a shot of SROA
legacy::PassManager PM;
PM.add(createSROAPass());
PM.add(Translator.createTranslateDirectBranchesPass());
PM.run(*TheModule);
// Obtain a new program counter to translate
uint64_t NewPC = 0;
std::tie(NewPC, Entry) = JumpTargets.peekJumpTarget();
VirtualAddress = NewPC;
CodePointer = Code.data() + (NewPC - LoadAddress);
} // End translations loop
Linker TheLinker(TheModule.get());
bool Result = TheLinker.linkInModule(HelpersModule.get(),
Linker::LinkOnlyNeeded);
assert(!Result && "Linking failed");
// Handle some specific QEMU functions as no-ops or abort
auto NoOpFunctionNames = make_array<const char *>("qemu_log_mask");
auto AbortFunctionNames = make_array<const char *>("cpu_restore_state",
"cpu_loop_exit");
for (auto Name : NoOpFunctionNames) {
Function *TheFunction = TheModule->getFunction(Name);
if (TheFunction != nullptr && TheFunction->empty()) {
assert(TheFunction->getReturnType()->isVoidTy());
ReturnInst::Create(Context,
nullptr,
BasicBlock::Create(Context, "", TheFunction));
}
}
for (auto Name : AbortFunctionNames) {
Function *TheFunction = TheModule->getFunction(Name);
if (TheFunction != nullptr && TheFunction->empty()) {
assert(TheModule->getFunction("abort") != nullptr);
auto *Body = BasicBlock::Create(Context, "", TheFunction);
CallInst::Create(TheModule->getFunction("abort"), { }, Body);
new UnreachableInst(Context, Body);
}
}
legacy::PassManager PM;
PM.add(createSROAPass());
PM.add(Variables.createCorrectCPUStateUsagePass());
PM.add(createDeadCodeEliminationPass());
PM.run(*TheModule);
// TODO: we have around all the usages of the PC, shall we drop them?
Delimiter->eraseFromParent();
JumpTargets.translateIndirectJumps();
Translator.removeNewPCMarkers();
Debug->generateDebugInfo();
}
void CodeGenerator::serialize() {
// Ask the debug handler if it already has a good copy of the IR, if not dump
// it
if (!Debug->copySource()) {
std::ofstream Output(OutputPath);
Debug->print(Output, false);
}
}