diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index 47cbbf5c1..3037145c2 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -851,7 +851,9 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) auto outOff = stack.pop(); auto outSize = stack.pop(); + gasMeter.commitCostBlock(gas); auto ret = ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize); + gasMeter.giveBack(gas); stack.push(ret); break; } diff --git a/evmcc/Ext.cpp b/evmcc/Ext.cpp index bb2a54870..1019a3986 100644 --- a/evmcc/Ext.cpp +++ b/evmcc/Ext.cpp @@ -165,7 +165,7 @@ Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* return address; } -llvm::Value* Ext::call(llvm::Value* _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize) +llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize) { m_builder.CreateStore(_gas, m_args[0]); auto receiveAddress = bswap(_receiveAddress); // to BE @@ -177,6 +177,7 @@ llvm::Value* Ext::call(llvm::Value* _gas, llvm::Value* _receiveAddress, llvm::Va m_builder.CreateStore(_outSize, m_arg7); llvm::Value* args[] = {m_args[0], m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_args[1]}; m_builder.CreateCall(m_call, args); + _gas = m_builder.CreateLoad(m_args[0]); // Return gas return m_builder.CreateLoad(m_args[1]); } @@ -284,10 +285,10 @@ EXPORT void ext_call(i256* _gas, h256* _receiveAddress, i256* _value, i256* _inO auto value = llvm2eth(*_value); auto ret = false; + auto gas = llvm2eth(*_gas); if (ext.balance(ext.myAddress) >= value) { ext.subBalance(value); - auto gas = llvm2eth(*_gas); auto receiveAddress = dev::right160(*_receiveAddress); auto inOff = static_cast(llvm2eth(*_inOff)); auto inSize = static_cast(llvm2eth(*_inSize)); @@ -299,7 +300,7 @@ EXPORT void ext_call(i256* _gas, h256* _receiveAddress, i256* _value, i256* _inO auto ret = ext.call(receiveAddress, value, inRef, &gas, outRef, onOp, {}, receiveAddress); } - // m_gas += gas; // TODO: Handle gas + *_gas = eth2llvm(gas); _ret->a = ret ? 1 : 0; } diff --git a/evmcc/Ext.h b/evmcc/Ext.h index 5c683ffdf..91eea2c5f 100644 --- a/evmcc/Ext.h +++ b/evmcc/Ext.h @@ -37,7 +37,7 @@ public: void suicide(llvm::Value* _address); llvm::Value* calldataload(llvm::Value* _index); llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); - llvm::Value* call(llvm::Value* _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize); + llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); llvm::Value* exp(llvm::Value* _left, llvm::Value* _right); diff --git a/evmcc/GasMeter.cpp b/evmcc/GasMeter.cpp index aae67f977..80e32a7ca 100644 --- a/evmcc/GasMeter.cpp +++ b/evmcc/GasMeter.cpp @@ -53,6 +53,9 @@ bool isCostBlockEnd(Instruction _inst) { // Basic block terminators like STOP are not needed on the list // as cost will be commited at the end of basic block + + // CALL & CALLCODE are commited manually + switch (_inst) { case Instruction::CALLDATACOPY: @@ -63,8 +66,6 @@ bool isCostBlockEnd(Instruction _inst) case Instruction::SSTORE: case Instruction::GAS: case Instruction::CREATE: - case Instruction::CALL: - case Instruction::CALLCODE: return true; default: @@ -107,16 +108,31 @@ void GasMeter::count(Instruction _inst) commitCostBlock(); } -void GasMeter::commitCostBlock() +void GasMeter::giveBack(llvm::Value* _gas) { + llvm::Value* gasCounter = m_builder.CreateLoad(m_gas, "gas"); + gasCounter = m_builder.CreateAdd(gasCounter, _gas); + m_builder.CreateStore(gasCounter, m_gas); +} + +void GasMeter::commitCostBlock(llvm::Value* _additionalCost) +{ + assert(!_additionalCost || m_checkCall); // _additionalCost => m_checkCall; Must be inside cost-block + // If any uncommited block if (m_checkCall) { - if (m_blockCost > 0) // If any cost - m_checkCall->setArgOperand(0, Constant::get(m_blockCost)); // Update block cost in gas check call - else + if (m_blockCost == 0 && !_additionalCost) // Do not check 0 + { m_checkCall->eraseFromParent(); // Remove the gas check call - + return; + } + + llvm::Value* cost = Constant::get(m_blockCost); + if (_additionalCost) + cost = m_builder.CreateAdd(cost, _additionalCost); + + m_checkCall->setArgOperand(0, cost); // Update block cost in gas check call m_checkCall = nullptr; // End cost-block m_blockCost = 0; } diff --git a/evmcc/GasMeter.h b/evmcc/GasMeter.h index c3ea9f84f..95e8eaf8d 100644 --- a/evmcc/GasMeter.h +++ b/evmcc/GasMeter.h @@ -19,8 +19,12 @@ public: /// Count step cost of instruction void count(dev::eth::Instruction _inst); - /// Finalize cost block by checking gas needed for the block before the block - void commitCostBlock(); + /// Finalize cost-block by checking gas needed for the block before the block + /// @param _additionalCost adds additional cost to cost-block before commit + void commitCostBlock(llvm::Value* _additionalCost = nullptr); + + /// Give back an amount of gas not used by a call + void giveBack(llvm::Value* _gas); /// Generate code that checks the cost of additional memory used by program void checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilder<>& _builder); diff --git a/evmcc/Memory.cpp b/evmcc/Memory.cpp index 0098ed1f9..39d6996ee 100644 --- a/evmcc/Memory.cpp +++ b/evmcc/Memory.cpp @@ -40,33 +40,27 @@ Memory::Memory(llvm::IRBuilder<>& _builder, llvm::Module* _module, GasMeter& _ga m_returnDataSize->setUnnamedAddr(true); // Address is not important m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, Type::WordPtr, false), llvm::Function::ExternalLinkage, "mem_resize", _module); + m_require = createRequireFunc(_module, _gasMeter); m_loadWord = createFunc(false, Type::i256, _module, _gasMeter); m_storeWord = createFunc(true, Type::i256, _module, _gasMeter); m_storeByte = createFunc(true, Type::Byte, _module, _gasMeter); } -llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, llvm::Module* _module, GasMeter& _gasMeter) +llvm::Function* Memory::createRequireFunc(llvm::Module* _module, GasMeter& _gasMeter) { - auto isWord = _valueType == Type::i256; - - llvm::Type* storeArgs[] = {Type::i256, _valueType}; - auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; - auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::i256, Type::i256, false); - auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, _module); + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::i256, false), llvm::Function::PrivateLinkage, "mem.require", _module); auto checkBB = llvm::BasicBlock::Create(func->getContext(), "check", func); auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "resize", func); - auto accessBB = llvm::BasicBlock::Create(func->getContext(), "access", func); + auto returnBB = llvm::BasicBlock::Create(func->getContext(), "return", func); // BB "check" llvm::IRBuilder<> builder(checkBB); - llvm::Value* index = func->arg_begin(); - index->setName("index"); - auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; - auto sizeRequired = builder.CreateAdd(index, Constant::get(valueSize), "sizeRequired"); + llvm::Value* sizeRequired = func->arg_begin(); + sizeRequired->setName("sizeRequired"); auto size = builder.CreateLoad(m_size, "size"); auto resizeNeeded = builder.CreateICmpULE(size, sizeRequired, "resizeNeeded"); - builder.CreateCondBr(resizeNeeded, resizeBB, accessBB); // OPT branch weights? + builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? // BB "resize" builder.SetInsertPoint(resizeBB); @@ -79,26 +73,51 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, llvm:: builder.CreateStore(sizeRequired, m_size); auto newData = builder.CreateCall(m_resize, m_size, "newData"); builder.CreateStore(newData, m_data); - builder.CreateBr(accessBB); + builder.CreateBr(returnBB); - // BB "access" - builder.SetInsertPoint(accessBB); - auto data = builder.CreateLoad(m_data, "data"); - auto ptr = builder.CreateGEP(data, index, "ptr"); + // BB "return" + builder.SetInsertPoint(returnBB); + builder.CreateRetVoid(); + return func; +} + +llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, llvm::Module* _module, GasMeter& _gasMeter) +{ + auto isWord = _valueType == Type::i256; + + llvm::Type* storeArgs[] = {Type::i256, _valueType}; + auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; + auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::i256, Type::i256, false); + auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, _module); + + auto origBB = m_builder.GetInsertBlock(); + auto origPt = m_builder.GetInsertPoint(); + + m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); + llvm::Value* index = func->arg_begin(); + index->setName("index"); + + auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; + this->require(index, Constant::get(valueSize)); + auto data = m_builder.CreateLoad(m_data, "data"); + auto ptr = m_builder.CreateGEP(data, index, "ptr"); if (isWord) - ptr = builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); + ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); if (_isStore) { llvm::Value* value = ++func->arg_begin(); value->setName("value"); - builder.CreateStore(value, ptr); - builder.CreateRetVoid(); + m_builder.CreateStore(value, ptr); + m_builder.CreateRetVoid(); } else { - auto ret = builder.CreateLoad(ptr); - builder.CreateRet(ret); + auto ret = m_builder.CreateLoad(ptr); + m_builder.CreateRet(ret); } + + m_builder.SetInsertPoint(origBB, origPt); + return func; } @@ -136,10 +155,20 @@ llvm::Value* Memory::getSize() return m_builder.CreateLoad(m_size); } -void Memory::registerReturnData(llvm::Value* _index, llvm::Value* _size) +void Memory::require(llvm::Value* _size) +{ + m_builder.CreateCall(m_require, _size); +} + +void Memory::require(llvm::Value* _offset, llvm::Value* _size) { - auto lastWord = m_builder.CreateAdd(_index, m_builder.CreateSub(_size, Constant::get(32)), "lastWord"); - loadWord(lastWord); // Make sure that memory is allocated and count gas + auto sizeRequired = m_builder.CreateAdd(_offset, _size, "sizeRequired"); + require(sizeRequired); +} + +void Memory::registerReturnData(llvm::Value* _index, llvm::Value* _size) +{ + require(_index, _size); // Make sure that memory is allocated and count gas m_builder.CreateStore(_index, m_returnDataOffset); m_builder.CreateStore(_size, m_returnDataSize); diff --git a/evmcc/Memory.h b/evmcc/Memory.h index 8b22294d7..c7ed58601 100644 --- a/evmcc/Memory.h +++ b/evmcc/Memory.h @@ -21,6 +21,12 @@ public: llvm::Value* getData(); llvm::Value* getSize(); + /// Requires this amount of memory. And counts gas fee for that memory. + void require(llvm::Value* _size); + + /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory. + void require(llvm::Value* _offset, llvm::Value* _size); + void registerReturnData(llvm::Value* _index, llvm::Value* _size); static dev::bytesConstRef getReturnData(); @@ -28,6 +34,7 @@ public: private: llvm::Function* createFunc(bool _isStore, llvm::Type* _type, llvm::Module* _module, GasMeter& _gasMeter); + llvm::Function* createRequireFunc(llvm::Module* _module, GasMeter& _gasMeter); private: llvm::IRBuilder<>& m_builder; @@ -42,6 +49,7 @@ private: llvm::Function* m_loadWord; llvm::Function* m_storeWord; llvm::Function* m_storeByte; + llvm::Function* m_require; llvm::Function* m_resize; llvm::Function* m_memDump;