Attack of dynamic initializers

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.

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.

More Reading
Older// Retro Pinball