BasicBlock.cpp
1 #include "BasicBlock.h" 2 3 #include <iostream> 4 5 #include "preprocessor/llvm_includes_start.h" 6 #include <llvm/IR/CFG.h> 7 #include <llvm/IR/Module.h> 8 #include <llvm/IR/Function.h> 9 #include <llvm/IR/Instructions.h> 10 #include <llvm/IR/IntrinsicInst.h> 11 #include <llvm/IR/IRBuilder.h> 12 #include <llvm/Support/raw_os_ostream.h> 13 #include "preprocessor/llvm_includes_end.h" 14 15 #include "RuntimeManager.h" 16 #include "Type.h" 17 #include "Utils.h" 18 19 namespace dev 20 { 21 namespace eth 22 { 23 namespace jit 24 { 25 26 BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): 27 m_firstInstrIdx{_firstInstrIdx}, 28 m_begin(_begin), 29 m_end(_end), 30 m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {".", std::to_string(_firstInstrIdx)}, _mainFunc)) 31 {} 32 33 LocalStack::LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager): 34 CompilerHelper(_builder) 35 { 36 // Call stack.prepare. min, max, size args will be filled up in finalize(). 37 auto undef = llvm::UndefValue::get(Type::Size); 38 m_sp = m_builder.CreateCall(getStackPrepareFunc(), 39 {_runtimeManager.getStackBase(), _runtimeManager.getStackSize(), undef, undef, undef, _runtimeManager.getJmpBuf()}, 40 {"sp", m_builder.GetInsertBlock()->getName()}); 41 } 42 43 void LocalStack::push(llvm::Value* _value) 44 { 45 assert(_value->getType() == Type::Word); 46 m_local.push_back(_value); 47 m_maxSize = std::max(m_maxSize, size()); 48 } 49 50 llvm::Value* LocalStack::pop() 51 { 52 auto item = get(0); 53 assert(!m_local.empty() || !m_input.empty()); 54 55 if (m_local.size() > 0) 56 m_local.pop_back(); 57 else 58 ++m_globalPops; 59 60 m_minSize = std::min(m_minSize, size()); 61 return item; 62 } 63 64 /// Copies the _index-th element of the local stack and pushes it back on the top. 65 void LocalStack::dup(size_t _index) 66 { 67 auto val = get(_index); 68 push(val); 69 } 70 71 /// Swaps the top element with the _index-th element of the local stack. 72 void LocalStack::swap(size_t _index) 73 { 74 assert(_index > 0); ///< _index must not be 0. 75 auto val = get(_index); 76 auto tos = get(0); 77 set(_index, tos); 78 set(0, val); 79 } 80 81 llvm::Value* LocalStack::get(size_t _index) 82 { 83 if (_index < m_local.size()) 84 return *(m_local.rbegin() + _index); // count from back 85 86 auto idx = _index - m_local.size() + m_globalPops; 87 if (idx >= m_input.size()) 88 m_input.resize(idx + 1); 89 auto& item = m_input[idx]; 90 91 if (!item) 92 { 93 // Fetch an item from global stack 94 ssize_t globalIdx = -static_cast<ssize_t>(idx) - 1; 95 auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); 96 item = m_builder.CreateAlignedLoad(slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. 97 m_minSize = std::min(m_minSize, globalIdx); // remember required stack size 98 } 99 100 return item; 101 } 102 103 void LocalStack::set(size_t _index, llvm::Value* _word) 104 { 105 if (_index < m_local.size()) 106 { 107 *(m_local.rbegin() + _index) = _word; 108 return; 109 } 110 111 auto idx = _index - m_local.size() + m_globalPops; 112 assert(idx < m_input.size()); 113 m_input[idx] = _word; 114 } 115 116 117 void LocalStack::finalize() 118 { 119 m_sp->setArgOperand(2, m_builder.getInt64(minSize())); 120 m_sp->setArgOperand(3, m_builder.getInt64(maxSize())); 121 m_sp->setArgOperand(4, m_builder.getInt64(size())); 122 123 if (auto term = m_builder.GetInsertBlock()->getTerminator()) 124 m_builder.SetInsertPoint(term); // Insert before terminator 125 126 auto inputIt = m_input.rbegin(); 127 auto localIt = m_local.begin(); 128 for (auto globalIdx = -static_cast<ssize_t>(m_input.size()); globalIdx < size(); ++globalIdx) 129 { 130 llvm::Value* item = nullptr; 131 if (globalIdx < -m_globalPops) 132 { 133 item = *inputIt++; // update input items (might contain original value) 134 if (!item) // some items are skipped 135 continue; 136 } 137 else 138 item = *localIt++; // store new items 139 140 auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); 141 m_builder.CreateAlignedStore(item, slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. 142 } 143 } 144 145 146 llvm::Function* LocalStack::getStackPrepareFunc() 147 { 148 static const auto c_funcName = "stack.prepare"; 149 if (auto func = getModule()->getFunction(c_funcName)) 150 return func; 151 152 llvm::Type* argsTys[] = {Type::WordPtr, Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; 153 auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argsTys, false), llvm::Function::PrivateLinkage, c_funcName, getModule()); 154 func->setDoesNotThrow(); 155 func->addAttribute(1, llvm::Attribute::ReadNone); 156 func->addAttribute(2, llvm::Attribute::NoAlias); 157 func->addAttribute(2, llvm::Attribute::NoCapture); 158 159 auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); 160 auto updateBB = llvm::BasicBlock::Create(func->getContext(), "Update", func); 161 auto outOfStackBB = llvm::BasicBlock::Create(func->getContext(), "OutOfStack", func); 162 163 auto iter = func->arg_begin(); 164 llvm::Argument* base = &(*iter++); 165 base->setName("base"); 166 llvm::Argument* sizePtr = &(*iter++); 167 sizePtr->setName("size.ptr"); 168 llvm::Argument* min = &(*iter++); 169 min->setName("min"); 170 llvm::Argument* max = &(*iter++); 171 max->setName("max"); 172 llvm::Argument* diff = &(*iter++); 173 diff->setName("diff"); 174 llvm::Argument* jmpBuf = &(*iter); 175 jmpBuf->setName("jmpBuf"); 176 177 InsertPointGuard guard{m_builder}; 178 m_builder.SetInsertPoint(checkBB); 179 auto sizeAlignment = getModule()->getDataLayout().getABITypeAlignment(Type::Size); 180 auto size = m_builder.CreateAlignedLoad(sizePtr, sizeAlignment, "size"); 181 auto minSize = m_builder.CreateAdd(size, min, "size.min", false, true); 182 auto maxSize = m_builder.CreateAdd(size, max, "size.max", true, true); 183 auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "ok.min"); 184 auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(RuntimeManager::stackSizeLimit), "ok.max"); 185 auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); 186 m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); 187 188 m_builder.SetInsertPoint(updateBB); 189 auto newSize = m_builder.CreateNSWAdd(size, diff, "size.next"); 190 m_builder.CreateAlignedStore(newSize, sizePtr, sizeAlignment); 191 auto sp = m_builder.CreateGEP(base, size, "sp"); 192 m_builder.CreateRet(sp); 193 194 m_builder.SetInsertPoint(outOfStackBB); 195 auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); 196 m_builder.CreateCall(longjmp, {jmpBuf}); 197 m_builder.CreateUnreachable(); 198 199 return func; 200 } 201 202 203 } 204 } 205 }