github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/core/vm/jit.go (about) 1 // Copyright 2014 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 package vm 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync/atomic" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/state" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/params" 28 "github.com/hashicorp/golang-lru" 29 ) 30 31 type progStatus int32 32 33 const ( 34 progUnknown progStatus = iota 35 progCompile 36 progReady 37 progError 38 ) 39 40 var programs *lru.Cache 41 42 func init() { 43 programs, _ = lru.New(defaultJitMaxCache) 44 } 45 46 // SetJITCacheSize recreates the program cache with the max given size. Setting 47 // a new cache is **not** thread safe. Use with caution. 48 func SetJITCacheSize(size int) { 49 programs, _ = lru.New(size) 50 } 51 52 // GetProgram returns the program by id or nil when non-existent 53 func GetProgram(id common.Hash) *Program { 54 if p, ok := programs.Get(id); ok { 55 return p.(*Program) 56 } 57 58 return nil 59 } 60 61 // GenProgramStatus returns the status of the given program id 62 func GetProgramStatus(id common.Hash) progStatus { 63 program := GetProgram(id) 64 if program != nil { 65 return progStatus(atomic.LoadInt32(&program.status)) 66 } 67 68 return progUnknown 69 } 70 71 // Program is a compiled program for the JIT VM and holds all required for 72 // running a compiled JIT program. 73 type Program struct { 74 Id common.Hash // Id of the program 75 status int32 // status should be accessed atomically 76 77 context *Context 78 79 instructions []instruction // instruction set 80 mapping map[uint64]int // real PC mapping to array indices 81 destinations map[uint64]struct{} // cached jump destinations 82 83 code []byte 84 } 85 86 // NewProgram returns a new JIT program 87 func NewProgram(code []byte) *Program { 88 program := &Program{ 89 Id: crypto.Sha3Hash(code), 90 mapping: make(map[uint64]int), 91 destinations: make(map[uint64]struct{}), 92 code: code, 93 } 94 95 programs.Add(program.Id, program) 96 return program 97 } 98 99 func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) { 100 // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit 101 // PUSH is also allowed to calculate the same price for all PUSHes 102 // DUP requirements are handled elsewhere (except for the stack limit check) 103 baseOp := op 104 if op >= PUSH1 && op <= PUSH32 { 105 baseOp = PUSH1 106 } 107 if op >= DUP1 && op <= DUP16 { 108 baseOp = DUP1 109 } 110 base := _baseCheck[baseOp] 111 instr := instruction{op, pc, fn, nil, data, base.gas, base.stackPop, base.stackPush} 112 113 p.instructions = append(p.instructions, instr) 114 p.mapping[pc] = len(p.instructions) - 1 115 } 116 117 // CompileProgram compiles the given program and return an error when it fails 118 func CompileProgram(program *Program) (err error) { 119 if progStatus(atomic.LoadInt32(&program.status)) == progCompile { 120 return nil 121 } 122 atomic.StoreInt32(&program.status, int32(progCompile)) 123 defer func() { 124 if err != nil { 125 atomic.StoreInt32(&program.status, int32(progError)) 126 } else { 127 atomic.StoreInt32(&program.status, int32(progReady)) 128 } 129 }() 130 131 // loop thru the opcodes and "compile" in to instructions 132 for pc := uint64(0); pc < uint64(len(program.code)); pc++ { 133 switch op := OpCode(program.code[pc]); op { 134 case ADD: 135 program.addInstr(op, pc, opAdd, nil) 136 case SUB: 137 program.addInstr(op, pc, opSub, nil) 138 case MUL: 139 program.addInstr(op, pc, opMul, nil) 140 case DIV: 141 program.addInstr(op, pc, opDiv, nil) 142 case SDIV: 143 program.addInstr(op, pc, opSdiv, nil) 144 case MOD: 145 program.addInstr(op, pc, opMod, nil) 146 case SMOD: 147 program.addInstr(op, pc, opSmod, nil) 148 case EXP: 149 program.addInstr(op, pc, opExp, nil) 150 case SIGNEXTEND: 151 program.addInstr(op, pc, opSignExtend, nil) 152 case NOT: 153 program.addInstr(op, pc, opNot, nil) 154 case LT: 155 program.addInstr(op, pc, opLt, nil) 156 case GT: 157 program.addInstr(op, pc, opGt, nil) 158 case SLT: 159 program.addInstr(op, pc, opSlt, nil) 160 case SGT: 161 program.addInstr(op, pc, opSgt, nil) 162 case EQ: 163 program.addInstr(op, pc, opEq, nil) 164 case ISZERO: 165 program.addInstr(op, pc, opIszero, nil) 166 case AND: 167 program.addInstr(op, pc, opAnd, nil) 168 case OR: 169 program.addInstr(op, pc, opOr, nil) 170 case XOR: 171 program.addInstr(op, pc, opXor, nil) 172 case BYTE: 173 program.addInstr(op, pc, opByte, nil) 174 case ADDMOD: 175 program.addInstr(op, pc, opAddmod, nil) 176 case MULMOD: 177 program.addInstr(op, pc, opMulmod, nil) 178 case SHA3: 179 program.addInstr(op, pc, opSha3, nil) 180 case ADDRESS: 181 program.addInstr(op, pc, opAddress, nil) 182 case BALANCE: 183 program.addInstr(op, pc, opBalance, nil) 184 case ORIGIN: 185 program.addInstr(op, pc, opOrigin, nil) 186 case CALLER: 187 program.addInstr(op, pc, opCaller, nil) 188 case CALLVALUE: 189 program.addInstr(op, pc, opCallValue, nil) 190 case CALLDATALOAD: 191 program.addInstr(op, pc, opCalldataLoad, nil) 192 case CALLDATASIZE: 193 program.addInstr(op, pc, opCalldataSize, nil) 194 case CALLDATACOPY: 195 program.addInstr(op, pc, opCalldataCopy, nil) 196 case CODESIZE: 197 program.addInstr(op, pc, opCodeSize, nil) 198 case EXTCODESIZE: 199 program.addInstr(op, pc, opExtCodeSize, nil) 200 case CODECOPY: 201 program.addInstr(op, pc, opCodeCopy, nil) 202 case EXTCODECOPY: 203 program.addInstr(op, pc, opExtCodeCopy, nil) 204 case GASPRICE: 205 program.addInstr(op, pc, opGasprice, nil) 206 case BLOCKHASH: 207 program.addInstr(op, pc, opBlockhash, nil) 208 case COINBASE: 209 program.addInstr(op, pc, opCoinbase, nil) 210 case TIMESTAMP: 211 program.addInstr(op, pc, opTimestamp, nil) 212 case NUMBER: 213 program.addInstr(op, pc, opNumber, nil) 214 case DIFFICULTY: 215 program.addInstr(op, pc, opDifficulty, nil) 216 case GASLIMIT: 217 program.addInstr(op, pc, opGasLimit, nil) 218 case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: 219 size := uint64(op - PUSH1 + 1) 220 bytes := getData([]byte(program.code), new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) 221 222 program.addInstr(op, pc, opPush, common.Bytes2Big(bytes)) 223 224 pc += size 225 226 case POP: 227 program.addInstr(op, pc, opPop, nil) 228 case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: 229 program.addInstr(op, pc, opDup, big.NewInt(int64(op-DUP1+1))) 230 case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: 231 program.addInstr(op, pc, opSwap, big.NewInt(int64(op-SWAP1+2))) 232 case LOG0, LOG1, LOG2, LOG3, LOG4: 233 program.addInstr(op, pc, opLog, big.NewInt(int64(op-LOG0))) 234 case MLOAD: 235 program.addInstr(op, pc, opMload, nil) 236 case MSTORE: 237 program.addInstr(op, pc, opMstore, nil) 238 case MSTORE8: 239 program.addInstr(op, pc, opMstore8, nil) 240 case SLOAD: 241 program.addInstr(op, pc, opSload, nil) 242 case SSTORE: 243 program.addInstr(op, pc, opSstore, nil) 244 case JUMP: 245 program.addInstr(op, pc, opJump, nil) 246 case JUMPI: 247 program.addInstr(op, pc, opJumpi, nil) 248 case JUMPDEST: 249 program.addInstr(op, pc, opJumpdest, nil) 250 program.destinations[pc] = struct{}{} 251 case PC: 252 program.addInstr(op, pc, opPc, big.NewInt(int64(pc))) 253 case MSIZE: 254 program.addInstr(op, pc, opMsize, nil) 255 case GAS: 256 program.addInstr(op, pc, opGas, nil) 257 case CREATE: 258 program.addInstr(op, pc, opCreate, nil) 259 case CALL: 260 program.addInstr(op, pc, opCall, nil) 261 case CALLCODE: 262 program.addInstr(op, pc, opCallCode, nil) 263 case RETURN: 264 program.addInstr(op, pc, opReturn, nil) 265 case SUICIDE: 266 program.addInstr(op, pc, opSuicide, nil) 267 case STOP: // Stop the context 268 program.addInstr(op, pc, opStop, nil) 269 default: 270 program.addInstr(op, pc, nil, nil) 271 } 272 } 273 274 return nil 275 } 276 277 // RunProgram runs the program given the enviroment and context and returns an 278 // error if the execution failed (non-consensus) 279 func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) { 280 return runProgram(program, 0, NewMemory(), newstack(), env, context, input) 281 } 282 283 func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, context *Context, input []byte) ([]byte, error) { 284 context.Input = input 285 286 var ( 287 caller = context.caller 288 statedb = env.State() 289 pc int = program.mapping[pcstart] 290 291 jump = func(to *big.Int) error { 292 if !validDest(program.destinations, to) { 293 nop := context.GetOp(to.Uint64()) 294 return fmt.Errorf("invalid jump destination (%v) %v", nop, to) 295 } 296 297 pc = program.mapping[to.Uint64()] 298 299 return nil 300 } 301 ) 302 303 for pc < len(program.instructions) { 304 instr := program.instructions[pc] 305 306 // calculate the new memory size and gas price for the current executing opcode 307 newMemSize, cost, err := jitCalculateGasAndSize(env, context, caller, instr, statedb, mem, stack) 308 if err != nil { 309 return nil, err 310 } 311 312 // Use the calculated gas. When insufficient gas is present, use all gas and return an 313 // Out Of Gas error 314 if !context.UseGas(cost) { 315 return nil, OutOfGasError 316 } 317 // Resize the memory calculated previously 318 mem.Resize(newMemSize.Uint64()) 319 320 // These opcodes return an argument and are thefor handled 321 // differently from the rest of the opcodes 322 switch instr.op { 323 case JUMP: 324 if err := jump(stack.pop()); err != nil { 325 return nil, err 326 } 327 continue 328 case JUMPI: 329 pos, cond := stack.pop(), stack.pop() 330 331 if cond.Cmp(common.BigTrue) >= 0 { 332 if err := jump(pos); err != nil { 333 return nil, err 334 } 335 continue 336 } 337 case RETURN: 338 offset, size := stack.pop(), stack.pop() 339 ret := mem.GetPtr(offset.Int64(), size.Int64()) 340 341 return context.Return(ret), nil 342 case SUICIDE: 343 instr.fn(instr, env, context, mem, stack) 344 345 return context.Return(nil), nil 346 case STOP: 347 return context.Return(nil), nil 348 default: 349 if instr.fn == nil { 350 return nil, fmt.Errorf("Invalid opcode %x", instr.op) 351 } 352 353 instr.fn(instr, env, context, mem, stack) 354 } 355 356 pc++ 357 } 358 359 context.Input = nil 360 361 return context.Return(nil), nil 362 } 363 364 // validDest checks if the given distination is a valid one given the 365 // destination table of the program 366 func validDest(dests map[uint64]struct{}, dest *big.Int) bool { 367 // PC cannot go beyond len(code) and certainly can't be bigger than 64bits. 368 // Don't bother checking for JUMPDEST in that case. 369 if dest.Cmp(bigMaxUint64) > 0 { 370 return false 371 } 372 _, ok := dests[dest.Uint64()] 373 return ok 374 } 375 376 // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for 377 // the operation. This does not reduce gas or resizes the memory. 378 func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef, instr instruction, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { 379 var ( 380 gas = new(big.Int) 381 newMemSize *big.Int = new(big.Int) 382 ) 383 err := jitBaseCheck(instr, stack, gas) 384 if err != nil { 385 return nil, nil, err 386 } 387 388 // stack Check, memory resize & gas phase 389 switch op := instr.op; op { 390 case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: 391 n := int(op - SWAP1 + 2) 392 err := stack.require(n) 393 if err != nil { 394 return nil, nil, err 395 } 396 gas.Set(GasFastestStep) 397 case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: 398 n := int(op - DUP1 + 1) 399 err := stack.require(n) 400 if err != nil { 401 return nil, nil, err 402 } 403 gas.Set(GasFastestStep) 404 case LOG0, LOG1, LOG2, LOG3, LOG4: 405 n := int(op - LOG0) 406 err := stack.require(n + 2) 407 if err != nil { 408 return nil, nil, err 409 } 410 411 mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] 412 413 add := new(big.Int) 414 gas.Add(gas, params.LogGas) 415 gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas)) 416 gas.Add(gas, add.Mul(mSize, params.LogDataGas)) 417 418 newMemSize = calcMemSize(mStart, mSize) 419 case EXP: 420 gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) 421 case SSTORE: 422 err := stack.require(2) 423 if err != nil { 424 return nil, nil, err 425 } 426 427 var g *big.Int 428 y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] 429 val := statedb.GetState(context.Address(), common.BigToHash(x)) 430 431 // This checks for 3 scenario's and calculates gas accordingly 432 // 1. From a zero-value address to a non-zero value (NEW VALUE) 433 // 2. From a non-zero value address to a zero-value address (DELETE) 434 // 3. From a nen-zero to a non-zero (CHANGE) 435 if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { 436 // 0 => non 0 437 g = params.SstoreSetGas 438 } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { 439 statedb.Refund(params.SstoreRefundGas) 440 441 g = params.SstoreClearGas 442 } else { 443 // non 0 => non 0 (or 0 => 0) 444 g = params.SstoreClearGas 445 } 446 gas.Set(g) 447 case SUICIDE: 448 if !statedb.IsDeleted(context.Address()) { 449 statedb.Refund(params.SuicideRefundGas) 450 } 451 case MLOAD: 452 newMemSize = calcMemSize(stack.peek(), u256(32)) 453 case MSTORE8: 454 newMemSize = calcMemSize(stack.peek(), u256(1)) 455 case MSTORE: 456 newMemSize = calcMemSize(stack.peek(), u256(32)) 457 case RETURN: 458 newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) 459 case SHA3: 460 newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) 461 462 words := toWordSize(stack.data[stack.len()-2]) 463 gas.Add(gas, words.Mul(words, params.Sha3WordGas)) 464 case CALLDATACOPY: 465 newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) 466 467 words := toWordSize(stack.data[stack.len()-3]) 468 gas.Add(gas, words.Mul(words, params.CopyGas)) 469 case CODECOPY: 470 newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) 471 472 words := toWordSize(stack.data[stack.len()-3]) 473 gas.Add(gas, words.Mul(words, params.CopyGas)) 474 case EXTCODECOPY: 475 newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) 476 477 words := toWordSize(stack.data[stack.len()-4]) 478 gas.Add(gas, words.Mul(words, params.CopyGas)) 479 480 case CREATE: 481 newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) 482 case CALL, CALLCODE: 483 gas.Add(gas, stack.data[stack.len()-1]) 484 485 if op == CALL { 486 if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { 487 gas.Add(gas, params.CallNewAccountGas) 488 } 489 } 490 491 if len(stack.data[stack.len()-3].Bytes()) > 0 { 492 gas.Add(gas, params.CallValueTransferGas) 493 } 494 495 x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) 496 y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) 497 498 newMemSize = common.BigMax(x, y) 499 } 500 501 if newMemSize.Cmp(common.Big0) > 0 { 502 newMemSizeWords := toWordSize(newMemSize) 503 newMemSize.Mul(newMemSizeWords, u256(32)) 504 505 if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { 506 // be careful reusing variables here when changing. 507 // The order has been optimised to reduce allocation 508 oldSize := toWordSize(big.NewInt(int64(mem.Len()))) 509 pow := new(big.Int).Exp(oldSize, common.Big2, Zero) 510 linCoef := oldSize.Mul(oldSize, params.MemoryGas) 511 quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) 512 oldTotalFee := new(big.Int).Add(linCoef, quadCoef) 513 514 pow.Exp(newMemSizeWords, common.Big2, Zero) 515 linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) 516 quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) 517 newTotalFee := linCoef.Add(linCoef, quadCoef) 518 519 fee := newTotalFee.Sub(newTotalFee, oldTotalFee) 520 gas.Add(gas, fee) 521 } 522 } 523 524 return newMemSize, gas, nil 525 } 526 527 // jitBaseCheck is the same as baseCheck except it doesn't do the look up in the 528 // gas table. This is done during compilation instead. 529 func jitBaseCheck(instr instruction, stack *stack, gas *big.Int) error { 530 err := stack.require(instr.spop) 531 if err != nil { 532 return err 533 } 534 535 if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(params.StackLimit.Int64()) { 536 return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64()) 537 } 538 539 // nil on gas means no base calculation 540 if instr.gas == nil { 541 return nil 542 } 543 544 gas.Add(gas, instr.gas) 545 546 return nil 547 }