comdat folding

Started by
7 comments, last by Norman Barrows 9 years, 7 months ago

Just for fun, i kicked the warning level up from 4 to all on the caveman project. it did catch a few explicit conversions i missed.

after suppressing the "#ifdef symbol is undefined" and "padding data structure" warnings in the windows and directx headers, and the "inlining" and "not inlining" warnings from the linker, i set linker output to verbose. then i tried all the different verbose setttings. when i tried - show comdat folding, this is what i got:

1>------ Rebuild All started: Project: caveman, Configuration: Release Win32 ------
1>cl : Command line warning D9025: overriding '/sdl-' with '/GS-'
1> modeler.cpp
1> Z3D.cpp
1> Zaudio.cpp
1> caveman.cpp
1> Generating code
1> Finished generating code
1>
1> Selected symbol:
1> "public: __thiscall Zdrawinfo::Zdrawinfo(void)" (??0Zdrawinfo@@QAE@XZ) from modeler.obj
1> Replaced symbol(s):
1> "public: __thiscall drawrec::drawrec(void)" (??0drawrec@@QAE@XZ) from Z3D.obj
1> "public: __thiscall ttyperec::ttyperec(void)" (??0ttyperec@@QAE@XZ) from Z3D.obj
1> "public: __thiscall objtyperec::objtyperec(void)" (??0objtyperec@@QAE@XZ) from caveman.obj
1> "public: __thiscall plantrec::plantrec(void)" (??0plantrec@@QAE@XZ) from caveman.obj
1> "public: __thiscall chunk2::chunk2(void)" (??0chunk2@@QAE@XZ) from caveman.obj
1>
1> Selected symbol:
1> "void __fastcall remove_stuff_near_cave(int,int,int)" (?remove_stuff_near_cave@@YIXHHH@Z) from caveman.obj
1> Replaced symbol(s):
1> "void __fastcall remove_stuff_near_rockshelter(int,int,int)" (?remove_stuff_near_rockshelter@@YIXHHH@Z) from caveman.obj
1>
1> Selected symbol:
1> "void __fastcall runstate_cornered(int)" (?runstate_cornered@@YIXH@Z) from caveman.obj
1> Replaced symbol(s):
1> "void __fastcall run_defend_location_attack(int)" (?run_defend_location_attack@@YIXH@Z) from caveman.obj
1>
1> Selected symbol:
1> "void __fastcall generate_CB2(int,int,int,int)" (?generate_CB2@@YIXHHHH@Z) from caveman.obj
1> Replaced symbol(s):
1> "void __fastcall generate_non_tropical_savanna_tree(int,int,int,int)" (?generate_non_tropical_savanna_tree@@YIXHHHH@Z) from caveman.obj
1>
1> ICF total savings: 774 bytes
1>
1> caveman.vcxproj -> C:\Rockland\caveman\Release\caveman.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

so this means that, for example, remove_stuff_near_rockshelter and remove_stuff_near_cave are identical at the object code and/or machine code level?

i understand the concept of how this could be, just want to confirm i'm reading this output correctly.

and:

1> Selected symbol:
1> "public: __thiscall Zdrawinfo::Zdrawinfo(void)" (??0Zdrawinfo@@QAE@XZ) from modeler.obj
1> Replaced symbol(s):
1> "public: __thiscall drawrec::drawrec(void)" (??0drawrec@@QAE@XZ) from Z3D.obj

means that internally the compiler is creating code that returns a pointer to a struct, and is using the same code for all? (all the symbols listed in this case are POD structs).

is it folding them because the structs just happen to be the same size, perhaps?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

The structs don't necessarily need to be the same size. __thiscall functions take the object argument by pointer. However, both functions are doing the same things to the same offsets into the pointer.

However, both functions are doing the same things to the same offsets into the pointer.


In other words, the actual machine code for the two functions are identical.

You can see this when debugging sometimes - especially with small getter functions - where you'll set a breakpoint on `Foo::getX()` but then the debugger breaks in some seemingly unrelated `Bar::getY()`. If you examine the assembly it'll turn out that they're running the exact same set of instructions. The CPU don't care none 'bout y'all OOPy semantics. tongue.png

It's been years since I've mucked with it but I'd imagine that with GCC's "partial inlining" it could be even more confusing. smile.png

Sean Middleditch – Game Systems Engineer – Join my team!


and:
1> Selected symbol:
1> "public: __thiscall Zdrawinfo::Zdrawinfo(void)" (??0Zdrawinfo@@QAE@XZ) from modeler.obj
1> Replaced symbol(s):
1> "public: __thiscall drawrec::drawrec(void)" (??0drawrec@@QAE@XZ) from Z3D.obj

means that internally the compiler is creating code that returns a pointer to a struct, and is using the same code for all? (all the symbols listed in this case are POD structs).

Why do your POD structs have constructors? A real POD struct would not need any constructor - all of its members would be left uninitialized.


Why do your POD structs have constructors? A real POD struct would not need any constructor - all of its members would be left uninitialized.

i thought these were weird too.

here's zdrawinfo:

struct Zdrawinfo
{
int type, // 0=mesh, 1=model, 2=2d billboard, 3=3d billboard
meshID, // for models: modelID
texID, // for models: aniID
alphatest,cull,clamp,materialID,rad,cliprng,data[5];
float sx,sy,sz,x,y,z,rx,ry,rz,range; // range is rng to camera. not currently used.
D3DXMATRIX mWorld;
};
its used as a parameters list for drawing calls.
and drawrec:
// drawlist record
struct drawrec
{
int meshID,texID,alphatest,cull,clamp,materialID;
D3DXMATRIX mWorld;
float range;
};
this is an actual entry in the render queue.
to draw, you fill in the info in a zdrawinfo struct, then call a drawing method:
Zdraw - adds mesh to render queue, creates world mat from eulers in the zdrawinfo struct
Zdraw2 - adds mesh to render queue, uses world mat from the zdrawinfo struct
Zdraw_immediate - draw_indexed_primitive mesh, creates world mat from eulers in the zdrawinfo struct
Zdraw_immediate2 - draw_indexed_primitive mesh, uses world mat from the zdrawinfo struct
Zd - adds item to render queue, based on type specified in the zdrawinfo struct (mesh, model, 2d billboard, 3d billboard)
when an item is added to the render queue, the info in the zdrawinfo is used to fill in the info in the drawrec. the render queue is an unordered static array of drawrecs, and uses a 2d bucket sorted index for drawing. as drawing calls are made, they are added to the queue and to the index.
and the two structs are not identical.... most strange.
"Indeed, most curious", as Spock would say...

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


In other words, the actual machine code for the two functions are identical.

that's what i thought. i thought it might point out places where i accidentally coded some little routine twice.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Just because you didn't write a constructor doesn't mean your type doesn't have a constructor.

In this case, your structs have identical (compiler-generated) constructors, which happen to do nothing.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


and the two structs are not identical.... most strange.

"Indeed, most curious", as Spock would say...

They both have D3DXMATRIX, which is not a POD type because it has a constructor. Its constructor doesn't actually happen to do anything, though. So, your two classes are not technically POD types either, and they get default compiled constructors.

I took a stab at building a project on debug, with comdat folding, with this set of structs (the names lie, none of them are close to "Big"):

struct BigTestStruct1
{
    BigTestStruct1();
 
    int a, b, c, d, e;
};
struct BigTestStruct2
{
    BigTestStruct2();
 
    int a, b, c, d, e, f, g;
};
struct BigTestStruct3
{
    int a, b, c, d, e;
};
struct BigTestStruct4
{
    int a, b, c, d, e, f, g;
};

I put the constructors in their own .cpp files. Anyway, I got this output:

1>      Selected symbol:
1>          "public: __thiscall std::allocator<char>::allocator<char>(void)" (??0?$allocator@D@std@@QAE@XZ) from ConsoleRandomTest.obj
1>      Replaced symbol(s):
1>          "public: __thiscall std::allocator<struct std::_Container_proxy>::allocator<struct std::_Container_proxy>(void)" (??0?$allocator@U_Container_proxy@std@@@std@@QAE@XZ) from ConsoleRandomTest.obj
1>          "public: __thiscall BigTestStruct1::BigTestStruct1(void)" (??0BigTestStruct1@@QAE@XZ) from BTS1.obj
1>          "public: __thiscall BigTestStruct2::BigTestStruct2(void)" (??0BigTestStruct2@@QAE@XZ) from BTS2.obj

3 and 4 weren't in the comdat folding log or the map file.



In this case, your structs have identical (compiler-generated) constructors, which happen to do nothing.

i figured it was something like that, thanks for clearing that up.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement