aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm/vm.go
diff options
context:
space:
mode:
authorJeffrey Wilcke <geffobscura@gmail.com>2015-07-18 05:09:36 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2015-08-07 18:52:17 +0800
commit846f34f78b5f76233655d0cf3611706e99f2efe2 (patch)
tree4e25f0320e30c0ed45b706835fd4377632bdcb45 /core/vm/vm.go
parent698e98d9814605bfea98ba3ad2fe7fda073cb2b1 (diff)
downloaddexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar.gz
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar.bz2
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar.lz
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar.xz
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.tar.zst
dexon-846f34f78b5f76233655d0cf3611706e99f2efe2.zip
core/vm, tests: implemented semi-jit vm
* changed stack and removed stack ptr. Let go decide on slice reuse.
Diffstat (limited to 'core/vm/vm.go')
-rw-r--r--core/vm/vm.go125
1 files changed, 77 insertions, 48 deletions
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 21e0a4665..c292b45d1 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -24,30 +24,19 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
)
// Vm implements VirtualMachine
type Vm struct {
env Environment
-
- err error
- // For logging
- debug bool
-
- BreakPoints []int64
- Stepping bool
- Fn string
-
- Recoverable bool
-
- // Will be called before the vm returns
- After func(*Context, error)
}
// New returns a new Virtual Machine
func New(env Environment) *Vm {
- return &Vm{env: env, debug: Debug, Recoverable: true}
+ return &Vm{env: env}
}
// Run loops and evaluates the contract's code with the given input data
@@ -55,17 +44,67 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
self.env.SetDepth(self.env.Depth() + 1)
defer self.env.SetDepth(self.env.Depth() - 1)
+ // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
+ defer func() {
+ if err != nil {
+ // In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
+ context.UseGas(context.Gas)
+
+ ret = context.Return(nil)
+ }
+ }()
+
+ if context.CodeAddr != nil {
+ if p := Precompiled[context.CodeAddr.Str()]; p != nil {
+ return self.RunPrecompiled(p, input, context)
+ }
+ }
+
+ var (
+ codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching
+ program *Program
+ )
+ if !DisableJit {
+ // Fetch program status.
+ // * If ready run using JIT
+ // * If unknown, compile in a seperate goroutine
+ // * If forced wait for compilation and run once done
+ if status := GetProgramStatus(codehash); status == progReady {
+ return RunProgram(GetProgram(codehash), self.env, context, input)
+ } else if status == progUnknown {
+ if ForceJit {
+ // Create and compile program
+ program = NewProgram(context.Code)
+ perr := CompileProgram(program)
+ if perr == nil {
+ return RunProgram(program, self.env, context, input)
+ }
+ glog.V(logger.Info).Infoln("error compiling program", err)
+ } else {
+ // create and compile the program. Compilation
+ // is done in a seperate goroutine
+ program = NewProgram(context.Code)
+ go func() {
+ err := CompileProgram(program)
+ if err != nil {
+ glog.V(logger.Info).Infoln("error compiling program", err)
+ return
+ }
+ }()
+ }
+ }
+ }
+
var (
caller = context.caller
code = context.Code
value = context.value
price = context.Price
- op OpCode // current opcode
- codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = self.env.State() // current state
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
+ statedb = self.env.State() // current state
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
pc = uint64(0) // program counter
@@ -89,32 +128,25 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
- if self.After != nil {
- self.After(context, err)
- }
-
if err != nil {
self.log(pc, op, context.Gas, cost, mem, stack, context, err)
-
- // In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
- context.UseGas(context.Gas)
-
- ret = context.Return(nil)
}
}()
- if context.CodeAddr != nil {
- if p := Precompiled[context.CodeAddr.Str()]; p != nil {
- return self.RunPrecompiled(p, input, context)
- }
- }
-
// Don't bother with the execution if there's no code.
if len(code) == 0 {
return context.Return(nil), nil
}
for {
+ // Overhead of the atomic read might not be worth it
+ /* TODO this still causes a few issues in the tests
+ if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
+ // move execution
+ glog.V(logger.Info).Infoln("Moved execution to JIT")
+ return runProgram(program, pc, mem, stack, self.env, context, input)
+ }
+ */
// The base for all big integer arithmetic
base := new(big.Int)
@@ -122,7 +154,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
op = context.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(self.env, context, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
@@ -130,11 +162,9 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
if !context.UseGas(cost) {
-
- context.UseGas(context.Gas)
-
- return context.Return(nil), OutOfGasError
+ return nil, OutOfGasError
}
+
// Resize the memory calculated previously
mem.Resize(newMemSize.Uint64())
// Add a log message
@@ -376,7 +406,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
addr := common.BigToAddress(stack.pop())
balance := statedb.GetBalance(addr)
- stack.push(balance)
+ stack.push(new(big.Int).Set(balance))
case ORIGIN:
origin := self.env.Origin()
@@ -388,7 +418,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
stack.push(common.Bytes2Big(caller.Bytes()))
case CALLVALUE:
- stack.push(value)
+ stack.push(new(big.Int).Set(value))
case CALLDATALOAD:
data := getData(input, stack.pop(), common.Big32)
@@ -441,7 +471,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
mem.Set(mOff.Uint64(), l.Uint64(), codeCopy)
case GASPRICE:
- stack.push(context.Price)
+ stack.push(new(big.Int).Set(context.Price))
case BLOCKHASH:
num := stack.pop()
@@ -471,11 +501,11 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case DIFFICULTY:
difficulty := self.env.Difficulty()
- stack.push(difficulty)
+ stack.push(new(big.Int).Set(difficulty))
case GASLIMIT:
- stack.push(self.env.GasLimit())
+ stack.push(new(big.Int).Set(self.env.GasLimit()))
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
size := uint64(op - PUSH1 + 1)
@@ -555,8 +585,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case MSIZE:
stack.push(big.NewInt(int64(mem.Len())))
case GAS:
- stack.push(context.Gas)
-
+ stack.push(new(big.Int).Set(context.Gas))
case CREATE:
var (
@@ -652,7 +681,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory.
-func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
+func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -759,7 +788,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
+ if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
gas.Add(gas, params.CallNewAccountGas)
}
}