Files
RedHotRoast-ios/HybridCLRData/LocalIl2CppData-OSXEditor/il2cpp/libil2cpp/hybridclr/metadata/MethodBodyCache.cpp
T
2026-07-17 14:03:00 +08:00

200 lines
5.3 KiB
C++

#include <unordered_map>
#include "MethodBodyCache.h"
#include "MetadataModule.h"
#include "AOTHomologousImage.h"
#include "Opcodes.h"
#include "MetadataDef.h"
#include "utils/HashUtils.h"
#include "../RuntimeConfig.h"
namespace hybridclr
{
namespace metadata
{
struct ImageTokenPair
{
hybridclr::metadata::Image* image;
uint32_t token;
};
struct ImageTokenPairHash
{
size_t operator()(const ImageTokenPair& s) const noexcept
{
return il2cpp::utils::HashUtils::Combine((size_t)s.image, (size_t)s.token);
}
};
struct ImageTokenPairEqualTo
{
bool operator()(const ImageTokenPair& p1, const ImageTokenPair& p2) const
{
return p1.image == p2.image && p1.token == p2.token;
}
};
enum class InlineMode : uint8_t
{
None,
Inlineable,
NotInlineable,
};
struct MethodBodyCacheInfo
{
MethodBody* methodBody;
int32_t accessVersion;
uint16_t accessCount;
InlineMode inlineMode;
};
constexpr int32_t kMinShrinkMethodBodyCacheInterval = 256;
static bool s_enableShrinkMethodBodyCache = true;
static int32_t s_methodBodyCacheVersion = 0;
static Il2CppHashMap<ImageTokenPair, MethodBodyCacheInfo*, ImageTokenPairHash, ImageTokenPairEqualTo> s_methodBodyCache;
static MethodBodyCacheInfo* GetOrInitMethodBodyCache(hybridclr::metadata::Image* image, uint32_t token)
{
ImageTokenPair key = { image, token };
auto it = s_methodBodyCache.find(key);
if (it != s_methodBodyCache.end())
{
return it->second;
}
MethodBody* methodBody = image->GetMethodBody(token);
MethodBodyCacheInfo* ci = (MethodBodyCacheInfo*)HYBRIDCLR_MALLOC_ZERO(sizeof(MethodBodyCacheInfo));
*ci = { methodBody, s_methodBodyCacheVersion, 0, InlineMode::None};
s_methodBodyCache[key] = ci;
return ci;
}
MethodBody* MethodBodyCache::GetMethodBody(hybridclr::metadata::Image* image, uint32_t token)
{
MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, token);
ci->accessVersion = s_methodBodyCacheVersion;
++ci->accessCount;
return ci->methodBody;
}
static void ShrinkMethodBodyCache(int32_t shrinkMethodBodyCacheInterval)
{
if (s_methodBodyCache.size() <= RuntimeConfig::GetMaxMethodBodyCacheSize())
{
return;
}
int32_t expiredVersion = s_methodBodyCacheVersion - shrinkMethodBodyCacheInterval;
for (auto it = s_methodBodyCache.begin(); it != s_methodBodyCache.end(); )
{
MethodBodyCacheInfo* ci = it->second;
// add extra interval when accessCount exceeded 1
int32_t accessVersion = ci->accessVersion + (ci->accessCount - 1) * shrinkMethodBodyCacheInterval;
if (accessVersion < expiredVersion)
{
if (ci->methodBody)
{
ci->methodBody->~MethodBody();
HYBRIDCLR_FREE(ci->methodBody);
}
HYBRIDCLR_FREE(ci);
s_methodBodyCache.erase(it++);
}
else
{
++it;
}
}
}
void MethodBodyCache::EnableShrinkMethodBodyCache(bool shrink)
{
s_enableShrinkMethodBodyCache = shrink;
if (shrink)
{
++s_methodBodyCacheVersion;
int32_t shrinkMethodBodyCacheInterval = std::max(RuntimeConfig::GetMaxMethodBodyCacheSize() / 2, kMinShrinkMethodBodyCacheInterval);
if (s_methodBodyCacheVersion % shrinkMethodBodyCacheInterval == 0)
{
ShrinkMethodBodyCache(shrinkMethodBodyCacheInterval);
}
}
}
static bool IsILCodeInlineable(const byte* ilcodeStart, uint32_t codeSize)
{
const byte* codeEnd = ilcodeStart + codeSize;
const byte* ip = ilcodeStart;
while (ip < codeEnd)
{
const OpCodeInfo* oc = DecodeOpCodeInfo(ip, codeEnd);
IL2CPP_ASSERT(oc);
int32_t opCodeSize = GetOpCodeSize(ip, oc);
ip += opCodeSize;
int32_t nextOffset = (int32_t)(ip - ilcodeStart);
IL2CPP_ASSERT(nextOffset >= 0 && nextOffset <= (int32_t)codeSize);
switch (oc->flow)
{
case FlowType::Branch:
case FlowType::CondBranch:
case FlowType::Throw:
{
return false;
}
default: break;
}
}
IL2CPP_ASSERT(ip == codeEnd);
return true;
}
static bool ComputeInlinable(metadata::Image* image, uint32_t token)
{
metadata::MethodBody* methodBody = MethodBodyCache::GetMethodBody(image, token);
if (methodBody == nullptr || methodBody->ilcodes == nullptr)
{
return false;
}
if ((int32_t)methodBody->codeSize > RuntimeConfig::GetMaxInlineableMethodBodySize() || !methodBody->exceptionClauses.empty())
{
return false;
}
return IsILCodeInlineable(methodBody->ilcodes, methodBody->codeSize);
}
bool MethodBodyCache::IsInlineable(const MethodInfo* methodInfo)
{
IL2CPP_ASSERT(methodInfo->isInterpterImpl);
metadata::Image* image = MetadataModule::GetUnderlyingInterpreterImage(methodInfo);
IL2CPP_ASSERT(image);
MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, methodInfo->token);
ci->accessVersion = s_methodBodyCacheVersion;
++ci->accessCount;
if (ci->inlineMode != InlineMode::None)
{
return ci->inlineMode == InlineMode::Inlineable;
}
bool inlineable = ComputeInlinable(image, methodInfo->token);
ci->inlineMode = inlineable ? InlineMode::Inlineable : InlineMode::NotInlineable;
return inlineable;
}
void MethodBodyCache::DisableInline(const MethodInfo* methodInfo)
{
IL2CPP_ASSERT(methodInfo->isInterpterImpl);
metadata::Image* image = MetadataModule::GetUnderlyingInterpreterImage(methodInfo);
IL2CPP_ASSERT(image);
MethodBodyCacheInfo* ci = GetOrInitMethodBodyCache(image, methodInfo->token);
ci->inlineMode = InlineMode::NotInlineable;
}
}
}