-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
winapisupp.cpp
: Dramatically shrink the function pointer table
#2841
Conversation
The only direct uses of `__KERNEL32Functions` are in `STOREFUNCTIONPOINTER` and `DYNAMICGETCACHEDFUNCTION`. The only use of `DYNAMICGETCACHEDFUNCTION` is in `IFDYNAMICGETCACHEDFUNCTION`. There are only two uses of `IFDYNAMICGETCACHEDFUNCTION`, loading `GetCurrentPackageId` and `GetSystemTimePreciseAsFileTime`. Therefore, all other table entries are unused. The comment `// This enum should not change` was introduced by GH 1194, but we were being overly cautious. The enum and table aren't mentioned outside of `stl/src`. Also, while the table was `extern "C"`, it wasn't exported. Therefore, they're purely internal to the separately compiled STL.
It was within `_CRT_BEGIN_C_HEADER` / `_CRT_END_C_HEADER`.
…Fuse declaration and definition. The definition and initialization of `__KERNEL32Functions` were effectively guarded by `#if !defined(_ONECORE)`, so we should extend that guard to all of the machinery. When fusing the declaration and definition, we're within `_CRT_BEGIN_C_HEADER` so we don't need `extern "C"`. We also don't need `extern` (the definition already has external linkage, and other TUs don't need this anyways). For style, initialize with `{}` instead of ` = {nullptr}`. They have the same effect here, but `{}` emphasizes that we want to value-initialize the entire table.
…unctions` machinery. We don't need this in other TUs. The unnamed namespace emphasizes that it's purely internal.
Now we need to guard against `eMaxKernel32Function` being `0`.
See: https://docs.microsoft.com/en-us/windows/win32/api/appmodel/nf-appmodel-getcurrentpackageid Verified that this is now identical to `decltype(&GetCurrentPackageId)`.
Update comment. Style: Directly test the function pointer.
We no longer need `PFNGETSYSTEMTIMEPRECISEASFILETIME`. Also, always emit `pf##function_name`.
We still need to export it for bincompat.
Verified with an internal build.
In the past there were calls from other TUs, that's why macros and extern "C". I'm not sure ifvthis won't be neeeded if we'll expand the table again. |
Going forward:
I expect that this will be uncommon, since it's rare for us to need new WinAPIs in the STL's DLL (since we can't add new exports to it); #2302 may be an example that needs it. Note that because the table wasn't exported, it is useless to anything outside of the STL's DLL (in particular, it cannot be accessed by the import lib), so isolating it to one TU in |
I'm speculatively mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed. |
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.
Approved with suggestions.
enum wrapKERNEL32Functions { | ||
#if !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
eGetCurrentPackageId, | ||
#endif // !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
|
||
#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
eGetSystemTimePreciseAsFileTime, | ||
#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
|
||
eMaxKernel32Function |
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.
it strikes me as not the best idea to preprocess out enumerators like this without explicitly assigning values. If this is ever passed across ABI boundaries (it does not appear to be now) it might blow up pretty immediately
Pre-existing though
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.
Agreed, pre-existing. I believe that the risk is low (as you mentioned, this table and its enumerators aren't part of the ABI surface area - also, the conditions are inherently of the form "how is the STL being built"), and it's further mitigated by this PR, as the table and its enumerators are now confined to an unnamed namespace in a single TU.
That said, it now seems to me that the case for having a table is weaker than ever. We never iterate over the table, so we could simply use separate function pointers (which the compiler could still store densely). That would avoid any concerns about varying enumerator values, and actually make debugging potentially easier. I may explore this in a followup PR.
#define STOREFUNCTIONPOINTER(instance, function_name) \ | ||
__KERNEL32Functions[e##function_name] = reinterpret_cast<PVOID>(GetProcAddress(instance, #function_name)) | ||
|
||
// Use this macro for retrieving a cached function pointer from a DLL | ||
#define IFDYNAMICGETCACHEDFUNCTION(name) \ | ||
if (const auto pf##name = reinterpret_cast<decltype(&name)>(__KERNEL32Functions[e##name]); pf##name) | ||
|
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.
now that we are only doing this a few times it might be easier to just expand these macros.
If we're going to use macros for this we could also use xmacros to define the above enumeration and the below initialization function at the same time.
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.
Hmm - the primary value of the macros is now enforcing the consistent use of decltype
, so I would hesitate to eliminate them completely.
X-macros are definitely a potential technique, although they are quite powerful/opaque. As there are a small number of locations (definition, initialization, usage) and they're slowly-changing, I tend to think that we don't need such machinery at this time - especially now that the number of enumerators is so much smaller.
I'll think about refactoring this in a followup PR. I think it might be feasible to have one macro for definition (using decltype
for individual variables) and one for initialization (ensuring that the GetProcAddress
uses the correct spelling), and then directly refer to the variable for usage.
Thanks for bringing this up!
Now that we've merged #2317, there are only a couple of functions we're dynamically loading.
__KERNEL32Functions
table.__KERNEL32Functions
are inSTOREFUNCTIONPOINTER
andDYNAMICGETCACHEDFUNCTION
.DYNAMICGETCACHEDFUNCTION
is inIFDYNAMICGETCACHEDFUNCTION
.IFDYNAMICGETCACHEDFUNCTION
, loadingGetCurrentPackageId
andGetSystemTimePreciseAsFileTime
.// This enum should not change
was introduced by Remove XP (and Server 2003) support from STL #1194, but we were being overly cautious. The enum and table aren't mentioned outside ofstl/src
. Also, while the table wasextern "C"
, it wasn't exported. Therefore, they're purely internal to the separately compiled STL.GetCurrentPackageId
.__KERNEL32Functions
machinery towinapisupp.cpp
._CRT_BEGIN_C_HEADER
/_CRT_END_C_HEADER
.__KERNEL32Functions
machinery with#if !defined(_ONECORE)
. Fuse declaration and definition.__KERNEL32Functions
were effectively guarded by#if !defined(_ONECORE)
, so we should extend that guard to all of the machinery._CRT_BEGIN_C_HEADER
so we don't needextern "C"
. We also don't needextern
(the definition already has external linkage, and other TUs don't need this anyways).{}
instead of= {nullptr}
. They have the same effect here, but{}
emphasizes that we want to value-initialize the entire table.extern "C"
for the__KERNEL32Functions
machinery.GetSystemTimePreciseAsFileTime
only when we need it.eMaxKernel32Function
being0
.PFNGETCURRENTPACKAGEID
typedef.decltype(&GetCurrentPackageId)
.DYNAMICGETCACHEDFUNCTION
intoIFDYNAMICGETCACHEDFUNCTION
.decltype
inIFDYNAMICGETCACHEDFUNCTION
.PFNGETSYSTEMTIMEPRECISEASFILETIME
.pf##function_name
.STOREFUNCTIONPOINTER
.winapisupp.cpp
isn't using<cstdint>
at all.__crt_IsPackagedAppHelper()
with range-for and auto.awint.hpp
doesn't need to declare__crtIsPackagedApp()
.decltype
forGetCurrentPackageId
.This shrinks the STL's DLL by a couple of KB on x64:
msvcp140_oss.dll
msvcp140d_oss.dll
libcpmt.lib
libcpmtd.lib