MethodBodyCache.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #include <unordered_map>
  2. #include "MethodBodyCache.h"
  3. #include "MetadataModule.h"
  4. #include "AOTHomologousImage.h"
  5. #include "Opcodes.h"
  6. #include "MetadataDef.h"
  7. #include "utils/HashUtils.h"
  8. #include "../RuntimeConfig.h"
  9. namespace hybridclr
  10. {
  11. namespace metadata
  12. {
  13. struct ImageTokenPair
  14. {
  15. hybridclr::metadata::Image* image;
  16. uint32_t token;
  17. };
  18. struct ImageTokenPairHash
  19. {
  20. size_t operator()(const ImageTokenPair& s) const noexcept
  21. {
  22. return il2cpp::utils::HashUtils::Combine((size_t)s.image, (size_t)s.token);
  23. }
  24. };
  25. struct ImageTokenPairEqualTo
  26. {
  27. bool operator()(const ImageTokenPair& p1, const ImageTokenPair& p2) const
  28. {
  29. return p1.image == p2.image && p1.token == p2.token;
  30. }
  31. };
  32. enum class InlineMode : uint8_t
  33. {
  34. None,
  35. Inlineable,
  36. NotInlineable,
  37. };
  38. struct MethodBodyCacheInfo
  39. {
  40. MethodBody* methodBody;
  41. int32_t accessVersion;
  42. uint16_t accessCount;
  43. InlineMode inlineMode;
  44. };
  45. constexpr int32_t kMinShrinkMethodBodyCacheInterval = 256;
  46. static bool s_enableShrinkMethodBodyCache = true;
  47. static int32_t s_methodBodyCacheVersion = 0;
  48. static Il2CppHashMap<ImageTokenPair, MethodBodyCacheInfo*, ImageTokenPairHash, ImageTokenPairEqualTo> s_methodBodyCache;
  49. static MethodBodyCacheInfo* GetOrInitMethodBodyCache(hybridclr::metadata::Image* image, uint32_t token)
  50. {
  51. ImageTokenPair key = { image, token };
  52. auto it = s_methodBodyCache.find(key);
  53. if (it != s_methodBodyCache.end())
  54. {
  55. return it->second;
  56. }
  57. MethodBody* methodBody = image->GetMethodBody(token);
  58. MethodBodyCacheInfo* ci = (MethodBodyCacheInfo*)HYBRIDCLR_MALLOC_ZERO(sizeof(MethodBodyCacheInfo));
  59. *ci = { methodBody, s_methodBodyCacheVersion, 0, InlineMode::None};
  60. s_methodBodyCache[key] = ci;
  61. return ci;
  62. }
  63. MethodBody* MethodBodyCache::GetMethodBody(hybridclr::metadata::Image* image, uint32_t token)
  64. {
  65. MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, token);
  66. ci->accessVersion = s_methodBodyCacheVersion;
  67. ++ci->accessCount;
  68. return ci->methodBody;
  69. }
  70. static void ShrinkMethodBodyCache(int32_t shrinkMethodBodyCacheInterval)
  71. {
  72. if (s_methodBodyCache.size() <= RuntimeConfig::GetMaxMethodBodyCacheSize())
  73. {
  74. return;
  75. }
  76. int32_t expiredVersion = s_methodBodyCacheVersion - shrinkMethodBodyCacheInterval;
  77. for (auto it = s_methodBodyCache.begin(); it != s_methodBodyCache.end(); )
  78. {
  79. MethodBodyCacheInfo* ci = it->second;
  80. // add extra interval when accessCount exceeded 1
  81. int32_t accessVersion = ci->accessVersion + (ci->accessCount - 1) * shrinkMethodBodyCacheInterval;
  82. if (accessVersion < expiredVersion)
  83. {
  84. if (ci->methodBody)
  85. {
  86. ci->methodBody->~MethodBody();
  87. HYBRIDCLR_FREE(ci->methodBody);
  88. }
  89. HYBRIDCLR_FREE(ci);
  90. s_methodBodyCache.erase(it++);
  91. }
  92. else
  93. {
  94. ++it;
  95. }
  96. }
  97. }
  98. void MethodBodyCache::EnableShrinkMethodBodyCache(bool shrink)
  99. {
  100. s_enableShrinkMethodBodyCache = shrink;
  101. if (shrink)
  102. {
  103. ++s_methodBodyCacheVersion;
  104. int32_t shrinkMethodBodyCacheInterval = std::max(RuntimeConfig::GetMaxMethodBodyCacheSize() / 2, kMinShrinkMethodBodyCacheInterval);
  105. if (s_methodBodyCacheVersion % shrinkMethodBodyCacheInterval == 0)
  106. {
  107. ShrinkMethodBodyCache(shrinkMethodBodyCacheInterval);
  108. }
  109. }
  110. }
  111. static bool IsILCodeInlineable(const byte* ilcodeStart, uint32_t codeSize)
  112. {
  113. const byte* codeEnd = ilcodeStart + codeSize;
  114. const byte* ip = ilcodeStart;
  115. while (ip < codeEnd)
  116. {
  117. const OpCodeInfo* oc = DecodeOpCodeInfo(ip, codeEnd);
  118. IL2CPP_ASSERT(oc);
  119. int32_t opCodeSize = GetOpCodeSize(ip, oc);
  120. ip += opCodeSize;
  121. int32_t nextOffset = (int32_t)(ip - ilcodeStart);
  122. IL2CPP_ASSERT(nextOffset >= 0 && nextOffset <= (int32_t)codeSize);
  123. switch (oc->flow)
  124. {
  125. case FlowType::Branch:
  126. case FlowType::CondBranch:
  127. case FlowType::Throw:
  128. {
  129. return false;
  130. }
  131. }
  132. }
  133. IL2CPP_ASSERT(ip == codeEnd);
  134. return true;
  135. }
  136. const int32_t kMaxInlineableCodeLength = 16;
  137. static bool ComputeInlinable(metadata::Image* image, uint32_t token)
  138. {
  139. metadata::MethodBody* methodBody = MethodBodyCache::GetMethodBody(image, token);
  140. if (methodBody == nullptr || methodBody->ilcodes == nullptr)
  141. {
  142. return false;
  143. }
  144. if (methodBody->codeSize > kMaxInlineableCodeLength || !methodBody->exceptionClauses.empty())
  145. {
  146. return false;
  147. }
  148. return IsILCodeInlineable(methodBody->ilcodes, methodBody->codeSize);
  149. }
  150. bool MethodBodyCache::IsInlineable(const MethodInfo* methodInfo)
  151. {
  152. IL2CPP_ASSERT(methodInfo->isInterpterImpl);
  153. metadata::Image* image = MetadataModule::GetUnderlyingInterpreterImage(methodInfo);
  154. IL2CPP_ASSERT(image);
  155. MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, methodInfo->token);
  156. ci->accessVersion = s_methodBodyCacheVersion;
  157. ++ci->accessCount;
  158. if (ci->inlineMode != InlineMode::None)
  159. {
  160. return ci->inlineMode == InlineMode::Inlineable;
  161. }
  162. bool inlineable = ComputeInlinable(image, methodInfo->token);
  163. ci->inlineMode = inlineable ? InlineMode::Inlineable : InlineMode::NotInlineable;
  164. return inlineable;
  165. }
  166. void MethodBodyCache::DisableInline(const MethodInfo* methodInfo)
  167. {
  168. IL2CPP_ASSERT(methodInfo->isInterpterImpl);
  169. metadata::Image* image = MetadataModule::GetUnderlyingInterpreterImage(methodInfo);
  170. IL2CPP_ASSERT(image);
  171. MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, methodInfo->token);
  172. ci->inlineMode = InlineMode::NotInlineable;
  173. }
  174. }
  175. }