My native C++ COM component uses ATL. In DllRegisterServer()
I call CComModule::RegisterServer()
:
STDAPI DllRegisterServer()
{
return _Module.RegisterServer(FALSE); // <<< notice FALSE here
}
F开发者_StackOverflowALSE
is passed to indicate to not register the type library.
ATL is available as sources, so I in fact compile the implementation of CComModule::RegisterServer()
. Somewhere down the call stack there's an if
statement:
if( doRegisterTypeLibrary ) { //<< FALSE goes here
// do some stuff, then call RegisterTypeLib()
}
The compiler sees all of the above code and so it can see that in fact the if
condition is always false, yet when I inspect the linker progress messages I see that the reference to RegisterTypeLib()
is still there, so the if
statement is not eliminated.
Can I make Visual C++ 9 perform better static analysis and actually see that some code is never called and not emit that code?
Do you have whole program optimization active [/GL]? This seems like the sort of optimization the compiler generally can't do on its own.
Are you sure the code isn't eliminated later in the compilation/linking process? Have you checked the generated ASM?
How is the RegisterTypeLib
function defined? Of course anything marked dllexport
can't be eliminated by the linker, but also any function not marked static (or placed in an anonymous namespace) can be referenced by multiple translation units, so the compiler won't be able to eliminate the function.
The linker can do it, but that might be one of the last optimizations it performs (I have no clue of the order in which it applies optimizations), so the symbols might still be there in the messages you're looking at, even if they're eliminated afterwards.
any code that is successfully inlined will only be generated if called. That's a simple way to do it, as long as the compiler will take the hint. inline is only a suggestion though
The inner call to AtlComModuleRegisterServer has external linkage, which generally prevents the optimizer from propagating the bRegTypeLib value down the call graph. Some of this can be better reasoned with in the disassembly.
So DllInstall(...)
calls CAtlDllModuleT::RegisterServer(0)
. This is the start of the problem:
push 0
call ?DllRegisterServer@?$CAtlDllModuleT@VCAtlTestModule@@@ATL@@QAEJH@Z
Let's just say for arguments sake that the compiler has verified CAtlDllModuleT::DllRegisterServer
is only called once and it's very safe to push the 0/FALSE down one more level... the external linkage prevents discarding AtlComModuleRegisterServer
, inlining it has a high cost (code duplication) and doesn't allow any additional whole-program optimizations. It is probably safer to keep the signature as-is and bail out early with a regular cdecl call...
?DllRegisterServer@?$CAtlDllModuleT@VCAtlTestModule@@@ATL@@QAEJH@Z proc near
<trimmed>
push 0
push edx
push offset ATL::_AtlComModule
call _AtlComModuleRegisterServer@12
This code can be improved in size due to the two constants, but it's likely to cost about the same amount of runtime. If performance is an issue consider explicitly setting the function layout order, you might save a page fault.
Turns out the key is to enable link-time code generator all the way through the compiler settings.
It must be enabled on the General tab - Whole program optimization must be set to "Use link-time code generation". It must also be enabled on the C++ -> Optimization tab - "Whole program optimization* must be set to "Enable link-time code generation". It must also be enabled on the Linker -> Optimization tab - Link Time Code Generation must be set to "Use Link Time Code Generation". Then /OPT:REF and /OPT:ICF (again, *Linker -> Optimization" tab) must both be enabled.
And this effectively removes the calls to RegisterTypeLib()
- it is no longer in the list of symbols imported.
精彩评论