From d5f38f5690caeb30794e62d4a1b2683a6107cfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 22 Jan 2015 18:00:15 +0100 Subject: JitVM: the EVM JIT bridge --- vm/vm_jit.go | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- vm/vm_jit_fake.go | 10 ++ 2 files changed, 364 insertions(+), 9 deletions(-) create mode 100644 vm/vm_jit_fake.go (limited to 'vm') diff --git a/vm/vm_jit.go b/vm/vm_jit.go index 0882bcf0c..eaebc0749 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -1,31 +1,376 @@ +// +build evmjit + package vm -import "math/big" +/* +#include +#include + +struct evmjit_result +{ + int32_t returnCode; + uint64_t returnDataSize; + void* returnData; +}; + +struct evmjit_result evmjit_run(void* _data, void* _env); + +#cgo LDFLAGS: -levmjit +*/ +import "C" + +import ( + "bytes" + "errors" + "fmt" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/state" + "math/big" + "unsafe" +) type JitVm struct { - env Environment - backup *Vm + env Environment + me ContextRef + callerAddr []byte + price *big.Int + data RuntimeData +} + +type i256 [32]byte + +const ( + Gas = iota + address + Caller + Origin + CallValue + CallDataSize + GasPrice + CoinBase + TimeStamp + Number + Difficulty + GasLimit + CodeSize + + _size + + ReturnDataOffset = CallValue // Reuse 2 fields for return data reference + ReturnDataSize = CallDataSize + SuicideDestAddress = address ///< Suicide balance destination address +) + +type RuntimeData struct { + elems [_size]i256 + callData *byte + code *byte +} + +func hash2llvm(h []byte) i256 { + var m i256 + copy(m[len(m)-len(h):], h) // right aligned copy + return m +} + +func llvm2hash(m *i256) []byte { + if len(m) != 32 { + panic("I don't know Go!") + } + return C.GoBytes(unsafe.Pointer(m), 32) +} + +func address2llvm(addr []byte) i256 { + n := hash2llvm(addr) + bswap(&n) + return n +} + +func bswap(m *i256) *i256 { + for i, l := 0, len(m); i < l/2; i++ { + m[i], m[l-i-1] = m[l-i-1], m[i] + } + return m +} + +func trim(m []byte) []byte { + skip := 0 + for i := 0; i < len(m); i++ { + if m[i] == 0 { + skip++ + } else { + break + } + } + return m[skip:] +} + +func getDataPtr(m []byte) *byte { + var p *byte + if len(m) > 0 { + p = &m[0] + } + return p +} + +func big2llvm(n *big.Int) i256 { + m := hash2llvm(n.Bytes()) + bswap(&m) + return m +} + +func llvm2big(m *i256) *big.Int { + n := big.NewInt(0) + for i := 0; i < len(m); i++ { + b := big.NewInt(int64(m[i])) + b.Lsh(b, uint(i)*8) + n.Add(n, b) + } + return n +} + +func llvm2bytes(data *byte, length uint64) []byte { + if length == 0 { + return nil + } + if data == nil { + panic("llvm2bytes: nil pointer to data") + } + return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length] +} + +func untested(condition bool, message string) { + if condition { + panic("Condition `" + message + "` tested. Remove assert.") + } +} + +func assert(condition bool, message string) { + if !condition { + panic("Assert `" + message + "` failed!") + } } func NewJitVm(env Environment) *JitVm { - backupVm := New(env) - return &JitVm{env: env, backup: backupVm} + return &JitVm{env: env} } func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { - return self.backup.Run(me, caller, code, value, gas, price, callData) + self.env.SetDepth(self.env.Depth() + 1) + + if Precompiled[string(me.Address())] != nil { + // if it's address of precopiled contract + // fallback to standard VM + stdVm := New(self.env) + return stdVm.Run(me, caller, code, value, gas, price, callData) + } + + self.me = me // FIXME: Make sure Run() is not used more than once + self.callerAddr = caller.Address() + self.price = price + + self.data.elems[Gas] = big2llvm(gas) + self.data.elems[address] = address2llvm(self.me.Address()) + self.data.elems[Caller] = address2llvm(caller.Address()) + self.data.elems[Origin] = address2llvm(self.env.Origin()) + self.data.elems[CallValue] = big2llvm(value) + self.data.elems[CallDataSize] = big2llvm(big.NewInt(int64(len(callData)))) // TODO: Keep call data size as i64 + self.data.elems[GasPrice] = big2llvm(price) + self.data.elems[CoinBase] = address2llvm(self.env.Coinbase()) + self.data.elems[TimeStamp] = big2llvm(big.NewInt(self.env.Time())) // TODO: Keep timestamp as i64 + self.data.elems[Number] = big2llvm(self.env.BlockNumber()) + self.data.elems[Difficulty] = big2llvm(self.env.Difficulty()) + self.data.elems[GasLimit] = big2llvm(self.env.GasLimit()) + self.data.elems[CodeSize] = big2llvm(big.NewInt(int64(len(code)))) // TODO: Keep code size as i64 + self.data.callData = getDataPtr(callData) + self.data.code = getDataPtr(code) + + result := C.evmjit_run(unsafe.Pointer(&self.data), unsafe.Pointer(self)) + //fmt.Printf("JIT result: %d\n", r) + + if result.returnCode >= 100 { + err = errors.New("OOG from JIT") + gas.SetInt64(0) // Set gas to 0, JIT does not bother + } else { + gasLeft := llvm2big(&self.data.elems[Gas]) // TODO: Set value directly to gas instance + gas.Set(gasLeft) + if result.returnCode == 1 { // RETURN + ret = C.GoBytes(result.returnData, C.int(result.returnDataSize)) + C.free(result.returnData) + } else if result.returnCode == 2 { // SUICIDE + state := self.Env().State() + receiverAddr := llvm2hash(bswap(&self.data.elems[address])) + receiverAddr = trim(receiverAddr) // TODO: trim all zeros or subslice 160bits? + receiver := state.GetOrNewStateObject(receiverAddr) + balance := state.GetBalance(me.Address()) + receiver.AddAmount(balance) + state.Delete(me.Address()) + } + } + + return } func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine { - return self.backup.Printf(format, v) + return self } func (self *JitVm) Endl() VirtualMachine { - return self.backup.Endl() + return self } func (self *JitVm) Env() Environment { return self.env } -//go is nice +//export env_sha3 +func env_sha3(dataPtr unsafe.Pointer, length uint64, resultPtr unsafe.Pointer) { + data := C.GoBytes(dataPtr, C.int(length)) + hash := crypto.Sha3(data) + result := (*i256)(resultPtr) + *result = hash2llvm(hash) +} + +//export env_sstore +func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) { + vm := (*JitVm)(vmPtr) + index := llvm2hash(bswap((*i256)(indexPtr))) + value := llvm2hash(bswap((*i256)(valuePtr))) + value = trim(value) + if len(value) == 0 { + prevValue := vm.env.State().GetState(vm.me.Address(), index) + if len(prevValue) != 0 { + vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund) + } + } + + vm.env.State().SetState(vm.me.Address(), index, value) +} + +//export env_sload +func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) { + vm := (*JitVm)(vmPtr) + index := llvm2hash(bswap((*i256)(indexPtr))) + value := vm.env.State().GetState(vm.me.Address(), index) + result := (*i256)(resultPtr) + *result = hash2llvm(value) + bswap(result) +} + +//export env_balance +func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + addr := llvm2hash((*i256)(_addr)) + balance := vm.Env().State().GetBalance(addr) + result := (*i256)(_result) + *result = big2llvm(balance) +} + +//export env_blockhash +func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + number := llvm2big((*i256)(_number)) + result := (*i256)(_result) + + currNumber := vm.Env().BlockNumber() + limit := big.NewInt(0).Sub(currNumber, big.NewInt(256)) + if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 { + hash := vm.Env().GetHash(uint64(number.Int64())) + *result = hash2llvm(hash) + } else { + *result = i256{} + } +} + +//export env_call +func env_call(_vm unsafe.Pointer, _gas unsafe.Pointer, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool { + vm := (*JitVm)(_vm) + + //fmt.Printf("env_call (depth %d)\n", vm.Env().Depth()) + + defer func() { + if r := recover(); r != nil { + fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r) + } + }() + + balance := vm.Env().State().GetBalance(vm.me.Address()) + value := llvm2big((*i256)(_value)) + + if balance.Cmp(value) >= 0 { + receiveAddr := llvm2hash((*i256)(_receiveAddr)) + inData := C.GoBytes(inDataPtr, C.int(inDataLen)) + outData := llvm2bytes(outDataPtr, outDataLen) + codeAddr := llvm2hash((*i256)(_codeAddr)) + llvmGas := (*i256)(_gas) + gas := llvm2big(llvmGas) + var out []byte + var err error + if bytes.Equal(codeAddr, receiveAddr) { + out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value) + } else { + out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value) + } + *llvmGas = big2llvm(gas) + if err == nil { + copy(outData, out) + return true + } + } + + return false +} + +//export env_create +func env_create(_vm unsafe.Pointer, _gas unsafe.Pointer, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + + value := llvm2big((*i256)(_value)) + initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance + result := (*i256)(_result) + *result = i256{} + + llvmGas := (*i256)(_gas) + gas := llvm2big(llvmGas) + + ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value) + if suberr == nil { + dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter + dataGas.Mul(dataGas, GasCreateByte) + gas.Sub(gas, dataGas) + *result = hash2llvm(ref.Address()) + } + *llvmGas = big2llvm(gas) +} + +//export env_log +func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) { + vm := (*JitVm)(_vm) + + data := C.GoBytes(dataPtr, C.int(dataLen)) + + topics := make([][]byte, 0, 4) + if _topic1 != nil { + topics = append(topics, llvm2hash((*i256)(_topic1))) + } + if _topic2 != nil { + topics = append(topics, llvm2hash((*i256)(_topic2))) + } + if _topic3 != nil { + topics = append(topics, llvm2hash((*i256)(_topic3))) + } + if _topic4 != nil { + topics = append(topics, llvm2hash((*i256)(_topic4))) + } + + vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data)) +} + +//export env_extcode +func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte { + vm := (*JitVm)(_vm) + addr := llvm2hash((*i256)(_addr)) + code := vm.Env().State().GetCode(addr) + *o_size = uint64(len(code)) + return getDataPtr(code) +} diff --git a/vm/vm_jit_fake.go b/vm/vm_jit_fake.go new file mode 100644 index 000000000..d6b5be45b --- /dev/null +++ b/vm/vm_jit_fake.go @@ -0,0 +1,10 @@ +// +build !evmjit + +package vm + +import "fmt" + +func NewJitVm(env Environment) VirtualMachine { + fmt.Printf("Warning! EVM JIT not enabled.\n") + return New(env) +} -- cgit v1.2.3 From 94106cc41ff3795b9b13044f5625c2e1089795f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 23 Jan 2015 16:45:22 +0100 Subject: JitVm code cleanups & refactoring. Some memory copies eliminated (i.e. in SHA3 calculation) --- vm/vm_jit.go | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'vm') diff --git a/vm/vm_jit.go b/vm/vm_jit.go index eaebc0749..0150a4eef 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -15,6 +15,8 @@ struct evmjit_result struct evmjit_result evmjit_run(void* _data, void* _env); +// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib +// More: https://github.com/ethereum/evmjit #cgo LDFLAGS: -levmjit */ import "C" @@ -74,10 +76,11 @@ func hash2llvm(h []byte) i256 { } func llvm2hash(m *i256) []byte { - if len(m) != 32 { - panic("I don't know Go!") - } - return C.GoBytes(unsafe.Pointer(m), 32) + return C.GoBytes(unsafe.Pointer(m), C.int(len(m))) +} + +func llvm2hashRef(m *i256) []byte { + return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)] } func address2llvm(addr []byte) i256 { @@ -86,6 +89,8 @@ func address2llvm(addr []byte) i256 { return n } +// bswap swap bytes of the 256-bit integer on LLVM side +// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations func bswap(m *i256) *i256 { for i, l := 0, len(m); i < l/2; i++ { m[i], m[l-i-1] = m[l-i-1], m[i] @@ -129,12 +134,14 @@ func llvm2big(m *i256) *big.Int { return n } -func llvm2bytes(data *byte, length uint64) []byte { +// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC) +// User must asure that referenced memory is available to Go until the data is copied or not needed any more +func llvm2bytesRef(data *byte, length uint64) []byte { if length == 0 { return nil } if data == nil { - panic("llvm2bytes: nil pointer to data") + panic("Unexpected nil data pointer") } return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length] } @@ -156,8 +163,10 @@ func NewJitVm(env Environment) *JitVm { } func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { + // TODO: depth is increased but never checked by VM. VM should not know about it at all. self.env.SetDepth(self.env.Depth() + 1) + // TODO: Move it to Env.Call() or sth if Precompiled[string(me.Address())] != nil { // if it's address of precopiled contract // fallback to standard VM @@ -165,7 +174,11 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi return stdVm.Run(me, caller, code, value, gas, price, callData) } - self.me = me // FIXME: Make sure Run() is not used more than once + if self.me != nil { + panic("JitVm.Run() can be called only once per JitVm instance") + } + + self.me = me self.callerAddr = caller.Address() self.price = price @@ -186,7 +199,6 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi self.data.code = getDataPtr(code) result := C.evmjit_run(unsafe.Pointer(&self.data), unsafe.Pointer(self)) - //fmt.Printf("JIT result: %d\n", r) if result.returnCode >= 100 { err = errors.New("OOG from JIT") @@ -198,9 +210,9 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi ret = C.GoBytes(result.returnData, C.int(result.returnDataSize)) C.free(result.returnData) } else if result.returnCode == 2 { // SUICIDE + // TODO: Suicide support logic should be moved to Env to be shared by VM implementations state := self.Env().State() - receiverAddr := llvm2hash(bswap(&self.data.elems[address])) - receiverAddr = trim(receiverAddr) // TODO: trim all zeros or subslice 160bits? + receiverAddr := llvm2hashRef(bswap(&self.data.elems[address])) receiver := state.GetOrNewStateObject(receiverAddr) balance := state.GetBalance(me.Address()) receiver.AddAmount(balance) @@ -224,8 +236,8 @@ func (self *JitVm) Env() Environment { } //export env_sha3 -func env_sha3(dataPtr unsafe.Pointer, length uint64, resultPtr unsafe.Pointer) { - data := C.GoBytes(dataPtr, C.int(length)) +func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) { + data := llvm2bytesRef(dataPtr, length) hash := crypto.Sha3(data) result := (*i256)(resultPtr) *result = hash2llvm(hash) @@ -300,7 +312,7 @@ func env_call(_vm unsafe.Pointer, _gas unsafe.Pointer, _receiveAddr unsafe.Point if balance.Cmp(value) >= 0 { receiveAddr := llvm2hash((*i256)(_receiveAddr)) inData := C.GoBytes(inDataPtr, C.int(inDataLen)) - outData := llvm2bytes(outDataPtr, outDataLen) + outData := llvm2bytesRef(outDataPtr, outDataLen) codeAddr := llvm2hash((*i256)(_codeAddr)) llvmGas := (*i256)(_gas) gas := llvm2big(llvmGas) -- cgit v1.2.3 From c71aff99dbb45d5998049604109457aa34ecf573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 26 Jan 2015 16:13:55 +0100 Subject: Update EVM JIT runtime data layout --- vm/vm_jit.go | 71 +++++++++++++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 41 deletions(-) (limited to 'vm') diff --git a/vm/vm_jit.go b/vm/vm_jit.go index 0150a4eef..4c0988df9 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -41,32 +41,22 @@ type JitVm struct { type i256 [32]byte -const ( - Gas = iota - address - Caller - Origin - CallValue - CallDataSize - GasPrice - CoinBase - TimeStamp - Number - Difficulty - GasLimit - CodeSize - - _size - - ReturnDataOffset = CallValue // Reuse 2 fields for return data reference - ReturnDataSize = CallDataSize - SuicideDestAddress = address ///< Suicide balance destination address -) - type RuntimeData struct { - elems [_size]i256 - callData *byte - code *byte + gas int64 + gasPrice int64 + callData *byte + callDataSize uint64 + address i256 + caller i256 + origin i256 + callValue i256 + coinBase i256 + difficulty i256 + gasLimit i256 + number uint64 + timestamp int64 + code *byte + codeSize uint64 } func hash2llvm(h []byte) i256 { @@ -182,21 +172,21 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi self.callerAddr = caller.Address() self.price = price - self.data.elems[Gas] = big2llvm(gas) - self.data.elems[address] = address2llvm(self.me.Address()) - self.data.elems[Caller] = address2llvm(caller.Address()) - self.data.elems[Origin] = address2llvm(self.env.Origin()) - self.data.elems[CallValue] = big2llvm(value) - self.data.elems[CallDataSize] = big2llvm(big.NewInt(int64(len(callData)))) // TODO: Keep call data size as i64 - self.data.elems[GasPrice] = big2llvm(price) - self.data.elems[CoinBase] = address2llvm(self.env.Coinbase()) - self.data.elems[TimeStamp] = big2llvm(big.NewInt(self.env.Time())) // TODO: Keep timestamp as i64 - self.data.elems[Number] = big2llvm(self.env.BlockNumber()) - self.data.elems[Difficulty] = big2llvm(self.env.Difficulty()) - self.data.elems[GasLimit] = big2llvm(self.env.GasLimit()) - self.data.elems[CodeSize] = big2llvm(big.NewInt(int64(len(code)))) // TODO: Keep code size as i64 + self.data.gas = gas.Int64() + self.data.gasPrice = price.Int64() self.data.callData = getDataPtr(callData) + self.data.callDataSize = uint64(len(callData)) + self.data.address = address2llvm(self.me.Address()) + self.data.caller = address2llvm(caller.Address()) + self.data.origin = address2llvm(self.env.Origin()) + self.data.callValue = big2llvm(value) + self.data.coinBase = address2llvm(self.env.Coinbase()) + self.data.difficulty = big2llvm(self.env.Difficulty()) + self.data.gasLimit = big2llvm(self.env.GasLimit()) + self.data.number = self.env.BlockNumber().Uint64() + self.data.timestamp = self.env.Time() self.data.code = getDataPtr(code) + self.data.codeSize = uint64(len(code)) result := C.evmjit_run(unsafe.Pointer(&self.data), unsafe.Pointer(self)) @@ -204,15 +194,14 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi err = errors.New("OOG from JIT") gas.SetInt64(0) // Set gas to 0, JIT does not bother } else { - gasLeft := llvm2big(&self.data.elems[Gas]) // TODO: Set value directly to gas instance - gas.Set(gasLeft) + gas.SetInt64(self.data.gas) if result.returnCode == 1 { // RETURN ret = C.GoBytes(result.returnData, C.int(result.returnDataSize)) C.free(result.returnData) } else if result.returnCode == 2 { // SUICIDE // TODO: Suicide support logic should be moved to Env to be shared by VM implementations state := self.Env().State() - receiverAddr := llvm2hashRef(bswap(&self.data.elems[address])) + receiverAddr := llvm2hashRef(bswap(&self.data.address)) receiver := state.GetOrNewStateObject(receiverAddr) balance := state.GetBalance(me.Address()) receiver.AddAmount(balance) -- cgit v1.2.3 From 079c59b929d5e09eebad6169c22f146bd2f060af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 26 Jan 2015 18:02:09 +0100 Subject: Update JitVm to new EVM JIT ABI (C interface) --- vm/vm_jit.go | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'vm') diff --git a/vm/vm_jit.go b/vm/vm_jit.go index 4c0988df9..38cab57da 100644 --- a/vm/vm_jit.go +++ b/vm/vm_jit.go @@ -3,17 +3,10 @@ package vm /* -#include -#include -struct evmjit_result -{ - int32_t returnCode; - uint64_t returnDataSize; - void* returnData; -}; - -struct evmjit_result evmjit_run(void* _data, void* _env); +void* evmjit_create(); +int evmjit_run(void* _jit, void* _data, void* _env); +void evmjit_destroy(void* _jit); // Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib // More: https://github.com/ethereum/evmjit @@ -188,17 +181,17 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi self.data.code = getDataPtr(code) self.data.codeSize = uint64(len(code)) - result := C.evmjit_run(unsafe.Pointer(&self.data), unsafe.Pointer(self)) + jit := C.evmjit_create() + retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self)) - if result.returnCode >= 100 { + if retCode < 0 { err = errors.New("OOG from JIT") gas.SetInt64(0) // Set gas to 0, JIT does not bother } else { gas.SetInt64(self.data.gas) - if result.returnCode == 1 { // RETURN - ret = C.GoBytes(result.returnData, C.int(result.returnDataSize)) - C.free(result.returnData) - } else if result.returnCode == 2 { // SUICIDE + if retCode == 1 { // RETURN + ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize)) + } else if retCode == 2 { // SUICIDE // TODO: Suicide support logic should be moved to Env to be shared by VM implementations state := self.Env().State() receiverAddr := llvm2hashRef(bswap(&self.data.address)) @@ -208,7 +201,8 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi state.Delete(me.Address()) } } - + + C.evmjit_destroy(jit); return } -- cgit v1.2.3 From 84adf77bf3492351de82f0ec820a1d280e85a5cd Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 29 Jan 2015 13:10:34 +0100 Subject: Added RPC "Call" for JS calls to contracts --- vm/vm.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'vm') diff --git a/vm/vm.go b/vm/vm.go index 37e4249f5..4364b1cb9 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -634,6 +634,8 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I continue } + self.Printf(" ~> false") + case JUMPDEST: case PC: stack.Push(big.NewInt(int64(pc))) -- cgit v1.2.3 From d52878c744fd7acce727feb41c2d4296e56826d3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 1 Feb 2015 15:29:57 +0100 Subject: Removed some VMEnv & Added VmType() to vm.Environment --- vm/environment.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'vm') diff --git a/vm/environment.go b/vm/environment.go index 8ec13ee41..8507e248f 100644 --- a/vm/environment.go +++ b/vm/environment.go @@ -22,6 +22,8 @@ type Environment interface { Transfer(from, to Account, amount *big.Int) error AddLog(state.Log) + VmType() Type + Depth() int SetDepth(i int) -- cgit v1.2.3 From 8ccde784f9035c0a7a8f234994538c817c5b9de7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 1 Feb 2015 15:30:29 +0100 Subject: Added (disabled) Jit validation --- vm/common.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'vm') diff --git a/vm/common.go b/vm/common.go index ff187001f..45a7187a9 100644 --- a/vm/common.go +++ b/vm/common.go @@ -18,6 +18,18 @@ const ( MaxVmTy ) +func NewVm(env Environment) VirtualMachine { + switch env.VmType() { + case JitVmTy: + return NewJitVm(env) + default: + vmlogger.Infoln("unsupported vm type %d", env.VmType()) + fallthrough + case StdVmTy: + return New(env) + } +} + var ( GasStep = big.NewInt(1) GasSha = big.NewInt(10) -- cgit v1.2.3 From faa54e59c1c276d153299c73afdea246941ec952 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 2 Feb 2015 20:01:10 -0800 Subject: Make sure that CALL addr is always 20 bytes --- vm/vm.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'vm') diff --git a/vm/vm.go b/vm/vm.go index 4364b1cb9..69e061e54 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -516,7 +516,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I case BLOCKHASH: num := stack.Pop() - n := U256(new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257)) + n := new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257) if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 { stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64()))) } else { @@ -681,8 +681,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Dbg.SetCode(context.Code) } case CALL, CALLCODE: - self.Endl() - gas := stack.Pop() // Pop gas and value of the stack. value, addr := stack.Popn() @@ -691,6 +689,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I // Pop return size and offset retSize, retOffset := stack.Popn() + address := ethutil.Address(addr.Bytes()) + self.Printf(" => %x", address).Endl() + // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) @@ -699,9 +700,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I err error ) if op == CALLCODE { - ret, err = self.env.CallCode(context, addr.Bytes(), args, gas, price, value) + ret, err = self.env.CallCode(context, address, args, gas, price, value) } else { - ret, err = self.env.Call(context, addr.Bytes(), args, gas, price, value) + ret, err = self.env.Call(context, address, args, gas, price, value) } if err != nil { -- cgit v1.2.3 From 3f03197daebace568a61bf69c06c97e30080a749 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 4 Feb 2015 07:39:02 -0800 Subject: Updated tests --- vm/vm.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'vm') diff --git a/vm/vm.go b/vm/vm.go index 69e061e54..2b8c90428 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -510,7 +510,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I case GASPRICE: stack.Push(context.Price) - self.Printf(" => %v", context.Price) + self.Printf(" => %x", context.Price) // 0x40 range case BLOCKHASH: @@ -643,6 +643,8 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I stack.Push(big.NewInt(int64(mem.Len()))) case GAS: stack.Push(context.Gas) + + self.Printf(" => %x", context.Gas) // 0x60 range case CREATE: @@ -653,6 +655,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I gas = new(big.Int).Set(context.Gas) addr []byte ) + self.Endl() context.UseGas(context.Gas) ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value) @@ -673,7 +676,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I stack.Push(ethutil.BigD(addr)) - self.Printf(" (*) %x", addr) } // Debug hook -- cgit v1.2.3 From 3f6baa45a7fb1ae5fd7966d2763a2a776a65eb96 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Feb 2015 23:46:45 +0100 Subject: Documented methods & removed old manifest --- vm/vm.go | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'vm') diff --git a/vm/vm.go b/vm/vm.go index 2b8c90428..29e1ade54 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -38,13 +38,6 @@ func New(env Environment) *Vm { func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { self.env.SetDepth(self.env.Depth() + 1) - msg := self.env.State().Manifest().AddMessage(&state.Message{ - To: me.Address(), From: caller.Address(), - Input: callData, - Origin: self.env.Origin(), - Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(), - Value: value, - }) context := NewContext(caller, me, code, gas, price) vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData) @@ -618,8 +611,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I val, loc := stack.Popn() statedb.SetState(context.Address(), loc.Bytes(), val) - msg.AddStorageChange(loc.Bytes()) - self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case JUMP: jump(pc, stack.Pop()) @@ -670,7 +661,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I dataGas.Mul(dataGas, GasCreateByte) if context.UseGas(dataGas) { ref.SetCode(ret) - msg.Output = ret } addr = ref.Address() @@ -713,7 +703,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I vmlogger.Debugln(err) } else { stack.Push(ethutil.BigTrue) - msg.Output = ret mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) } -- cgit v1.2.3