diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index c5167fd385..80a01cc551 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -609,6 +609,7 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) { auto index = stack.pop(); auto value = stack.pop(); + gasMeter.countSStore(ext, index, value); ext.setStore(index, value); break; } @@ -689,8 +690,7 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) case Instruction::GAS: { - auto value = builder.CreateLoad(gasMeter.getLLVMGasVar()); - stack.push(value); + stack.push(gasMeter.getGas()); break; } diff --git a/evmcc/ExecutionEngine.cpp b/evmcc/ExecutionEngine.cpp index 23507073a4..cae15b18be 100644 --- a/evmcc/ExecutionEngine.cpp +++ b/evmcc/ExecutionEngine.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include "Runtime.h" #include "Memory.h" #include "Type.h" @@ -89,7 +91,7 @@ int ExecutionEngine::run(std::unique_ptr _module) ext->code = decltype(ext->code)(fakecode, 8); // Init runtime - uint64_t gas = 1000000; + uint64_t gas = 100; Runtime runtime(gas, std::move(ext)); auto entryFunc = module->getFunction("main"); @@ -99,9 +101,20 @@ int ExecutionEngine::run(std::unique_ptr _module) exit(1); } - auto result = exec->runFunction(entryFunc, {}); + + ReturnCode returnCode; + try + { + auto result = exec->runFunction(entryFunc, {}); + returnCode = static_cast(result.IntVal.getZExtValue()); + } + catch (const dev::eth::OutOfGas&) + { + returnCode = ReturnCode::OutOfGas; + } + gas = static_cast(Runtime::getGas()); - auto returnCode = static_cast(result.IntVal.getZExtValue()); + if (returnCode == ReturnCode::Return) { auto&& returnData = Memory::getReturnData(); // TODO: It might be better to place is in Runtime interface @@ -110,10 +123,8 @@ int ExecutionEngine::run(std::unique_ptr _module) for (auto it = returnData.begin(), end = returnData.end(); it != end; ++it) std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)*it << " "; std::cout << "]\n"; - - return 10; } - return 0; + return static_cast(returnCode); } } diff --git a/evmcc/GasMeter.cpp b/evmcc/GasMeter.cpp index 99b5e549db..10a8fca98b 100644 --- a/evmcc/GasMeter.cpp +++ b/evmcc/GasMeter.cpp @@ -9,6 +9,7 @@ #include "Type.h" #include "Utils.h" +#include "Ext.h" namespace evmcc { @@ -82,11 +83,27 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, llvm::Module* _module): m_gas = new llvm::GlobalVariable(*_module, Type::i256, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "gas"); m_gas->setUnnamedAddr(true); // Address is not important + m_rtExit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::MainReturn, false), llvm::Function::ExternalLinkage, "rt_exit", _module); + m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::i256, false), llvm::Function::PrivateLinkage, "gas.check", _module); InsertPointGuard guard(m_builder); - m_builder.SetInsertPoint(llvm::BasicBlock::Create(_builder.getContext(), {}, m_gasCheckFunc)); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "check", m_gasCheckFunc); + auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "outOfGas", m_gasCheckFunc); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "update", m_gasCheckFunc); + + m_builder.SetInsertPoint(checkBB); llvm::Value* cost = m_gasCheckFunc->arg_begin(); - llvm::Value* gas = m_builder.CreateLoad(m_gas); + cost->setName("cost"); + llvm::Value* gas = m_builder.CreateLoad(m_gas, "gas"); + auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas"); + m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); + + m_builder.SetInsertPoint(outOfGasBB); + m_builder.CreateCall(m_rtExit, Constant::get(ReturnCode::OutOfGas)); + m_builder.CreateRetVoid(); + + m_builder.SetInsertPoint(updateBB); gas = m_builder.CreateSub(gas, cost); m_builder.CreateStore(gas, m_gas); m_builder.CreateRetVoid(); @@ -100,12 +117,34 @@ void GasMeter::count(Instruction _inst) m_checkCall = m_builder.CreateCall(m_gasCheckFunc, llvm::UndefValue::get(Type::i256)); } - m_blockCost += getStepCost(_inst); + if (_inst != Instruction::SSTORE) // Handle cost of SSTORE separately in countSStore() + m_blockCost += getStepCost(_inst); if (isCostBlockEnd(_inst)) commitCostBlock(); } +void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) +{ + assert(!m_checkCall); // Everything should've been commited before + + static const auto sstoreCost = static_cast(c_sstoreGas); + + // [ADD] if oldValue == 0 and newValue != 0 => 2*cost + // [DEL] if oldValue != 0 and newValue == 0 => 0 + + auto oldValue = _ext.store(_index); + auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); + auto newValueIsZero = m_builder.CreateICmpEQ(_newValue, Constant::get(0), "newValueIsZero"); + auto oldValueIsntZero = m_builder.CreateICmpNE(oldValue, Constant::get(0), "oldValueIsntZero"); + auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); + auto isAdd = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isAdd"); + auto isDel = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDel"); + auto cost = m_builder.CreateSelect(isAdd, Constant::get(2 * sstoreCost), Constant::get(sstoreCost), "cost"); + cost = m_builder.CreateSelect(isDel, Constant::get(0), cost, "cost"); + m_builder.CreateCall(m_gasCheckFunc, cost); +} + void GasMeter::giveBack(llvm::Value* _gas) { llvm::Value* gasCounter = m_builder.CreateLoad(m_gas, "gas"); @@ -144,9 +183,9 @@ void GasMeter::checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilde _builder.CreateCall(m_gasCheckFunc, cost); } -llvm::GlobalVariable* GasMeter::getLLVMGasVar() +llvm::Value* GasMeter::getGas() { - return m_gas; + return m_builder.CreateLoad(m_gas, "gas"); } } diff --git a/evmcc/GasMeter.h b/evmcc/GasMeter.h index 95e8eaf8de..8738b49c09 100644 --- a/evmcc/GasMeter.h +++ b/evmcc/GasMeter.h @@ -19,6 +19,9 @@ public: /// Count step cost of instruction void count(dev::eth::Instruction _inst); + /// Calculate & count gas cost for SSTORE instruction + void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); + /// 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); @@ -29,7 +32,7 @@ public: /// Generate code that checks the cost of additional memory used by program void checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilder<>& _builder); - llvm::GlobalVariable* getLLVMGasVar(); + llvm::Value* getGas(); private: /// Cumulative gas cost of a block of instructions @@ -38,6 +41,7 @@ private: llvm::IRBuilder<>& m_builder; llvm::CallInst* m_checkCall = nullptr; llvm::GlobalVariable* m_gas; + llvm::Function* m_rtExit; llvm::Function* m_gasCheckFunc; }; diff --git a/evmcc/Memory.cpp b/evmcc/Memory.cpp index 1838afa4b7..e40d61f3b1 100644 --- a/evmcc/Memory.cpp +++ b/evmcc/Memory.cpp @@ -40,6 +40,10 @@ 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); + llvm::AttrBuilder attrBuilder; + attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); + m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); + m_require = createRequireFunc(_module, _gasMeter); m_loadWord = createFunc(false, Type::i256, _module, _gasMeter); m_storeWord = createFunc(true, Type::i256, _module, _gasMeter); diff --git a/evmcc/Runtime.cpp b/evmcc/Runtime.cpp index a373d95014..834a8bbfff 100644 --- a/evmcc/Runtime.cpp +++ b/evmcc/Runtime.cpp @@ -1,12 +1,27 @@ #include "Runtime.h" +#include + +#include "Type.h" + namespace evmcc { static Runtime* g_runtime; -extern "C" { EXPORT i256 gas; } +extern "C" +{ +EXPORT i256 gas; + +EXPORT void rt_exit(int32_t _returnCode) +{ + auto returnCode = static_cast(_returnCode); + if (returnCode == ReturnCode::OutOfGas) + BOOST_THROW_EXCEPTION(dev::eth::OutOfGas()); +} + +} Runtime::Runtime(dev::u256 _gas, std::unique_ptr _ext): m_ext(std::move(_ext)) diff --git a/evmcc/Type.h b/evmcc/Type.h index a8501d897c..7271832664 100644 --- a/evmcc/Type.h +++ b/evmcc/Type.h @@ -34,6 +34,7 @@ enum class ReturnCode Suicide = 2, BadJumpDestination = 101, + OutOfGas = 102, }; struct Constant