/* * (C) 2010 by Computer System Laboratory, IIS, Academia Sinica, Taiwan. * See COPYRIGHT in top-level directory. */ #ifndef __LLVM_OPC_H #define __LLVM_OPC_H #include "llvm/IR/LegacyPassManager.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "qemu-types.h" #include "llvm-types.h" #include "llvm-translator.h" #include "llvm.h" //#define ASSERT //#define VERIFY_TB #define IRDebug(idx) \ do { \ dbg() << DEBUG_ENTRY << "op_" << llvm_op_defs[idx].name << ": " \ << llvm_op_defs[idx].nb_oargs << " " \ << llvm_op_defs[idx].nb_iargs << " " \ << llvm_op_defs[idx].nb_cargs << "\n"; \ } while (0) #define IRError(fmt,args...) hqemu_error(fmt,##args) #ifdef ASSERT #define AssertType(t) \ do { \ if (!(t)) \ hqemu_error("invalid type.\n"); \ } while(0) #else #define AssertType(t) #endif #define IRAbort() \ do { \ if (!LLEnv->isTraceMode()) { \ Func->dump(); \ hqemu_error("fixme.\n"); \ } \ Builder->Abort(); \ } while (0) class LLVMTranslator; class NotifyInfo; class OptimizationInfo; /* Patch flags. * NOTE: patch flags must be synchronized with those in the LLVM backend. */ enum { PATCH_HQEMU = 0x4182U, PATCH_DUMMY, PATCH_EXIT_TB, PATCH_DIRECT_JUMP, PATCH_TRACE_BLOCK_CHAINING, PATCH_QMMU, }; /* * Register is used to describe the pseudo registers used by QEMU TCG op. */ struct Register { /* Status of the register. */ enum { STATE_NONE = 0x0, STATE_REV = 0x1, /* Register is reserved */ STATE_REG = 0x2, /* Register is promoted */ STATE_MEM = 0x4, /* Register is in CPUArchState memory */ STATE_LOC = 0x8, /* Register is a local register */ STATE_TMP = 0x10, /* Register is a tmp register */ }; int State; /* State of the register */ int Base; intptr_t Off; /* Register offset of CPUArchState */ int Size; /* Register size */ std::string Name; /* Name string of this register */ bool Dirty; /* This register is updated or not */ Type *Ty; /* Register type in LLVM */ Value *Data; /* Data value if this regisrer is promoted */ Value *AI; /* Register as Alloca */ Register *Alias; Register() : State(STATE_NONE), Off(-1), Dirty(false), Ty(nullptr), Data(nullptr), AI(nullptr), Alias(nullptr) {} void set(int base, intptr_t off, std::string name) { Base = base; Off = off; Name = name; } void reset(int state, int size, Type *ty) { State = state; Size = size; Ty = ty; Dirty = false; Data = AI = nullptr; } void Promote() { State |= STATE_REG; } void Demote() { State &= ~STATE_REG; } Value *getData() { return Data; } Register &getAlias() { return *Alias; } void setState(int state) { State = state; } void setData(Value *data, bool dirty = false) { if (Alias) { Alias->setData(data, dirty); return; } Data = data; Dirty = dirty; Promote(); } bool isRev() { return State & STATE_REV; } bool isReg() { return State & STATE_REG; } bool isMem() { return State & STATE_MEM; } bool isLocal() { return State & STATE_LOC; } bool isDirty() { return Dirty; } bool isAlias() { return Alias != nullptr; } }; /* * TraceBuilder provides the facilities to build a trace in IRFactory. */ class TraceBuilder { typedef std::map<target_ulong, std::pair<GraphNode*, BasicBlock*> > NodeBuildMap; typedef std::vector<std::pair<BranchInst*, GraphNode*> > BranchList; IRFactory *IF; OptimizationInfo *Opt; GraphNode *CurrNode; /* The current CFG node to process */ NodeBuildMap Nodes; BranchList Branches; NodeVec NodeQueue; /* CFG nodes to be translated */ NodeSet NodeVisisted; NodeVec NodeUsed; bool Aborted; uint32_t Attribute; TraceInfo *Trace; public: TraceBuilder(IRFactory *IRF, OptimizationInfo *Opt); ~TraceBuilder() {} void ConvertToTCGIR(CPUArchState *env); void ConvertToLLVMIR(); void Abort(); void Finalize(); bool isAborted() { return Aborted; } OptimizationInfo *getOpt() { return Opt; } TraceInfo *getTrace() { return Trace; } GraphNode *getEntryNode() { return Opt->getCFG(); } GraphNode *getCurrNode() { return CurrNode; } unsigned getNumNodes() { return Nodes.size(); } std::string getPCString(GraphNode *Node) { std::stringstream ss; ss << std::hex << Node->getGuestPC(); return ss.str(); } GraphNode *getNextNode() { if (NodeQueue.empty()) return nullptr; CurrNode = NodeQueue.back(); NodeQueue.pop_back(); if (NodeVisisted.find(CurrNode) != NodeVisisted.end()) return getNextNode(); NodeVisisted.insert(CurrNode); NodeUsed.push_back(CurrNode); return CurrNode; } target_ulong getGuestPC(GraphNode *Node) { #if defined(TARGET_I386) return Node->getTB()->pc - Node->getTB()->cs_base; #else return Node->getTB()->pc; #endif } void setUniqueNode(GraphNode *Node) { target_ulong gpc = getGuestPC(Node); if (Nodes.find(gpc) == Nodes.end()) Nodes[gpc] = std::make_pair(Node, nullptr); } void setBasicBlock(GraphNode *Node, BasicBlock *BB) { target_ulong gpc = getGuestPC(Node); if (Nodes.find(gpc) == Nodes.end()) hqemu_error("internal error.\n"); Nodes[gpc].second = BB; } void setBranch(BranchInst *BI, GraphNode *Node) { Branches.push_back(std::make_pair(BI, Node)); target_ulong gpc = getGuestPC(Node); if (!Nodes[gpc].second) NodeQueue.push_back(Node); } GraphNode *getNode(target_ulong gpc) { return Nodes.find(gpc) == Nodes.end() ? nullptr : Nodes[gpc].first; } BasicBlock *getBasicBlock(GraphNode *Node) { target_ulong gpc = getGuestPC(Node); if (Nodes.find(gpc) == Nodes.end()) hqemu_error("internal error.\n"); return Nodes[gpc].second; } void addAttribute(uint32_t Attr) { Attribute |= Attr; } }; #define META_CONST "const" #define META_GVA "gva" #define META_LOOP "loop" #define META_EXIT "exit" #define META_CC "cc" class MDFactory { uint32_t UID; LLVMContext &Context; MDNode *Dummy; ConstantInt *getUID() { return ConstantInt::get(IntegerType::get(Context, 32), UID++); } public: MDFactory(Module *M); ~MDFactory(); MDNode *getMDNode(ArrayRef<ConstantInt*> V); DebugLoc getDebugLoc(unsigned Line, unsigned Col, Function *F, ArrayRef<ConstantInt*> Meta); void setConst(Instruction *I) { I->setMetadata(META_CONST, Dummy); } void setGuestMemory(Instruction *I) { I->setMetadata(META_GVA, Dummy); } void setLoop(Instruction *I) { I->setMetadata(META_LOOP, Dummy); } void setExit(Instruction *I) { I->setMetadata(META_EXIT, Dummy); } void setCondition(Instruction *I) { I->setMetadata(META_CC, Dummy); } static bool isConst(Instruction *I) { return I->getMetadata(META_CONST); } static bool isGuestMemory(Instruction *I) { return I->getMetadata(META_GVA); } static bool isLoop(Instruction *I) { return I->getMetadata(META_LOOP); } static bool isExit(Instruction *I) { return I->getMetadata(META_EXIT); } static bool isCondition(Instruction *I) { return I->getMetadata(META_CC); } static void setConstStatic(LLVMContext &Context, Instruction *I, ArrayRef<ConstantInt*> V); }; /* * IRFactory conducts QEMU TCG opcodes to LLVM IR conversion. */ class IRFactory { typedef std::map<std::pair<intptr_t, Type *>, Value *> StatePtrMap; typedef std::map<TCGArg, BasicBlock *> LabelMap; enum { COHERENCE_NONE = 0, COHERENCE_GLOBAL, COHERENCE_ALL, }; bool InitOnce; /* Basic types */ Type *VoidTy; IntegerType *Int8Ty; IntegerType *Int16Ty; IntegerType *Int32Ty; IntegerType *Int64Ty; IntegerType *Int128Ty; IntegerType *IntPtrTy; PointerType *Int8PtrTy; PointerType *Int16PtrTy; PointerType *Int32PtrTy; PointerType *Int64PtrTy; Type *FloatTy; Type *DoubleTy; Type *FP80Ty; Type *FP128Ty; ConstantInt *ExitAddr; LLVMTranslator &Translator; /* Uplink to the LLVMTranslator instance */ LLVMContext *Context; /* Translator local context */ Module *Mod; /* The LLVM module */ ExecutionEngine *EE; /* The JIT compiler */ EventListener *Listener; /* The JIT listener */ JITEventListener *IntelJIT; /* The Intel JIT listener */ const DataLayout *DL; /* Data layout */ TraceBuilder *Builder; MDFactory *MF; MCDisasm *HostDisAsm; HelperMap &Helpers; std::vector<BaseRegister> &BaseReg; /* TCG base register */ std::vector<Register> Reg; /* TCG virtual registers */ LabelMap Labels; /* TCG labels */ int Segment; GuestBaseRegister &GuestBaseReg; /* Reserved guest base register */ Function *Func; /* The container of LLVM IR to be translated */ BasicBlock *InitBB; /* BasicBlock for variable decalaration */ BasicBlock *CurrBB; /* Current BasicBlock to insert LLVM IR */ BasicBlock *ExitBB; /* Temp BasicBlock as the exit-function stub */ BranchInst *LastInst; /* Position to insert LLVM IR */ Instruction *CPU; /* Base register with (char*) type */ Instruction *CPUStruct; /* Base register with (struct CPUArchState*) type */ Instruction *GEPInsertPos; /* Position to insert GEP instruction */ StatePtrMap StatePtr; IVec InlineCalls; /* Helpers to be inlined */ std::map<std::string, BasicBlock*> CommonBB; IVec IndirectBrs; IVec toErase; BBVec toSink; std::set<Function *> ClonedFuncs; bool runPasses; void CreateJIT(); void DeleteJIT(); /* Initialize basic types used during IR conversion. */ void InitializeTypes(); /* Store dirty states back to CPU state in the memory. */ void SaveGlobals(int level, Instruction *InsertPos); /* Sync PC to CPU state in the memory. */ void CreateStorePC(Instruction *InsertPos); /* Get or insert the pointer to the CPU state. */ Value *StatePointer(Register ®); Value *StatePointer(Register ®, intptr_t Off, Type *PTy); /* Load value from the CPU state in the memory. */ Value *LoadState(Register ®); void StoreState(Register ®, Instruction *InsertPos); /* Load/Store data from/to the guest memory. */ Value *QEMULoad(Value *AddrL, Value *AddrH, TCGMemOpIdx oi); void QEMUStore(Value *Data, Value *AddrL, Value *AddrH, TCGMemOpIdx oi); Value *ConvertCPUType(Function *F, int Idx, Instruction *InsertPos); Value *ConvertCPUType(Function *F, int Idx, BasicBlock *InsertPos); Value *ConvertEndian(Value *V, int opc); Value *getExtendValue(Value *V, Type *Ty, int opc); Value *getTruncValue(Value *V, int opc); int getSizeInBits(int opc) { return 8 * (1 << (opc & MO_SIZE)); } Value *ConcatTLBVersion(Value *GVA); /* Return the LLVM instruction that stores PC. For the guest's register * size larger than the host, replace the multiple store-PC instructions * to one single store-PC instruction. */ StoreInst *getStorePC(); /* Create both chaining and exiting stubs. */ void InsertLinkAndExit(Instruction *InsertPos); /* Create exit stub */ void InsertExit(uintptr_t RetVal, bool setExit = false); /* Find the next node of a trace according to the brach pc. * Return null if we cannot find one. */ GraphNode *findNextNode(target_ulong pc); /* Perform internal linking of basic blocks to form a region. */ void TraceLink(StoreInst *SI); /* Link basic blocks of direct branch. */ void TraceLinkDirectJump(GraphNode *NextNode, StoreInst *SI); void TraceLinkDirectJump(StoreInst *SI); /* Link basic blocks of indirect branch. */ void TraceLinkIndirectJump(GraphNode *NextNode, StoreInst *SI); /* Insert code for IBTC hash table lookup. */ void InsertLookupIBTC(GraphNode *CurrNode); /* Insert code for CPBL hash table lookup. */ void InsertLookupCPBL(GraphNode *CurrNode); void TraceValidateCPBL(GraphNode *NextNode, StoreInst *StorePC); /* Insert bswap intrinsic instruction. */ Value *CreateBSwap(Type *Ty, Value *V, Instruction *InsertPos); /* Given the size, return its PointerType. */ PointerType *getPointerTy(int Size, unsigned AS = 0); /* Analyze a helper function to determine if it will be inlined or not. */ int AnalyzeInlineCost(CallSite CS); /* Perform helper function inlining. */ void ProcessInline(); void VerifyFunction(Function &F); /* Legalize LLVM IR before running the pre-defined passes. */ void PreProcess(); void Optimize(); /* Legalize LLVM IR after running the pre-defined passes. */ void PostProcess(); void FinalizeObject(); void InitializeLLVMPasses(legacy::FunctionPassManager *FPM); uint32_t setRestorePoint(TCGMemOpIdx oi) { if (oi != (uint16_t)oi) hqemu_error("key value too large.\n"); return (NI.setRestorePoint() << 16) | oi; } public: typedef void (IRFactory::*FuncPtr)(const TCGArg *); NotifyInfo &NI; /* Info to pass among translator and JIT */ /* QEMU TCG IR to LLVM IR converion routines. */ #define DEF(name, oargs, iargs, cargs, flags) void op_ ## name(const TCGArg *); #include "tcg-opc.h" #undef DEF IRFactory(LLVMTranslator *Trans); ~IRFactory(); void CreateSession(TraceBuilder *builder); void DeleteSession(); /* Prepare the initial LLVM Function, BasicBlocks and variables. */ void CreateFunction(); void CreateBlock(); /* Start LLVM JIT compilation. */ void Compile(); /* Set instruction BI to jump to the basic block BB. */ void setSuccessor(BranchInst *BI, BasicBlock *BB); /* Get function pointer of the IR converion routines. */ void *getOpcFunc(); Function *ResolveFunction(std::string Name); LLVMTranslator &getTranslator() { return Translator; } LLVMContext &getContext() { return *Context; } const DataLayout *getDL() { return DL; } MDFactory *getMDFactory() { return MF; } HelperMap &getHelpers() { return Helpers; } TraceInfo *getTrace() { return Builder->getTrace(); } Value *getGuestBase() { return GuestBaseReg.Base; } Instruction *getDefaultCPU(Function &F); public: static bool isStateOfPC(intptr_t Off); }; #endif /* * vim: ts=8 sts=4 sw=4 expandtab */