From 186435d4560e63295546f3c8261e03e9cb2cd451 Mon Sep 17 00:00:00 2001 From: Alessandro Di Federico Date: Sat, 9 Jan 2016 11:15:29 +0100 Subject: [PATCH] Switch to incremental creation of the dispatcher Before this patch the dispatcher area was created all at once at a final stage, however it's useful also while translating, since it keeps all the code reachable, which is particularly important to be able to build a exhaustive dominator tree. * Create the dispatcher area when a new instance of `JumpTargetManager` is created. * Create a fake conditional branch to the dispatcher at the beginning of the `root` function. * Incrementally build the dispatcher's switch case in `JumpTargetManager::getBlockAt`. --- codegenerator.cpp | 10 ++++++++++ jumptargetmanager.cpp | 41 ++++++++++++++++------------------------- jumptargetmanager.h | 11 ++++++++--- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/codegenerator.cpp b/codegenerator.cpp index b712e55c9..04bb5ac41 100644 --- a/codegenerator.cpp +++ b/codegenerator.cpp @@ -515,6 +515,16 @@ void CodeGenerator::translate(size_t LoadAddress, GlobalVariable *PCReg = Variables.getByEnvOffset(ptc.pc, "pc"); JumpTargetManager JumpTargets(*TheModule, PCReg, MainFunction); + + // Create a new block where the translation will start and emit a tautological + // branch to it, with false part being the dispatcher, which is always + // reachable + Entry = BasicBlock::Create(Context, + "translation-start", + MainFunction); + Builder.CreateCondBr(Builder.getTrue(), Entry, JumpTargets.dispatcher()); + + std::map LabeledBasicBlocks; std::vector Blocks; diff --git a/jumptargetmanager.cpp b/jumptargetmanager.cpp index cf172229f..051bdaf65 100644 --- a/jumptargetmanager.cpp +++ b/jumptargetmanager.cpp @@ -31,11 +31,14 @@ JumpTargetManager::JumpTargetManager(Module& TheModule, OriginalInstructionAddresses(), JumpTargets(), PCReg(PCReg), - ExitTB(nullptr) { + ExitTB(nullptr), + Dispatcher(nullptr), + DispatcherSwitch(nullptr) { FunctionType *ExitTBTy = FunctionType::get(Type::getVoidTy(Context), { }, false); ExitTB = cast(TheModule.getOrInsertFunction("exitTB", ExitTBTy)); + createDispatcher(TheFunction, PCReg, true); } /// Handle a new program counter. We might already have a basic block for that @@ -121,8 +124,6 @@ void JumpTargetManager::translateIndirectJumps() { if (ExitTB->use_empty()) return; - BasicBlock *Dispatcher = createDispatcher(TheFunction, PCReg, true); - auto I = ExitTB->use_begin(); while (I != ExitTB->use_end()) { Use& ExitTBUse = *I++; @@ -203,6 +204,11 @@ BasicBlock *JumpTargetManager::getBlockAt(uint64_t PC) { NewBlock = BasicBlock::Create(Context, Name.str(), TheFunction); Unexplored.push_back(BlockWithAddress(PC, NewBlock)); + + // Create a case for the address associated to the new block + auto *PCRegType = PCReg->getType(); + auto *SwitchType = cast(PCRegType->getPointerElementType()); + DispatcherSwitch->addCase(ConstantInt::get(SwitchType, PC), NewBlock); } // Associate the PC with the chosen basic block @@ -212,9 +218,11 @@ BasicBlock *JumpTargetManager::getBlockAt(uint64_t PC) { // 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) { +// If this function looks weird it's because it has been designed to be able +// to create the dispatcher in the "root" function or in a standalone function +void JumpTargetManager::createDispatcher(Function *OutputFunction, + Value *SwitchOnPtr, + bool JumpDirectly) { IRBuilder<> Builder(Context); // Create the first block of the dispatcher @@ -245,25 +253,8 @@ BasicBlock *JumpTargetManager::createDispatcher(Function *OutputFunction, 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; + Dispatcher = Entry; + DispatcherSwitch = Switch; } const JumpTargetManager::BlockWithAddress JumpTargetManager::NoMoreTargets = diff --git a/jumptargetmanager.h b/jumptargetmanager.h index 518f35938..9958f703e 100644 --- a/jumptargetmanager.h +++ b/jumptargetmanager.h @@ -12,6 +12,7 @@ class Function; class Instruction; class LLVMContext; class Module; +class SwitchInst; class Value; } @@ -69,13 +70,15 @@ public: /// Get or create a block for the given PC llvm::BasicBlock *getBlockAt(uint64_t PC); + llvm::BasicBlock *dispatcher() { return Dispatcher; } + 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); + void createDispatcher(llvm::Function *OutputFunction, + llvm::Value *SwitchOnPtr, + bool JumpDirectly); private: using BlockMap = std::map; @@ -93,6 +96,8 @@ private: std::vector Unexplored; llvm::Value *PCReg; llvm::Function *ExitTB; + llvm::BasicBlock *Dispatcher; + llvm::SwitchInst *DispatcherSwitch; }; #endif // _JUMPTARGETMANAGER_H