Fabcoin Core  0.16.2
P2P Digital Currency
GasMeter.cpp
Go to the documentation of this file.
1 #include "GasMeter.h"
2 
4 #include <llvm/IR/IntrinsicInst.h>
6 
7 #include "JIT.h"
8 #include "Ext.h"
9 #include "RuntimeManager.h"
10 
11 namespace dev
12 {
13 namespace eth
14 {
15 namespace jit
16 {
17 
19  CompilerHelper(_builder),
20  m_runtimeManager(_runtimeManager),
21  m_mode(mode)
22 {
23  llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr};
24  m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule());
25  m_gasCheckFunc->setDoesNotThrow();
26  m_gasCheckFunc->setDoesNotCapture(1);
27 
28  auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc);
29  auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
30  auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
31 
32  auto iter = m_gasCheckFunc->arg_begin();
33  llvm::Argument* gasPtr = &(*iter++);
34  gasPtr->setName("gasPtr");
35  llvm::Argument* cost = &(*iter++);
36  cost->setName("cost");
37  llvm::Argument* jmpBuf = &(*iter);
38  jmpBuf->setName("jmpBuf");
39 
41  m_builder.SetInsertPoint(checkBB);
42  auto gas = m_builder.CreateLoad(gasPtr, "gas");
43  auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
44  auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions
45  m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue);
46 
47  m_builder.SetInsertPoint(updateBB);
48  m_builder.CreateStore(gasUpdated, gasPtr);
49  m_builder.CreateRetVoid();
50 
51  m_builder.SetInsertPoint(outOfGasBB);
52  m_runtimeManager.abort(jmpBuf);
53  m_builder.CreateUnreachable();
54 }
55 
57 {
58  if (!m_checkCall)
59  {
60  // Create gas check call with mocked block cost at begining of current cost-block
62  }
63 
64  m_blockCost += getStepCost(_inst);
65 }
66 
67 void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
68 {
69  if (_cost->getType() == Type::Word)
70  {
71  auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word);
72  auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh");
73  auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas);
74  _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost");
75  }
76 
77  assert(_cost->getType() == Type::Gas);
78  m_builder.CreateCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()});
79 }
80 
82 {
83  // Additional cost is 1 per significant byte of exponent
84  // lz - leading zeros
85  // cost = ((256 - lz) + 7) / 8
86 
87  // OPT: Can gas update be done in exp algorithm?
88  auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
89  auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)});
90  auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
91  auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
92  auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
93  auto exponentByteCost = m_mode >= EVM_CLEARING ? 50 : JITSchedule::expByteGas::value;
94  count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(exponentByteCost)));
95 }
96 
97 void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue)
98 {
99  auto oldValue = _ext.sload(_index);
100  auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero");
101  auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
102  auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
103  assert(JITSchedule::sstoreResetGas::value == JITSchedule::sstoreClearGas::value && "Update SSTORE gas cost");
104  auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(JITSchedule::sstoreSetGas::value), m_builder.getInt64(JITSchedule::sstoreResetGas::value), "cost");
105  count(cost);
106 }
107 
109 {
111  assert(m_blockCost > 0); // LOGn instruction is already counted
112  assert(JITSchedule::logDataGas::value != 1 && "Log data gas cost has changed. Update GasMeter.");
113  count(m_builder.CreateNUWMul(_dataLength, Constant::get(JITSchedule::logDataGas::value))); // TODO: Use i64
114 }
115 
117 {
119  assert(m_blockCost > 0); // SHA3 instruction is already counted
120 
121  // TODO: This round ups to 32 happens in many places
122  assert(JITSchedule::sha3WordGas::value != 1 && "SHA3 data cost has changed. Update GasMeter");
123  auto dataLength64 = m_builder.CreateTrunc(_dataLength, Type::Gas);
124  auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, m_builder.getInt64(31)), m_builder.getInt64(32));
125  auto cost64 = m_builder.CreateNUWMul(m_builder.getInt64(JITSchedule::sha3WordGas::value), words64);
126  count(cost64);
127 }
128 
130 {
131  assert(_gas->getType() == Type::Gas);
133 }
134 
136 {
137  // If any uncommited block
138  if (m_checkCall)
139  {
140  if (m_blockCost == 0) // Do not check 0
141  {
142  m_checkCall->eraseFromParent(); // Remove the gas check call
143  m_checkCall = nullptr;
144  return;
145  }
146 
147  m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
148  m_checkCall = nullptr; // End cost-block
149  m_blockCost = 0;
150  }
151  assert(m_blockCost == 0);
152 }
153 
154 void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
155 {
156  assert(JITSchedule::memoryGas::value != 1 && "Memory gas cost has changed. Update GasMeter.");
157  count(_additionalMemoryInWords, _jmpBuf, _gasPtr);
158 }
159 
161 {
162  assert(JITSchedule::copyGas::value != 1 && "Copy gas cost has changed. Update GasMeter.");
163  count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(JITSchedule::copyGas::value)));
164 }
165 
167 {
168  switch (inst)
169  {
170  // Tier 0
171  case Instruction::STOP:
172  case Instruction::RETURN:
173  case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore()
174  return JITSchedule::stepGas0::value;
175 
176  // Tier 1
178  case Instruction::ORIGIN:
179  case Instruction::CALLER:
186  case Instruction::NUMBER:
189  case Instruction::POP:
190  case Instruction::PC:
191  case Instruction::MSIZE:
192  case Instruction::GAS:
193  return JITSchedule::stepGas1::value;
194 
195  // Tier 2
196  case Instruction::ADD:
197  case Instruction::SUB:
198  case Instruction::LT:
199  case Instruction::GT:
200  case Instruction::SLT:
201  case Instruction::SGT:
202  case Instruction::EQ:
203  case Instruction::ISZERO:
204  case Instruction::AND:
205  case Instruction::OR:
206  case Instruction::XOR:
207  case Instruction::NOT:
208  case Instruction::BYTE:
212  case Instruction::MLOAD:
213  case Instruction::MSTORE:
218  return JITSchedule::stepGas2::value;
219 
220  // Tier 3
221  case Instruction::MUL:
222  case Instruction::DIV:
223  case Instruction::SDIV:
224  case Instruction::MOD:
225  case Instruction::SMOD:
227  return JITSchedule::stepGas3::value;
228 
229  // Tier 4
230  case Instruction::ADDMOD:
231  case Instruction::MULMOD:
232  case Instruction::JUMP:
233  return JITSchedule::stepGas4::value;
234 
235  // Tier 5
236  case Instruction::EXP:
237  case Instruction::JUMPI:
238  return JITSchedule::stepGas5::value;
239 
240  // Tier 6
242  return m_mode >= EVM_ANTI_DOS ? 400 : JITSchedule::stepGas6::value;
243 
246  return m_mode >= EVM_ANTI_DOS ? 700 : JITSchedule::stepGas6::value;
247 
249  return JITSchedule::stepGas6::value;
250 
251  case Instruction::SHA3:
252  return JITSchedule::sha3Gas::value;
253 
254  case Instruction::SLOAD:
255  return m_mode >= EVM_ANTI_DOS ? 200 : JITSchedule::sloadGas::value;
256 
258  return JITSchedule::jumpdestGas::value;
259 
260  case Instruction::LOG0:
261  case Instruction::LOG1:
262  case Instruction::LOG2:
263  case Instruction::LOG3:
264  case Instruction::LOG4:
265  {
266  auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0);
267  return JITSchedule::logGas::value + numTopics * JITSchedule::logTopicGas::value;
268  }
269 
270  case Instruction::CALL:
273  return m_mode >= EVM_ANTI_DOS ? 700 : JITSchedule::callGas::value;
274 
275  case Instruction::CREATE:
276  return JITSchedule::createGas::value;
277 
279  return m_mode >= EVM_ANTI_DOS ? 5000 : JITSchedule::stepGas0::value;
280 
281  default:
282  // For invalid instruction just return 0.
283  return 0;
284  }
285 }
286 
287 }
288 }
289 }
signed greater-than comparision
bitwise OR operation
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
exponential operation
unsigned modular addition
get external code size (from another contract)
RuntimeManager & m_runtimeManager
Definition: GasMeter.h:61
int64_t m_blockCost
Cumulative gas cost of a block of instructions Handle overflow.
Definition: GasMeter.h:56
void commitCostBlock()
Finalize cost-block by checking gas needed for the block before the block.
Definition: GasMeter.cpp:135
unsigned modular multiplication
void setGas(llvm::Value *_gas)
get the block&#39;s timestamp
void countSha3Data(llvm::Value *_dataLength)
Count gas cost of SHA3 data.
Definition: GasMeter.cpp:116
llvm::Module * getModule()
Reference to the IR module being compiled.
conditionally alter the program counter
llvm::Value * sload(llvm::Value *_index)
Definition: Ext.cpp:265
void countCopy(llvm::Value *_copyWords)
Count addional gas cost for memory copy.
Definition: GasMeter.cpp:160
copy input data in current environment to memory
evm_mode m_mode
EVM compatibility mode.
Definition: GasMeter.h:64
addition operation
retrieve single byte from word
static llvm::Type * Void
Definition: Type.h:32
get execution origination address
evm_mode mode
Definition: SmartVM.cpp:47
Makes a log entry; 4 topics.
get the block&#39;s number
integer division operation
assert(len-trim+(2 *lenIndices)<=WIDTH)
get the block&#39;s coinbase address
set a potential jump destination
Instruction
Virtual machine bytecode instruction.
Definition: Instruction.h:16
get input data of current environment
save byte to memory
halt execution and register account for later deletion
signed integer division operation
load word from storage
#define ANY_PUSH
Definition: Instruction.h:169
signed less-than comparision
halt execution returning output data
Base class for compiler helpers like Memory, GasMeter, etc.
get address of currently executing account
remove item from stack
get hash of most recent complete block
get the program counter
static llvm::PointerType * BytePtr
Definition: Type.h:30
compute SHA3-256 hash
Config::Value_type Value
greater-than comparision
get size of code running in current environment
Makes a log entry; no topics.
get the size of active memory
get the block&#39;s gas limit
#define ANY_DUP
Definition: Instruction.h:202
get balance of the given account
save word to storage
message-call with another account&#39;s code only
less-than comparision
static llvm::IntegerType * Word
Definition: Type.h:21
equality comparision
static llvm::ConstantInt * gasMax
Definition: Type.h:49
get size of input data in current environment
void countExp(llvm::Value *_exponent)
Calculate & count additional gas cost for EXP instruction.
Definition: GasMeter.cpp:81
extend length of signed integer
static llvm::ConstantInt * get(int64_t _n)
Returns word-size constant.
Definition: Type.cpp:55
copy code running in current environment to memory
bitwise AND operation
evm_mode
EVM compatibility mode aka chain mode.
Definition: evm.h:359
signed modulo remainder operation
subtraction operation
mulitplication operation
like CALLCODE but keeps caller&#39;s value and sender
void abort(llvm::Value *_jmpBuf)
get the amount of available gas
copy external code (from another contract)
void giveBack(llvm::Value *_gas)
Give back an amount of gas not used by a call.
Definition: GasMeter.cpp:129
get deposited value by the instruction/transaction responsible for this execution ...
get price of gas in current environment
void countLogData(llvm::Value *_dataLength)
Count gas cost of LOG data.
Definition: GasMeter.cpp:108
alter the program counter to a jumpdest
GasMeter(IRBuilder &_builder, RuntimeManager &_runtimeManager, evm_mode mode)
Definition: GasMeter.cpp:18
Makes a log entry; 1 topic.
llvm::Function * m_gasCheckFunc
Definition: GasMeter.h:59
bitwise XOR operation
#define ANY_SWAP
Definition: Instruction.h:219
message-call into an account
get the block&#39;s difficulty
save word to memory
void count(Instruction _inst)
Count step cost of instruction.
Definition: GasMeter.cpp:56
void countMemory(llvm::Value *_additionalMemoryInWords, llvm::Value *_jmpBuf, llvm::Value *_gasPtr)
Generate code that checks the cost of additional memory used by program.
Definition: GasMeter.cpp:154
IRBuilder & m_builder
Reference to parent compiler IR builder.
llvm::IRBuilder<> IRBuilder
static llvm::MDNode * expectTrue
Definition: Type.h:42
int64_t getStepCost(Instruction inst) const
Definition: GasMeter.cpp:166
Makes a log entry; 3 topics.
void countSStore(class Ext &_ext, llvm::Value *_index, llvm::Value *_newValue)
Calculate & count gas cost for SSTORE instruction.
Definition: GasMeter.cpp:97
load word from memory
simple not operator
static llvm::IntegerType * Gas
Definition: Type.h:26
bitwise NOT opertation
llvm::CallInst * m_checkCall
Definition: GasMeter.h:58
modulo remainder operation
create a new account with associated code
Makes a log entry; 2 topics.