Ethereum  PoC-8
The C++ Implementation of Ethereum
LegacyVMOpt.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 #include "LegacyVM.h"
19 
20 using namespace std;
21 using namespace dev;
22 using namespace dev::eth;
23 
24 std::array<InstructionMetric, 256> LegacyVM::c_metrics;
25 void LegacyVM::initMetrics()
26 {
27  static bool done =
28  []()
29  {
30  for (unsigned i = 0; i < 256; ++i)
31  {
33  c_metrics[i].gasPriceTier = op.gasPriceTier;
34  c_metrics[i].args = op.args;
35  c_metrics[i].ret = op.ret;
36  }
37  return true;
38  } ();
39  (void)done;
40 }
41 
42 void LegacyVM::copyCode(int _extraBytes)
43 {
44  // Copy code so that it can be safely modified and extend code by
45  // _extraBytes zero bytes to allow reading virtual data at the end
46  // of the code without bounds checks.
47  auto extendedSize = m_ext->code.size() + _extraBytes;
48  m_code.reserve(extendedSize);
49  m_code = m_ext->code;
50  m_code.resize(extendedSize);
51 }
52 
53 void LegacyVM::optimize()
54 {
55  copyCode(33);
56 
57  size_t const nBytes = m_ext->code.size();
58 
59  // build a table of jump destinations for use in verifyJumpDest
60 
61  TRACE_STR(1, "Build JUMPDEST table")
62  for (size_t pc = 0; pc < nBytes; ++pc)
63  {
64  Instruction op = Instruction(m_code[pc]);
65  TRACE_OP(2, pc, op);
66 
67  // make synthetic ops in user code trigger invalid instruction if run
68  if (
69  op == Instruction::PUSHC ||
70  op == Instruction::JUMPC ||
71  op == Instruction::JUMPCI
72  )
73  {
74  TRACE_OP(1, pc, op);
75  m_code[pc] = (byte)Instruction::INVALID;
76  }
77 
78  if (op == Instruction::JUMPDEST)
79  {
80  m_jumpDests.push_back(pc);
81  }
82  else if (
83  (byte)Instruction::PUSH1 <= (byte)op &&
84  (byte)op <= (byte)Instruction::PUSH32
85  )
86  {
87  pc += (byte)op - (byte)Instruction::PUSH1 + 1;
88  }
89 #if EIP_615
90  else if (
91  op == Instruction::JUMPTO ||
92  op == Instruction::JUMPIF ||
93  op == Instruction::JUMPSUB)
94  {
95  ++pc;
96  pc += 4;
97  }
98  else if (op == Instruction::JUMPV || op == Instruction::JUMPSUBV)
99  {
100  ++pc;
101  pc += 4 * m_code[pc]; // number of 4-byte dests followed by table
102  }
103  else if (op == Instruction::BEGINSUB)
104  {
105  m_beginSubs.push_back(pc);
106  }
107  else if (op == Instruction::BEGINDATA)
108  {
109  break;
110  }
111 #endif
112  }
113 
114 #ifdef EVM_DO_FIRST_PASS_OPTIMIZATION
115 
116  TRACE_STR(1, "Do first pass optimizations")
117  for (size_t pc = 0; pc < nBytes; ++pc)
118  {
119  u256 val = 0;
120  Instruction op = Instruction(m_code[pc]);
121 
122  if ((byte)Instruction::PUSH1 <= (byte)op && (byte)op <= (byte)Instruction::PUSH32)
123  {
124  byte nPush = (byte)op - (byte)Instruction::PUSH1 + 1;
125 
126  // decode pushed bytes to integral value
127  val = m_code[pc+1];
128  for (uint64_t i = pc+2, n = nPush; --n; ++i) {
129  val = (val << 8) | m_code[i];
130  }
131 
132  #if EVM_USE_CONSTANT_POOL
133 
134  // add value to constant pool and replace PUSHn with PUSHC
135  // place offset in code as 2 bytes MSB-first
136  // followed by one byte count of remaining pushed bytes
137  if (5 < nPush)
138  {
139  uint16_t pool_off = m_pool.size();
140  TRACE_VAL(1, "stash", val);
141  TRACE_VAL(1, "... in pool at offset" , pool_off);
142  m_pool.push_back(val);
143 
144  TRACE_PRE_OPT(1, pc, op);
145  m_code[pc] = byte(op = Instruction::PUSHC);
146  m_code[pc+3] = nPush - 2;
147  m_code[pc+2] = pool_off & 0xff;
148  m_code[pc+1] = pool_off >> 8;
149  TRACE_POST_OPT(1, pc, op);
150  }
151 
152  #endif
153 
154  #if EVM_REPLACE_CONST_JUMP
155  // replace JUMP or JUMPI to constant location with JUMPC or JUMPCI
156  // verifyJumpDest is M = log(number of jump destinations)
157  // outer loop is N = number of bytes in code array
158  // so complexity is N log M, worst case is N log N
159  size_t i = pc + nPush + 1;
160  op = Instruction(m_code[i]);
161  if (op == Instruction::JUMP)
162  {
163  TRACE_VAL(1, "Replace const JUMP with JUMPC to", val)
164  TRACE_PRE_OPT(1, i, op);
165 
166  if (0 <= verifyJumpDest(val, false))
167  m_code[i] = byte(op = Instruction::JUMPC);
168 
169  TRACE_POST_OPT(1, i, op);
170  }
171  else if (op == Instruction::JUMPI)
172  {
173  TRACE_VAL(1, "Replace const JUMPI with JUMPCI to", val)
174  TRACE_PRE_OPT(1, i, op);
175 
176  if (0 <= verifyJumpDest(val, false))
177  m_code[i] = byte(op = Instruction::JUMPCI);
178 
179  TRACE_POST_OPT(1, i, op);
180  }
181  #endif
182 
183  pc += nPush;
184  }
185  }
186  TRACE_STR(1, "Finished optimizations")
187 #endif
188 }
189 
190 
191 //
192 // Init interpreter on entry.
193 //
194 void LegacyVM::initEntry()
195 {
196  m_bounce = &LegacyVM::interpretCases;
197  initMetrics();
198  optimize();
199 }
200 
201 
202 // Implementation of EXP.
203 //
204 // This implements exponentiation by squaring algorithm.
205 // Is faster than boost::multiprecision::powm() because it avoids explicit
206 // mod operation.
207 // Do not inline it.
208 u256 LegacyVM::exp256(u256 _base, u256 _exponent)
209 {
210  using boost::multiprecision::limb_type;
211  u256 result = 1;
212  while (_exponent)
213  {
214  if (static_cast<limb_type>(_exponent) & 1) // If exponent is odd.
215  result *= _base;
216  _base *= _base;
217  _exponent >>= 1;
218  }
219  return result;
220 }
TRACE_OP
#define TRACE_OP(level, pc, op)
Definition: LegacyVMConfig.h:112
byte
uint8_t byte
Definition: Common.h:57
dev::eth
Definition: BasicAuthority.h:32
dev::eth::InstructionInfo::ret
int const ret
Number of items placed (back) on the stack by this instruction, assuming args items were removed.
Definition: Instruction.h:252
dev::eth::InstructionInfo::args
int const args
Number of items required on the stack for this instruction (and, for the purposes of ret,...
Definition: Instruction.h:251
TRACE_POST_OPT
#define TRACE_POST_OPT(level, pc, op)
Definition: LegacyVMConfig.h:114
dev::eth::InstructionInfo
Information structure for a particular instruction.
Definition: Instruction.h:249
dev::eth::InstructionInfo::gasPriceTier
Tier const gasPriceTier
Tier for gas pricing.
Definition: Instruction.h:253
dev::eth::Instruction
Instruction
Virtual machine bytecode instruction.
Definition: Instruction.h:29
LegacyVM.h
std
Definition: FixedHash.h:393
dev::u256
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void > > u256
Definition: Common.h:121
dev
Definition: Address.cpp:21
TRACE_PRE_OPT
#define TRACE_PRE_OPT(level, pc, op)
Definition: LegacyVMConfig.h:113
TRACE_VAL
#define TRACE_VAL(level, name, val)
Definition: LegacyVMConfig.h:111
TRACE_STR
#define TRACE_STR(level, str)
Definition: LegacyVMConfig.h:110
dev::eth::instructionInfo
InstructionInfo instructionInfo(Instruction _inst)
Information on all the instructions.
Definition: Instruction.cpp:220