SmartVM.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /*
  2. This file is part of cpp-ethereum.
  3. cpp-ethereum is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. cpp-ethereum is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include "SmartVM.h"
  15. #include <unordered_map>
  16. #include <thread>
  17. #include <libdevcore/concurrent_queue.h>
  18. #include <libdevcore/Log.h>
  19. #include <libdevcore/Guards.h>
  20. #include "VMFactory.h"
  21. #include "JitVM.h"
  22. namespace dev
  23. {
  24. namespace eth
  25. {
  26. namespace
  27. {
  28. struct JitInfo: LogChannel { static const char* name() { return "JIT"; }; static const int verbosity = 11; };
  29. using HitMap = std::unordered_map<h256, uint64_t>;
  30. HitMap& getHitMap()
  31. {
  32. static HitMap s_hitMap;
  33. return s_hitMap;
  34. }
  35. struct JitTask
  36. {
  37. bytes code;
  38. h256 codeHash;
  39. evm_mode mode;
  40. static JitTask createStopSentinel() { return JitTask(); }
  41. bool isStopSentinel()
  42. {
  43. assert((!code.empty() || !codeHash) && "'empty code => empty hash' invariant failed");
  44. return code.empty();
  45. }
  46. };
  47. class JitWorker
  48. {
  49. concurrent_queue<JitTask> m_queue;
  50. std::thread m_worker; // Worker must be last to initialize
  51. void work()
  52. {
  53. clog(JitInfo) << "JIT worker started.";
  54. JitTask task;
  55. while (!(task = m_queue.pop()).isStopSentinel())
  56. {
  57. clog(JitInfo) << "Compilation... " << task.codeHash;
  58. JitVM::compile(task.mode, {task.code.data(), task.code.size()}, task.codeHash);
  59. clog(JitInfo) << " ...finished " << task.codeHash;
  60. }
  61. clog(JitInfo) << "JIT worker finished.";
  62. }
  63. public:
  64. JitWorker() noexcept: m_worker([this]{ work(); })
  65. {}
  66. ~JitWorker()
  67. {
  68. push(JitTask::createStopSentinel());
  69. m_worker.join();
  70. }
  71. void push(JitTask&& _task) { m_queue.push(std::move(_task)); }
  72. };
  73. }
  74. bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
  75. {
  76. auto vmKind = VMKind::Interpreter; // default VM
  77. auto mode = JitVM::scheduleToMode(_ext.evmSchedule());
  78. // Jitted EVM code already in memory?
  79. if (JitVM::isCodeReady(mode, _ext.codeHash))
  80. {
  81. clog(JitInfo) << "JIT: " << _ext.codeHash;
  82. vmKind = VMKind::JIT;
  83. }
  84. else if (!_ext.code.empty()) // This check is needed for VM tests
  85. {
  86. static JitWorker s_worker;
  87. // Check EVM code hit count
  88. static const uint64_t c_hitTreshold = 2;
  89. auto& hits = getHitMap()[_ext.codeHash];
  90. ++hits;
  91. if (hits == c_hitTreshold)
  92. {
  93. clog(JitInfo) << "Schedule: " << _ext.codeHash;
  94. s_worker.push({_ext.code, _ext.codeHash, mode});
  95. }
  96. clog(JitInfo) << "Interpreter: " << _ext.codeHash;
  97. }
  98. // TODO: Selected VM must be kept only because it returns reference to its internal memory.
  99. // VM implementations should be stateless, without escaping memory reference.
  100. m_selectedVM = VMFactory::create(vmKind);
  101. return m_selectedVM->execImpl(io_gas, _ext, _onOp);
  102. }
  103. }
  104. }