github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/vm/vm_jit.go (about)

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