diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f1408e2d..12bdd0b9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") add_definitions("-D_FILE_OFFSET_BITS=64") include_directories(argparse/) -add_executable(revamb ptcdump.cpp main.cpp ptctollvmir.cpp argparse/argparse.c) +add_executable(revamb ptcdump.cpp main.cpp debughelper.cpp variablemanager.cpp + jumptargetmanager.cpp instructiontranslator.cpp codegenerator.cpp + argparse/argparse.c) target_link_libraries(revamb dl m ${LLVM_LIBRARIES}) include(tests/Tests.cmake) diff --git a/codegenerator.cpp b/codegenerator.cpp new file mode 100644 index 000000000..dcbfd7650 --- /dev/null +++ b/codegenerator.cpp @@ -0,0 +1,264 @@ +/// \file +/// \brief This file handles the whole translation process from the input +/// assembly to LLVM IR. + +// Standard includes +#include +#include +#include +#include + +// LLVM includes +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_os_ostream.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/IR/LegacyPassManager.h" + +// Local includes +#include "codegenerator.h" +#include "debughelper.h" +#include "jumptargetmanager.h" +#include "instructiontranslator.h" +#include "ptcinterface.h" +#include "variablemanager.h" + +using namespace llvm; + +static bool startsWith(std::string String, std::string Prefix) { + return String.substr(0, Prefix.size()) == Prefix; +} + +// 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)), + CPUStateType(nullptr), + HelpersModuleLayout(nullptr) +{ + OriginalInstrMDKind = Context.getMDKindID("oi"); + PTCInstrMDKind = Context.getMDKindID("pi"); + DbgMDKind = Context.getMDKindID("dbg"); + + SMDiagnostic Errors; + HelpersModule = parseIRFile(Helpers, Errors, Context); + + using ElectionMap = std::map; + using ElectionMapElement = std::pair; + ElectionMap EnvElection; + const std::string HelperPrefix = "helper_"; + for (Function& HelperFunction : *HelpersModule) { + if (startsWith(HelperFunction.getName(), HelperPrefix) + && HelperFunction.getFunctionType()->getNumParams() > 1) { + + for (Type *Candidate : HelperFunction.getFunctionType()->params()) { + if (Candidate->isPointerTy()) { + auto *PointeeType = Candidate->getPointerElementType(); + auto *EnvType = dyn_cast(PointeeType); + // Ensure it is a struct and not a union + if (EnvType != nullptr && EnvType->getNumElements() > 1) { + + auto It = EnvElection.find(EnvType); + if (It != EnvElection.end()) + EnvElection[EnvType]++; + else + EnvElection[EnvType] = 1; + } + } + } + } + } + + assert(EnvElection.size() > 0); + + CPUStateType = std::max_element(EnvElection.begin(), + EnvElection.end(), + [] (ElectionMapElement& It1, + ElectionMapElement& It2) { + return It1.second < It2.second; + })->first; + + HelpersModuleLayout = &HelpersModule->getDataLayout(); +} + +void CodeGenerator::translate(size_t LoadAddress, + ArrayRef 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, + CPUStateType, + HelpersModuleLayout); + + GlobalVariable *PCReg = Variables.getByCPUStateOffset(ptc.get_pc(), "pc"); + + JumpTargetManager JumpTargets(*TheModule, PCReg, MainFunction); + std::map LabeledBasicBlocks; + std::vector 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; + + // Skip everything is before the first PTC_INSTRUCTION_op_debug_insn_start + while (j < InstructionList->instruction_count && + InstructionList->instructions[j].opc != + PTC_INSTRUCTION_op_debug_insn_start) { + j++; + } + + assert(j < InstructionList->instruction_count); + + 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 + + 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); + } +} diff --git a/ptctollvmir.h b/codegenerator.h similarity index 93% rename from ptctollvmir.h rename to codegenerator.h index 5cff79237..d6add9cd5 100644 --- a/ptctollvmir.h +++ b/codegenerator.h @@ -1,13 +1,18 @@ -#ifndef _PTCTOLLVMIR_H -#define _PTCTOLLVMIR_H +#ifndef _CODEGENERATOR_H +#define _CODEGENERATOR_H +// Standard includes #include #include #include +// LLVM includes #include "llvm/ADT/ArrayRef.h" + +// Local includes #include "revamb.h" +// Forward declarations namespace llvm { class LLVMContext; class Function; @@ -71,4 +76,4 @@ private: unsigned DbgMDKind; }; -#endif // _PTCTOLLVMIR_H +#endif // _CODEGENERATOR_H diff --git a/debughelper.cpp b/debughelper.cpp new file mode 100644 index 000000000..c43c8a0e8 --- /dev/null +++ b/debughelper.cpp @@ -0,0 +1,255 @@ +/// \file +/// \brief This file handles debugging information generation. + + +// Standard includes +#include + +// LLVM includes +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_os_ostream.h" + +// Local includes +#include "debughelper.h" + +using namespace llvm; + +/// Boring code to get the text of the metadata with the specified kind +/// associated to the given instruction +static MDString *getMD(const Instruction *Instruction, + unsigned Kind) { + assert(Instruction != nullptr); + + Metadata *MD = Instruction->getMetadata(Kind); + + if (MD == nullptr) + return nullptr; + + auto Node = dyn_cast(MD); + + assert(Node != nullptr); + + const MDOperand& Operand = Node->getOperand(0); + + Metadata *MDOperand = Operand.get(); + + if (MDOperand == nullptr) + return nullptr; + + auto *String = dyn_cast(MDOperand); + assert(String != nullptr); + + return String; +} + +/// Writes the text contained in the metadata with the specified kind ID to the +/// output stream, unless that metadata is exactly the same as in the previous +/// instruction. +static void writeMetadataIfNew(const Instruction *TheInstruction, + unsigned MDKind, + formatted_raw_ostream &Output, + StringRef Prefix) { + MDString *MD = getMD(TheInstruction, MDKind); + if (MD != nullptr) { + const Instruction *PrevInstruction = nullptr; + + if (TheInstruction != TheInstruction->getParent()->begin()) + PrevInstruction = TheInstruction->getPrevNode(); + + if (PrevInstruction == nullptr || getMD(PrevInstruction, MDKind) != MD) + Output << Prefix << MD->getString(); + + } +} + +DebugAnnotationWriter::DebugAnnotationWriter(LLVMContext& Context, + Metadata *Scope, + bool DebugInfo) : + Context(Context), + Scope(Scope), + DebugInfo(DebugInfo) +{ + OriginalInstrMDKind = Context.getMDKindID("oi"); + PTCInstrMDKind = Context.getMDKindID("pi"); + DbgMDKind = Context.getMDKindID("dbg"); + +} + +void DebugAnnotationWriter::emitInstructionAnnot(const Instruction *Instr, + formatted_raw_ostream &Output) { + + writeMetadataIfNew(Instr, OriginalInstrMDKind, Output, "\n\n ; "); + writeMetadataIfNew(Instr, PTCInstrMDKind, Output, "\n ; "); + + if (DebugInfo) { + // If DebugInfo is activated the generated LLVM IR textual representation + // will contain some reference to dangling pointers. So ignore the output + // stream if you're using the annotator to generate debug info about the IR + // itself. + assert(Scope != nullptr); + + // Flushing is required to have correct line and column numbers + Output.flush(); + auto *Location = DILocation::get(Context, + Output.getLine() + 1, + Output.getColumn(), + Scope); + + // Sorry Bjarne + auto *NonConstInstruction = const_cast(Instr); + NonConstInstruction->setMetadata(DbgMDKind, Location); + } +} + +DebugHelper::DebugHelper(std::string Output, + std::string Debug, + Module *TheModule, + DebugInfoType Type) : + OutputPath(Output), + DebugPath(Debug), + Builder(*TheModule), + Type(Type), + TheModule(TheModule) +{ + OriginalInstrMDKind = TheModule->getContext().getMDKindID("oi"); + PTCInstrMDKind = TheModule->getContext().getMDKindID("pi"); + DbgMDKind = TheModule->getContext().getMDKindID("dbg"); + + // Generate automatically the name of the source file for debugging + if (DebugPath.empty()) { + if (Type == DebugInfoType::PTC) + DebugPath = OutputPath + ".ptc"; + else if (Type == DebugInfoType::OriginalAssembly) + DebugPath = OutputPath + ".S"; + else if (Type == DebugInfoType::LLVMIR) + DebugPath = OutputPath; + } + + if (Type != DebugInfoType::None) { + CompileUnit = Builder.createCompileUnit(dwarf::DW_LANG_C, + DebugPath, + "", + "revamb", + false, + "", + 0 /* Runtime version */); + + // Add the current debug info version into the module. + TheModule->addModuleFlag(Module::Warning, "Debug Info Version", + DEBUG_METADATA_VERSION); + TheModule->addModuleFlag(Module::Warning, "Dwarf Version", 2); + } +} + +void DebugHelper::newFunction(Function *Function) { + if (Type != DebugInfoType::None) { + DISubroutineType *EmptyType = nullptr; + EmptyType = Builder.createSubroutineType(Builder.getOrCreateTypeArray({})); + + CurrentFunction = Function; + CurrentSubprogram = Builder.createFunction(CompileUnit, /* Scope */ + Function->getName(), + StringRef(), /* Linkage name */ + CompileUnit->getFile(), + 1, /* Line */ + EmptyType, /* Subroutine type */ + false, /* isLocalToUnit */ + true, /* isDefinition */ + 1, /* ScopeLine */ + DINode::FlagPrototyped, + false, /* isOptimized */ + CurrentFunction /* Function */); + } +} + +void DebugHelper::generateDebugInfo() { + switch (Type) { + case DebugInfoType::PTC: + case DebugInfoType::OriginalAssembly: + { + assert(CurrentSubprogram != nullptr && CurrentFunction != nullptr); + + // Generate the source file and the debugging information in tandem + + unsigned LineIndex = 1; + unsigned MetadataKind = Type == DebugInfoType::PTC ? + PTCInstrMDKind : OriginalInstrMDKind; + + MDString *Last = nullptr; + std::ofstream Source(DebugPath); + for (BasicBlock& Block : *CurrentFunction) { + for (Instruction& Instruction : Block) { + MDString *Body = getMD(&Instruction, MetadataKind); + + if (Body != nullptr && Last != Body) { + Last = Body; + std::string BodyString = Body->getString().str(); + + Source << BodyString; + + auto *Location = DILocation::get(TheModule->getContext(), + LineIndex, + 0, + CurrentSubprogram); + Instruction.setMetadata(DbgMDKind, Location); + LineIndex += std::count(BodyString.begin(), + BodyString.end(), + '\n'); + } + } + } + + Builder.finalize(); + break; + } + case DebugInfoType::LLVMIR: + { + // Use the annotator to obtain line and column of the textual LLVM IR for + // each instruction. Discard the output since it will contain errors, + // regenerating it later will give a correct result. + Builder.finalize(); + + raw_null_ostream NullStream; + TheModule->print(NullStream, annotator(true /* DebugInfo */)); + + std::ofstream Output(DebugPath); + raw_os_ostream Stream(Output); + TheModule->print(Stream, annotator(false)); + + break; + } + default: + break; + } + +} + +void DebugHelper::print(std::ostream& Output, bool DebugInfo) { + raw_os_ostream OutputStream(Output); + TheModule->print(OutputStream, annotator(DebugInfo)); +} + +bool DebugHelper::copySource() { + // If debug info refer to LLVM IR, just copy the output file + if (Type == DebugInfoType::LLVMIR && DebugPath != OutputPath) { + std::ifstream Source(DebugPath, std::ios::binary); + std::ofstream Destination(OutputPath, std::ios::binary); + + Destination << Source.rdbuf(); + + return true; + } + + return false; +} + +DebugAnnotationWriter *DebugHelper::annotator(bool DebugInfo) { + Annotator.reset(new DebugAnnotationWriter(TheModule->getContext(), + CurrentSubprogram, + DebugInfo)); + return Annotator.get(); +} diff --git a/debughelper.h b/debughelper.h new file mode 100644 index 000000000..7cd05578c --- /dev/null +++ b/debughelper.h @@ -0,0 +1,90 @@ +#ifndef _DEBUGHELPER_H +#define _DEBUGHELPER_H + +// Standard includes +#include +#include +#include + +// LLVM includes +#include "llvm/IR/DIBuilder.h" + +// Local includes +#include "revamb.h" + +namespace llvm { +class DIBuilder; +class Module; +class DICompileUnit; +class DISubprogram; +class Function; +} + +/// AssemblyAnnotationWriter implementation inserting in the generated LLVM IR +/// comments containing the original assembly and the PTC. It can also decorate +/// the IR with debug information (i.e. DILocations) refered to the generated +/// LLVM IR itself. +class DebugAnnotationWriter : public llvm::AssemblyAnnotationWriter { +public: + DebugAnnotationWriter(llvm::LLVMContext& Context, + llvm::Metadata *Scope, + bool DebugInfo); + + virtual void emitInstructionAnnot(const llvm::Instruction *TheInstruction, + llvm::formatted_raw_ostream &Output); + +private: + llvm::LLVMContext &Context; + llvm::Metadata *Scope; + unsigned OriginalInstrMDKind; + unsigned PTCInstrMDKind; + unsigned DbgMDKind; + bool DebugInfo; +}; + +/// Handle all the debug-related operations of code generation +class DebugHelper { +public: + DebugHelper(std::string Output, + std::string Debug, + llvm::Module *TheModule, + DebugInfoType Type); + + /// \brief Handle a new function + /// + /// Generates the debug information for the given function and caches it for + /// future use. + void newFunction(llvm::Function *Function); + + /// Decorates the current function with the request debug info + void generateDebugInfo(); + + /// Serializes to the given stream the module, with or without debug info + void print(std::ostream& Output, bool DebugInfo); + + /// Copy the debug file to the output path, if they are the same + bool copySource(); + +private: + /// Create a new AssemblyAnnotationWriter + /// + /// \param DebugInfo whether to decorate the IR with debug information or not + DebugAnnotationWriter *annotator(bool DebugInfo); + +private: + std::string OutputPath; + std::string DebugPath; + llvm::DIBuilder Builder; + DebugInfoType Type; + llvm::Module *TheModule; + llvm::DICompileUnit *CompileUnit; + llvm::DISubprogram *CurrentSubprogram; + llvm::Function *CurrentFunction; + std::unique_ptr Annotator; + + unsigned OriginalInstrMDKind; + unsigned PTCInstrMDKind; + unsigned DbgMDKind; +}; + +#endif // _DEBUGHELPER_H diff --git a/instructiontranslator.cpp b/instructiontranslator.cpp new file mode 100644 index 000000000..331ee4faf --- /dev/null +++ b/instructiontranslator.cpp @@ -0,0 +1,1304 @@ +/// \file +/// \brief This file implements the logic to translate a PTC instruction in to +/// LLVM IR. + +// Standard includes +#include +#include + +// LLVM includes +#include "llvm/IR/CFG.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Casting.h" + +// Local includes +#include "instructiontranslator.h" +#include "jumptargetmanager.h" +#include "ptcinterface.h" +#include "rai.h" +#include "range.h" +#include "transformadapter.h" +#include "variablemanager.h" + +using namespace llvm; + +/// Helper function to destroy an unconditional branch and, in case, the target +/// basic block, if it doesn't have any predecessors left. +static void purgeBranch(BasicBlock::iterator I) { + auto *DeadBranch = dyn_cast(I); + // We allow only an unconditional branch and nothing else + assert(DeadBranch != nullptr && + DeadBranch->isUnconditional() && + ++I == DeadBranch->getParent()->end()); + + // Obtain the target of the dead branch + BasicBlock *DeadBranchTarget = DeadBranch->getSuccessor(0); + + // Destroy the dead branch + DeadBranch->eraseFromParent(); + + // Check if someone else was jumping there and then destroy + if (pred_empty(DeadBranchTarget)) + DeadBranchTarget->eraseFromParent(); +} + +static uint64_t getConst(Value *Constant) { + return cast(Constant)->getLimitedValue(); +} + +namespace PTC { + + template + class InstructionImpl; + + enum ArgumentType { + In, + Out, + Const + }; + + template + class InstructionArgumentsIterator : + public RandomAccessIterator, + false> { + public: + using base = RandomAccessIterator; + + InstructionArgumentsIterator& + operator=(const InstructionArgumentsIterator& r) { + base::operator=(r); + TheInstruction = r.TheInstruction; + return *this; + } + + + InstructionArgumentsIterator(const InstructionArgumentsIterator& r) : + base(r), + TheInstruction(r.TheInstruction) { } + + InstructionArgumentsIterator(const InstructionArgumentsIterator& r, + unsigned Index) : + base(Index), + TheInstruction(r.TheInstruction) { } + + InstructionArgumentsIterator(PTCInstruction *TheInstruction, + unsigned Index) : + base(Index), + TheInstruction(TheInstruction) { } + + bool isCompatible(const InstructionArgumentsIterator& r) const { + return TheInstruction == r.TheInstruction; + } + + public: + uint64_t get(unsigned Index) const; + + private: + PTCInstruction *TheInstruction; + }; + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_call_instruction_in_arg(&ptc, TheInstruction, Index); + } + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_call_instruction_const_arg(&ptc, TheInstruction, Index); + } + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_call_instruction_out_arg(&ptc, TheInstruction, Index); + } + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_instruction_in_arg(&ptc, TheInstruction, Index); + } + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_instruction_const_arg(&ptc, TheInstruction, Index); + } + + template<> + inline uint64_t + InstructionArgumentsIterator::get(unsigned Index) const { + return ptc_instruction_out_arg(&ptc, TheInstruction, Index); + } + + template + class InstructionImpl { + private: + template + using arguments = InstructionArgumentsIterator; + public: + InstructionImpl(PTCInstruction *TheInstruction) : + TheInstruction(TheInstruction), + InArguments(arguments(TheInstruction, 0), + arguments(TheInstruction, inArgCount())), + ConstArguments(arguments(TheInstruction, 0), + arguments(TheInstruction, constArgCount())), + OutArguments(arguments(TheInstruction, 0), + arguments(TheInstruction, outArgCount())) + { } + + PTCOpcode opcode() const { + return TheInstruction->opc; + } + + std::string helperName() const { + assert(IsCall); + PTCHelperDef *Helper = ptc_find_helper(&ptc, ConstArguments[0]); + assert(Helper != nullptr && Helper->name != nullptr); + return std::string(Helper->name); + } + + private: + PTCInstruction* TheInstruction; + + public: + const Range> InArguments; + const Range> ConstArguments; + const Range> OutArguments; + + private: + unsigned inArgCount() const; + unsigned constArgCount() const; + unsigned outArgCount() const; + }; + + using Instruction = InstructionImpl; + using CallInstruction = InstructionImpl; + + template<> + inline unsigned CallInstruction::inArgCount() const { + return ptc_call_instruction_in_arg_count(&ptc, TheInstruction); + } + + template<> + inline unsigned Instruction::inArgCount() const { + return ptc_instruction_in_arg_count(&ptc, TheInstruction); + } + + template<> + inline unsigned CallInstruction::constArgCount() const { + return ptc_call_instruction_const_arg_count(&ptc, TheInstruction); + } + + template<> + inline unsigned Instruction::constArgCount() const { + return ptc_instruction_const_arg_count(&ptc, TheInstruction); + } + + template<> + inline unsigned CallInstruction::outArgCount() const { + return ptc_call_instruction_out_arg_count(&ptc, TheInstruction); + } + + template<> + inline unsigned Instruction::outArgCount() const { + return ptc_instruction_out_arg_count(&ptc, TheInstruction); + } + +} + +/// Converts a PTC condition into an LLVM predicate +/// +/// \param Condition the input PTC condition. +/// +/// \return the corresponding LLVM predicate. +static CmpInst::Predicate conditionToPredicate(PTCCondition Condition) { + switch (Condition) { + case PTC_COND_NEVER: + // TODO: this is probably wrong + return CmpInst::FCMP_FALSE; + case PTC_COND_ALWAYS: + // TODO: this is probably wrong + return CmpInst::FCMP_TRUE; + case PTC_COND_EQ: + return CmpInst::ICMP_EQ; + case PTC_COND_NE: + return CmpInst::ICMP_NE; + case PTC_COND_LT: + return CmpInst::ICMP_SLT; + case PTC_COND_GE: + return CmpInst::ICMP_SGE; + case PTC_COND_LE: + return CmpInst::ICMP_SLE; + case PTC_COND_GT: + return CmpInst::ICMP_SGT; + case PTC_COND_LTU: + return CmpInst::ICMP_ULT; + case PTC_COND_GEU: + return CmpInst::ICMP_UGE; + case PTC_COND_LEU: + return CmpInst::ICMP_ULE; + case PTC_COND_GTU: + return CmpInst::ICMP_UGT; + default: + llvm_unreachable("Unknown comparison operator"); + } +} + +/// Obtains the LLVM binary operation corresponding to the specified PTC opcode. +/// +/// \param Opcode the PTC opcode. +/// +/// \return the LLVM binary operation matching opcode. +static Instruction::BinaryOps opcodeToBinaryOp(PTCOpcode Opcode) { + switch (Opcode) { + case PTC_INSTRUCTION_op_add_i32: + case PTC_INSTRUCTION_op_add_i64: + case PTC_INSTRUCTION_op_add2_i32: + case PTC_INSTRUCTION_op_add2_i64: + return Instruction::Add; + case PTC_INSTRUCTION_op_sub_i32: + case PTC_INSTRUCTION_op_sub_i64: + case PTC_INSTRUCTION_op_sub2_i32: + case PTC_INSTRUCTION_op_sub2_i64: + return Instruction::Sub; + case PTC_INSTRUCTION_op_mul_i32: + case PTC_INSTRUCTION_op_mul_i64: + return Instruction::Mul; + case PTC_INSTRUCTION_op_div_i32: + case PTC_INSTRUCTION_op_div_i64: + return Instruction::SDiv; + case PTC_INSTRUCTION_op_divu_i32: + case PTC_INSTRUCTION_op_divu_i64: + return Instruction::UDiv; + case PTC_INSTRUCTION_op_rem_i32: + case PTC_INSTRUCTION_op_rem_i64: + return Instruction::SRem; + case PTC_INSTRUCTION_op_remu_i32: + case PTC_INSTRUCTION_op_remu_i64: + return Instruction::URem; + case PTC_INSTRUCTION_op_and_i32: + case PTC_INSTRUCTION_op_and_i64: + return Instruction::And; + case PTC_INSTRUCTION_op_or_i32: + case PTC_INSTRUCTION_op_or_i64: + return Instruction::Or; + case PTC_INSTRUCTION_op_xor_i32: + case PTC_INSTRUCTION_op_xor_i64: + return Instruction::Xor; + case PTC_INSTRUCTION_op_shl_i32: + case PTC_INSTRUCTION_op_shl_i64: + return Instruction::Shl; + case PTC_INSTRUCTION_op_shr_i32: + case PTC_INSTRUCTION_op_shr_i64: + return Instruction::LShr; + case PTC_INSTRUCTION_op_sar_i32: + case PTC_INSTRUCTION_op_sar_i64: + return Instruction::AShr; + default: + llvm_unreachable("PTC opcode is not a binary operator"); + } +} + +/// Returns the maximum value which can be represented with the specified number +/// of bits. +static uint64_t getMaxValue(unsigned Bits) { + if (Bits == 32) + return 0xffffffff; + else if (Bits == 64) + return 0xffffffffffffffff; + else + llvm_unreachable("Not the number of bits in a integer type"); +} + +/// Maps an opcode the corresponding input and output register size. +/// +/// \return the size, in bits, of the registers used by the opcode. +static unsigned getRegisterSize(unsigned Opcode) { + switch (Opcode) { + case PTC_INSTRUCTION_op_add2_i32: + case PTC_INSTRUCTION_op_add_i32: + case PTC_INSTRUCTION_op_andc_i32: + case PTC_INSTRUCTION_op_and_i32: + case PTC_INSTRUCTION_op_brcond2_i32: + case PTC_INSTRUCTION_op_brcond_i32: + case PTC_INSTRUCTION_op_bswap16_i32: + case PTC_INSTRUCTION_op_bswap32_i32: + case PTC_INSTRUCTION_op_deposit_i32: + case PTC_INSTRUCTION_op_div2_i32: + case PTC_INSTRUCTION_op_div_i32: + case PTC_INSTRUCTION_op_divu2_i32: + case PTC_INSTRUCTION_op_divu_i32: + case PTC_INSTRUCTION_op_eqv_i32: + case PTC_INSTRUCTION_op_ext16s_i32: + case PTC_INSTRUCTION_op_ext16u_i32: + case PTC_INSTRUCTION_op_ext8s_i32: + case PTC_INSTRUCTION_op_ext8u_i32: + case PTC_INSTRUCTION_op_ld16s_i32: + case PTC_INSTRUCTION_op_ld16u_i32: + case PTC_INSTRUCTION_op_ld8s_i32: + case PTC_INSTRUCTION_op_ld8u_i32: + case PTC_INSTRUCTION_op_ld_i32: + case PTC_INSTRUCTION_op_movcond_i32: + case PTC_INSTRUCTION_op_mov_i32: + case PTC_INSTRUCTION_op_movi_i32: + case PTC_INSTRUCTION_op_mul_i32: + case PTC_INSTRUCTION_op_muls2_i32: + case PTC_INSTRUCTION_op_mulsh_i32: + case PTC_INSTRUCTION_op_mulu2_i32: + case PTC_INSTRUCTION_op_muluh_i32: + case PTC_INSTRUCTION_op_nand_i32: + case PTC_INSTRUCTION_op_neg_i32: + case PTC_INSTRUCTION_op_nor_i32: + case PTC_INSTRUCTION_op_not_i32: + case PTC_INSTRUCTION_op_orc_i32: + case PTC_INSTRUCTION_op_or_i32: + case PTC_INSTRUCTION_op_qemu_ld_i32: + case PTC_INSTRUCTION_op_qemu_st_i32: + case PTC_INSTRUCTION_op_rem_i32: + case PTC_INSTRUCTION_op_remu_i32: + case PTC_INSTRUCTION_op_rotl_i32: + case PTC_INSTRUCTION_op_rotr_i32: + case PTC_INSTRUCTION_op_sar_i32: + case PTC_INSTRUCTION_op_setcond2_i32: + case PTC_INSTRUCTION_op_setcond_i32: + case PTC_INSTRUCTION_op_shl_i32: + case PTC_INSTRUCTION_op_shr_i32: + case PTC_INSTRUCTION_op_st16_i32: + case PTC_INSTRUCTION_op_st8_i32: + case PTC_INSTRUCTION_op_st_i32: + case PTC_INSTRUCTION_op_sub2_i32: + case PTC_INSTRUCTION_op_sub_i32: + case PTC_INSTRUCTION_op_trunc_shr_i32: + case PTC_INSTRUCTION_op_xor_i32: + return 32; + case PTC_INSTRUCTION_op_add2_i64: + case PTC_INSTRUCTION_op_add_i64: + case PTC_INSTRUCTION_op_andc_i64: + case PTC_INSTRUCTION_op_and_i64: + case PTC_INSTRUCTION_op_brcond_i64: + case PTC_INSTRUCTION_op_bswap16_i64: + case PTC_INSTRUCTION_op_bswap32_i64: + case PTC_INSTRUCTION_op_bswap64_i64: + case PTC_INSTRUCTION_op_deposit_i64: + case PTC_INSTRUCTION_op_div2_i64: + case PTC_INSTRUCTION_op_div_i64: + case PTC_INSTRUCTION_op_divu2_i64: + case PTC_INSTRUCTION_op_divu_i64: + case PTC_INSTRUCTION_op_eqv_i64: + case PTC_INSTRUCTION_op_ext16s_i64: + case PTC_INSTRUCTION_op_ext16u_i64: + case PTC_INSTRUCTION_op_ext32s_i64: + case PTC_INSTRUCTION_op_ext32u_i64: + case PTC_INSTRUCTION_op_ext8s_i64: + case PTC_INSTRUCTION_op_ext8u_i64: + case PTC_INSTRUCTION_op_ld16s_i64: + case PTC_INSTRUCTION_op_ld16u_i64: + case PTC_INSTRUCTION_op_ld32s_i64: + case PTC_INSTRUCTION_op_ld32u_i64: + case PTC_INSTRUCTION_op_ld8s_i64: + case PTC_INSTRUCTION_op_ld8u_i64: + case PTC_INSTRUCTION_op_ld_i64: + case PTC_INSTRUCTION_op_movcond_i64: + case PTC_INSTRUCTION_op_mov_i64: + case PTC_INSTRUCTION_op_movi_i64: + case PTC_INSTRUCTION_op_mul_i64: + case PTC_INSTRUCTION_op_muls2_i64: + case PTC_INSTRUCTION_op_mulsh_i64: + case PTC_INSTRUCTION_op_mulu2_i64: + case PTC_INSTRUCTION_op_muluh_i64: + case PTC_INSTRUCTION_op_nand_i64: + case PTC_INSTRUCTION_op_neg_i64: + case PTC_INSTRUCTION_op_nor_i64: + case PTC_INSTRUCTION_op_not_i64: + case PTC_INSTRUCTION_op_orc_i64: + case PTC_INSTRUCTION_op_or_i64: + case PTC_INSTRUCTION_op_qemu_ld_i64: + case PTC_INSTRUCTION_op_qemu_st_i64: + case PTC_INSTRUCTION_op_rem_i64: + case PTC_INSTRUCTION_op_remu_i64: + case PTC_INSTRUCTION_op_rotl_i64: + case PTC_INSTRUCTION_op_rotr_i64: + case PTC_INSTRUCTION_op_sar_i64: + case PTC_INSTRUCTION_op_setcond_i64: + case PTC_INSTRUCTION_op_shl_i64: + case PTC_INSTRUCTION_op_shr_i64: + case PTC_INSTRUCTION_op_st16_i64: + case PTC_INSTRUCTION_op_st32_i64: + case PTC_INSTRUCTION_op_st8_i64: + case PTC_INSTRUCTION_op_st_i64: + case PTC_INSTRUCTION_op_sub2_i64: + case PTC_INSTRUCTION_op_sub_i64: + case PTC_INSTRUCTION_op_xor_i64: + return 64; + case PTC_INSTRUCTION_op_br: + case PTC_INSTRUCTION_op_call: + case PTC_INSTRUCTION_op_debug_insn_start: + case PTC_INSTRUCTION_op_discard: + case PTC_INSTRUCTION_op_exit_tb: + case PTC_INSTRUCTION_op_goto_tb: + case PTC_INSTRUCTION_op_set_label: + return 0; + default: + llvm_unreachable("Unexpected opcode"); + break; + } +} + +/// Create a compare instruction given a comparison operator and the operands +/// +/// \param Builder the builder to use to create the instruction. +/// \param RawCondition the PTC condition. +/// \param FirstOperand the first operand of the comparison. +/// \param SecondOperand the second operand of the comparison. +/// +/// \return a compare instruction. +template +static Value *CreateICmp(T& Builder, + uint64_t RawCondition, + Value *FirstOperand, + Value *SecondOperand) { + PTCCondition Condition = static_cast(RawCondition); + return Builder.CreateICmp(conditionToPredicate(Condition), + FirstOperand, + SecondOperand); +} +void TranslateDirectBranchesPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); +} + +bool TranslateDirectBranchesPass::runOnFunction(Function &F) { + LLVMContext &Context = F.getParent()->getContext(); + + for (Use& PCUse : JTM->PC()->uses()) { + // TODO: what to do in case of read of the PC? + // Is the PC the store destination? + if (PCUse.getOperandNo() == 1) { + if (auto Jump = dyn_cast(PCUse.getUser())) { + Value *Destination = Jump->getValueOperand(); + + // Is destination a constant? + if (auto Address = dyn_cast(Destination)) { + // If necessary notify the about the existence of the basic block + // coming after this jump + // TODO: handle delay slots + BasicBlock *FakeFallthrough = JTM->getBlockAt(getNextPC(Jump)); + + // Compute the actual PC and get the associated BasicBlock + uint64_t TargetPC = Address->getSExtValue(); + BasicBlock *TargetBlock = JTM->getBlockAt(TargetPC); + + // Use a conditional branch here, even if the condition is always + // true. This way the "fallthrough" basic block is always reachable + // and the dominator tree computation works properly even if the + // dispatcher switch has not been emitted yet + auto *True = ConstantInt::getTrue(Context); + Instruction *Branch = BranchInst::Create(TargetBlock, + FakeFallthrough, + True); + + // Cleanup of what's afterwards (only a unconditional jump is allowed) + BasicBlock::iterator I = Jump; + BasicBlock::iterator BlockEnd = Jump->getParent()->end(); + if (++I != BlockEnd) + purgeBranch(I); + + Branch->insertAfter(Jump); + Jump->eraseFromParent(); + } + } else + llvm_unreachable("Unknown instruction using the PC"); + } else + llvm_unreachable("Unhandled usage of the PC"); + } + + return true; +} + +uint64_t TranslateDirectBranchesPass::getNextPC(Instruction *TheInstruction) { + DominatorTree& DT = getAnalysis().getDomTree(); + + BasicBlock *Block = TheInstruction->getParent(); + BasicBlock::iterator It(TheInstruction); + + while (true) { + BasicBlock::iterator Begin(Block->begin()); + + // Go back towards the beginning of the basic block looking for a call to + // NewPCMarker + CallInst *Marker = nullptr; + for (; It != Begin; It--) + if ((Marker = dyn_cast(&*It))) + if (Marker->getCalledFunction() == NewPCMarker) { + uint64_t PC = getConst(Marker->getArgOperand(0)); + uint64_t Size = getConst(Marker->getArgOperand(1)); + assert(Size != 0); + return PC + Size; + } + + auto *Node = DT.getNode(Block); + assert(Node != nullptr); + + Block = Node->getIDom()->getBlock(); + It = Block->end(); + } + + llvm_unreachable("Can't find the PC marker"); +} + +char TranslateDirectBranchesPass::ID = 0; +static RegisterPass X("translate-db", + "Translate Direct Branches" + " Pass", + false, + false); + +using LBM = InstructionTranslator::LabeledBlocksMap; +InstructionTranslator::InstructionTranslator(IRBuilder<>& Builder, + VariableManager& Variables, + JumpTargetManager& JumpTargets, + LBM& LabeledBasicBlocks, + std::vector Blocks, + Module& TheModule, + Function *TheFunction, + Architecture& SourceArchitecture, + Architecture& TargetArchitecture) : + Builder(Builder), + Variables(Variables), + JumpTargets(JumpTargets), + LabeledBasicBlocks(LabeledBasicBlocks), + Blocks(Blocks), + TheModule(TheModule), + TheFunction(TheFunction), + SourceArchitecture(SourceArchitecture), + TargetArchitecture(TargetArchitecture), + NewPCMarker(nullptr), + LastMarker(nullptr) { + + auto &Context = TheModule.getContext(); + NewPCMarker = Function::Create(FunctionType::get(Type::getVoidTy(Context), + { + Type::getInt64Ty(Context), + Type::getInt64Ty(Context) + }, + false), + GlobalValue::ExternalLinkage, + "newpc", + &TheModule); + } + +TranslateDirectBranchesPass +*InstructionTranslator::createTranslateDirectBranchesPass() { + return new TranslateDirectBranchesPass(&JumpTargets, NewPCMarker); +} + +void InstructionTranslator::removeNewPCMarkers() { + + std::vector ToDelete; + + for (User *Call : NewPCMarker->users()) + if (cast(Call)->getParent() != nullptr) + ToDelete.push_back(cast(Call)); + + for (Instruction *TheInstruction : ToDelete) + TheInstruction->eraseFromParent(); + + NewPCMarker->eraseFromParent(); +} + +void InstructionTranslator::closeLastInstruction(uint64_t PC) { + assert(LastMarker != nullptr); + + auto *Operand = cast(LastMarker->getArgOperand(0)); + uint64_t StartPC = Operand->getLimitedValue(); + + assert(PC > StartPC); + LastMarker->setArgOperand(1, Builder.getInt64(PC - StartPC)); + + LastMarker = nullptr; +} + +std::pair +InstructionTranslator::newInstruction(PTCInstruction *Instr, + bool IsFirst) { + const PTC::Instruction TheInstruction(Instr); + // A new original instruction, let's create a new metadata node + // referencing it for all the next instructions to come + uint64_t PC = TheInstruction.ConstArguments[0]; + + // TODO: replace using a field in Architecture + if (TheInstruction.ConstArguments.size() > 1) + PC |= TheInstruction.ConstArguments[1] << 32; + + std::stringstream OriginalStringStream; + disassembleOriginal(OriginalStringStream, PC); + std::string OriginalString = OriginalStringStream.str(); + LLVMContext& Context = TheModule.getContext(); + MDString *MDOriginalString = MDString::get(Context, OriginalString); + MDNode *MDOriginalInstr = MDNode::getDistinct(Context, MDOriginalString); + + if (!IsFirst) { + // Check if this PC already has a block and use it + bool ShouldContinue; + BasicBlock *DivergeTo = JumpTargets.newPC(PC, ShouldContinue); + if (DivergeTo != nullptr) { + Builder.CreateBr(DivergeTo); + + if (ShouldContinue) { + // The block is empty, let's fill it + Blocks.push_back(DivergeTo); + Builder.SetInsertPoint(DivergeTo); + Variables.newBasicBlock(); + } else { + // The block contains already translated code, early exit + return { true, MDOriginalInstr }; + } + } + } + + if (LastMarker != nullptr) + closeLastInstruction(PC); + LastMarker = Builder.CreateCall(NewPCMarker, + { Builder.getInt64(PC), Builder.getInt64(0) }); + + if (!IsFirst) { + // Inform the JumpTargetManager about the new PC we met + BasicBlock::iterator CurrentIt = Builder.GetInsertPoint(); + if (CurrentIt == Builder.GetInsertBlock()->begin()) + JumpTargets.registerBlock(PC, Builder.GetInsertBlock()); + else + JumpTargets.registerInstruction(PC, LastMarker); + } + + return { false, MDOriginalInstr }; +} + +void InstructionTranslator::translateCall(PTCInstruction *Instr) { + const PTC::CallInstruction TheCall(Instr); + + auto LoadArgs = [this] (uint64_t TemporaryId) -> Value * { + return Builder.CreateLoad(Variables.getOrCreate(TemporaryId)); + }; + + auto GetValueType = [] (Value *Argument) { return Argument->getType(); }; + + std::vector InArgs = (TheCall.InArguments | LoadArgs).toVector(); + std::vector InArgsType = (InArgs | GetValueType).toVector(); + + // TODO: handle multiple return arguments + assert(TheCall.OutArguments.size() <= 1); + + Value *ResultDestination = nullptr; + Type *ResultType = nullptr; + + if (TheCall.OutArguments.size() != 0) { + ResultDestination = Variables.getOrCreate(TheCall.OutArguments[0]); + ResultType = ResultDestination->getType()->getPointerElementType(); + } else { + ResultType = Builder.getVoidTy(); + } + + auto *CalleeType = FunctionType::get(ResultType, + ArrayRef(InArgsType), + false); + + std::string HelperName = "helper_" + TheCall.helperName(); + Constant *FunctionDeclaration = TheModule.getOrInsertFunction(HelperName, + CalleeType); + Value *Result = Builder.CreateCall(FunctionDeclaration, InArgs); + + if (TheCall.OutArguments.size() != 0) + Builder.CreateStore(Result, ResultDestination); +} + +void InstructionTranslator::translate(PTCInstruction *Instr) { + const PTC::Instruction TheInstruction(Instr); + + auto LoadArgs = [this] (uint64_t TemporaryId) -> Value * { + return Builder.CreateLoad(Variables.getOrCreate(TemporaryId)); + }; + + auto ConstArgs = TheInstruction.ConstArguments; + auto InArgs = TheInstruction.InArguments | LoadArgs; + + std::vector Result = translateOpcode(TheInstruction.opcode(), + ConstArgs.toVector(), + InArgs.toVector()); + + assert(Result.size() == (size_t) TheInstruction.OutArguments.size()); + // TODO: use ZipIterator here + for (unsigned I = 0; I < Result.size(); I++) + Builder.CreateStore(Result[I], + Variables.getOrCreate(TheInstruction.OutArguments[I])); +} + +std::vector +InstructionTranslator::translateOpcode(PTCOpcode Opcode, + std::vector ConstArguments, + std::vector InArguments) { + LLVMContext& Context = TheModule.getContext(); + unsigned RegisterSize = getRegisterSize(Opcode); + Type *RegisterType = nullptr; + if (RegisterSize == 32) + RegisterType = Builder.getInt32Ty(); + else if (RegisterSize == 64) + RegisterType = Builder.getInt64Ty(); + else if (RegisterSize != 0) + llvm_unreachable("Unexpected register size"); + + switch (Opcode) { + case PTC_INSTRUCTION_op_movi_i32: + case PTC_INSTRUCTION_op_movi_i64: + return { ConstantInt::get(RegisterType, ConstArguments[0]) }; + case PTC_INSTRUCTION_op_discard: + // Let's overwrite the discarded temporary with a 0 + return { ConstantInt::get(RegisterType, 0) }; + case PTC_INSTRUCTION_op_mov_i32: + case PTC_INSTRUCTION_op_mov_i64: + return { Builder.CreateTrunc(InArguments[0], RegisterType) }; + case PTC_INSTRUCTION_op_setcond_i32: + case PTC_INSTRUCTION_op_setcond_i64: + { + Value *Compare = CreateICmp(Builder, + ConstArguments[0], + InArguments[0], + InArguments[1]); + // TODO: convert single-bit registers to i1 + return { Builder.CreateZExt(Compare, RegisterType) }; + } + case PTC_INSTRUCTION_op_movcond_i32: // Resist the fallthrough temptation + case PTC_INSTRUCTION_op_movcond_i64: + { + Value *Compare = CreateICmp(Builder, + ConstArguments[0], + InArguments[0], + InArguments[1]); + Value *Select = Builder.CreateSelect(Compare, + InArguments[2], + InArguments[3]); + return { Select }; + } + case PTC_INSTRUCTION_op_qemu_ld_i32: + case PTC_INSTRUCTION_op_qemu_ld_i64: + case PTC_INSTRUCTION_op_qemu_st_i32: + case PTC_INSTRUCTION_op_qemu_st_i64: + { + PTCLoadStoreArg MemoryAccess; + MemoryAccess = ptc.parse_load_store_arg(ConstArguments[0]); + + // What are we supposed to do in this case? + assert(MemoryAccess.access_type != PTC_MEMORY_ACCESS_UNKNOWN); + + unsigned AccessAlignment = 0; + if (MemoryAccess.access_type == PTC_MEMORY_ACCESS_UNALIGNED) + AccessAlignment = 1; + else + AccessAlignment = SourceArchitecture.defaultAlignment(); + + // Load size + IntegerType *MemoryType = nullptr; + switch (ptc_get_memory_access_size(MemoryAccess.type)) { + case PTC_MO_8: + MemoryType = Builder.getInt8Ty(); + break; + case PTC_MO_16: + MemoryType = Builder.getInt16Ty(); + break; + case PTC_MO_32: + MemoryType = Builder.getInt32Ty(); + break; + case PTC_MO_64: + MemoryType = Builder.getInt64Ty(); + break; + default: + llvm_unreachable("Unexpected load size"); + } + + bool SignExtend = ptc_is_sign_extended_load(MemoryAccess.type); + + // // TODO: handle 64 on 32 + // // TODO: handle endianess mismatch + // assert(SourceArchitecture.endianess() == + // TargetArchitecture.endianess() && + // "Different endianess between the source and the target is not " + // "supported yet"); + + Value *Pointer = nullptr; + if (Opcode == PTC_INSTRUCTION_op_qemu_ld_i32 || + Opcode == PTC_INSTRUCTION_op_qemu_ld_i64) { + + Pointer = Builder.CreateIntToPtr(InArguments[0], + MemoryType->getPointerTo()); + Value *Load = Builder.CreateAlignedLoad(Pointer, AccessAlignment); + + if (SignExtend) + return { Builder.CreateSExt(Load, RegisterType) }; + else + return { Builder.CreateZExt(Load, RegisterType) }; + + } else if (Opcode == PTC_INSTRUCTION_op_qemu_st_i32 || + Opcode == PTC_INSTRUCTION_op_qemu_st_i64) { + + Pointer = Builder.CreateIntToPtr(InArguments[1], + MemoryType->getPointerTo()); + Value *Value = Builder.CreateTrunc(InArguments[0], MemoryType); + Builder.CreateAlignedStore(Value, Pointer, AccessAlignment); + + return { }; + } else + llvm_unreachable("Unknown load type"); + } + case PTC_INSTRUCTION_op_ld8u_i32: + case PTC_INSTRUCTION_op_ld8s_i32: + case PTC_INSTRUCTION_op_ld16u_i32: + case PTC_INSTRUCTION_op_ld16s_i32: + case PTC_INSTRUCTION_op_ld_i32: + case PTC_INSTRUCTION_op_ld8u_i64: + case PTC_INSTRUCTION_op_ld8s_i64: + case PTC_INSTRUCTION_op_ld16u_i64: + case PTC_INSTRUCTION_op_ld16s_i64: + case PTC_INSTRUCTION_op_ld32u_i64: + case PTC_INSTRUCTION_op_ld32s_i64: + case PTC_INSTRUCTION_op_ld_i64: + { + Value *Base = dyn_cast(InArguments[0])->getPointerOperand(); + assert(Base != nullptr && Variables.isEnv(Base)); + Value *Target = Variables.getByCPUStateOffset(ConstArguments[0]); + + Value *EnvField = Builder.CreateLoad(Target); + Value *Fitted = Builder.CreateZExtOrTrunc(EnvField, RegisterType); + + return { Fitted }; + } + case PTC_INSTRUCTION_op_st8_i32: + case PTC_INSTRUCTION_op_st16_i32: + case PTC_INSTRUCTION_op_st_i32: + case PTC_INSTRUCTION_op_st8_i64: + case PTC_INSTRUCTION_op_st16_i64: + case PTC_INSTRUCTION_op_st32_i64: + case PTC_INSTRUCTION_op_st_i64: + { + Value *Base = dyn_cast(InArguments[1])->getPointerOperand(); + assert(Base != nullptr && Variables.isEnv(Base)); + Value *Target = Variables.getByCPUStateOffset(ConstArguments[0]); + Type *TargetPointer = Target->getType()->getPointerElementType(); + Value *ToStore = Builder.CreateZExt(InArguments[0], TargetPointer); + Builder.CreateStore(ToStore, Target); + return { }; + } + case PTC_INSTRUCTION_op_add_i32: + case PTC_INSTRUCTION_op_sub_i32: + case PTC_INSTRUCTION_op_mul_i32: + case PTC_INSTRUCTION_op_div_i32: + case PTC_INSTRUCTION_op_divu_i32: + case PTC_INSTRUCTION_op_rem_i32: + case PTC_INSTRUCTION_op_remu_i32: + case PTC_INSTRUCTION_op_and_i32: + case PTC_INSTRUCTION_op_or_i32: + case PTC_INSTRUCTION_op_xor_i32: + case PTC_INSTRUCTION_op_shl_i32: + case PTC_INSTRUCTION_op_shr_i32: + case PTC_INSTRUCTION_op_sar_i32: + case PTC_INSTRUCTION_op_add_i64: + case PTC_INSTRUCTION_op_sub_i64: + case PTC_INSTRUCTION_op_mul_i64: + case PTC_INSTRUCTION_op_div_i64: + case PTC_INSTRUCTION_op_divu_i64: + case PTC_INSTRUCTION_op_rem_i64: + case PTC_INSTRUCTION_op_remu_i64: + case PTC_INSTRUCTION_op_and_i64: + case PTC_INSTRUCTION_op_or_i64: + case PTC_INSTRUCTION_op_xor_i64: + case PTC_INSTRUCTION_op_shl_i64: + case PTC_INSTRUCTION_op_shr_i64: + case PTC_INSTRUCTION_op_sar_i64: + { + // TODO: assert on sizes? + Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode); + Value *Operation = Builder.CreateBinOp(BinaryOp, + InArguments[0], + InArguments[1]); + return { Operation }; + } + case PTC_INSTRUCTION_op_div2_i32: + case PTC_INSTRUCTION_op_divu2_i32: + case PTC_INSTRUCTION_op_div2_i64: + case PTC_INSTRUCTION_op_divu2_i64: + { + Instruction::BinaryOps DivisionOp, RemainderOp; + + if (Opcode == PTC_INSTRUCTION_op_div2_i32 || + Opcode == PTC_INSTRUCTION_op_div2_i64) { + DivisionOp = Instruction::SDiv; + RemainderOp = Instruction::SRem; + } else if (Opcode == PTC_INSTRUCTION_op_div2_i32 || + Opcode == PTC_INSTRUCTION_op_div2_i64) { + DivisionOp = Instruction::UDiv; + RemainderOp = Instruction::URem; + } else + llvm_unreachable("Unknown operation type"); + + // TODO: we're ignoring InArguments[1], which is the MSB + // TODO: assert on sizes? + Value *Division = Builder.CreateBinOp(DivisionOp, + InArguments[0], + InArguments[2]); + Value *Remainder = Builder.CreateBinOp(RemainderOp, + InArguments[0], + InArguments[2]); + return { Division, Remainder }; + } + case PTC_INSTRUCTION_op_rotr_i32: + case PTC_INSTRUCTION_op_rotr_i64: + case PTC_INSTRUCTION_op_rotl_i32: + case PTC_INSTRUCTION_op_rotl_i64: + { + Value *Bits = ConstantInt::get(RegisterType, RegisterSize); + + Instruction::BinaryOps FirstShiftOp, SecondShiftOp; + if (Opcode == PTC_INSTRUCTION_op_rotl_i32 || + Opcode == PTC_INSTRUCTION_op_rotl_i64) { + FirstShiftOp = Instruction::LShr; + SecondShiftOp = Instruction::Shl; + } else if (Opcode == PTC_INSTRUCTION_op_rotr_i32 || + Opcode == PTC_INSTRUCTION_op_rotr_i64) { + FirstShiftOp = Instruction::Shl; + SecondShiftOp = Instruction::LShr; + } else + llvm_unreachable("Unexpected opcode"); + + Value *FirstShift = Builder.CreateBinOp(FirstShiftOp, + InArguments[0], + InArguments[1]); + Value *SecondShiftAmount = Builder.CreateSub(Bits, + InArguments[1]); + Value *SecondShift = Builder.CreateBinOp(SecondShiftOp, + InArguments[0], + SecondShiftAmount); + + return { Builder.CreateOr(FirstShift, SecondShift) }; + } + case PTC_INSTRUCTION_op_deposit_i32: + case PTC_INSTRUCTION_op_deposit_i64: + { + unsigned Position = ConstArguments[0]; + if (Position == RegisterSize) + return { InArguments[0] }; + + unsigned Length = ConstArguments[1]; + uint64_t Bits = 0; + + // Thou shall not << 32 + if (Length == RegisterSize) + Bits = getMaxValue(RegisterSize); + else + Bits = (1 << Length) - 1; + + // result = (t1 & ~(bits << position)) | ((t2 & bits) << position) + uint64_t BaseMask = ~(Bits << Position); + Value *MaskedBase = Builder.CreateAnd(InArguments[0], BaseMask); + Value *Deposit = Builder.CreateAnd(InArguments[1], Bits); + Value *ShiftedDeposit = Builder.CreateShl(Deposit, Position); + Value *Result = Builder.CreateOr(MaskedBase, ShiftedDeposit); + + return { Result }; + } + case PTC_INSTRUCTION_op_ext8s_i32: + case PTC_INSTRUCTION_op_ext16s_i32: + case PTC_INSTRUCTION_op_ext8u_i32: + case PTC_INSTRUCTION_op_ext16u_i32: + case PTC_INSTRUCTION_op_ext8s_i64: + case PTC_INSTRUCTION_op_ext16s_i64: + case PTC_INSTRUCTION_op_ext32s_i64: + case PTC_INSTRUCTION_op_ext8u_i64: + case PTC_INSTRUCTION_op_ext16u_i64: + case PTC_INSTRUCTION_op_ext32u_i64: + { + Type *SourceType = nullptr; + switch (Opcode) { + case PTC_INSTRUCTION_op_ext8s_i32: + case PTC_INSTRUCTION_op_ext8u_i32: + case PTC_INSTRUCTION_op_ext8s_i64: + case PTC_INSTRUCTION_op_ext8u_i64: + SourceType = Builder.getInt8Ty(); + break; + case PTC_INSTRUCTION_op_ext16s_i32: + case PTC_INSTRUCTION_op_ext16u_i32: + case PTC_INSTRUCTION_op_ext16s_i64: + case PTC_INSTRUCTION_op_ext16u_i64: + SourceType = Builder.getInt16Ty(); + break; + case PTC_INSTRUCTION_op_ext32s_i64: + case PTC_INSTRUCTION_op_ext32u_i64: + SourceType = Builder.getInt32Ty(); + break; + default: + llvm_unreachable("Unexpected opcode"); + } + + Value *Truncated = Builder.CreateTrunc(InArguments[0], SourceType); + + switch (Opcode) { + case PTC_INSTRUCTION_op_ext8s_i32: + case PTC_INSTRUCTION_op_ext8s_i64: + case PTC_INSTRUCTION_op_ext16s_i32: + case PTC_INSTRUCTION_op_ext16s_i64: + case PTC_INSTRUCTION_op_ext32s_i64: + return { Builder.CreateSExt(Truncated, RegisterType) }; + case PTC_INSTRUCTION_op_ext8u_i32: + case PTC_INSTRUCTION_op_ext8u_i64: + case PTC_INSTRUCTION_op_ext16u_i32: + case PTC_INSTRUCTION_op_ext16u_i64: + case PTC_INSTRUCTION_op_ext32u_i64: + return { Builder.CreateZExt(Truncated, RegisterType) }; + default: + llvm_unreachable("Unexpected opcode"); + } + } + case PTC_INSTRUCTION_op_not_i32: + case PTC_INSTRUCTION_op_not_i64: + return { Builder.CreateXor(InArguments[0], getMaxValue(RegisterSize)) }; + case PTC_INSTRUCTION_op_neg_i32: + case PTC_INSTRUCTION_op_neg_i64: + { + auto *InitialValue = ConstantInt::get(RegisterType, 0); + return { Builder.CreateSub(InitialValue, InArguments[0]) }; + } + case PTC_INSTRUCTION_op_andc_i32: + case PTC_INSTRUCTION_op_andc_i64: + case PTC_INSTRUCTION_op_orc_i32: + case PTC_INSTRUCTION_op_orc_i64: + case PTC_INSTRUCTION_op_eqv_i32: + case PTC_INSTRUCTION_op_eqv_i64: + { + Instruction::BinaryOps ExternalOp; + switch (Opcode) { + case PTC_INSTRUCTION_op_andc_i32: + case PTC_INSTRUCTION_op_andc_i64: + ExternalOp = Instruction::And; + break; + case PTC_INSTRUCTION_op_orc_i32: + case PTC_INSTRUCTION_op_orc_i64: + ExternalOp = Instruction::Or; + break; + case PTC_INSTRUCTION_op_eqv_i32: + case PTC_INSTRUCTION_op_eqv_i64: + ExternalOp = Instruction::Xor; + break; + default: + llvm_unreachable("Unexpected opcode"); + } + + Value *Negate = Builder.CreateXor(InArguments[1], + getMaxValue(RegisterSize)); + Value *Result = Builder.CreateBinOp(ExternalOp, InArguments[0], Negate); + return { Result }; + } + case PTC_INSTRUCTION_op_nand_i32: + case PTC_INSTRUCTION_op_nand_i64: + { + Value *AndValue = Builder.CreateAnd(InArguments[0], InArguments[1]); + Value *Result = Builder.CreateXor(AndValue, getMaxValue(RegisterSize)); + return { Result }; + } + case PTC_INSTRUCTION_op_nor_i32: + case PTC_INSTRUCTION_op_nor_i64: + { + Value *OrValue = Builder.CreateOr(InArguments[0], InArguments[1]); + Value *Result = Builder.CreateXor(OrValue, getMaxValue(RegisterSize)); + return { Result }; + } + case PTC_INSTRUCTION_op_bswap16_i32: + case PTC_INSTRUCTION_op_bswap32_i32: + case PTC_INSTRUCTION_op_bswap16_i64: + case PTC_INSTRUCTION_op_bswap32_i64: + case PTC_INSTRUCTION_op_bswap64_i64: + { + Type *SwapType = nullptr; + switch (Opcode) { + case PTC_INSTRUCTION_op_bswap16_i32: + case PTC_INSTRUCTION_op_bswap16_i64: + SwapType = Builder.getInt16Ty(); + case PTC_INSTRUCTION_op_bswap32_i32: + case PTC_INSTRUCTION_op_bswap32_i64: + SwapType = Builder.getInt32Ty(); + case PTC_INSTRUCTION_op_bswap64_i64: + SwapType = Builder.getInt64Ty(); + default: + llvm_unreachable("Unexpected opcode"); + } + + Value *Truncated = Builder.CreateTrunc(InArguments[0], SwapType); + + std::vector BSwapParameters { RegisterType }; + Function *BSwapFunction = Intrinsic::getDeclaration(&TheModule, + Intrinsic::bswap, + BSwapParameters); + Value *Swapped = Builder.CreateCall(BSwapFunction, Truncated); + + return { Builder.CreateZExt(Swapped, RegisterType) }; + } + case PTC_INSTRUCTION_op_set_label: + { + unsigned LabelId = ptc.get_arg_label_id(ConstArguments[0]); + std::string Label = "L" + std::to_string(LabelId); + + BasicBlock *Fallthrough = nullptr; + auto ExistingBasicBlock = LabeledBasicBlocks.find(Label); + + if (ExistingBasicBlock == LabeledBasicBlocks.end()) { + Fallthrough = BasicBlock::Create(Context, Label, TheFunction); + LabeledBasicBlocks[Label] = Fallthrough; + } else { + // A basic block with that label already exist + Fallthrough = LabeledBasicBlocks[Label]; + + // Ensure it's empty + assert(Fallthrough->begin() == Fallthrough->end()); + + // Move it to the bottom + Fallthrough->removeFromParent(); + TheFunction->getBasicBlockList().push_back(Fallthrough); + } + + Builder.CreateBr(Fallthrough); + + Blocks.push_back(Fallthrough); + Builder.SetInsertPoint(Fallthrough); + Variables.newBasicBlock(); + + return { }; + } + case PTC_INSTRUCTION_op_br: + case PTC_INSTRUCTION_op_brcond_i32: + case PTC_INSTRUCTION_op_brcond2_i32: + case PTC_INSTRUCTION_op_brcond_i64: + { + // We take the last constant arguments, which is the LabelId both in + // conditional and unconditional jumps + unsigned LabelId = ptc.get_arg_label_id(ConstArguments.back()); + std::string Label = "L" + std::to_string(LabelId); + + BasicBlock *Fallthrough = BasicBlock::Create(Context, "", TheFunction); + + // Look for a matching label + BasicBlock *Target = nullptr; + auto ExistingBasicBlock = LabeledBasicBlocks.find(Label); + + // No matching label, create a temporary block + if (ExistingBasicBlock == LabeledBasicBlocks.end()) { + Target = BasicBlock::Create(Context, Label, TheFunction); + LabeledBasicBlocks[Label] = Target; + } else + Target = LabeledBasicBlocks[Label]; + + if (Opcode == PTC_INSTRUCTION_op_br) { + // Unconditional jump + Builder.CreateBr(Target); + } else if (Opcode == PTC_INSTRUCTION_op_brcond_i32 || + Opcode == PTC_INSTRUCTION_op_brcond_i64) { + // Conditional jump + Value *Compare = CreateICmp(Builder, + ConstArguments[0], + InArguments[0], + InArguments[1]); + Builder.CreateCondBr(Compare, Target, Fallthrough); + } else + llvm_unreachable("Unhandled opcode"); + + Blocks.push_back(Fallthrough); + Builder.SetInsertPoint(Fallthrough); + Variables.newBasicBlock(); + + return { }; + } + case PTC_INSTRUCTION_op_call: + // TODO: implement call to helpers + llvm_unreachable("Call to helpers not implemented"); + case PTC_INSTRUCTION_op_exit_tb: + case PTC_INSTRUCTION_op_goto_tb: + // Nothing to do here + return { }; + case PTC_INSTRUCTION_op_add2_i32: + case PTC_INSTRUCTION_op_sub2_i32: + case PTC_INSTRUCTION_op_add2_i64: + case PTC_INSTRUCTION_op_sub2_i64: + { + Value *FirstOperandLow = nullptr; + Value *FirstOperandHigh = nullptr; + Value *SecondOperandLow = nullptr; + Value *SecondOperandHigh = nullptr; + + IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); + + FirstOperandLow = Builder.CreateSExt(InArguments[0], DestinationType); + FirstOperandHigh = Builder.CreateSExt(InArguments[1], DestinationType); + SecondOperandLow = Builder.CreateSExt(InArguments[2], DestinationType); + SecondOperandHigh = Builder.CreateSExt(InArguments[3], DestinationType); + + FirstOperandHigh = Builder.CreateShl(FirstOperandHigh, RegisterSize); + SecondOperandHigh = Builder.CreateShl(SecondOperandHigh, RegisterSize); + + Value *FirstOperand = Builder.CreateOr(FirstOperandHigh, FirstOperandLow); + Value *SecondOperand = Builder.CreateOr(SecondOperandHigh, + SecondOperandLow); + + Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode); + + Value *Result = Builder.CreateBinOp(BinaryOp, FirstOperand, SecondOperand); + + Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); + Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); + Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, RegisterType); + + return { ResultLow, ResultHigh }; + } + case PTC_INSTRUCTION_op_mulu2_i32: + case PTC_INSTRUCTION_op_mulu2_i64: + case PTC_INSTRUCTION_op_muls2_i32: + case PTC_INSTRUCTION_op_muls2_i64: + { + IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); + + Value *FirstOperand = nullptr; + Value *SecondOperand = nullptr; + + if (Opcode == PTC_INSTRUCTION_op_muls2_i32 + || Opcode == PTC_INSTRUCTION_op_muls2_i64) { + FirstOperand = Builder.CreateZExt(InArguments[0], DestinationType); + SecondOperand = Builder.CreateZExt(InArguments[1], DestinationType); + } else if (Opcode == PTC_INSTRUCTION_op_muls2_i32 + || Opcode == PTC_INSTRUCTION_op_muls2_i64) { + FirstOperand = Builder.CreateSExt(InArguments[0], DestinationType); + SecondOperand = Builder.CreateSExt(InArguments[1], DestinationType); + } else + llvm_unreachable("Unexpected opcode"); + + Value *Result = Builder.CreateMul(FirstOperand, SecondOperand); + + Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); + Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); + Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, RegisterType); + + return { ResultLow, ResultHigh }; + } + case PTC_INSTRUCTION_op_muluh_i32: + case PTC_INSTRUCTION_op_mulsh_i32: + case PTC_INSTRUCTION_op_muluh_i64: + case PTC_INSTRUCTION_op_mulsh_i64: + + case PTC_INSTRUCTION_op_setcond2_i32: + + case PTC_INSTRUCTION_op_trunc_shr_i32: + llvm_unreachable("Instruction not implemented"); + default: + llvm_unreachable("Unknown opcode"); + } +} diff --git a/instructiontranslator.h b/instructiontranslator.h new file mode 100644 index 000000000..0f85c2d97 --- /dev/null +++ b/instructiontranslator.h @@ -0,0 +1,102 @@ +#ifndef _INSTRUCTIONTRANSLATOR_H +#define _INSTRUCTIONTRANSLATOR_H + +// Standard includes +#include +#include +#include + +// LLVM includes +#include "llvm/IR/IRBuilder.h" +#include "llvm/Pass.h" + +// Local includes +#include "revamb.h" +#include "ptcdump.h" + +// Forward declarations +namespace llvm { +class BasicBlock; +class CallInst; +class Function; +class MDNode; +class Module; +} + +class JumpTargetManager; +class VariableManager; + +class TranslateDirectBranchesPass : public llvm::FunctionPass { +public: + static char ID; + + TranslateDirectBranchesPass() : llvm::FunctionPass(ID), + JTM(nullptr), + NewPCMarker(nullptr) { } + + TranslateDirectBranchesPass(JumpTargetManager *JTM, + llvm::Function *NewPCMarker) : + FunctionPass(ID), + JTM(JTM), + NewPCMarker(NewPCMarker) { } + + void getAnalysisUsage(llvm::AnalysisUsage &AU) const; + + bool runOnFunction(llvm::Function &F) override; + +private: + uint64_t getNextPC(llvm::Instruction *TheInstruction); + +private: + llvm::Value *PCReg; + JumpTargetManager *JTM; + llvm::Function *NewPCMarker; +}; + +class InstructionTranslator { +public: + using LabeledBlocksMap = std::map; + InstructionTranslator(llvm::IRBuilder<>& Builder, + VariableManager& Variables, + JumpTargetManager& JumpTargets, + LabeledBlocksMap& LabeledBasicBlocks, + std::vector Blocks, + llvm::Module& TheModule, + llvm::Function *TheFunction, + Architecture& SourceArchitecture, + Architecture& TargetArchitecture); + + TranslateDirectBranchesPass *createTranslateDirectBranchesPass(); + + std::pair newInstruction(PTCInstruction *Instr, + bool IsFirst); + void translate(PTCInstruction *Instr); + void translateCall(PTCInstruction *Instr); + + void removeNewPCMarkers(); + + void closeLastInstruction(uint64_t PC); + + private: + std::vector + translateOpcode(PTCOpcode Opcode, + std::vector ConstArguments, + std::vector InArguments); +private: + llvm::IRBuilder<>& Builder; + VariableManager& Variables; + JumpTargetManager& JumpTargets; + std::map& LabeledBasicBlocks; + std::vector Blocks; + llvm::Module& TheModule; + + llvm::Function *TheFunction; + + Architecture& SourceArchitecture; + Architecture& TargetArchitecture; + + llvm::Function *NewPCMarker; + llvm::CallInst *LastMarker; +}; + +#endif // _INSTRUCTIONTRANSLATOR_H diff --git a/jumptargetmanager.cpp b/jumptargetmanager.cpp new file mode 100644 index 000000000..bf296bdde --- /dev/null +++ b/jumptargetmanager.cpp @@ -0,0 +1,241 @@ +/// \file +/// \brief This file handles the possible jump targets encountered during +/// translation and the creation and management of the respective +/// BasicBlock. + +// Standard includes +#include + +// LLVM includes +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" + +// Local includes +#include "jumptargetmanager.h" + +using namespace llvm; + +/// Helper function to destroy an unconditional branch and, in case, the +/// target basic block, if it doesn't have any predecessors left. +static void purgeBranch(BasicBlock::iterator I) { + auto *DeadBranch = dyn_cast(I); + // We allow only an unconditional branch and nothing else + assert(DeadBranch != nullptr && + DeadBranch->isUnconditional() && + ++I == DeadBranch->getParent()->end()); + + // Obtain the target of the dead branch + BasicBlock *DeadBranchTarget = DeadBranch->getSuccessor(0); + + // Destroy the dead branch + DeadBranch->eraseFromParent(); + + // Check if someone else was jumping there and then destroy + if (pred_empty(DeadBranchTarget)) + DeadBranchTarget->eraseFromParent(); +} + +JumpTargetManager::JumpTargetManager(Module& TheModule, + Value *PCReg, + Function *TheFunction) : + TheModule(TheModule), + Context(TheModule.getContext()), + TheFunction(TheFunction), + OriginalInstructionAddresses(), + JumpTargets(), + PCReg(PCReg) { } + +/// Handle a new program counter. We might already have a basic block for that +/// program counter, or we could even have a translation for it. Return one +/// of these, if appropriate. +/// +/// \param PC the new program counter. +/// \param ShouldContinue an out parameter indicating whether the returned +/// basic block was just a placeholder or actually contains a +/// translation. +/// +/// \return the basic block to use from now on, or null if the program counter +/// is not associated to a basic block. +BasicBlock *JumpTargetManager::newPC(uint64_t PC, bool& ShouldContinue) { + // Did we already meet this PC? + auto It = JumpTargets.find(PC); + if (It != JumpTargets.end()) { + // If it was planned to explore it in the future, just to do it now + for (auto It = Unexplored.begin(); It != Unexplored.end(); It++) { + if (It->first == PC) { + Unexplored.erase(It, It + 1); + ShouldContinue = true; + assert(It->second->empty()); + return It->second; + } + } + + // It wasn't planned to visit it, so we've already been there, just jump + // there + assert(!It->second->empty()); + ShouldContinue = false; + return It->second; + } + + // We don't know anything about this PC + return nullptr; +} + +/// Save the PC-Instruction association for future use (jump target) +void JumpTargetManager::registerInstruction(uint64_t PC, + Instruction *Instruction) { + // Never save twice a PC + assert(OriginalInstructionAddresses.find(PC) == + OriginalInstructionAddresses.end()); + OriginalInstructionAddresses[PC] = Instruction; +} + +/// Save the PC-BasicBlock association for futur use (jump target) +void JumpTargetManager::registerBlock(uint64_t PC, BasicBlock *Block) { + // If we already met it, it must point to the same block + auto It = JumpTargets.find(PC); + assert(It == JumpTargets.end() || It->second == Block); + if (It->second != Block) + JumpTargets[PC] = Block; +} + +void JumpTargetManager::translateIndirectJumps() { + BasicBlock *Dispatcher = createDispatcher(TheFunction, PCReg, true); + + for (Use& PCUse : PCReg->uses()) { + if (PCUse.getOperandNo() == 1) { + if (auto Jump = dyn_cast(PCUse.getUser())) { + BasicBlock::iterator It(Jump); + auto *Branch = BranchInst::Create(Dispatcher, ++It); + + // Cleanup everything it's aftewards + BasicBlock *Parent = Jump->getParent(); + Instruction *ToDelete = &*(--Parent->end()); + while (ToDelete != Branch) { + if (auto DeadBranch = dyn_cast(ToDelete)) + purgeBranch(DeadBranch); + else + ToDelete->eraseFromParent(); + + ToDelete = &*(--Parent->end()); + } + } + } + } +} + +Value *JumpTargetManager::PC() { + return PCReg; +} + +/// Pop from the list of program counters to explore +/// +/// \return a pair containing the PC and the initial block to use, or +/// JumpTarget::NoMoreTargets if we're done. +JumpTargetManager::BlockWithAddress JumpTargetManager::peekJumpTarget() { + if (Unexplored.empty()) + return NoMoreTargets; + else { + BlockWithAddress Result = Unexplored.back(); + Unexplored.pop_back(); + return Result; + } +} + +/// Get or create a block for the given PC +BasicBlock *JumpTargetManager::getBlockAt(uint64_t PC) { + // Do we already have a BasicBlock for this PC? + BlockMap::iterator TargetIt = JumpTargets.find(PC); + if (TargetIt != JumpTargets.end()) { + // Case 1: there's already a BasicBlock for that address, return it + return TargetIt->second; + } + + // Did we already meet this PC (i.e. do we know what's the associated + // instruction)? + BasicBlock *NewBlock = nullptr; + InstructionMap::iterator InstrIt = OriginalInstructionAddresses.find(PC); + if (InstrIt != OriginalInstructionAddresses.end()) { + // Case 2: the address has already been met, but needs to be promoted to + // BasicBlock level. + BasicBlock *ContainingBlock = InstrIt->second->getParent(); + if (InstrIt->second == &*ContainingBlock->begin()) + NewBlock = ContainingBlock; + else { + assert(InstrIt->second != nullptr && + InstrIt->second != ContainingBlock->end()); + // Split the block in the appropriate position. Note that + // OriginalInstructionAddresses stores a reference to the last generated + // instruction for the previous instruction. + Instruction *Next = InstrIt->second->getNextNode(); + NewBlock = ContainingBlock->splitBasicBlock(Next); + } + } else { + // Case 3: the address has never been met, create a temporary one, register + // it for future exploration and return it + NewBlock = BasicBlock::Create(Context, "", TheFunction); + Unexplored.push_back(BlockWithAddress(PC, NewBlock)); + } + + // Associate the PC with the chosen basic block + JumpTargets[PC] = NewBlock; + return NewBlock; +} + +// TODO: instead of a gigantic switch case we could map the original memory area +// and write the address of the translated basic block at the jump target +BasicBlock *JumpTargetManager::createDispatcher(Function *OutputFunction, + Value *SwitchOnPtr, + bool JumpDirectly) { + IRBuilder<> Builder(Context); + + // Create the first block of the function + BasicBlock *Entry = BasicBlock::Create(Context, "", OutputFunction); + + // The default case of the switch statement it's an unhandled cases + auto *Default = BasicBlock::Create(Context, "", OutputFunction); + Builder.SetInsertPoint(Default); + Builder.CreateUnreachable(); + + // Switch on the first argument of the function + Builder.SetInsertPoint(Entry); + Value *SwitchOn = Builder.CreateLoad(SwitchOnPtr); + SwitchInst *Switch = Builder.CreateSwitch(SwitchOn, Default); + auto *SwitchOnType = cast(SwitchOn->getType()); + + { + // We consider a jump to NULL as a program end + auto *NullBlock = BasicBlock::Create(Context, "", OutputFunction); + Switch->addCase(ConstantInt::get(SwitchOnType, 0), NullBlock); + Builder.SetInsertPoint(NullBlock); + Builder.CreateRetVoid(); + } + + // Create a case for each jump target we saw so far + for (auto& Pair : JumpTargets) { + // Create a case for the address associated to the current block + auto *Block = BasicBlock::Create(Context, "", OutputFunction); + Switch->addCase(ConstantInt::get(SwitchOnType, Pair.first), Block); + + Builder.SetInsertPoint(Block); + if (JumpDirectly) { + // Assume we're injecting the switch case directly into the function + // the blocks are in, so we can jump to the target block directly + assert(Pair.second->getParent() == OutputFunction); + Builder.CreateBr(Pair.second); + } else { + // Return the address of the current block + Builder.CreateRet(BlockAddress::get(OutputFunction, Pair.second)); + } + } + + return Entry; +} + +const JumpTargetManager::BlockWithAddress JumpTargetManager::NoMoreTargets = + JumpTargetManager::BlockWithAddress(0, nullptr); diff --git a/jumptargetmanager.h b/jumptargetmanager.h new file mode 100644 index 000000000..9915d4f1f --- /dev/null +++ b/jumptargetmanager.h @@ -0,0 +1,85 @@ +#ifndef _JUMPTARGETMANAGER_H +#define _JUMPTARGETMANAGER_H + +// Standard includes +#include +#include + +// Forward declarations +namespace llvm { +class BasicBlock; +class Function; +class Instruction; +class LLVMContext; +class Module; +class Value; +} + +class JumpTargetManager { +public: + using BlockWithAddress = std::pair; + static const BlockWithAddress NoMoreTargets; + +public: + JumpTargetManager(llvm::Module& TheModule, + llvm::Value *PCReg, + llvm::Function *TheFunction); + + /// Handle a new program counter. We might already have a basic block for that + /// program counter, or we could even have a translation for it. Return one + /// of these, if appropriate. + /// + /// \param PC the new program counter. + /// \param ShouldContinue an out parameter indicating whether the returned + /// basic block was just a placeholder or actually contains a + /// translation. + /// + /// \return the basic block to use from now on, or null if the program counter + /// is not associated to a basic block. + llvm::BasicBlock *newPC(uint64_t PC, bool& ShouldContinue); + + /// Save the PC-Instruction association for future use (jump target) + void registerInstruction(uint64_t PC, llvm::Instruction *Instruction); + + /// Save the PC-BasicBlock association for futur use (jump target) + void registerBlock(uint64_t PC, llvm::BasicBlock *Block); + + void translateIndirectJumps(); + + llvm::Value *PC(); + + /// Pop from the list of program counters to explore + /// + /// \return a pair containing the PC and the initial block to use, or + /// JumpTarget::NoMoreTargets if we're done. + BlockWithAddress peekJumpTarget(); + + /// Get or create a block for the given PC + llvm::BasicBlock *getBlockAt(uint64_t PC); + +private: + // TODO: instead of a gigantic switch case we could map the original memory + // area and write the address of the translated basic block at the jump + // target + llvm::BasicBlock *createDispatcher(llvm::Function *OutputFunction, + llvm::Value *SwitchOnPtr, + bool JumpDirectly); + +private: + using BlockMap = std::map; + using InstructionMap = std::map; + + llvm::Module &TheModule; + llvm::LLVMContext& Context; + llvm::Function* TheFunction; + /// Holds the association between a PC and the last generated instruction for + /// the previous instruction. + InstructionMap OriginalInstructionAddresses; + /// Holds the association between a PC and a BasicBlock. + BlockMap JumpTargets; + /// Queue of program counters we still have to translate. + std::vector Unexplored; + llvm::Value *PCReg; +}; + +#endif // _JUMPTARGETMANAGER_H diff --git a/main.cpp b/main.cpp index ed945c571..1864dc50a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,4 @@ +/// \file /// \brief This file takes care of handling command-line parameters and loading /// the appropriate flavour of libtinycode-*.so @@ -17,7 +18,7 @@ extern "C" { #include "revamb.h" #include "argparse.h" #include "ptcinterface.h" -#include "ptctollvmir.h" +#include "codegenerator.h" static const unsigned BUF_SIZE = 4096; static const unsigned MAX_INPUT_BUFFER = 10 * 1024 * 1024; diff --git a/ptcdump.cpp b/ptcdump.cpp index f2c8edd1b..5961600a2 100644 --- a/ptcdump.cpp +++ b/ptcdump.cpp @@ -1,11 +1,14 @@ /// \file /// \brief This file handles dumping PTC to text -#include -#include -#include +// Standard includes #include +#include +#include +#include #include + +// Local includes #include "ptcinterface.h" static const int MAX_TEMP_NAME_LENGTH = 128; @@ -198,7 +201,8 @@ int dumpInstruction(std::ostream& Result, PTCInstructionList *Instructions, case PTC_INSTRUCTION_op_brcond2_i32: { PTCInstructionArg Arg = ptc_instruction_const_arg(&ptc, - &Instruction, i); + &Instruction, + i); Result << "," << "$L" << ptc.get_arg_label_id(Arg); /* Consume one more argument */ diff --git a/ptcdump.h b/ptcdump.h index efc6ceac9..414df35ba 100644 --- a/ptcdump.h +++ b/ptcdump.h @@ -1,8 +1,11 @@ #ifndef _PTCDUMP_H #define _PTCDUMP_H +// Standard includes #include #include + +// Local includes #include "ptc.h" /// Writes to a stream the string representation of the PTC instruction with the diff --git a/ptctollvmir.cpp b/ptctollvmir.cpp deleted file mode 100644 index 7804eeaca..000000000 --- a/ptctollvmir.cpp +++ /dev/null @@ -1,2310 +0,0 @@ -/// \file -/// \brief This file handles the translation from QEMU's PTC to LLVM IR. - -#include -#include -#include -#include -#include - -// LLVM API -#include "llvm/IR/Constants.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Metadata.h" -#include "llvm/Support/Casting.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/AssemblyAnnotationWriter.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/raw_os_ostream.h" -#include "llvm/IRReader/IRReader.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Dominators.h" - -#include "ptctollvmir.h" -#include "ptcinterface.h" -#include "ptcdump.h" -#include "rai.h" -#include "range.h" -#include "transformadapter.h" - -using namespace llvm; - -static uint64_t getConst(Value *Constant) { - return cast(Constant)->getLimitedValue(); -} - -/// Helper function to destroy an unconditional branch and, in case, the -/// target basic block, if it doesn't have any predecessors left. -static void purgeBranch(BasicBlock::iterator I) { - auto *DeadBranch = dyn_cast(I); - // We allow only an unconditional branch and nothing else - assert(DeadBranch != nullptr && - DeadBranch->isUnconditional() && - ++I == DeadBranch->getParent()->end()); - - // Obtain the target of the dead branch - BasicBlock *DeadBranchTarget = DeadBranch->getSuccessor(0); - - // Destroy the dead branch - DeadBranch->eraseFromParent(); - - // Check if someone else was jumping there and then destroy - if (pred_empty(DeadBranchTarget)) - DeadBranchTarget->eraseFromParent(); -} - -namespace PTC { - -template -class InstructionImpl; - -enum ArgumentType { - In, - Out, - Const -}; - -template -class InstructionArgumentsIterator : - public RandomAccessIterator, - false> { -public: - using base = RandomAccessIterator; - - InstructionArgumentsIterator& - operator=(const InstructionArgumentsIterator& r) { - base::operator=(r); - TheInstruction = r.TheInstruction; - return *this; - } - - - InstructionArgumentsIterator(const InstructionArgumentsIterator& r) : - base(r), - TheInstruction(r.TheInstruction) - { } - - InstructionArgumentsIterator(const InstructionArgumentsIterator& r, - unsigned Index) : - base(Index), - TheInstruction(r.TheInstruction) - { } - - InstructionArgumentsIterator(PTCInstruction *TheInstruction, unsigned Index) : - base(Index), - TheInstruction(TheInstruction) - { } - - bool isCompatible(const InstructionArgumentsIterator& r) const { - return TheInstruction == r.TheInstruction; - } - -public: - uint64_t get(unsigned Index) const; - -private: - PTCInstruction *TheInstruction; -}; - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_call_instruction_in_arg(&ptc, TheInstruction, Index); -} - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_call_instruction_const_arg(&ptc, TheInstruction, Index); -} - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_call_instruction_out_arg(&ptc, TheInstruction, Index); -} - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_instruction_in_arg(&ptc, TheInstruction, Index); -} - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_instruction_const_arg(&ptc, TheInstruction, Index); -} - -template<> -inline uint64_t -InstructionArgumentsIterator::get(unsigned Index) const { - return ptc_instruction_out_arg(&ptc, TheInstruction, Index); -} - -template -class InstructionImpl { -private: - template - using arguments = InstructionArgumentsIterator; -public: - InstructionImpl(PTCInstruction *TheInstruction) : - TheInstruction(TheInstruction), - InArguments(arguments(TheInstruction, 0), - arguments(TheInstruction, inArgCount())), - ConstArguments(arguments(TheInstruction, 0), - arguments(TheInstruction, constArgCount())), - OutArguments(arguments(TheInstruction, 0), - arguments(TheInstruction, outArgCount())) - { } - - PTCOpcode opcode() const { - return TheInstruction->opc; - } - - std::string helperName() const { - assert(IsCall); - PTCHelperDef *Helper = ptc_find_helper(&ptc, ConstArguments[0]); - assert(Helper != nullptr && Helper->name != nullptr); - return std::string(Helper->name); - } - -private: - PTCInstruction* TheInstruction; - -public: - const Range> InArguments; - const Range> ConstArguments; - const Range> OutArguments; - -private: - unsigned inArgCount() const; - unsigned constArgCount() const; - unsigned outArgCount() const; -}; - -using Instruction = InstructionImpl; -using CallInstruction = InstructionImpl; - -template<> -inline unsigned CallInstruction::inArgCount() const { - return ptc_call_instruction_in_arg_count(&ptc, TheInstruction); -} - -template<> -inline unsigned Instruction::inArgCount() const { - return ptc_instruction_in_arg_count(&ptc, TheInstruction); -} - -template<> -inline unsigned CallInstruction::constArgCount() const { - return ptc_call_instruction_const_arg_count(&ptc, TheInstruction); -} - -template<> -inline unsigned Instruction::constArgCount() const { - return ptc_instruction_const_arg_count(&ptc, TheInstruction); -} - -template<> -inline unsigned CallInstruction::outArgCount() const { - return ptc_call_instruction_out_arg_count(&ptc, TheInstruction); -} - -template<> -inline unsigned Instruction::outArgCount() const { - return ptc_instruction_out_arg_count(&ptc, TheInstruction); -} - -} - -/// Boring code to get the text of the metadata with the specified kind -/// associated to the given instruction -static MDString *getMD(const Instruction *Instruction, - unsigned Kind) { - assert(Instruction != nullptr); - - Metadata *MD = Instruction->getMetadata(Kind); - - if (MD == nullptr) - return nullptr; - - auto Node = dyn_cast(MD); - - assert(Node != nullptr); - - const MDOperand& Operand = Node->getOperand(0); - - Metadata *MDOperand = Operand.get(); - - if (MDOperand == nullptr) - return nullptr; - - auto *String = dyn_cast(MDOperand); - assert(String != nullptr); - - return String; -} - -/// AssemblyAnnotationWriter implementation inserting in the generated LLVM IR -/// comments containing the original assembly and the PTC. It can also decorate -/// the IR with debug information (i.e. DILocations) refered to the generated -/// LLVM IR itself. -class DebugAnnotationWriter : public AssemblyAnnotationWriter { -public: - DebugAnnotationWriter(LLVMContext& Context, - Metadata *Scope, - bool DebugInfo) : Context(Context), - Scope(Scope), - DebugInfo(DebugInfo) { - OriginalInstrMDKind = Context.getMDKindID("oi"); - PTCInstrMDKind = Context.getMDKindID("pi"); - DbgMDKind = Context.getMDKindID("dbg"); - } - - virtual void emitInstructionAnnot(const Instruction *TheInstruction, - formatted_raw_ostream &Output) { - - writeMetadataIfNew(TheInstruction, OriginalInstrMDKind, Output, "\n\n ; "); - writeMetadataIfNew(TheInstruction, PTCInstrMDKind, Output, "\n ; "); - - if (DebugInfo) { - // If DebugInfo is activated the generated LLVM IR textual representation - // will contain some reference to dangling pointers. So ignore the output - // stream if you're using the annotator to generate debug info about the - // IR itself. - assert(Scope != nullptr); - - // Flushing is required to have correct line and column numbers - Output.flush(); - auto *Location = DILocation::get(Context, - Output.getLine() + 1, - Output.getColumn(), - Scope); - - // Sorry Bjarne - auto *NonConstInstruction = const_cast(TheInstruction); - NonConstInstruction->setMetadata(DbgMDKind, Location); - } - } - -private: - /// Writes the text contained in the metadata with the specified kind ID to - /// the output stream, unless that metadata is exactly the same as in the - /// previous instruction. - static void writeMetadataIfNew(const Instruction *TheInstruction, - unsigned MDKind, - formatted_raw_ostream &Output, - StringRef Prefix) { - MDString *MD = getMD(TheInstruction, MDKind); - if (MD != nullptr) { - const Instruction *PrevInstruction = nullptr; - - if (TheInstruction != TheInstruction->getParent()->begin()) - PrevInstruction = TheInstruction->getPrevNode(); - - if (PrevInstruction == nullptr || getMD(PrevInstruction, MDKind) != MD) - Output << Prefix << MD->getString(); - - } - } - -private: - LLVMContext &Context; - Metadata *Scope; - unsigned OriginalInstrMDKind; - unsigned PTCInstrMDKind; - unsigned DbgMDKind; - bool DebugInfo; -}; - -/// \brief Maintains the list of variables required by PTC. -/// -/// It can be queried for a variable, which, if not already existing, will be -/// created on the fly. -class VariableManager { -public: - VariableManager(Module& TheModule, - StructType *CPUStateType, - const DataLayout *HelpersModuleLayout) : - TheModule(TheModule), - Builder(TheModule.getContext()), - CPUStateType(CPUStateType), - HelpersModuleLayout(HelpersModuleLayout), - Env(nullptr) { } - - /// Given a PTC temporary identifier, checks if it already exists in the - /// generatd LLVM IR, and, if not, it creates it. - /// - /// \param TemporaryId the PTC temporary identifier. - /// - /// \return an Value wrapping the request global or local variable. - // TODO: rename to getByTemporaryId - Value *getOrCreate(unsigned int TemporaryId); - - GlobalVariable *getByCPUStateOffset(intptr_t Offset, std::string Name); - - /// Informs the VariableManager that a new function has begun, so it can - /// discard function- and basic block-level variables. - /// - /// \param Delimiter the new point where to insert allocations for local - /// variables. - /// \param Instructions the new PTCInstructionList to use from now on. - void newFunction(Instruction *Delimiter=nullptr, - PTCInstructionList *Instructions=nullptr) { - LocalTemporaries.clear(); - newBasicBlock(Delimiter, Instructions); - } - - /// Informs the VariableManager that a new basic block has begun, so it can - /// discard basic block-level variables. - /// - /// \param Delimiter the new point where to insert allocations for local - /// variables. - /// \param Instructions the new PTCInstructionList to use from now on. - void newBasicBlock(Instruction *Delimiter=nullptr, - PTCInstructionList *Instructions=nullptr) { - Temporaries.clear(); - if (Instructions != nullptr) - this->Instructions = Instructions; - - if (Delimiter != nullptr) - Builder.SetInsertPoint(Delimiter); - } - - void newBasicBlock(BasicBlock *Delimiter, - PTCInstructionList *Instructions=nullptr) { - Temporaries.clear(); - if (Instructions != nullptr) - this->Instructions = Instructions; - - if (Delimiter != nullptr) - Builder.SetInsertPoint(Delimiter); - } - - bool isEnv(Value *TheValue) { - auto *Load = dyn_cast(TheValue); - if (Load != nullptr) - return Load->getPointerOperand() == Env; - - return TheValue == Env; - } - -private: - Module& TheModule; - IRBuilder<> Builder; - using TemporariesMap = std::map; - using GlobalsMap = std::map; - GlobalsMap CPUStateGlobals; - GlobalsMap OtherGlobals; - TemporariesMap Temporaries; - TemporariesMap LocalTemporaries; - PTCInstructionList *Instructions; - - StructType *CPUStateType; - const DataLayout *HelpersModuleLayout; - - Value *Env; -}; - -static Type *getTypeAtOffset(const DataLayout *TheLayout, - StructType *TheStruct, - intptr_t Offset) { - const StructLayout *Layout = TheLayout->getStructLayout(TheStruct); - unsigned FieldIndex = Layout->getElementContainingOffset(Offset); - uint64_t FieldOffset = Layout->getElementOffset(FieldIndex); - - Type *VariableType = TheStruct->getTypeAtIndex(FieldIndex); - - if (VariableType->isIntegerTy()) - return VariableType; - else if (VariableType->isArrayTy()) - return VariableType->getArrayElementType(); - else if (VariableType->isStructTy()) - return getTypeAtOffset(TheLayout, - dyn_cast(VariableType), - Offset - FieldOffset); - else - llvm_unreachable("Unexpected data type"); -} - -GlobalVariable* VariableManager::getByCPUStateOffset(intptr_t Offset, - std::string Name="") { - - GlobalsMap::iterator it = CPUStateGlobals.find(Offset); - if (it != CPUStateGlobals.end()) { - // TODO: handle renaming - return it->second; - } else { - Type *VariableType = getTypeAtOffset(HelpersModuleLayout, - CPUStateType, - Offset); - - if (Name.size() == 0) { - std::stringstream NameStream; - NameStream << "state_0x" << std::hex << Offset; - Name = NameStream.str(); - } - - auto *NewVariable = new GlobalVariable(TheModule, - VariableType, - false, - GlobalValue::ExternalLinkage, - ConstantInt::get(VariableType, 0), - Name); - assert(NewVariable != nullptr); - CPUStateGlobals[Offset] = NewVariable; - - return NewVariable; - } - -} - -Value* VariableManager::getOrCreate(unsigned int TemporaryId) { - assert(Instructions != nullptr); - - PTCTemp *Temporary = ptc_temp_get(Instructions, TemporaryId); - Type *VariableType = Temporary->type == PTC_TYPE_I32 ? - Builder.getInt32Ty() : Builder.getInt64Ty(); - - if (ptc_temp_is_global(Instructions, TemporaryId)) { - // Basically we use fixed_reg to detect "env" - if (Temporary->fixed_reg == 0) { - return getByCPUStateOffset(Temporary->mem_offset, - StringRef(Temporary->name)); - } else { - GlobalsMap::iterator it = OtherGlobals.find(TemporaryId); - if (it != OtherGlobals.end()) { - return it->second; - } else { - auto InitialValue = ConstantInt::get(VariableType, 0); - GlobalVariable *Result = new GlobalVariable(TheModule, - VariableType, - false, - GlobalValue::ExternalLinkage, - InitialValue, - StringRef(Temporary->name)); - - if (Result->getName() == "env") - Env = Result; - - OtherGlobals[TemporaryId] = Result; - return Result; - } - } - } else if (Temporary->temp_local) { - TemporariesMap::iterator it = LocalTemporaries.find(TemporaryId); - if (it != LocalTemporaries.end()) { - return it->second; - } else { - AllocaInst *NewTemporary = Builder.CreateAlloca(VariableType); - LocalTemporaries[TemporaryId] = NewTemporary; - return NewTemporary; - } - } else { - TemporariesMap::iterator it = Temporaries.find(TemporaryId); - if (it != Temporaries.end()) { - return it->second; - } else { - AllocaInst *NewTemporary = Builder.CreateAlloca(VariableType); - Temporaries[TemporaryId] = NewTemporary; - return NewTemporary; - } - } -} - -/// Converts a PTC condition into an LLVM predicate -/// -/// \param Condition the input PTC condition. -/// -/// \return the corresponding LLVM predicate. -static CmpInst::Predicate conditionToPredicate(PTCCondition Condition) { - switch (Condition) { - case PTC_COND_NEVER: - // TODO: this is probably wrong - return CmpInst::FCMP_FALSE; - case PTC_COND_ALWAYS: - // TODO: this is probably wrong - return CmpInst::FCMP_TRUE; - case PTC_COND_EQ: - return CmpInst::ICMP_EQ; - case PTC_COND_NE: - return CmpInst::ICMP_NE; - case PTC_COND_LT: - return CmpInst::ICMP_SLT; - case PTC_COND_GE: - return CmpInst::ICMP_SGE; - case PTC_COND_LE: - return CmpInst::ICMP_SLE; - case PTC_COND_GT: - return CmpInst::ICMP_SGT; - case PTC_COND_LTU: - return CmpInst::ICMP_ULT; - case PTC_COND_GEU: - return CmpInst::ICMP_UGE; - case PTC_COND_LEU: - return CmpInst::ICMP_ULE; - case PTC_COND_GTU: - return CmpInst::ICMP_UGT; - default: - llvm_unreachable("Unknown comparison operator"); - } -} - -/// Obtains the LLVM binary operation corresponding to the specified PTC opcode. -/// -/// \param Opcode the PTC opcode. -/// -/// \return the LLVM binary operation matching opcode. -static Instruction::BinaryOps opcodeToBinaryOp(PTCOpcode Opcode) { - switch (Opcode) { - case PTC_INSTRUCTION_op_add_i32: - case PTC_INSTRUCTION_op_add_i64: - case PTC_INSTRUCTION_op_add2_i32: - case PTC_INSTRUCTION_op_add2_i64: - return Instruction::Add; - case PTC_INSTRUCTION_op_sub_i32: - case PTC_INSTRUCTION_op_sub_i64: - case PTC_INSTRUCTION_op_sub2_i32: - case PTC_INSTRUCTION_op_sub2_i64: - return Instruction::Sub; - case PTC_INSTRUCTION_op_mul_i32: - case PTC_INSTRUCTION_op_mul_i64: - return Instruction::Mul; - case PTC_INSTRUCTION_op_div_i32: - case PTC_INSTRUCTION_op_div_i64: - return Instruction::SDiv; - case PTC_INSTRUCTION_op_divu_i32: - case PTC_INSTRUCTION_op_divu_i64: - return Instruction::UDiv; - case PTC_INSTRUCTION_op_rem_i32: - case PTC_INSTRUCTION_op_rem_i64: - return Instruction::SRem; - case PTC_INSTRUCTION_op_remu_i32: - case PTC_INSTRUCTION_op_remu_i64: - return Instruction::URem; - case PTC_INSTRUCTION_op_and_i32: - case PTC_INSTRUCTION_op_and_i64: - return Instruction::And; - case PTC_INSTRUCTION_op_or_i32: - case PTC_INSTRUCTION_op_or_i64: - return Instruction::Or; - case PTC_INSTRUCTION_op_xor_i32: - case PTC_INSTRUCTION_op_xor_i64: - return Instruction::Xor; - case PTC_INSTRUCTION_op_shl_i32: - case PTC_INSTRUCTION_op_shl_i64: - return Instruction::Shl; - case PTC_INSTRUCTION_op_shr_i32: - case PTC_INSTRUCTION_op_shr_i64: - return Instruction::LShr; - case PTC_INSTRUCTION_op_sar_i32: - case PTC_INSTRUCTION_op_sar_i64: - return Instruction::AShr; - default: - llvm_unreachable("PTC opcode is not a binary operator"); - } -} - -/// Returns the maximum value which can be represented with the specified number -/// of bits. -static uint64_t getMaxValue(unsigned Bits) { - if (Bits == 32) - return 0xffffffff; - else if (Bits == 64) - return 0xffffffffffffffff; - else - llvm_unreachable("Not the number of bits in a integer type"); -} - -/// Maps an opcode the corresponding input and output register size. -/// -/// \return the size, in bits, of the registers used by the opcode. -static unsigned getRegisterSize(unsigned Opcode) { - switch (Opcode) { - case PTC_INSTRUCTION_op_add2_i32: - case PTC_INSTRUCTION_op_add_i32: - case PTC_INSTRUCTION_op_andc_i32: - case PTC_INSTRUCTION_op_and_i32: - case PTC_INSTRUCTION_op_brcond2_i32: - case PTC_INSTRUCTION_op_brcond_i32: - case PTC_INSTRUCTION_op_bswap16_i32: - case PTC_INSTRUCTION_op_bswap32_i32: - case PTC_INSTRUCTION_op_deposit_i32: - case PTC_INSTRUCTION_op_div2_i32: - case PTC_INSTRUCTION_op_div_i32: - case PTC_INSTRUCTION_op_divu2_i32: - case PTC_INSTRUCTION_op_divu_i32: - case PTC_INSTRUCTION_op_eqv_i32: - case PTC_INSTRUCTION_op_ext16s_i32: - case PTC_INSTRUCTION_op_ext16u_i32: - case PTC_INSTRUCTION_op_ext8s_i32: - case PTC_INSTRUCTION_op_ext8u_i32: - case PTC_INSTRUCTION_op_ld16s_i32: - case PTC_INSTRUCTION_op_ld16u_i32: - case PTC_INSTRUCTION_op_ld8s_i32: - case PTC_INSTRUCTION_op_ld8u_i32: - case PTC_INSTRUCTION_op_ld_i32: - case PTC_INSTRUCTION_op_movcond_i32: - case PTC_INSTRUCTION_op_mov_i32: - case PTC_INSTRUCTION_op_movi_i32: - case PTC_INSTRUCTION_op_mul_i32: - case PTC_INSTRUCTION_op_muls2_i32: - case PTC_INSTRUCTION_op_mulsh_i32: - case PTC_INSTRUCTION_op_mulu2_i32: - case PTC_INSTRUCTION_op_muluh_i32: - case PTC_INSTRUCTION_op_nand_i32: - case PTC_INSTRUCTION_op_neg_i32: - case PTC_INSTRUCTION_op_nor_i32: - case PTC_INSTRUCTION_op_not_i32: - case PTC_INSTRUCTION_op_orc_i32: - case PTC_INSTRUCTION_op_or_i32: - case PTC_INSTRUCTION_op_qemu_ld_i32: - case PTC_INSTRUCTION_op_qemu_st_i32: - case PTC_INSTRUCTION_op_rem_i32: - case PTC_INSTRUCTION_op_remu_i32: - case PTC_INSTRUCTION_op_rotl_i32: - case PTC_INSTRUCTION_op_rotr_i32: - case PTC_INSTRUCTION_op_sar_i32: - case PTC_INSTRUCTION_op_setcond2_i32: - case PTC_INSTRUCTION_op_setcond_i32: - case PTC_INSTRUCTION_op_shl_i32: - case PTC_INSTRUCTION_op_shr_i32: - case PTC_INSTRUCTION_op_st16_i32: - case PTC_INSTRUCTION_op_st8_i32: - case PTC_INSTRUCTION_op_st_i32: - case PTC_INSTRUCTION_op_sub2_i32: - case PTC_INSTRUCTION_op_sub_i32: - case PTC_INSTRUCTION_op_trunc_shr_i32: - case PTC_INSTRUCTION_op_xor_i32: - return 32; - case PTC_INSTRUCTION_op_add2_i64: - case PTC_INSTRUCTION_op_add_i64: - case PTC_INSTRUCTION_op_andc_i64: - case PTC_INSTRUCTION_op_and_i64: - case PTC_INSTRUCTION_op_brcond_i64: - case PTC_INSTRUCTION_op_bswap16_i64: - case PTC_INSTRUCTION_op_bswap32_i64: - case PTC_INSTRUCTION_op_bswap64_i64: - case PTC_INSTRUCTION_op_deposit_i64: - case PTC_INSTRUCTION_op_div2_i64: - case PTC_INSTRUCTION_op_div_i64: - case PTC_INSTRUCTION_op_divu2_i64: - case PTC_INSTRUCTION_op_divu_i64: - case PTC_INSTRUCTION_op_eqv_i64: - case PTC_INSTRUCTION_op_ext16s_i64: - case PTC_INSTRUCTION_op_ext16u_i64: - case PTC_INSTRUCTION_op_ext32s_i64: - case PTC_INSTRUCTION_op_ext32u_i64: - case PTC_INSTRUCTION_op_ext8s_i64: - case PTC_INSTRUCTION_op_ext8u_i64: - case PTC_INSTRUCTION_op_ld16s_i64: - case PTC_INSTRUCTION_op_ld16u_i64: - case PTC_INSTRUCTION_op_ld32s_i64: - case PTC_INSTRUCTION_op_ld32u_i64: - case PTC_INSTRUCTION_op_ld8s_i64: - case PTC_INSTRUCTION_op_ld8u_i64: - case PTC_INSTRUCTION_op_ld_i64: - case PTC_INSTRUCTION_op_movcond_i64: - case PTC_INSTRUCTION_op_mov_i64: - case PTC_INSTRUCTION_op_movi_i64: - case PTC_INSTRUCTION_op_mul_i64: - case PTC_INSTRUCTION_op_muls2_i64: - case PTC_INSTRUCTION_op_mulsh_i64: - case PTC_INSTRUCTION_op_mulu2_i64: - case PTC_INSTRUCTION_op_muluh_i64: - case PTC_INSTRUCTION_op_nand_i64: - case PTC_INSTRUCTION_op_neg_i64: - case PTC_INSTRUCTION_op_nor_i64: - case PTC_INSTRUCTION_op_not_i64: - case PTC_INSTRUCTION_op_orc_i64: - case PTC_INSTRUCTION_op_or_i64: - case PTC_INSTRUCTION_op_qemu_ld_i64: - case PTC_INSTRUCTION_op_qemu_st_i64: - case PTC_INSTRUCTION_op_rem_i64: - case PTC_INSTRUCTION_op_remu_i64: - case PTC_INSTRUCTION_op_rotl_i64: - case PTC_INSTRUCTION_op_rotr_i64: - case PTC_INSTRUCTION_op_sar_i64: - case PTC_INSTRUCTION_op_setcond_i64: - case PTC_INSTRUCTION_op_shl_i64: - case PTC_INSTRUCTION_op_shr_i64: - case PTC_INSTRUCTION_op_st16_i64: - case PTC_INSTRUCTION_op_st32_i64: - case PTC_INSTRUCTION_op_st8_i64: - case PTC_INSTRUCTION_op_st_i64: - case PTC_INSTRUCTION_op_sub2_i64: - case PTC_INSTRUCTION_op_sub_i64: - case PTC_INSTRUCTION_op_xor_i64: - return 64; - case PTC_INSTRUCTION_op_br: - case PTC_INSTRUCTION_op_call: - case PTC_INSTRUCTION_op_debug_insn_start: - case PTC_INSTRUCTION_op_discard: - case PTC_INSTRUCTION_op_exit_tb: - case PTC_INSTRUCTION_op_goto_tb: - case PTC_INSTRUCTION_op_set_label: - return 0; - default: - llvm_unreachable("Unexpected opcode"); - break; - } -} - -/// Create a compare instruction given a comparison operator and the operands -/// -/// \param Builder the builder to use to create the instruction. -/// \param RawCondition the PTC condition. -/// \param FirstOperand the first operand of the comparison. -/// \param SecondOperand the second operand of the comparison. -/// -/// \return a compare instruction. -template -static Value *CreateICmp(T& Builder, - uint64_t RawCondition, - Value *FirstOperand, - Value *SecondOperand) { - PTCCondition Condition = static_cast(RawCondition); - return Builder.CreateICmp(conditionToPredicate(Condition), - FirstOperand, - SecondOperand); -} - -class JumpTargetManager { -public: - using BlockWithAddress = std::pair; - static const BlockWithAddress NoMoreTargets; - -public: - JumpTargetManager(Module& TheModule, - Value *PCReg, - Function *TheFunction) : - TheModule(TheModule), - Context(TheModule.getContext()), - TheFunction(TheFunction), - OriginalInstructionAddresses(), - JumpTargets(), - PCReg(PCReg) { } - - /// Handle a new program counter. We might already have a basic block for that - /// program counter, or we could even have a translation for it. Return one - /// of these, if appropriate. - /// - /// \param PC the new program counter. - /// \param ShouldContinue an out parameter indicating whether the returned - /// basic block was just a placeholder or actually contains a - /// translation. - /// - /// \return the basic block to use from now on, or null if the program counter - /// is not associated to a basic block. - BasicBlock *newPC(uint64_t PC, bool& ShouldContinue) { - // Did we already meet this PC? - auto It = JumpTargets.find(PC); - if (It != JumpTargets.end()) { - // If it was planned to explore it in the future, just to do it now - for (auto It = Unexplored.begin(); It != Unexplored.end(); It++) { - if (It->first == PC) { - Unexplored.erase(It, It + 1); - ShouldContinue = true; - assert(It->second->empty()); - return It->second; - } - } - - // It wasn't planned to visit it, so we've already been there, just jump - // there - assert(!It->second->empty()); - ShouldContinue = false; - return It->second; - } - - // We don't know anything about this PC - return nullptr; - } - - /// Save the PC-Instruction association for future use (jump target) - void registerInstruction(uint64_t PC, Instruction *Instruction) { - // Never save twice a PC - assert(OriginalInstructionAddresses.find(PC) == - OriginalInstructionAddresses.end()); - OriginalInstructionAddresses[PC] = Instruction; - } - - /// Save the PC-BasicBlock association for futur use (jump target) - void registerBlock(uint64_t PC, BasicBlock *Block) { - // If we already met it, it must point to the same block - auto It = JumpTargets.find(PC); - assert(It == JumpTargets.end() || It->second == Block); - if (It->second != Block) - JumpTargets[PC] = Block; - } - - void translateIndirectJumps() { - BasicBlock *Dispatcher = createDispatcher(TheFunction, PCReg, true); - - for (Use& PCUse : PCReg->uses()) { - if (PCUse.getOperandNo() == 1) { - if (auto Jump = dyn_cast(PCUse.getUser())) { - BasicBlock::iterator It(Jump); - auto *Branch = BranchInst::Create(Dispatcher, ++It); - - // Cleanup everything it's aftewards - BasicBlock *Parent = Jump->getParent(); - Instruction *ToDelete = &*(--Parent->end()); - while (ToDelete != Branch) { - if (auto DeadBranch = dyn_cast(ToDelete)) - purgeBranch(DeadBranch); - else - ToDelete->eraseFromParent(); - - ToDelete = &*(--Parent->end()); - } - } - } - } - } - - Value *PC() { - return PCReg; - } - - /// Pop from the list of program counters to explore - /// - /// \return a pair containing the PC and the initial block to use, or - /// JumpTarget::NoMoreTargets if we're done. - BlockWithAddress peekJumpTarget() { - if (Unexplored.empty()) - return NoMoreTargets; - else { - BlockWithAddress Result = Unexplored.back(); - Unexplored.pop_back(); - return Result; - } - } - - /// Get or create a block for the given PC - BasicBlock *getBlockAt(uint64_t PC) { - // Do we already have a BasicBlock for this PC? - BlockMap::iterator TargetIt = JumpTargets.find(PC); - if (TargetIt != JumpTargets.end()) { - // Case 1: there's already a BasicBlock for that address, return it - return TargetIt->second; - } - - // Did we already meet this PC (i.e. do we know what's the associated - // instruction)? - BasicBlock *NewBlock = nullptr; - InstructionMap::iterator InstrIt = OriginalInstructionAddresses.find(PC); - if (InstrIt != OriginalInstructionAddresses.end()) { - // Case 2: the address has already been met, but needs to be promoted to - // BasicBlock level. - BasicBlock *ContainingBlock = InstrIt->second->getParent(); - if (InstrIt->second == &*ContainingBlock->begin()) - NewBlock = ContainingBlock; - else { - assert(InstrIt->second != nullptr && - InstrIt->second != ContainingBlock->end()); - // Split the block in the appropriate position. Note that - // OriginalInstructionAddresses stores a reference to the last generated - // instruction for the previous instruction. - Instruction *Next = InstrIt->second->getNextNode(); - NewBlock = ContainingBlock->splitBasicBlock(Next); - } - } else { - // Case 3: the address has never been met, create a temporary one, - // register it for future exploration and return it - NewBlock = BasicBlock::Create(Context, "", TheFunction); - Unexplored.push_back(BlockWithAddress(PC, NewBlock)); - } - - // Associate the PC with the chosen basic block - JumpTargets[PC] = NewBlock; - return NewBlock; - } - -private: - // TODO: instead of a gigantic switch case we could map the original memory - // area and write the address of the translated basic block at the jump - // target - BasicBlock *createDispatcher(Function *OutputFunction, - Value *SwitchOnPtr, - bool JumpDirectly) { - IRBuilder<> Builder(Context); - - // Create the first block of the function - BasicBlock *Entry = BasicBlock::Create(Context, "", OutputFunction); - - // The default case of the switch statement it's an unhandled cases - auto *Default = BasicBlock::Create(Context, "", OutputFunction); - Builder.SetInsertPoint(Default); - Builder.CreateUnreachable(); - - // Switch on the first argument of the function - Builder.SetInsertPoint(Entry); - Value *SwitchOn = Builder.CreateLoad(SwitchOnPtr); - SwitchInst *Switch = Builder.CreateSwitch(SwitchOn, Default); - auto *SwitchOnType = cast(SwitchOn->getType()); - - { - // We consider a jump to NULL as a program end - auto *NullBlock = BasicBlock::Create(Context, "", OutputFunction); - Switch->addCase(ConstantInt::get(SwitchOnType, 0), NullBlock); - Builder.SetInsertPoint(NullBlock); - Builder.CreateRetVoid(); - } - - // Create a case for each jump target we saw so far - for (auto& Pair : JumpTargets) { - // Create a case for the address associated to the current block - auto *Block = BasicBlock::Create(Context, "", OutputFunction); - Switch->addCase(ConstantInt::get(SwitchOnType, Pair.first), Block); - - Builder.SetInsertPoint(Block); - if (JumpDirectly) { - // Assume we're injecting the switch case directly into the function - // the blocks are in, so we can jump to the target block directly - assert(Pair.second->getParent() == OutputFunction); - Builder.CreateBr(Pair.second); - } else { - // Return the address of the current block - Builder.CreateRet(BlockAddress::get(OutputFunction, Pair.second)); - } - } - - return Entry; - } - -private: - using BlockMap = std::map; - using InstructionMap = std::map; - - Module &TheModule; - LLVMContext& Context; - Function* TheFunction; - /// Holds the association between a PC and the last generated instruction for - /// the previous instruction. - InstructionMap OriginalInstructionAddresses; - /// Holds the association between a PC and a BasicBlock. - BlockMap JumpTargets; - /// Queue of program counters we still have to translate. - std::vector Unexplored; - Value *PCReg; -}; - -const JumpTargetManager::BlockWithAddress JumpTargetManager::NoMoreTargets = - JumpTargetManager::BlockWithAddress(0, nullptr); - -class TranslateDirectBranchesPass : public FunctionPass { -public: - static char ID; - - TranslateDirectBranchesPass() : FunctionPass(ID), - JTM(nullptr), - NewPCMarker(nullptr) { } - - TranslateDirectBranchesPass(JumpTargetManager *JTM, - Function *NewPCMarker) : - FunctionPass(ID), - JTM(JTM), - NewPCMarker(NewPCMarker) { } - - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - } - - bool runOnFunction(Function &F) override { - LLVMContext &Context = F.getParent()->getContext(); - - for (Use& PCUse : JTM->PC()->uses()) { - // TODO: what to do in case of read of the PC? - // Is the PC the store destination? - if (PCUse.getOperandNo() == 1) { - if (auto Jump = dyn_cast(PCUse.getUser())) { - Value *Destination = Jump->getValueOperand(); - - // Is destination a constant? - if (auto Address = dyn_cast(Destination)) { - // If necessary notify the about the existence of the basic block - // coming after this jump - // TODO: handle delay slots - BasicBlock *FakeFallthrough = JTM->getBlockAt(getNextPC(Jump)); - - // Compute the actual PC and get the associated BasicBlock - uint64_t TargetPC = Address->getSExtValue(); - BasicBlock *TargetBlock = JTM->getBlockAt(TargetPC); - - // Use a conditional branch here, even if the condition is always - // true. This way the "fallthrough" basic block is always reachable - // and the dominator tree computation works properly even if the - // dispatcher switch has not been emitted yet - Instruction *Branch = BranchInst::Create(TargetBlock, - FakeFallthrough, - ConstantInt::getTrue(Context)); - - // Cleanup of what's afterwards (only a unconditional jump is - // allowed) - BasicBlock::iterator I = Jump; - BasicBlock::iterator BlockEnd = Jump->getParent()->end(); - if (++I != BlockEnd) - purgeBranch(I); - - Branch->insertAfter(Jump); - Jump->eraseFromParent(); - } - } else - llvm_unreachable("Unknown instruction using the PC"); - } else - llvm_unreachable("Unhandled usage of the PC"); - } - - return true; - } - -private: - uint64_t getNextPC(Instruction *TheInstruction) { - DominatorTree& DT = getAnalysis().getDomTree(); - - BasicBlock *Block = TheInstruction->getParent(); - BasicBlock::iterator It(TheInstruction); - - while (true) { - BasicBlock::iterator Begin(Block->begin()); - - // Go back towards the beginning of the basic block looking for a call to - // NewPCMarker - CallInst *Marker = nullptr; - for (; It != Begin; It--) - if ((Marker = dyn_cast(&*It))) - if (Marker->getCalledFunction() == NewPCMarker) { - uint64_t PC = getConst(Marker->getArgOperand(0)); - uint64_t Size = getConst(Marker->getArgOperand(1)); - assert(Size != 0); - return PC + Size; - } - - auto *Node = DT.getNode(Block); - assert(Node != nullptr); - - Block = Node->getIDom()->getBlock(); - It = Block->end(); - } - - llvm_unreachable("Can't find the PC marker"); - } - -private: - Value *PCReg; - JumpTargetManager *JTM; - Function *NewPCMarker; -}; - -char TranslateDirectBranchesPass::ID = 0; -static RegisterPass X("hello", "Hello World Pass", false, false); - -/// Handle all the debug-related operations of code generation -class DebugHelper { -public: - DebugHelper(std::string Output, - std::string Debug, - Module *TheModule, - DebugInfoType Type) : - OutputPath(Output), - DebugPath(Debug), - Builder(*TheModule), - Type(Type), - TheModule(TheModule) - { - OriginalInstrMDKind = TheModule->getContext().getMDKindID("oi"); - PTCInstrMDKind = TheModule->getContext().getMDKindID("pi"); - DbgMDKind = TheModule->getContext().getMDKindID("dbg"); - - // Generate automatically the name of the source file for debugging - if (DebugPath.empty()) { - if (Type == DebugInfoType::PTC) - DebugPath = OutputPath + ".ptc"; - else if (Type == DebugInfoType::OriginalAssembly) - DebugPath = OutputPath + ".S"; - else if (Type == DebugInfoType::LLVMIR) - DebugPath = OutputPath; - } - - if (Type != DebugInfoType::None) { - CompileUnit = Builder.createCompileUnit(dwarf::DW_LANG_C, - DebugPath, - "", - "revamb", - false, - "", - 0 /* Runtime version */); - - // Add the current debug info version into the module. - TheModule->addModuleFlag(Module::Warning, "Debug Info Version", - DEBUG_METADATA_VERSION); - TheModule->addModuleFlag(Module::Warning, "Dwarf Version", 2); - } - } - - /// \brief Handle a new function - /// - /// Generates the debug information for the given function and caches it for - /// future use. - void newFunction(Function *Function) { - if (Type != DebugInfoType::None) { - DISubroutineType *EmptyType = nullptr; - EmptyType = Builder.createSubroutineType(Builder.getOrCreateTypeArray({})); - - CurrentFunction = Function; - CurrentSubprogram = Builder.createFunction(CompileUnit, /* Scope */ - Function->getName(), - StringRef(), /* Linkage name */ - CompileUnit->getFile(), - 1, /* Line */ - EmptyType, /* Subroutine type */ - false, /* isLocalToUnit */ - true, /* isDefinition */ - 1, /* ScopeLine */ - DINode::FlagPrototyped, - false, /* isOptimized */ - CurrentFunction /* Function */); - } - } - - /// Decorates the current function with the request debug info - void generateDebugInfo() { - switch (Type) { - case DebugInfoType::PTC: - case DebugInfoType::OriginalAssembly: - { - assert(CurrentSubprogram != nullptr && CurrentFunction != nullptr); - - // Generate the source file and the debugging information in tandem - - unsigned LineIndex = 1; - unsigned MetadataKind = Type == DebugInfoType::PTC ? - PTCInstrMDKind : OriginalInstrMDKind; - - MDString *Last = nullptr; - std::ofstream Source(DebugPath); - for (BasicBlock& Block : *CurrentFunction) { - for (Instruction& Instruction : Block) { - MDString *Body = getMD(&Instruction, MetadataKind); - - if (Body != nullptr && Last != Body) { - Last = Body; - std::string BodyString = Body->getString().str(); - - Source << BodyString; - - auto *Location = DILocation::get(TheModule->getContext(), - LineIndex, - 0, - CurrentSubprogram); - Instruction.setMetadata(DbgMDKind, Location); - LineIndex += std::count(BodyString.begin(), - BodyString.end(), - '\n'); - } - } - } - - Builder.finalize(); - break; - } - case DebugInfoType::LLVMIR: - { - // Use the annotator to obtain line and column of the textual LLVM IR - // for each instruction. Discard the output since it will contain - // errors, regenerating it later will give a correct result. - Builder.finalize(); - - raw_null_ostream NullStream; - TheModule->print(NullStream, annotator(true /* DebugInfo */)); - - std::ofstream Output(DebugPath); - raw_os_ostream Stream(Output); - TheModule->print(Stream, annotator(false)); - - break; - } - default: - break; - } - - } - - void print(std::ostream& Output, bool DebugInfo) { - raw_os_ostream OutputStream(Output); - TheModule->print(OutputStream, annotator(DebugInfo)); - } - - /// Copy the debug file to the output path, if they are the same - bool copySource() { - // If debug info refer to LLVM IR, just copy the output file - if (Type == DebugInfoType::LLVMIR && DebugPath != OutputPath) { - std::ifstream Source(DebugPath, std::ios::binary); - std::ofstream Destination(OutputPath, std::ios::binary); - - Destination << Source.rdbuf(); - - return true; - } - - return false; - } - -private: - /// Create a new AssemblyAnnotationWriter - /// - /// \param DebugInfo whether to decorate the IR with debug information or not - DebugAnnotationWriter *annotator(bool DebugInfo) { - Annotator.reset(new DebugAnnotationWriter(TheModule->getContext(), - CurrentSubprogram, - DebugInfo)); - return Annotator.get(); - } - -private: - std::string OutputPath; - std::string DebugPath; - DIBuilder Builder; - DebugInfoType Type; - Module *TheModule; - DICompileUnit *CompileUnit; - DISubprogram *CurrentSubprogram; - Function *CurrentFunction; - std::unique_ptr Annotator; - - unsigned OriginalInstrMDKind; - unsigned PTCInstrMDKind; - unsigned DbgMDKind; -}; - -// Outline the destructor for the sake of privacy in the header -CodeGenerator::~CodeGenerator() = default; - -static bool startsWith(std::string String, std::string Prefix) { - return String.substr(0, Prefix.size()) == Prefix; -} - -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)), - CPUStateType(nullptr), - HelpersModuleLayout(nullptr) -{ - OriginalInstrMDKind = Context.getMDKindID("oi"); - PTCInstrMDKind = Context.getMDKindID("pi"); - DbgMDKind = Context.getMDKindID("dbg"); - - SMDiagnostic Errors; - HelpersModule = parseIRFile(Helpers, Errors, Context); - - using ElectionMap = std::map; - using ElectionMapElement = std::pair; - ElectionMap EnvElection; - const std::string HelperPrefix = "helper_"; - for (Function& HelperFunction : *HelpersModule) { - if (startsWith(HelperFunction.getName(), HelperPrefix) - && HelperFunction.getFunctionType()->getNumParams() > 1) { - - for (Type *Candidate : HelperFunction.getFunctionType()->params()) { - if (Candidate->isPointerTy()) { - auto *PointeeType = Candidate->getPointerElementType(); - auto *EnvType = dyn_cast(PointeeType); - // Ensure it is a struct and not a union - if (EnvType != nullptr && EnvType->getNumElements() > 1) { - - auto It = EnvElection.find(EnvType); - if (It != EnvElection.end()) - EnvElection[EnvType]++; - else - EnvElection[EnvType] = 1; - } - } - } - } - } - - assert(EnvElection.size() > 0); - - CPUStateType = std::max_element(EnvElection.begin(), - EnvElection.end(), - [] (ElectionMapElement& It1, - ElectionMapElement& It2) { - return It1.second < It2.second; - })->first; - - HelpersModuleLayout = &HelpersModule->getDataLayout(); -} - -class InstructionTranslator { -public: - InstructionTranslator(IRBuilder<>& Builder, - VariableManager& Variables, - JumpTargetManager& JumpTargets, - std::map& LabeledBasicBlocks, - std::vector Blocks, - Module& TheModule, - Function *TheFunction, - Architecture& SourceArchitecture, - Architecture& TargetArchitecture) : - Builder(Builder), - Variables(Variables), - JumpTargets(JumpTargets), - LabeledBasicBlocks(LabeledBasicBlocks), - Blocks(Blocks), - TheModule(TheModule), - TheFunction(TheFunction), - SourceArchitecture(SourceArchitecture), - TargetArchitecture(TargetArchitecture), - NewPCMarker(nullptr), - LastMarker(nullptr) { - - auto &Context = TheModule.getContext(); - NewPCMarker = Function::Create(FunctionType::get(Type::getVoidTy(Context), - { - Type::getInt64Ty(Context), - Type::getInt64Ty(Context) - }, - false), - GlobalValue::ExternalLinkage, - "newpc", - &TheModule); - } - - TranslateDirectBranchesPass *createTranslateDirectBranchesPass() { - return new TranslateDirectBranchesPass(&JumpTargets, NewPCMarker); - } - - std::pair newInstruction(PTCInstruction *Instr, - bool IsFirst); - void translate(PTCInstruction *Instr); - void translateCall(PTCInstruction *Instr); - - void removeNewPCMarkers() { - - std::vector ToDelete; - - for (User *Call : NewPCMarker->users()) - if (cast(Call)->getParent() != nullptr) - ToDelete.push_back(cast(Call)); - - for (Instruction *TheInstruction : ToDelete) - TheInstruction->eraseFromParent(); - - NewPCMarker->eraseFromParent(); - } - - void closeLastInstruction(uint64_t PC) { - assert(LastMarker != nullptr); - - auto *Operand = cast(LastMarker->getArgOperand(0)); - uint64_t StartPC = Operand->getLimitedValue(); - - assert(PC > StartPC); - LastMarker->setArgOperand(1, Builder.getInt64(PC - StartPC)); - - LastMarker = nullptr; - } - -private: - std::vector translateOpcode(PTCOpcode Opcode, - std::vector ConstArguments, - std::vector InArguments); -private: - IRBuilder<>& Builder; - VariableManager& Variables; - JumpTargetManager& JumpTargets; - std::map& LabeledBasicBlocks; - std::vector Blocks; - Module& TheModule; - - Function *TheFunction; - - Architecture& SourceArchitecture; - Architecture& TargetArchitecture; - - Function *NewPCMarker; - CallInst *LastMarker; -}; - -std::pair -InstructionTranslator::newInstruction(PTCInstruction *Instr, - bool IsFirst) { - const PTC::Instruction TheInstruction(Instr); - // A new original instruction, let's create a new metadata node - // referencing it for all the next instructions to come - uint64_t PC = TheInstruction.ConstArguments[0]; - - // TODO: replace using a field in Architecture - if (TheInstruction.ConstArguments.size() > 1) - PC |= TheInstruction.ConstArguments[1] << 32; - - std::stringstream OriginalStringStream; - disassembleOriginal(OriginalStringStream, PC); - std::string OriginalString = OriginalStringStream.str(); - LLVMContext& Context = TheModule.getContext(); - MDString *MDOriginalString = MDString::get(Context, OriginalString); - MDNode *MDOriginalInstr = MDNode::getDistinct(Context, MDOriginalString); - - if (!IsFirst) { - // Check if this PC already has a block and use it - bool ShouldContinue; - BasicBlock *DivergeTo = JumpTargets.newPC(PC, ShouldContinue); - if (DivergeTo != nullptr) { - Builder.CreateBr(DivergeTo); - - if (ShouldContinue) { - // The block is empty, let's fill it - Blocks.push_back(DivergeTo); - Builder.SetInsertPoint(DivergeTo); - Variables.newBasicBlock(); - } else { - // The block contains already translated code, early exit - return { true, MDOriginalInstr }; - } - } - } - - if (LastMarker != nullptr) - closeLastInstruction(PC); - LastMarker = Builder.CreateCall(NewPCMarker, - { Builder.getInt64(PC), Builder.getInt64(0) }); - - if (!IsFirst) { - // Inform the JumpTargetManager about the new PC we met - BasicBlock::iterator CurrentIt = Builder.GetInsertPoint(); - if (CurrentIt == Builder.GetInsertBlock()->begin()) - JumpTargets.registerBlock(PC, Builder.GetInsertBlock()); - else - JumpTargets.registerInstruction(PC, LastMarker); - } - - return { false, MDOriginalInstr }; -} - -void InstructionTranslator::translateCall(PTCInstruction *Instr) { - const PTC::CallInstruction TheCall(Instr); - - auto LoadArgs = [this] (uint64_t TemporaryId) -> Value * { - return Builder.CreateLoad(Variables.getOrCreate(TemporaryId)); - }; - - auto GetValueType = [] (Value *Argument) { return Argument->getType(); }; - - std::vector InArgs = (TheCall.InArguments | LoadArgs).toVector(); - std::vector InArgsType = (InArgs | GetValueType).toVector(); - - // TODO: handle multiple return arguments - assert(TheCall.OutArguments.size() <= 1); - - Value *ResultDestination = nullptr; - Type *ResultType = nullptr; - - if (TheCall.OutArguments.size() != 0) { - ResultDestination = Variables.getOrCreate(TheCall.OutArguments[0]); - ResultType = ResultDestination->getType()->getPointerElementType(); - } else { - ResultType = Builder.getVoidTy(); - } - - auto *CalleeType = FunctionType::get(ResultType, - ArrayRef(InArgsType), - false); - - std::string HelperName = "helper_" + TheCall.helperName(); - Constant *FunctionDeclaration = TheModule.getOrInsertFunction(HelperName, - CalleeType); - Value *Result = Builder.CreateCall(FunctionDeclaration, InArgs); - - if (TheCall.OutArguments.size() != 0) - Builder.CreateStore(Result, ResultDestination); -} - -void InstructionTranslator::translate(PTCInstruction *Instr) { - const PTC::Instruction TheInstruction(Instr); - - auto LoadArgs = [this] (uint64_t TemporaryId) -> Value * { - return Builder.CreateLoad(Variables.getOrCreate(TemporaryId)); - }; - - auto ConstArgs = TheInstruction.ConstArguments; - auto InArgs = TheInstruction.InArguments | LoadArgs; - - std::vector Result = translateOpcode(TheInstruction.opcode(), - ConstArgs.toVector(), - InArgs.toVector()); - - assert(Result.size() == (size_t) TheInstruction.OutArguments.size()); - // TODO: use ZipIterator here - for (unsigned I = 0; I < Result.size(); I++) - Builder.CreateStore(Result[I], - Variables.getOrCreate(TheInstruction.OutArguments[I])); -} - -std::vector -InstructionTranslator::translateOpcode(PTCOpcode Opcode, - std::vector ConstArguments, - std::vector InArguments) { - LLVMContext& Context = TheModule.getContext(); - unsigned RegisterSize = getRegisterSize(Opcode); - Type *RegisterType = nullptr; - if (RegisterSize == 32) - RegisterType = Builder.getInt32Ty(); - else if (RegisterSize == 64) - RegisterType = Builder.getInt64Ty(); - else if (RegisterSize != 0) - llvm_unreachable("Unexpected register size"); - - switch (Opcode) { - case PTC_INSTRUCTION_op_movi_i32: - case PTC_INSTRUCTION_op_movi_i64: - return { ConstantInt::get(RegisterType, ConstArguments[0]) }; - case PTC_INSTRUCTION_op_discard: - // Let's overwrite the discarded temporary with a 0 - return { ConstantInt::get(RegisterType, 0) }; - case PTC_INSTRUCTION_op_mov_i32: - case PTC_INSTRUCTION_op_mov_i64: - return { Builder.CreateTrunc(InArguments[0], RegisterType) }; - case PTC_INSTRUCTION_op_setcond_i32: - case PTC_INSTRUCTION_op_setcond_i64: - { - Value *Compare = CreateICmp(Builder, - ConstArguments[0], - InArguments[0], - InArguments[1]); - // TODO: convert single-bit registers to i1 - return { Builder.CreateZExt(Compare, RegisterType) }; - } - case PTC_INSTRUCTION_op_movcond_i32: // Resist the fallthrough temptation - case PTC_INSTRUCTION_op_movcond_i64: - { - Value *Compare = CreateICmp(Builder, - ConstArguments[0], - InArguments[0], - InArguments[1]); - Value *Select = Builder.CreateSelect(Compare, - InArguments[2], - InArguments[3]); - return { Select }; - } - case PTC_INSTRUCTION_op_qemu_ld_i32: - case PTC_INSTRUCTION_op_qemu_ld_i64: - case PTC_INSTRUCTION_op_qemu_st_i32: - case PTC_INSTRUCTION_op_qemu_st_i64: - { - PTCLoadStoreArg MemoryAccess; - MemoryAccess = ptc.parse_load_store_arg(ConstArguments[0]); - - // What are we supposed to do in this case? - assert(MemoryAccess.access_type != PTC_MEMORY_ACCESS_UNKNOWN); - - unsigned AccessAlignment = 0; - if (MemoryAccess.access_type == PTC_MEMORY_ACCESS_UNALIGNED) - AccessAlignment = 1; - else - AccessAlignment = SourceArchitecture.defaultAlignment(); - - // Load size - IntegerType *MemoryType = nullptr; - switch (ptc_get_memory_access_size(MemoryAccess.type)) { - case PTC_MO_8: - MemoryType = Builder.getInt8Ty(); - break; - case PTC_MO_16: - MemoryType = Builder.getInt16Ty(); - break; - case PTC_MO_32: - MemoryType = Builder.getInt32Ty(); - break; - case PTC_MO_64: - MemoryType = Builder.getInt64Ty(); - break; - default: - llvm_unreachable("Unexpected load size"); - } - - bool SignExtend = ptc_is_sign_extended_load(MemoryAccess.type); - - // // TODO: handle 64 on 32 - // // TODO: handle endianess mismatch - // assert(SourceArchitecture.endianess() == - // TargetArchitecture.endianess() && - // "Different endianess between the source and the target is not " - // "supported yet"); - - Value *Pointer = nullptr; - if (Opcode == PTC_INSTRUCTION_op_qemu_ld_i32 || - Opcode == PTC_INSTRUCTION_op_qemu_ld_i64) { - - Pointer = Builder.CreateIntToPtr(InArguments[0], - MemoryType->getPointerTo()); - Value *Load = Builder.CreateAlignedLoad(Pointer, - AccessAlignment); - - if (SignExtend) - return { Builder.CreateSExt(Load, RegisterType) }; - else - return { Builder.CreateZExt(Load, RegisterType) }; - - } else if (Opcode == PTC_INSTRUCTION_op_qemu_st_i32 || - Opcode == PTC_INSTRUCTION_op_qemu_st_i64) { - - Pointer = Builder.CreateIntToPtr(InArguments[1], - MemoryType->getPointerTo()); - Value *Value = Builder.CreateTrunc(InArguments[0], MemoryType); - Builder.CreateAlignedStore(Value, Pointer, AccessAlignment); - - return { }; - } else - llvm_unreachable("Unknown load type"); - } - case PTC_INSTRUCTION_op_ld8u_i32: - case PTC_INSTRUCTION_op_ld8s_i32: - case PTC_INSTRUCTION_op_ld16u_i32: - case PTC_INSTRUCTION_op_ld16s_i32: - case PTC_INSTRUCTION_op_ld_i32: - case PTC_INSTRUCTION_op_ld8u_i64: - case PTC_INSTRUCTION_op_ld8s_i64: - case PTC_INSTRUCTION_op_ld16u_i64: - case PTC_INSTRUCTION_op_ld16s_i64: - case PTC_INSTRUCTION_op_ld32u_i64: - case PTC_INSTRUCTION_op_ld32s_i64: - case PTC_INSTRUCTION_op_ld_i64: - { - Value *Base = dyn_cast(InArguments[0])->getPointerOperand(); - assert(Base != nullptr && Variables.isEnv(Base)); - Value *Target = Variables.getByCPUStateOffset(ConstArguments[0]); - - Value *EnvField = Builder.CreateLoad(Target); - Value *Fitted = Builder.CreateZExtOrTrunc(EnvField, RegisterType); - - return { Fitted }; - } - case PTC_INSTRUCTION_op_st8_i32: - case PTC_INSTRUCTION_op_st16_i32: - case PTC_INSTRUCTION_op_st_i32: - case PTC_INSTRUCTION_op_st8_i64: - case PTC_INSTRUCTION_op_st16_i64: - case PTC_INSTRUCTION_op_st32_i64: - case PTC_INSTRUCTION_op_st_i64: - { - Value *Base = dyn_cast(InArguments[1])->getPointerOperand(); - assert(Base != nullptr && Variables.isEnv(Base)); - Value *Target = Variables.getByCPUStateOffset(ConstArguments[0]); - Value *ToStore = Builder.CreateZExt(InArguments[0], Target->getType()->getPointerElementType()); - Builder.CreateStore(ToStore, Target); - return { }; - } - case PTC_INSTRUCTION_op_add_i32: - case PTC_INSTRUCTION_op_sub_i32: - case PTC_INSTRUCTION_op_mul_i32: - case PTC_INSTRUCTION_op_div_i32: - case PTC_INSTRUCTION_op_divu_i32: - case PTC_INSTRUCTION_op_rem_i32: - case PTC_INSTRUCTION_op_remu_i32: - case PTC_INSTRUCTION_op_and_i32: - case PTC_INSTRUCTION_op_or_i32: - case PTC_INSTRUCTION_op_xor_i32: - case PTC_INSTRUCTION_op_shl_i32: - case PTC_INSTRUCTION_op_shr_i32: - case PTC_INSTRUCTION_op_sar_i32: - case PTC_INSTRUCTION_op_add_i64: - case PTC_INSTRUCTION_op_sub_i64: - case PTC_INSTRUCTION_op_mul_i64: - case PTC_INSTRUCTION_op_div_i64: - case PTC_INSTRUCTION_op_divu_i64: - case PTC_INSTRUCTION_op_rem_i64: - case PTC_INSTRUCTION_op_remu_i64: - case PTC_INSTRUCTION_op_and_i64: - case PTC_INSTRUCTION_op_or_i64: - case PTC_INSTRUCTION_op_xor_i64: - case PTC_INSTRUCTION_op_shl_i64: - case PTC_INSTRUCTION_op_shr_i64: - case PTC_INSTRUCTION_op_sar_i64: - { - // TODO: assert on sizes? - Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode); - Value *Operation = Builder.CreateBinOp(BinaryOp, - InArguments[0], - InArguments[1]); - return { Operation }; - } - case PTC_INSTRUCTION_op_div2_i32: - case PTC_INSTRUCTION_op_divu2_i32: - case PTC_INSTRUCTION_op_div2_i64: - case PTC_INSTRUCTION_op_divu2_i64: - { - Instruction::BinaryOps DivisionOp, RemainderOp; - - if (Opcode == PTC_INSTRUCTION_op_div2_i32 || - Opcode == PTC_INSTRUCTION_op_div2_i64) { - DivisionOp = Instruction::SDiv; - RemainderOp = Instruction::SRem; - } else if (Opcode == PTC_INSTRUCTION_op_div2_i32 || - Opcode == PTC_INSTRUCTION_op_div2_i64) { - DivisionOp = Instruction::UDiv; - RemainderOp = Instruction::URem; - } else - llvm_unreachable("Unknown operation type"); - - // TODO: we're ignoring InArguments[1], which is the MSB - // TODO: assert on sizes? - Value *Division = Builder.CreateBinOp(DivisionOp, - InArguments[0], - InArguments[2]); - Value *Remainder = Builder.CreateBinOp(RemainderOp, - InArguments[0], - InArguments[2]); - return { Division, Remainder }; - } - case PTC_INSTRUCTION_op_rotr_i32: - case PTC_INSTRUCTION_op_rotr_i64: - case PTC_INSTRUCTION_op_rotl_i32: - case PTC_INSTRUCTION_op_rotl_i64: - { - Value *Bits = ConstantInt::get(RegisterType, RegisterSize); - - Instruction::BinaryOps FirstShiftOp, SecondShiftOp; - if (Opcode == PTC_INSTRUCTION_op_rotl_i32 || - Opcode == PTC_INSTRUCTION_op_rotl_i64) { - FirstShiftOp = Instruction::LShr; - SecondShiftOp = Instruction::Shl; - } else if (Opcode == PTC_INSTRUCTION_op_rotr_i32 || - Opcode == PTC_INSTRUCTION_op_rotr_i64) { - FirstShiftOp = Instruction::Shl; - SecondShiftOp = Instruction::LShr; - } else - llvm_unreachable("Unexpected opcode"); - - Value *FirstShift = Builder.CreateBinOp(FirstShiftOp, - InArguments[0], - InArguments[1]); - Value *SecondShiftAmount = Builder.CreateSub(Bits, - InArguments[1]); - Value *SecondShift = Builder.CreateBinOp(SecondShiftOp, - InArguments[0], - SecondShiftAmount); - - return { Builder.CreateOr(FirstShift, SecondShift) }; - } - case PTC_INSTRUCTION_op_deposit_i32: - case PTC_INSTRUCTION_op_deposit_i64: - { - unsigned Position = ConstArguments[0]; - if (Position == RegisterSize) - return { InArguments[0] }; - - unsigned Length = ConstArguments[1]; - uint64_t Bits = 0; - - // Thou shall not << 32 - if (Length == RegisterSize) - Bits = getMaxValue(RegisterSize); - else - Bits = (1 << Length) - 1; - - // result = (t1 & ~(bits << position)) | ((t2 & bits) << position) - uint64_t BaseMask = ~(Bits << Position); - Value *MaskedBase = Builder.CreateAnd(InArguments[0], BaseMask); - Value *Deposit = Builder.CreateAnd(InArguments[1], Bits); - Value *ShiftedDeposit = Builder.CreateShl(Deposit, Position); - Value *Result = Builder.CreateOr(MaskedBase, ShiftedDeposit); - - return { Result }; - } - case PTC_INSTRUCTION_op_ext8s_i32: - case PTC_INSTRUCTION_op_ext16s_i32: - case PTC_INSTRUCTION_op_ext8u_i32: - case PTC_INSTRUCTION_op_ext16u_i32: - case PTC_INSTRUCTION_op_ext8s_i64: - case PTC_INSTRUCTION_op_ext16s_i64: - case PTC_INSTRUCTION_op_ext32s_i64: - case PTC_INSTRUCTION_op_ext8u_i64: - case PTC_INSTRUCTION_op_ext16u_i64: - case PTC_INSTRUCTION_op_ext32u_i64: - { - Type *SourceType = nullptr; - switch (Opcode) { - case PTC_INSTRUCTION_op_ext8s_i32: - case PTC_INSTRUCTION_op_ext8u_i32: - case PTC_INSTRUCTION_op_ext8s_i64: - case PTC_INSTRUCTION_op_ext8u_i64: - SourceType = Builder.getInt8Ty(); - break; - case PTC_INSTRUCTION_op_ext16s_i32: - case PTC_INSTRUCTION_op_ext16u_i32: - case PTC_INSTRUCTION_op_ext16s_i64: - case PTC_INSTRUCTION_op_ext16u_i64: - SourceType = Builder.getInt16Ty(); - break; - case PTC_INSTRUCTION_op_ext32s_i64: - case PTC_INSTRUCTION_op_ext32u_i64: - SourceType = Builder.getInt32Ty(); - break; - default: - llvm_unreachable("Unexpected opcode"); - } - - Value *Truncated = Builder.CreateTrunc(InArguments[0], - SourceType); - - switch (Opcode) { - case PTC_INSTRUCTION_op_ext8s_i32: - case PTC_INSTRUCTION_op_ext8s_i64: - case PTC_INSTRUCTION_op_ext16s_i32: - case PTC_INSTRUCTION_op_ext16s_i64: - case PTC_INSTRUCTION_op_ext32s_i64: - return { Builder.CreateSExt(Truncated, RegisterType) }; - case PTC_INSTRUCTION_op_ext8u_i32: - case PTC_INSTRUCTION_op_ext8u_i64: - case PTC_INSTRUCTION_op_ext16u_i32: - case PTC_INSTRUCTION_op_ext16u_i64: - case PTC_INSTRUCTION_op_ext32u_i64: - return { Builder.CreateZExt(Truncated, RegisterType) }; - default: - llvm_unreachable("Unexpected opcode"); - } - } - case PTC_INSTRUCTION_op_not_i32: - case PTC_INSTRUCTION_op_not_i64: - return { Builder.CreateXor(InArguments[0], getMaxValue(RegisterSize)) }; - case PTC_INSTRUCTION_op_neg_i32: - case PTC_INSTRUCTION_op_neg_i64: - return { - Builder.CreateSub(ConstantInt::get(RegisterType, 0), - InArguments[0]) - }; - case PTC_INSTRUCTION_op_andc_i32: - case PTC_INSTRUCTION_op_andc_i64: - case PTC_INSTRUCTION_op_orc_i32: - case PTC_INSTRUCTION_op_orc_i64: - case PTC_INSTRUCTION_op_eqv_i32: - case PTC_INSTRUCTION_op_eqv_i64: - { - Instruction::BinaryOps ExternalOp; - switch (Opcode) { - case PTC_INSTRUCTION_op_andc_i32: - case PTC_INSTRUCTION_op_andc_i64: - ExternalOp = Instruction::And; - break; - case PTC_INSTRUCTION_op_orc_i32: - case PTC_INSTRUCTION_op_orc_i64: - ExternalOp = Instruction::Or; - break; - case PTC_INSTRUCTION_op_eqv_i32: - case PTC_INSTRUCTION_op_eqv_i64: - ExternalOp = Instruction::Xor; - break; - default: - llvm_unreachable("Unexpected opcode"); - } - - Value *Negate = Builder.CreateXor(InArguments[1], - getMaxValue(RegisterSize)); - Value *Result = Builder.CreateBinOp(ExternalOp, - InArguments[0], - Negate); - return { Result }; - } - case PTC_INSTRUCTION_op_nand_i32: - case PTC_INSTRUCTION_op_nand_i64: - { - Value *AndValue = Builder.CreateAnd(InArguments[0], - InArguments[1]); - Value *Result = Builder.CreateXor(AndValue, - getMaxValue(RegisterSize)); - return { Result }; - } - case PTC_INSTRUCTION_op_nor_i32: - case PTC_INSTRUCTION_op_nor_i64: - { - Value *OrValue = Builder.CreateOr(InArguments[0], - InArguments[1]); - Value *Result = Builder.CreateXor(OrValue, - getMaxValue(RegisterSize)); - return { Result }; - } - case PTC_INSTRUCTION_op_bswap16_i32: - case PTC_INSTRUCTION_op_bswap32_i32: - case PTC_INSTRUCTION_op_bswap16_i64: - case PTC_INSTRUCTION_op_bswap32_i64: - case PTC_INSTRUCTION_op_bswap64_i64: - { - Type *SwapType = nullptr; - switch (Opcode) { - case PTC_INSTRUCTION_op_bswap16_i32: - case PTC_INSTRUCTION_op_bswap16_i64: - SwapType = Builder.getInt16Ty(); - case PTC_INSTRUCTION_op_bswap32_i32: - case PTC_INSTRUCTION_op_bswap32_i64: - SwapType = Builder.getInt32Ty(); - case PTC_INSTRUCTION_op_bswap64_i64: - SwapType = Builder.getInt64Ty(); - default: - llvm_unreachable("Unexpected opcode"); - } - - Value *Truncated = Builder.CreateTrunc(InArguments[0], SwapType); - - std::vector BSwapParameters { RegisterType }; - Function *BSwapFunction = Intrinsic::getDeclaration(&TheModule, - Intrinsic::bswap, - BSwapParameters); - Value *Swapped = Builder.CreateCall(BSwapFunction, Truncated); - - return { Builder.CreateZExt(Swapped, RegisterType) }; - } - case PTC_INSTRUCTION_op_set_label: - { - unsigned LabelId = ptc.get_arg_label_id(ConstArguments[0]); - std::string Label = "L" + std::to_string(LabelId); - - BasicBlock *Fallthrough = nullptr; - auto ExistingBasicBlock = LabeledBasicBlocks.find(Label); - - if (ExistingBasicBlock == LabeledBasicBlocks.end()) { - Fallthrough = BasicBlock::Create(Context, Label, TheFunction); - LabeledBasicBlocks[Label] = Fallthrough; - } else { - // A basic block with that label already exist - Fallthrough = LabeledBasicBlocks[Label]; - - // Ensure it's empty - assert(Fallthrough->begin() == Fallthrough->end()); - - // Move it to the bottom - Fallthrough->removeFromParent(); - TheFunction->getBasicBlockList().push_back(Fallthrough); - } - - Builder.CreateBr(Fallthrough); - - Blocks.push_back(Fallthrough); - Builder.SetInsertPoint(Fallthrough); - Variables.newBasicBlock(); - - return { }; - } - case PTC_INSTRUCTION_op_br: - case PTC_INSTRUCTION_op_brcond_i32: - case PTC_INSTRUCTION_op_brcond2_i32: - case PTC_INSTRUCTION_op_brcond_i64: - { - // We take the last constant arguments, which is the LabelId both in - // conditional and unconditional jumps - unsigned LabelId = ptc.get_arg_label_id(ConstArguments.back()); - std::string Label = "L" + std::to_string(LabelId); - - BasicBlock *Fallthrough = BasicBlock::Create(Context, - "", - TheFunction); - - // Look for a matching label - BasicBlock *Target = nullptr; - auto ExistingBasicBlock = LabeledBasicBlocks.find(Label); - - // No matching label, create a temporary block - if (ExistingBasicBlock == LabeledBasicBlocks.end()) { - Target = BasicBlock::Create(Context, - Label, - TheFunction); - LabeledBasicBlocks[Label] = Target; - } else - Target = LabeledBasicBlocks[Label]; - - if (Opcode == PTC_INSTRUCTION_op_br) { - // Unconditional jump - Builder.CreateBr(Target); - } else if (Opcode == PTC_INSTRUCTION_op_brcond_i32 || - Opcode == PTC_INSTRUCTION_op_brcond_i64) { - // Conditional jump - Value *Compare = CreateICmp(Builder, - ConstArguments[0], - InArguments[0], - InArguments[1]); - Builder.CreateCondBr(Compare, Target, Fallthrough); - } else - llvm_unreachable("Unhandled opcode"); - - Blocks.push_back(Fallthrough); - Builder.SetInsertPoint(Fallthrough); - Variables.newBasicBlock(); - - return { }; - } - case PTC_INSTRUCTION_op_call: - // TODO: implement call to helpers - llvm_unreachable("Call to helpers not implemented"); - case PTC_INSTRUCTION_op_exit_tb: - case PTC_INSTRUCTION_op_goto_tb: - // Nothing to do here - return { }; - case PTC_INSTRUCTION_op_add2_i32: - case PTC_INSTRUCTION_op_sub2_i32: - case PTC_INSTRUCTION_op_add2_i64: - case PTC_INSTRUCTION_op_sub2_i64: - { - Value *FirstOperandLow = nullptr; - Value *FirstOperandHigh = nullptr; - Value *SecondOperandLow = nullptr; - Value *SecondOperandHigh = nullptr; - - IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); - - FirstOperandLow = Builder.CreateSExt(InArguments[0], DestinationType); - FirstOperandHigh = Builder.CreateSExt(InArguments[1], DestinationType); - SecondOperandLow = Builder.CreateSExt(InArguments[2], DestinationType); - SecondOperandHigh = Builder.CreateSExt(InArguments[3], - DestinationType); - - FirstOperandHigh = Builder.CreateShl(FirstOperandHigh, RegisterSize); - SecondOperandHigh = Builder.CreateShl(SecondOperandHigh, RegisterSize); - - Value *FirstOperand = Builder.CreateOr(FirstOperandHigh, - FirstOperandLow); - Value *SecondOperand = Builder.CreateOr(SecondOperandHigh, - SecondOperandLow); - - Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode); - - Value *Result = Builder.CreateBinOp(BinaryOp, - FirstOperand, - SecondOperand); - - Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); - Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); - Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, - RegisterType); - - return { ResultLow, ResultHigh }; - } - case PTC_INSTRUCTION_op_mulu2_i32: - case PTC_INSTRUCTION_op_mulu2_i64: - case PTC_INSTRUCTION_op_muls2_i32: - case PTC_INSTRUCTION_op_muls2_i64: - { - IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); - - Value *FirstOperand = nullptr; - Value *SecondOperand = nullptr; - - if (Opcode == PTC_INSTRUCTION_op_muls2_i32 - || Opcode == PTC_INSTRUCTION_op_muls2_i64) { - FirstOperand = Builder.CreateZExt(InArguments[0], DestinationType); - SecondOperand = Builder.CreateZExt(InArguments[1], DestinationType); - } else if (Opcode == PTC_INSTRUCTION_op_muls2_i32 - || Opcode == PTC_INSTRUCTION_op_muls2_i64) { - FirstOperand = Builder.CreateSExt(InArguments[0], DestinationType); - SecondOperand = Builder.CreateSExt(InArguments[1], DestinationType); - } else - llvm_unreachable("Unexpected opcode"); - - Value *Result = Builder.CreateMul(FirstOperand, SecondOperand); - - Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); - Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); - Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, - RegisterType); - - return { ResultLow, ResultHigh }; - } - case PTC_INSTRUCTION_op_muluh_i32: - case PTC_INSTRUCTION_op_mulsh_i32: - case PTC_INSTRUCTION_op_muluh_i64: - case PTC_INSTRUCTION_op_mulsh_i64: - - case PTC_INSTRUCTION_op_setcond2_i32: - - case PTC_INSTRUCTION_op_trunc_shr_i32: - llvm_unreachable("Instruction not implemented"); - default: - llvm_unreachable("Unknown opcode"); - } -} - -void CodeGenerator::translate(size_t LoadAddress, - ArrayRef 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, - CPUStateType, - HelpersModuleLayout); - - GlobalVariable *PCReg = Variables.getByCPUStateOffset(ptc.get_pc(), "pc"); - - JumpTargetManager JumpTargets(*TheModule, PCReg, MainFunction); - std::map LabeledBasicBlocks; - std::vector 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; - - // Skip everything is before the first PTC_INSTRUCTION_op_debug_insn_start - while (j < InstructionList->instruction_count && - InstructionList->instructions[j].opc != - PTC_INSTRUCTION_op_debug_insn_start) { - j++; - } - - assert(j < InstructionList->instruction_count); - - 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 - - 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); - } -} diff --git a/revamb.h b/revamb.h index c2232df8c..c105140fa 100644 --- a/revamb.h +++ b/revamb.h @@ -1,6 +1,7 @@ #ifndef _REVAMB_H #define _REVAMB_H +// Standard includes #include // Path to the QEMU libraries should be given by the build system diff --git a/transformadapter.h b/transformadapter.h index 784d35284..06e3039f7 100644 --- a/transformadapter.h +++ b/transformadapter.h @@ -1,8 +1,10 @@ #ifndef _TRANSFORMADAPTER_H #define _TRANSFORMADAPTER_H +// Standard includes #include +// Local includes #include "range.h" #include "iteratorwrapper.h" @@ -90,5 +92,4 @@ auto operator|(C Input, R Transformer) { // return Transformer.transform(make_range(Input)); // } - #endif // _TRANSFORMADAPTER_H diff --git a/variablemanager.cpp b/variablemanager.cpp new file mode 100644 index 000000000..38845d91c --- /dev/null +++ b/variablemanager.cpp @@ -0,0 +1,176 @@ +/// \file +/// \brief This file handles the creation and management of global variables, +/// i.e. mainly parts of the CPU state + +// Standard includes +#include +#include +#include + +// LLVM includes +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Casting.h" + +// Local includes +#include "variablemanager.h" +#include "ptcdump.h" + +using namespace llvm; + +static Type *getTypeAtOffset(const DataLayout *TheLayout, + StructType *TheStruct, + intptr_t Offset) { + const StructLayout *Layout = TheLayout->getStructLayout(TheStruct); + unsigned FieldIndex = Layout->getElementContainingOffset(Offset); + uint64_t FieldOffset = Layout->getElementOffset(FieldIndex); + + Type *VariableType = TheStruct->getTypeAtIndex(FieldIndex); + + if (VariableType->isIntegerTy()) + return VariableType; + else if (VariableType->isArrayTy()) + return VariableType->getArrayElementType(); + else if (VariableType->isStructTy()) + return getTypeAtOffset(TheLayout, + dyn_cast(VariableType), + Offset - FieldOffset); + else + llvm_unreachable("Unexpected data type"); +} + +VariableManager::VariableManager(Module& TheModule, + StructType *CPUStateType, + const DataLayout *HelpersModuleLayout) : + TheModule(TheModule), + Builder(TheModule.getContext()), + CPUStateType(CPUStateType), + HelpersModuleLayout(HelpersModuleLayout), + Env(nullptr) { } + +void VariableManager::newFunction(Instruction *Delimiter, + PTCInstructionList *Instructions) { + LocalTemporaries.clear(); + newBasicBlock(Delimiter, Instructions); +} + +/// Informs the VariableManager that a new basic block has begun, so it can +/// discard basic block-level variables. +/// +/// \param Delimiter the new point where to insert allocations for local +/// variables. +/// \param Instructions the new PTCInstructionList to use from now on. +void VariableManager::newBasicBlock(Instruction *Delimiter, + PTCInstructionList *Instructions) { + Temporaries.clear(); + if (Instructions != nullptr) + this->Instructions = Instructions; + + if (Delimiter != nullptr) + Builder.SetInsertPoint(Delimiter); +} + +void VariableManager::newBasicBlock(BasicBlock *Delimiter, + PTCInstructionList *Instructions) { + Temporaries.clear(); + if (Instructions != nullptr) + this->Instructions = Instructions; + + if (Delimiter != nullptr) + Builder.SetInsertPoint(Delimiter); +} + +bool VariableManager::isEnv(Value *TheValue) { + auto *Load = dyn_cast(TheValue); + if (Load != nullptr) + return Load->getPointerOperand() == Env; + + return TheValue == Env; +} + +GlobalVariable* VariableManager::getByCPUStateOffset(intptr_t Offset, + std::string Name) { + + GlobalsMap::iterator it = CPUStateGlobals.find(Offset); + if (it != CPUStateGlobals.end()) { + // TODO: handle renaming + return it->second; + } else { + Type *VariableType = getTypeAtOffset(HelpersModuleLayout, + CPUStateType, + Offset); + + if (Name.size() == 0) { + std::stringstream NameStream; + NameStream << "state_0x" << std::hex << Offset; + Name = NameStream.str(); + } + + auto *NewVariable = new GlobalVariable(TheModule, + VariableType, + false, + GlobalValue::ExternalLinkage, + ConstantInt::get(VariableType, 0), + Name); + assert(NewVariable != nullptr); + CPUStateGlobals[Offset] = NewVariable; + + return NewVariable; + } + +} + +Value* VariableManager::getOrCreate(unsigned int TemporaryId) { + assert(Instructions != nullptr); + + PTCTemp *Temporary = ptc_temp_get(Instructions, TemporaryId); + Type *VariableType = Temporary->type == PTC_TYPE_I32 ? + Builder.getInt32Ty() : Builder.getInt64Ty(); + + if (ptc_temp_is_global(Instructions, TemporaryId)) { + // Basically we use fixed_reg to detect "env" + if (Temporary->fixed_reg == 0) { + return getByCPUStateOffset(Temporary->mem_offset, + StringRef(Temporary->name)); + } else { + GlobalsMap::iterator it = OtherGlobals.find(TemporaryId); + if (it != OtherGlobals.end()) { + return it->second; + } else { + auto InitialValue = ConstantInt::get(VariableType, 0); + GlobalVariable *Result = new GlobalVariable(TheModule, + VariableType, + false, + GlobalValue::ExternalLinkage, + InitialValue, + StringRef(Temporary->name)); + + if (Result->getName() == "env") + Env = Result; + + OtherGlobals[TemporaryId] = Result; + return Result; + } + } + } else if (Temporary->temp_local) { + TemporariesMap::iterator it = LocalTemporaries.find(TemporaryId); + if (it != LocalTemporaries.end()) { + return it->second; + } else { + AllocaInst *NewTemporary = Builder.CreateAlloca(VariableType); + LocalTemporaries[TemporaryId] = NewTemporary; + return NewTemporary; + } + } else { + TemporariesMap::iterator it = Temporaries.find(TemporaryId); + if (it != Temporaries.end()) { + return it->second; + } else { + AllocaInst *NewTemporary = Builder.CreateAlloca(VariableType); + Temporaries[TemporaryId] = NewTemporary; + return NewTemporary; + } + } +} diff --git a/variablemanager.h b/variablemanager.h new file mode 100644 index 000000000..d4769dd32 --- /dev/null +++ b/variablemanager.h @@ -0,0 +1,87 @@ +#ifndef _VARIABLEMANAGER_H +#define _VARIABLEMANAGER_H + +// Standard includes +#include +#include +#include + +// LLVM includes +#include "llvm/IR/IRBuilder.h" + +// Local includes +#include "ptcdump.h" + +namespace llvm { +class AllocaInst; +class BasicBlock; +class DataLayout; +class GlobalVariable; +class Module; +class StructType; +class Value; +} + +/// \brief Maintains the list of variables required by PTC. +/// +/// It can be queried for a variable, which, if not already existing, will be +/// created on the fly. +class VariableManager { +public: + VariableManager(llvm::Module& TheModule, + llvm::StructType *CPUStateType, + const llvm::DataLayout *HelpersModuleLayout); + + /// Given a PTC temporary identifier, checks if it already exists in the + /// generatd LLVM IR, and, if not, it creates it. + /// + /// \param TemporaryId the PTC temporary identifier. + /// + /// \return an Value wrapping the request global or local variable. + // TODO: rename to getByTemporaryId + llvm::Value *getOrCreate(unsigned int TemporaryId); + + llvm::GlobalVariable *getByCPUStateOffset(intptr_t Offset, + std::string Name=""); + + /// Informs the VariableManager that a new function has begun, so it can + /// discard function- and basic block-level variables. + /// + /// \param Delimiter the new point where to insert allocations for local + /// variables. + /// \param Instructions the new PTCInstructionList to use from now on. + void newFunction(llvm::Instruction *Delimiter=nullptr, + PTCInstructionList *Instructions=nullptr); + + /// Informs the VariableManager that a new basic block has begun, so it can + /// discard basic block-level variables. + /// + /// \param Delimiter the new point where to insert allocations for local + /// variables. + /// \param Instructions the new PTCInstructionList to use from now on. + void newBasicBlock(llvm::Instruction *Delimiter=nullptr, + PTCInstructionList *Instructions=nullptr); + + void newBasicBlock(llvm::BasicBlock *Delimiter, + PTCInstructionList *Instructions=nullptr); + + bool isEnv(llvm::Value *TheValue); + +private: + llvm::Module& TheModule; + llvm::IRBuilder<> Builder; + using TemporariesMap = std::map; + using GlobalsMap = std::map; + GlobalsMap CPUStateGlobals; + GlobalsMap OtherGlobals; + TemporariesMap Temporaries; + TemporariesMap LocalTemporaries; + PTCInstructionList *Instructions; + + llvm::StructType *CPUStateType; + const llvm::DataLayout *HelpersModuleLayout; + + llvm::Value *Env; +}; + +#endif // _VARIABLEMANAGER_H