CodeBlock.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2014 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <cstddef>
  5. #include <vector>
  6. #include "Common/Assert.h"
  7. #include "Common/CommonTypes.h"
  8. #include "Common/MemoryUtil.h"
  9. namespace Common
  10. {
  11. // Everything that needs to generate code should inherit from this.
  12. // You get memory management for free, plus, you can use all emitter functions without
  13. // having to prefix them with gen-> or something similar.
  14. // Example implementation:
  15. // class JIT : public CodeBlock<ARMXEmitter> {}
  16. template <class T, bool executable = true>
  17. class CodeBlock : public T
  18. {
  19. private:
  20. // A privately used function to set the executable RAM space to something invalid.
  21. // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
  22. // instruction
  23. virtual void PoisonMemory() = 0;
  24. protected:
  25. u8* region = nullptr;
  26. // Size of region we can use.
  27. size_t region_size = 0;
  28. // Original size of the region we allocated.
  29. size_t total_region_size = 0;
  30. bool m_is_child = false;
  31. std::vector<CodeBlock*> m_children;
  32. public:
  33. CodeBlock() = default;
  34. virtual ~CodeBlock()
  35. {
  36. if (region)
  37. FreeCodeSpace();
  38. }
  39. CodeBlock(const CodeBlock&) = delete;
  40. CodeBlock& operator=(const CodeBlock&) = delete;
  41. CodeBlock(CodeBlock&&) = delete;
  42. CodeBlock& operator=(CodeBlock&&) = delete;
  43. // Call this before you generate any code.
  44. void AllocCodeSpace(size_t size)
  45. {
  46. region_size = size;
  47. total_region_size = size;
  48. if constexpr (executable)
  49. region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
  50. else
  51. region = static_cast<u8*>(Common::AllocateMemoryPages(total_region_size));
  52. T::SetCodePtr(region, region + size);
  53. }
  54. // Always clear code space with breakpoints, so that if someone accidentally executes
  55. // uninitialized, it just breaks into the debugger.
  56. void ClearCodeSpace()
  57. {
  58. PoisonMemory();
  59. ResetCodePtr();
  60. }
  61. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
  62. void FreeCodeSpace()
  63. {
  64. ASSERT(!m_is_child);
  65. Common::FreeMemoryPages(region, total_region_size);
  66. region = nullptr;
  67. region_size = 0;
  68. total_region_size = 0;
  69. for (CodeBlock* child : m_children)
  70. {
  71. child->region = nullptr;
  72. child->region_size = 0;
  73. child->total_region_size = 0;
  74. }
  75. }
  76. bool IsInSpace(const u8* ptr) const { return ptr >= region && ptr < (region + region_size); }
  77. bool IsInSpaceOrChildSpace(const u8* ptr) const
  78. {
  79. return ptr >= region && ptr < (region + total_region_size);
  80. }
  81. void WriteProtect(bool allow_execute)
  82. {
  83. Common::WriteProtectMemory(region, region_size, allow_execute);
  84. }
  85. void UnWriteProtect(bool allow_execute)
  86. {
  87. Common::UnWriteProtectMemory(region, region_size, allow_execute);
  88. }
  89. void ResetCodePtr() { T::SetCodePtr(region, region + region_size); }
  90. size_t GetSpaceLeft() const
  91. {
  92. ASSERT(static_cast<size_t>(T::GetCodePtr() - region) < region_size);
  93. return region_size - (T::GetCodePtr() - region);
  94. }
  95. bool IsAlmostFull() const
  96. {
  97. // This should be bigger than the biggest block ever.
  98. return GetSpaceLeft() < 0x10000;
  99. }
  100. bool HasChildren() const { return region_size != total_region_size; }
  101. u8* AllocChildCodeSpace(size_t child_size)
  102. {
  103. ASSERT_MSG(DYNA_REC, child_size <= GetSpaceLeft(), "Insufficient space for child allocation.");
  104. u8* child_region = region + region_size - child_size;
  105. region_size -= child_size;
  106. ResetCodePtr();
  107. return child_region;
  108. }
  109. void AddChildCodeSpace(CodeBlock* child, size_t child_size)
  110. {
  111. u8* child_region = AllocChildCodeSpace(child_size);
  112. child->m_is_child = true;
  113. child->region = child_region;
  114. child->region_size = child_size;
  115. child->total_region_size = child_size;
  116. child->ResetCodePtr();
  117. m_children.emplace_back(child);
  118. }
  119. };
  120. } // namespace Common