jit_optimiser.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package vm
  2. import (
  3. "math/big"
  4. "time"
  5. "github.com/ethereum/go-ethereum/logger"
  6. "github.com/ethereum/go-ethereum/logger/glog"
  7. )
  8. // optimeProgram optimises a JIT program creating segments out of program
  9. // instructions. Currently covered are multi-pushes and static jumps
  10. func optimiseProgram(program *Program) {
  11. var load []instruction
  12. var (
  13. statsJump = 0
  14. statsPush = 0
  15. )
  16. if glog.V(logger.Debug) {
  17. glog.Infof("optimising %x\n", program.Id[:4])
  18. tstart := time.Now()
  19. defer func() {
  20. glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush)
  21. }()
  22. }
  23. /*
  24. code := Parse(program.code)
  25. for _, test := range [][]OpCode{
  26. []OpCode{PUSH, PUSH, ADD},
  27. []OpCode{PUSH, PUSH, SUB},
  28. []OpCode{PUSH, PUSH, MUL},
  29. []OpCode{PUSH, PUSH, DIV},
  30. } {
  31. matchCount := 0
  32. MatchFn(code, test, func(i int) bool {
  33. matchCount++
  34. return true
  35. })
  36. fmt.Printf("found %d match count on: %v\n", matchCount, test)
  37. }
  38. */
  39. for i := 0; i < len(program.instructions); i++ {
  40. instr := program.instructions[i].(instruction)
  41. switch {
  42. case instr.op.IsPush():
  43. load = append(load, instr)
  44. case instr.op.IsStaticJump():
  45. if len(load) == 0 {
  46. continue
  47. }
  48. // if the push load is greater than 1, finalise that
  49. // segment first
  50. if len(load) > 2 {
  51. seg, size := makePushSeg(load[:len(load)-1])
  52. program.instructions[i-size-1] = seg
  53. statsPush++
  54. }
  55. // create a segment consisting of a pre determined
  56. // jump, destination and validity.
  57. seg := makeStaticJumpSeg(load[len(load)-1].data, program)
  58. program.instructions[i-1] = seg
  59. statsJump++
  60. load = nil
  61. default:
  62. // create a new N pushes segment
  63. if len(load) > 1 {
  64. seg, size := makePushSeg(load)
  65. program.instructions[i-size] = seg
  66. statsPush++
  67. }
  68. load = nil
  69. }
  70. }
  71. }
  72. // makePushSeg creates a new push segment from N amount of push instructions
  73. func makePushSeg(instrs []instruction) (pushSeg, int) {
  74. var (
  75. data []*big.Int
  76. gas = new(big.Int)
  77. )
  78. for _, instr := range instrs {
  79. data = append(data, instr.data)
  80. gas.Add(gas, instr.gas)
  81. }
  82. return pushSeg{data, gas}, len(instrs)
  83. }
  84. // makeStaticJumpSeg creates a new static jump segment from a predefined
  85. // destination (PUSH, JUMP).
  86. func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
  87. gas := new(big.Int)
  88. gas.Add(gas, _baseCheck[PUSH1].gas)
  89. gas.Add(gas, _baseCheck[JUMP].gas)
  90. contract := &Contract{Code: program.code}
  91. pos, err := jump(program.mapping, program.destinations, contract, to)
  92. return jumpSeg{pos, err, gas}
  93. }