/ libevmjit / BasicBlock.cpp
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  }