Skip to content

Commit 9d61d78

Browse files
committed
core/vm: abstracted instruction execution away from JIT
Moved the execution of instructions to the instruction it self. This will allow for specialised instructions (e.g. segments) to be execution in the same manner as regular instructions.
1 parent 10ed107 commit 9d61d78

File tree

2 files changed

+88
-76
lines changed

2 files changed

+88
-76
lines changed

core/vm/instructions.go

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package vm
1818

1919
import (
20+
"fmt"
2021
"math/big"
2122

2223
"github.com/ethereum/go-ethereum/common"
@@ -25,16 +26,16 @@ import (
2526
)
2627

2728
type programInstruction interface {
28-
Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack)
29+
// executes the program instruction and allows the instruction to modify the state of the program
30+
do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error)
31+
// returns whether the program instruction halts the execution of the JIT
32+
halts() bool
33+
// Returns the current op code (debugging purposes)
34+
Op() OpCode
2935
}
3036

3137
type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack)
3238

33-
// Do executes the function. This implements programInstruction
34-
func (fn instrFn) Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
35-
fn(instr, pc, env, contract, memory, stack)
36-
}
37-
3839
type instruction struct {
3940
op OpCode
4041
pc uint64
@@ -44,6 +45,73 @@ type instruction struct {
4445
gas *big.Int
4546
spop int
4647
spush int
48+
49+
returns bool
50+
}
51+
52+
func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract *Contract, to *big.Int) (uint64, error) {
53+
if !validDest(destinations, to) {
54+
nop := contract.GetOp(to.Uint64())
55+
return 0, fmt.Errorf("invalid jump destination (%v) %v", nop, to)
56+
}
57+
58+
return mapping[to.Uint64()], nil
59+
}
60+
61+
func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) {
62+
// calculate the new memory size and gas price for the current executing opcode
63+
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
// Use the calculated gas. When insufficient gas is present, use all gas and return an
69+
// Out Of Gas error
70+
if !contract.UseGas(cost) {
71+
return nil, OutOfGasError
72+
}
73+
// Resize the memory calculated previously
74+
memory.Resize(newMemSize.Uint64())
75+
76+
// These opcodes return an argument and are thefor handled
77+
// differently from the rest of the opcodes
78+
switch instr.op {
79+
case JUMP:
80+
if pos, err := jump(program.mapping, program.destinations, contract, stack.pop()); err != nil {
81+
return nil, err
82+
} else {
83+
*pc = pos
84+
return nil, nil
85+
}
86+
case JUMPI:
87+
pos, cond := stack.pop(), stack.pop()
88+
if cond.Cmp(common.BigTrue) >= 0 {
89+
if pos, err := jump(program.mapping, program.destinations, contract, pos); err != nil {
90+
return nil, err
91+
} else {
92+
*pc = pos
93+
return nil, nil
94+
}
95+
}
96+
case RETURN:
97+
offset, size := stack.pop(), stack.pop()
98+
return memory.GetPtr(offset.Int64(), size.Int64()), nil
99+
default:
100+
if instr.fn == nil {
101+
return nil, fmt.Errorf("Invalid opcode 0x%x", instr.op)
102+
}
103+
instr.fn(instr, pc, env, contract, memory, stack)
104+
}
105+
*pc++
106+
return nil, nil
107+
}
108+
109+
func (instr instruction) halts() bool {
110+
return instr.returns
111+
}
112+
113+
func (instr instruction) Op() OpCode {
114+
return instr.op
47115
}
48116

49117
func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *stack) {
@@ -536,8 +604,6 @@ func opStop(instr instruction, pc *uint64, env Environment, contract *Contract,
536604
}
537605

538606
func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
539-
//receiver := env.Db().GetOrNewStateObject(common.BigToAddress(stack.pop()))
540-
//receiver.AddBalance(balance)
541607
balance := env.Db().GetBalance(contract.Address())
542608
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
543609

core/vm/jit.go

Lines changed: 14 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ type Program struct {
8686

8787
contract *Contract
8888

89-
instructions []instruction // instruction set
90-
mapping map[uint64]int // real PC mapping to array indices
91-
destinations map[uint64]struct{} // cached jump destinations
89+
instructions []programInstruction // instruction set
90+
mapping map[uint64]uint64 // real PC mapping to array indices
91+
destinations map[uint64]struct{} // cached jump destinations
9292

9393
code []byte
9494
}
@@ -97,7 +97,7 @@ type Program struct {
9797
func NewProgram(code []byte) *Program {
9898
program := &Program{
9999
Id: crypto.Sha3Hash(code),
100-
mapping: make(map[uint64]int),
100+
mapping: make(map[uint64]uint64),
101101
destinations: make(map[uint64]struct{}),
102102
code: code,
103103
}
@@ -118,10 +118,12 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
118118
baseOp = DUP1
119119
}
120120
base := _baseCheck[baseOp]
121-
instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush}
121+
122+
returns := op == RETURN || op == SUICIDE || op == STOP
123+
instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
122124

123125
p.instructions = append(p.instructions, instr)
124-
p.mapping[pc] = len(p.instructions) - 1
126+
p.mapping[pc] = uint64(len(p.instructions) - 1)
125127
}
126128

127129
// CompileProgram compiles the given program and return an error when it fails
@@ -301,21 +303,8 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
301303
contract.Input = input
302304

303305
var (
304-
caller = contract.caller
305-
statedb = env.Db()
306-
pc int = program.mapping[pcstart]
307-
instrCount = 0
308-
309-
jump = func(to *big.Int) error {
310-
if !validDest(program.destinations, to) {
311-
nop := contract.GetOp(to.Uint64())
312-
return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
313-
}
314-
315-
pc = program.mapping[to.Uint64()]
316-
317-
return nil
318-
}
306+
pc uint64 = program.mapping[pcstart]
307+
instrCount = 0
319308
)
320309

321310
if glog.V(logger.Debug) {
@@ -326,62 +315,19 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
326315
}()
327316
}
328317

329-
for pc < len(program.instructions) {
318+
for pc < uint64(len(program.instructions)) {
330319
instrCount++
331320

332321
instr := program.instructions[pc]
333322

334-
// calculate the new memory size and gas price for the current executing opcode
335-
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, caller, instr, statedb, mem, stack)
323+
ret, err := instr.do(program, &pc, env, contract, mem, stack)
336324
if err != nil {
337325
return nil, err
338326
}
339327

340-
// Use the calculated gas. When insufficient gas is present, use all gas and return an
341-
// Out Of Gas error
342-
if !contract.UseGas(cost) {
343-
return nil, OutOfGasError
344-
}
345-
// Resize the memory calculated previously
346-
mem.Resize(newMemSize.Uint64())
347-
348-
// These opcodes return an argument and are thefor handled
349-
// differently from the rest of the opcodes
350-
switch instr.op {
351-
case JUMP:
352-
if err := jump(stack.pop()); err != nil {
353-
return nil, err
354-
}
355-
continue
356-
case JUMPI:
357-
pos, cond := stack.pop(), stack.pop()
358-
359-
if cond.Cmp(common.BigTrue) >= 0 {
360-
if err := jump(pos); err != nil {
361-
return nil, err
362-
}
363-
continue
364-
}
365-
case RETURN:
366-
offset, size := stack.pop(), stack.pop()
367-
ret := mem.GetPtr(offset.Int64(), size.Int64())
368-
328+
if instr.halts() {
369329
return contract.Return(ret), nil
370-
case SUICIDE:
371-
instr.fn(instr, nil, env, contract, mem, stack)
372-
373-
return contract.Return(nil), nil
374-
case STOP:
375-
return contract.Return(nil), nil
376-
default:
377-
if instr.fn == nil {
378-
return nil, fmt.Errorf("Invalid opcode %x", instr.op)
379-
}
380-
381-
instr.fn(instr, nil, env, contract, mem, stack)
382330
}
383-
384-
pc++
385331
}
386332

387333
contract.Input = nil
@@ -403,7 +349,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
403349

404350
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
405351
// the operation. This does not reduce gas or resizes the memory.
406-
func jitCalculateGasAndSize(env Environment, contract *Contract, caller ContractRef, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
352+
func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
407353
var (
408354
gas = new(big.Int)
409355
newMemSize *big.Int = new(big.Int)

0 commit comments

Comments
 (0)