Fabcoin Core  0.16.2
P2P Digital Currency
VMOpt.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 
19 #include <libethereum/ExtVM.h>
20 #include "VMConfig.h"
21 #include "VM.h"
22 using namespace std;
23 using namespace dev;
24 using namespace dev::eth;
25 
26 void VM::reportStackUse()
27 {
28  static intptr_t p = 0;
29  intptr_t q = intptr_t(&q);
30  if (p)
31  cerr << "STACK: " << p << " - " << q << " = " << (p - q) << endl;
32  p = q;
33 }
34 
35 std::array<InstructionMetric, 256> VM::c_metrics;
36 void VM::initMetrics()
37 {
38  static bool done=false;
39  if (!done)
40  {
41  for (unsigned i = 0; i < 256; ++i)
42  {
44  c_metrics[i].gasPriceTier = op.gasPriceTier;
45  c_metrics[i].args = op.args;
46  c_metrics[i].ret = op.ret;
47  }
48  }
49  done = true;
50 }
51 
52 void VM::copyCode(int _extraBytes)
53 {
54  // Copy code so that it can be safely modified and extend code by
55  // _extraBytes zero bytes to allow reading virtual data at the end
56  // of the code without bounds checks.
57  auto extendedSize = m_ext->code.size() + _extraBytes;
58  m_codeSpace.reserve(extendedSize);
59  m_codeSpace = m_ext->code;
60  m_codeSpace.resize(extendedSize);
61  m_code = m_codeSpace.data();
62 }
63 
65 {
66  copyCode(33);
67 
68  size_t const nBytes = m_ext->code.size();
69 
70  // build a table of jump destinations for use in verifyJumpDest
71 
72  TRACE_STR(1, "Build JUMPDEST table")
73  for (size_t pc = 0; pc < nBytes; ++pc)
74  {
75  Instruction op = Instruction(m_code[pc]);
76  TRACE_OP(2, pc, op);
77 
78  // make synthetic ops in user code trigger invalid instruction if run
79  if (
80  op == Instruction::PUSHC ||
81  op == Instruction::JUMPC ||
82  op == Instruction::JUMPCI
83  )
84  {
85  TRACE_OP(1, pc, op);
86  m_code[pc] = (byte)Instruction::BAD;
87  }
88 
89  if (op == Instruction::JUMPDEST)
90  {
91  m_jumpDests.push_back(pc);
92  }
93  else if (
94  (byte)Instruction::PUSH1 <= (byte)op &&
95  (byte)op <= (byte)Instruction::PUSH32
96  )
97  {
98  pc += (byte)op - (byte)Instruction::PUSH1 + 1;
99  }
100 #if EVM_JUMPS_AND_SUBS
101  else if (
102  op == Instruction::JUMPTO ||
103  op == Instruction::JUMPIF ||
104  op == Instruction::JUMPSUB)
105  {
106  ++pc;
107  pc += 4;
108  }
109  else if (op == Instruction::JUMPV || op == Instruction::JUMPSUBV)
110  {
111  ++pc;
112  pc += 4 * m_code[pc]; // number of 4-byte dests followed by table
113  }
114  else if (op == Instruction::BEGINSUB)
115  {
116  m_beginSubs.push_back(pc);
117  }
118  else if (op == Instruction::BEGINDATA)
119  {
120  break;
121  }
122 #endif
123  }
124 
125 #ifdef EVM_DO_FIRST_PASS_OPTIMIZATION
126 
127  #ifdef EVM_USE_CONSTANT_POOL
128 
129  // maintain constant pool as a hash table of up to 256 u256 constants
130  struct hash256
131  {
132  // FNV chosen as good, fast, and byte-at-a-time
133  const uint32_t FNV_PRIME1 = 2166136261;
134  const uint32_t FNV_PRIME2 = 16777619;
135  uint32_t hash = FNV_PRIME1;
136 
137  u256 (&table)[256];
138  bool empty[256];
139 
140  hash256(u256 (&table)[256]) : table(table)
141  {
142  for (int i = 0; i < 256; ++i)
143  {
144  table[i] = 0;
145  empty[i] = true;
146  }
147  }
148 
149  void hashInit() { hash = FNV_PRIME1; }
150 
151  // hash in successive bytes
152  void hashByte(byte b) { hash ^= (b), hash *= FNV_PRIME2; }
153 
154  // fold hash into 1 byte
155  byte getHash() { return ((hash >> 8) ^ hash) & 0xff; }
156 
157  // insert value at byte index in table, false if collision
158  bool insertVal(byte hash, u256& val)
159  {
160  if (empty[hash])
161  {
162  empty[hash] = false;
163  table[hash] = val;
164  return true;
165  }
166  return table[hash] == val;
167  }
168  } constantPool(m_pool);
169  #define CONST_POOL_HASH_INIT() constantPool.hashInit()
170  #define CONST_POOL_HASH_BYTE(b) constantPool.hashByte(b)
171  #define CONST_POOL_GET_HASH() constantPool.getHash()
172  #define CONST_POOL_INSERT_VAL(hash, val) constantPool.insertVal((hash), (val))
173  #else
174  #define CONST_POOL_HASH_INIT()
175  #define CONST_POOL_HASH_BYTE(b)
176  #define CONST_POOL_GET_HASH() 0
177  #define CONST_POOL_INSERT_VAL(hash, val) false
178  #endif
179 
180  TRACE_STR(1, "Do first pass optimizations")
181  for (size_t pc = 0; pc < nBytes; ++pc)
182  {
183  u256 val = 0;
184  Instruction op = Instruction(m_code[pc]);
185 
186  if ((byte)Instruction::PUSH1 <= (byte)op && (byte)op <= (byte)Instruction::PUSH32)
187  {
188  byte nPush = (byte)op - (byte)Instruction::PUSH1 + 1;
189 
190 
191  // decode pushed bytes to integral value
192  CONST_POOL_HASH_INIT();
193  val = m_code[pc+1];
194  for (uint64_t i = pc+2, n = nPush; --n; ++i) {
195  val = (val << 8) | m_code[i];
196  CONST_POOL_HASH_BYTE(m_code[i]);
197  }
198 
199  #ifdef EVM_USE_CONSTANT_POOL
200  if (1 < nPush)
201  {
202  // try to put value in constant pool at hash index
203  // if there is no collision replace PUSHn with PUSHC
204  TRACE_PRE_OPT(1, pc, op);
205  byte hash = CONST_POOL_GET_HASH();
206  if (CONST_POOL_INSERT_VAL(hash, val))
207  {
208  m_code[pc] = (byte)Instruction::PUSHC;
209  m_code[pc+1] = hash;
210  m_code[pc+2] = nPush - 1;
211  TRACE_VAL(1, "constant pooled", val);
212  }
213  TRACE_POST_OPT(1, pc, op);
214  }
215  #endif
216 
217  #ifdef EVM_REPLACE_CONST_JUMP
218  // replace JUMP or JUMPI to constant location with JUMPC or JUMPCI
219  // verifyJumpDest is M = log(number of jump destinations)
220  // outer loop is N = number of bytes in code array
221  // so complexity is N log M, worst case is N log N
222  size_t i = pc + nPush + 1;
223  op = Instruction(m_code[i]);
224  if (op == Instruction::JUMP)
225  {
226  TRACE_STR(1, "Replace const JUMPC")
227  TRACE_PRE_OPT(1, i, op);
228 
229  if (0 <= verifyJumpDest(val, false))
230  m_code[i] = byte(op = Instruction::JUMPC);
231 
232  TRACE_POST_OPT(1, i, op);
233  }
234  else if (op == Instruction::JUMPI)
235  {
236  TRACE_STR(1, "Replace const JUMPCI")
237  TRACE_PRE_OPT(1, i, op);
238 
239  if (0 <= verifyJumpDest(val, false))
240  m_code[i] = byte(op = Instruction::JUMPCI);
241 
242  TRACE_POST_OPT(1, ii, op);
243  }
244  #endif
245 
246  pc += nPush;
247  }
248 
249  }
250  TRACE_STR(1, "Finished optimizations")
251 #endif
252 }
253 
254 
255 //
256 // Init interpreter on entry.
257 //
258 void VM::initEntry()
259 {
260  m_bounce = &VM::interpretCases;
261  interpretCases(); // first call initializes jump table
262  initMetrics();
263  optimize();
264 }
265 
266 
267 // Implementation of EXP.
268 //
269 // This implements exponentiation by squaring algorithm.
270 // Is faster than boost::multiprecision::powm() because it avoids explicit
271 // mod operation.
272 // Do not inline it.
273 u256 VM::exp256(u256 _base, u256 _exponent)
274 {
275  using boost::multiprecision::limb_type;
276  u256 result = 1;
277  while (_exponent)
278  {
279  if (static_cast<limb_type>(_exponent) & 1) // If exponent is odd.
280  result *= _base;
281  _base *= _base;
282  _exponent >>= 1;
283  }
284  return result;
285 }
#define TRACE_STR(level, str)
Definition: VMConfig.h:103
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
#define TRACE_OP(level, pc, op)
Definition: VMConfig.h:105
uint8_t byte
Definition: Common.h:57
int args
Number of items required on the stack for this instruction (and, for the purposes of ret...
Definition: Instruction.h:262
#define TRACE_PRE_OPT(level, pc, op)
Definition: VMConfig.h:106
void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2)
Definition: picosha2.h:304
#define TRACE_POST_OPT(level, pc, op)
Definition: VMConfig.h:107
std::hash for asio::adress
Definition: Common.h:323
Instruction
Virtual machine bytecode instruction.
Definition: Instruction.h:16
InstructionInfo instructionInfo(Instruction _inst)
Information on all the instructions.
int ret
Number of items placed (back) on the stack by this instruction, assuming args items were removed...
Definition: Instruction.h:263
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void >> u256
Definition: Common.h:125
#define b(i, j)
Instruction
Virtual machine bytecode instruction.
Definition: Instruction.h:39
Information structure for a particular instruction.
Definition: Instruction.h:258
uint8_t byte
Definition: Common.h:10
bool optimize(llvm::Module &_module)
Definition: Optimizer.cpp:74
Tier gasPriceTier
Tier for gas pricing.
Definition: Instruction.h:265
#define TRACE_VAL(level, name, val)
Definition: VMConfig.h:104