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 }