Attack of dynamic initializers
16/Feb 2011
Recently, I’ve been looking a little bit what’s taking space in our executables and found an interesting ‘feature’ of MSVC. It has troubles with expanding floating-point constants that rely on another constants (…that rely on another constant, yes it requires 2 levels of indirection to break). Consider the following example:
const float CONST_1 = 3.f;
const float CONST_2 = 2.f * CONST_1;
const float CONST_3 = 1.f / CONST_2;
When compiled & linked with all optimizations enabled, including WPO, we find this in generated assembly code:
??__ECONST_3@@YAXXZ PROC ; `dynamic initializer for 'CONST_3'', COMDAT
; 251 : const float CONST_3 = 1.f / CONST_2;
movss xmm0, DWORD PTR __real@3f800000
divss xmm0, DWORD PTR _CONST_2
movss DWORD PTR _CONST_3, xmm0
ret 0
??__ECONST_3@@YAXXZ ENDP ; `dynamic initializer for 'CONST_3''
_CONST_3 DD 01H DUP (?)
...
_CONST_1 DD 040400000r ; 3
_CONST_2 DD 040c00000r ; 6
As you can see - it has no problem with calculating CONST_1 & CONST_2 at compile time, but for CONST_3 it needs to generate function that computes the final value (before entering main).
Now, let’s change CONST_3 to CONST_3 = 1.f / (2.f * CONST_1) (ie. we expand CONST_2 by hand & remove a level of indirection) and compare generated assembly code:
_CONST_2 DD 040c00000r ; 6
_CONST_3 DD 03e2aaaabr ; 0.166667
That’s better… Obviously, old school #define solution works as well. It’s not really a big deal, unless you put it in some global header, but it was an interesting thing to discover. Next time – a story of vector `ctors’….
Old comments
cb 2011-02-16 01:21:52
According to the standard, floats in C++ are initialized like classes - that is, at _cinit time - not like ints or enums which act like true compiler constants.
Most compilers will do some compile-time float constant evaluation, but it’s totally undefined and they have the option to do none at all.
David Neubelt 2011-02-16 05:36:18
Yah, but who cares about the C++ standard… we just care about the compiler doing the ‘right’ thing.
:p
NA 2011-02-16 09:13:08
maybe you can avoid this behaviour with “__forceinline”?
shash 2011-02-16 12:35:57
Found the same behaviour while working on 4k intros (demoscene related), quite strange imho :P
Todd 2011-04-13 21:01:24
Maybe the preprocessor can only do two preprocessor passes by default…I dunno though.