github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/core/vm/vm_jit.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // +build evmjit
    18  
    19  package vm
    20  
    21  /*
    22  
    23  void* evmjit_create();
    24  int   evmjit_run(void* _jit, void* _data, void* _env);
    25  void  evmjit_destroy(void* _jit);
    26  
    27  // Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
    28  // More: https://github.com/ethereum/evmjit
    29  #cgo LDFLAGS: -levmjit
    30  */
    31  import "C"
    32  
    33  import (
    34  	"bytes"
    35  	"errors"
    36  	"fmt"
    37  	"math/big"
    38  	"unsafe"
    39  
    40  	"github.com/ethereum/go-ethereum/core/state"
    41  	"github.com/ethereum/go-ethereum/crypto"
    42  	"github.com/ethereum/go-ethereum/params"
    43  )
    44  
    45  type JitVm struct {
    46  	env        Environment
    47  	me         ContextRef
    48  	callerAddr []byte
    49  	price      *big.Int
    50  	data       RuntimeData
    51  }
    52  
    53  type i256 [32]byte
    54  
    55  type RuntimeData struct {
    56  	gas          int64
    57  	gasPrice     int64
    58  	callData     *byte
    59  	callDataSize uint64
    60  	address      i256
    61  	caller       i256
    62  	origin       i256
    63  	callValue    i256
    64  	coinBase     i256
    65  	difficulty   i256
    66  	gasLimit     i256
    67  	number       uint64
    68  	timestamp    int64
    69  	code         *byte
    70  	codeSize     uint64
    71  	codeHash     i256
    72  }
    73  
    74  func hash2llvm(h []byte) i256 {
    75  	var m i256
    76  	copy(m[len(m)-len(h):], h) // right aligned copy
    77  	return m
    78  }
    79  
    80  func llvm2hash(m *i256) []byte {
    81  	return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
    82  }
    83  
    84  func llvm2hashRef(m *i256) []byte {
    85  	return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
    86  }
    87  
    88  func address2llvm(addr []byte) i256 {
    89  	n := hash2llvm(addr)
    90  	bswap(&n)
    91  	return n
    92  }
    93  
    94  // bswap swap bytes of the 256-bit integer on LLVM side
    95  // TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
    96  func bswap(m *i256) *i256 {
    97  	for i, l := 0, len(m); i < l/2; i++ {
    98  		m[i], m[l-i-1] = m[l-i-1], m[i]
    99  	}
   100  	return m
   101  }
   102  
   103  func trim(m []byte) []byte {
   104  	skip := 0
   105  	for i := 0; i < len(m); i++ {
   106  		if m[i] == 0 {
   107  			skip++
   108  		} else {
   109  			break
   110  		}
   111  	}
   112  	return m[skip:]
   113  }
   114  
   115  func getDataPtr(m []byte) *byte {
   116  	var p *byte
   117  	if len(m) > 0 {
   118  		p = &m[0]
   119  	}
   120  	return p
   121  }
   122  
   123  func big2llvm(n *big.Int) i256 {
   124  	m := hash2llvm(n.Bytes())
   125  	bswap(&m)
   126  	return m
   127  }
   128  
   129  func llvm2big(m *i256) *big.Int {
   130  	n := big.NewInt(0)
   131  	for i := 0; i < len(m); i++ {
   132  		b := big.NewInt(int64(m[i]))
   133  		b.Lsh(b, uint(i)*8)
   134  		n.Add(n, b)
   135  	}
   136  	return n
   137  }
   138  
   139  // llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
   140  // User must asure that referenced memory is available to Go until the data is copied or not needed any more
   141  func llvm2bytesRef(data *byte, length uint64) []byte {
   142  	if length == 0 {
   143  		return nil
   144  	}
   145  	if data == nil {
   146  		panic("Unexpected nil data pointer")
   147  	}
   148  	return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
   149  }
   150  
   151  func untested(condition bool, message string) {
   152  	if condition {
   153  		panic("Condition `" + message + "` tested. Remove assert.")
   154  	}
   155  }
   156  
   157  func assert(condition bool, message string) {
   158  	if !condition {
   159  		panic("Assert `" + message + "` failed!")
   160  	}
   161  }
   162  
   163  func NewJitVm(env Environment) *JitVm {
   164  	return &JitVm{env: env}
   165  }
   166  
   167  func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
   168  	// TODO: depth is increased but never checked by VM. VM should not know about it at all.
   169  	self.env.SetDepth(self.env.Depth() + 1)
   170  
   171  	// TODO: Move it to Env.Call() or sth
   172  	if Precompiled[string(me.Address())] != nil {
   173  		// if it's address of precopiled contract
   174  		// fallback to standard VM
   175  		stdVm := New(self.env)
   176  		return stdVm.Run(me, caller, code, value, gas, price, callData)
   177  	}
   178  
   179  	if self.me != nil {
   180  		panic("JitVm.Run() can be called only once per JitVm instance")
   181  	}
   182  
   183  	self.me = me
   184  	self.callerAddr = caller.Address()
   185  	self.price = price
   186  
   187  	self.data.gas = gas.Int64()
   188  	self.data.gasPrice = price.Int64()
   189  	self.data.callData = getDataPtr(callData)
   190  	self.data.callDataSize = uint64(len(callData))
   191  	self.data.address = address2llvm(self.me.Address())
   192  	self.data.caller = address2llvm(caller.Address())
   193  	self.data.origin = address2llvm(self.env.Origin())
   194  	self.data.callValue = big2llvm(value)
   195  	self.data.coinBase = address2llvm(self.env.Coinbase())
   196  	self.data.difficulty = big2llvm(self.env.Difficulty())
   197  	self.data.gasLimit = big2llvm(self.env.GasLimit())
   198  	self.data.number = self.env.BlockNumber().Uint64()
   199  	self.data.timestamp = self.env.Time()
   200  	self.data.code = getDataPtr(code)
   201  	self.data.codeSize = uint64(len(code))
   202  	self.data.codeHash = hash2llvm(crypto.Sha3(code)) // TODO: Get already computed hash?
   203  
   204  	jit := C.evmjit_create()
   205  	retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
   206  
   207  	if retCode < 0 {
   208  		err = errors.New("OOG from JIT")
   209  		gas.SetInt64(0) // Set gas to 0, JIT does not bother
   210  	} else {
   211  		gas.SetInt64(self.data.gas)
   212  		if retCode == 1 { // RETURN
   213  			ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
   214  		} else if retCode == 2 { // SUICIDE
   215  			// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
   216  			state := self.Env().State()
   217  			receiverAddr := llvm2hashRef(bswap(&self.data.address))
   218  			receiver := state.GetOrNewStateObject(receiverAddr)
   219  			balance := state.GetBalance(me.Address())
   220  			receiver.AddBalance(balance)
   221  			state.Delete(me.Address())
   222  		}
   223  	}
   224  
   225  	C.evmjit_destroy(jit)
   226  	return
   227  }
   228  
   229  func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
   230  	return self
   231  }
   232  
   233  func (self *JitVm) Endl() VirtualMachine {
   234  	return self
   235  }
   236  
   237  func (self *JitVm) Env() Environment {
   238  	return self.env
   239  }
   240  
   241  //export env_sha3
   242  func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
   243  	data := llvm2bytesRef(dataPtr, length)
   244  	hash := crypto.Sha3(data)
   245  	result := (*i256)(resultPtr)
   246  	*result = hash2llvm(hash)
   247  }
   248  
   249  //export env_sstore
   250  func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
   251  	vm := (*JitVm)(vmPtr)
   252  	index := llvm2hash(bswap((*i256)(indexPtr)))
   253  	value := llvm2hash(bswap((*i256)(valuePtr)))
   254  	value = trim(value)
   255  	if len(value) == 0 {
   256  		prevValue := vm.env.State().GetState(vm.me.Address(), index)
   257  		if len(prevValue) != 0 {
   258  			vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
   259  		}
   260  	}
   261  
   262  	vm.env.State().SetState(vm.me.Address(), index, value)
   263  }
   264  
   265  //export env_sload
   266  func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
   267  	vm := (*JitVm)(vmPtr)
   268  	index := llvm2hash(bswap((*i256)(indexPtr)))
   269  	value := vm.env.State().GetState(vm.me.Address(), index)
   270  	result := (*i256)(resultPtr)
   271  	*result = hash2llvm(value)
   272  	bswap(result)
   273  }
   274  
   275  //export env_balance
   276  func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
   277  	vm := (*JitVm)(_vm)
   278  	addr := llvm2hash((*i256)(_addr))
   279  	balance := vm.Env().State().GetBalance(addr)
   280  	result := (*i256)(_result)
   281  	*result = big2llvm(balance)
   282  }
   283  
   284  //export env_blockhash
   285  func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
   286  	vm := (*JitVm)(_vm)
   287  	number := llvm2big((*i256)(_number))
   288  	result := (*i256)(_result)
   289  
   290  	currNumber := vm.Env().BlockNumber()
   291  	limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
   292  	if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
   293  		hash := vm.Env().GetHash(uint64(number.Int64()))
   294  		*result = hash2llvm(hash)
   295  	} else {
   296  		*result = i256{}
   297  	}
   298  }
   299  
   300  //export env_call
   301  func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
   302  	vm := (*JitVm)(_vm)
   303  
   304  	//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())
   305  
   306  	defer func() {
   307  		if r := recover(); r != nil {
   308  			fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
   309  		}
   310  	}()
   311  
   312  	balance := vm.Env().State().GetBalance(vm.me.Address())
   313  	value := llvm2big((*i256)(_value))
   314  
   315  	if balance.Cmp(value) >= 0 {
   316  		receiveAddr := llvm2hash((*i256)(_receiveAddr))
   317  		inData := C.GoBytes(inDataPtr, C.int(inDataLen))
   318  		outData := llvm2bytesRef(outDataPtr, outDataLen)
   319  		codeAddr := llvm2hash((*i256)(_codeAddr))
   320  		gas := big.NewInt(*_gas)
   321  		var out []byte
   322  		var err error
   323  		if bytes.Equal(codeAddr, receiveAddr) {
   324  			out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
   325  		} else {
   326  			out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
   327  		}
   328  		*_gas = gas.Int64()
   329  		if err == nil {
   330  			copy(outData, out)
   331  			return true
   332  		}
   333  	}
   334  
   335  	return false
   336  }
   337  
   338  //export env_create
   339  func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
   340  	vm := (*JitVm)(_vm)
   341  
   342  	value := llvm2big((*i256)(_value))
   343  	initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
   344  	result := (*i256)(_result)
   345  	*result = i256{}
   346  
   347  	gas := big.NewInt(*_gas)
   348  	ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
   349  	if suberr == nil {
   350  		dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
   351  		dataGas.Mul(dataGas, params.CreateDataGas)
   352  		gas.Sub(gas, dataGas)
   353  		*result = hash2llvm(ref.Address())
   354  	}
   355  	*_gas = gas.Int64()
   356  }
   357  
   358  //export env_log
   359  func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
   360  	vm := (*JitVm)(_vm)
   361  
   362  	data := C.GoBytes(dataPtr, C.int(dataLen))
   363  
   364  	topics := make([][]byte, 0, 4)
   365  	if _topic1 != nil {
   366  		topics = append(topics, llvm2hash((*i256)(_topic1)))
   367  	}
   368  	if _topic2 != nil {
   369  		topics = append(topics, llvm2hash((*i256)(_topic2)))
   370  	}
   371  	if _topic3 != nil {
   372  		topics = append(topics, llvm2hash((*i256)(_topic3)))
   373  	}
   374  	if _topic4 != nil {
   375  		topics = append(topics, llvm2hash((*i256)(_topic4)))
   376  	}
   377  
   378  	vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64()))
   379  }
   380  
   381  //export env_extcode
   382  func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
   383  	vm := (*JitVm)(_vm)
   384  	addr := llvm2hash((*i256)(_addr))
   385  	code := vm.Env().State().GetCode(addr)
   386  	*o_size = uint64(len(code))
   387  	return getDataPtr(code)
   388  }