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 }