-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[wasm][coreclr] 2nd part of call helpers generator, reverse calls #122007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c8c3fc0
a3f20dc
89556d8
270a506
f4d47bf
c1a754f
fb8d531
2fdf1e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -623,10 +623,33 @@ namespace | |
| if (!GetSignatureKey(sig, keyBuffer, keyBufferLen)) | ||
| return NULL; | ||
|
|
||
| return LookupThunk(keyBuffer); | ||
| void* thunk = LookupThunk(keyBuffer); | ||
| #ifdef _DEBUG | ||
| if (thunk == NULL) | ||
| printf("WASM calli missing for key: %s\n", keyBuffer); | ||
radekdoulik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #endif | ||
| return thunk; | ||
| } | ||
|
|
||
| ULONG CreateFallbackKey(MethodDesc* pMD) | ||
| { | ||
| _ASSERTE(pMD != nullptr); | ||
|
|
||
| // the fallback key is in the form $"{MethodName}#{Method.GetParameters().Length}:{AssemblyName}:{Namespace}:{TypeName}"; | ||
| LPCUTF8 pszNamespace = nullptr; | ||
| LPCUTF8 pszName = pMD->GetMethodTable()->GetFullyQualifiedNameInfo(&pszNamespace); | ||
| MetaSig sig(pMD); | ||
| SString strFullName; | ||
| strFullName.Printf("%s#%d:%s:%s:%s", | ||
| pMD->GetName(), | ||
| sig.NumFixedArgs(), | ||
| pMD->GetAssembly()->GetSimpleName(), | ||
| pszNamespace != nullptr ? pszNamespace : "", | ||
| pszName); | ||
|
|
||
| return strFullName.Hash(); | ||
| } | ||
radekdoulik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // TODO: This hashing function should be replaced. | ||
| ULONG CreateKey(MethodDesc* pMD) | ||
| { | ||
| _ASSERTE(pMD != nullptr); | ||
|
|
@@ -645,30 +668,54 @@ namespace | |
|
|
||
| typedef MapSHash<ULONG, const ReverseThunkMapValue*> HashToReverseThunkHash; | ||
| HashToReverseThunkHash* reverseThunkCache = nullptr; | ||
| HashToReverseThunkHash* reverseThunkFallbackCache = nullptr; | ||
|
|
||
| HashToReverseThunkHash* CreateReverseThunkHashTable(bool fallback) | ||
| { | ||
| HashToReverseThunkHash* newTable = new HashToReverseThunkHash(); | ||
| newTable->Reallocate(g_ReverseThunksCount * HashToReverseThunkHash::s_density_factor_denominator / HashToReverseThunkHash::s_density_factor_numerator + 1); | ||
| for (size_t i = 0; i < g_ReverseThunksCount; i++) | ||
| { | ||
| newTable->Add(fallback ? g_ReverseThunks[i].fallbackKey : g_ReverseThunks[i].key, &g_ReverseThunks[i].value); | ||
| } | ||
|
|
||
| HashToReverseThunkHash **ppCache = fallback ? &reverseThunkFallbackCache : &reverseThunkCache; | ||
| if (InterlockedCompareExchangeT(ppCache, newTable, nullptr) != nullptr) | ||
| { | ||
| // Another thread won the race, discard ours | ||
| delete newTable; | ||
| } | ||
| return *ppCache; | ||
| } | ||
|
|
||
| const ReverseThunkMapValue* LookupThunk(MethodDesc* pMD) | ||
| { | ||
| HashToReverseThunkHash* table = VolatileLoad(&reverseThunkCache); | ||
| if (table == nullptr) | ||
| { | ||
| HashToReverseThunkHash* newTable = new HashToReverseThunkHash(); | ||
| newTable->Reallocate(g_ReverseThunksCount * HashToReverseThunkHash::s_density_factor_denominator / HashToReverseThunkHash::s_density_factor_numerator + 1); | ||
| for (size_t i = 0; i < g_ReverseThunksCount; i++) | ||
| { | ||
| newTable->Add(g_ReverseThunks[i].key, &g_ReverseThunks[i].value); | ||
| } | ||
|
|
||
| if (InterlockedCompareExchangeT(&reverseThunkCache, newTable, nullptr) != nullptr) | ||
| { | ||
| // Another thread won the race, discard ours | ||
| delete newTable; | ||
| } | ||
| table = reverseThunkCache; | ||
| table = CreateReverseThunkHashTable(false /* fallback */); | ||
| } | ||
|
|
||
| ULONG key = CreateKey(pMD); | ||
|
|
||
| // Try primary key, it is based on Assembly fully qualified name and method token | ||
| const ReverseThunkMapValue* thunk; | ||
| if (table->Lookup(key, &thunk)) | ||
| { | ||
| return thunk; | ||
| } | ||
|
|
||
| // Try fallback key, that is based on method properties and assembly name | ||
| // The fallback is used when the assembly is trimmed and the token and assembly fully qualified name | ||
| // may change. | ||
| table = VolatileLoad(&reverseThunkFallbackCache); | ||
| if (table == nullptr) | ||
| { | ||
| table = CreateReverseThunkHashTable(true /* fallback */); | ||
| } | ||
|
|
||
| key = CreateFallbackKey(pMD); | ||
|
|
||
|
Comment on lines
+701
to
+718
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if we get a key collision? Is it mechanically prevented from happening somewhere?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking deeper, I'm concerned that we're using a low quality 32-bit hash here, so I would hope that at least if we get a hash collision during thunk lookup, it will fail deterministically with a clear error message so we can go back and fix it. Ideally we would use a more robust 64-bit hash to make the odds of a collision way lower, or do an actual string check. But I don't know if thunk lookup is on a hot path to the point that we can't afford it. |
||
| bool success = table->Lookup(key, &thunk); | ||
| return success ? thunk : nullptr; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, having trimming enabled makes the primary key irrelevant. I'm assuming that having trimming enabled is a must have default on wasm. So does it even make sense to have 2 keys in the first place ?