vm_jit.go 10.5 KB
Newer Older
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 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
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 13 14
// GNU Lesser General Public License for more details.
//
// 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 18
// +build evmjit

19 20
package vm

21 22
/*

23 24 25
void* evmjit_create();
int   evmjit_run(void* _jit, void* _data, void* _env);
void  evmjit_destroy(void* _jit);
26

27 28
// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
// More: https://github.com/ethereum/evmjit
29 30 31 32 33 34 35 36 37 38
#cgo LDFLAGS: -levmjit
*/
import "C"

import (
	"bytes"
	"errors"
	"fmt"
	"math/big"
	"unsafe"
Felix Lange's avatar
Felix Lange committed
39 40 41 42

	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/params"
43
)
44 45

type JitVm struct {
46 47 48 49 50 51 52 53 54 55
	env        Environment
	me         ContextRef
	callerAddr []byte
	price      *big.Int
	data       RuntimeData
}

type i256 [32]byte

type RuntimeData struct {
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
	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
71
	codeHash     i256
72 73 74 75 76 77 78 79 80
}

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 {
81 82 83 84 85
	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)]
86 87 88 89 90 91 92 93
}

func address2llvm(addr []byte) i256 {
	n := hash2llvm(addr)
	bswap(&n)
	return n
}

94 95
// 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
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
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
}

139 140 141
// 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 {
142 143 144 145
	if length == 0 {
		return nil
	}
	if data == nil {
146
		panic("Unexpected nil data pointer")
147 148 149 150 151 152 153 154 155 156 157 158 159 160
	}
	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!")
	}
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 170
	self.env.SetDepth(self.env.Depth() + 1)

171
	// TODO: Move it to Env.Call() or sth
172 173 174 175 176 177 178
	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)
	}

179 180 181 182 183
	if self.me != nil {
		panic("JitVm.Run() can be called only once per JitVm instance")
	}

	self.me = me
184 185 186
	self.callerAddr = caller.Address()
	self.price = price

187 188
	self.data.gas = gas.Int64()
	self.data.gasPrice = price.Int64()
189
	self.data.callData = getDataPtr(callData)
190 191 192 193 194 195 196 197 198 199
	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()
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 205
	jit := C.evmjit_create()
	retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
206

207
	if retCode < 0 {
208 209 210
		err = errors.New("OOG from JIT")
		gas.SetInt64(0) // Set gas to 0, JIT does not bother
	} else {
211
		gas.SetInt64(self.data.gas)
212 213 214
		if retCode == 1 { // RETURN
			ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
		} 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 219
			receiver := state.GetOrNewStateObject(receiverAddr)
			balance := state.GetBalance(me.Address())
Paweł Bylica's avatar
Paweł Bylica committed
220
			receiver.AddBalance(balance)
221 222 223
			state.Delete(me.Address())
		}
	}
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 238 239 240
}

func (self *JitVm) Env() Environment {
	return self.env
}

241
//export env_sha3
242 243
func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
	data := llvm2bytesRef(dataPtr, length)
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	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
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 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	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))
318
		outData := llvm2bytesRef(outDataPtr, outDataLen)
319
		codeAddr := llvm2hash((*i256)(_codeAddr))
320
		gas := big.NewInt(*_gas)
321 322 323 324 325 326 327
		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)
		}
328
		*_gas = gas.Int64()
329 330 331 332 333 334 335 336 337 338
		if err == nil {
			copy(outData, out)
			return true
		}
	}

	return false
}

//export env_create
339
func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
340 341 342 343 344 345 346
	vm := (*JitVm)(_vm)

	value := llvm2big((*i256)(_value))
	initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
	result := (*i256)(_result)
	*result = i256{}

347
	gas := big.NewInt(*_gas)
348 349 350
	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
351
		dataGas.Mul(dataGas, params.CreateDataGas)
352 353 354
		gas.Sub(gas, dataGas)
		*result = hash2llvm(ref.Address())
	}
355
	*_gas = gas.Int64()
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
}

//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)))
	}

378
	vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64()))
379 380 381 382 383 384 385 386 387 388
}

//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)
}