Hi Ryan,<div><br></div><div>There are a few different C++ notions which are relevant here:</div><div><br></div><div> * default-initialization is what happens when no initializer is specified for an object</div><div> * value-initialization is what happens when an empty initializer is specified (as empty parens, or in C++11, as empty braces)</div>
<div> * zero-initialization is the step which is memset()ing your object to 0s.</div><div><br></div><div>Both default-initialization and value-initialization can result in a default constructor being called. But value-initialization sometimes performs zero-initialization first, when the default constructor is not user-provided.</div>
<div><br></div><div>Example:</div><div><br></div><div>#include <iostream></div><div><br></div><div>struct A {</div><div>  A() { std::cout << n << std::endl; }</div><div>  int n;</div><div>};</div><div>struct B {</div>
<div>  A a;</div><div>};</div><div>struct C {</div><div>  C() {}</div><div>  A a;</div><div>};</div><div><br></div><div>int main() {</div><div>  char *p = new char[sizeof(B)];</div><div>  *(int*)p = 1;</div><div>  new (p) B; // prints 1</div>
<div><br></div><div>  char *q = new char[sizeof(B)];</div><div>  *(int*)q = 1;</div><div>  new (q) B(); // prints 0</div><div><br></div><div>  char *r = new char[sizeof(B)];</div><div>  *(int*)r = 1;</div><div>  new (r) C; // prints 1</div>
<div><br></div><div>  char *s = new char[sizeof(B)];</div><div>  *(int*)s = 1;</div><div>  new (s) C(); // prints 1</div><div>}</div><div><br></div><div>Note that new B() triggers zero-initialization, because it calls a non-user-provided default constructor using empty parens. The other cases do not, either because they call a user-provided constructor or because they use default-initialization rather than value-initialization.<br>
<br><div class="gmail_quote">On Mon, Jul 30, 2012 at 12:58 PM, Ryan C. Gordon <span dir="ltr"><<a href="mailto:icculus@icculus.org" target="_blank">icculus@icculus.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
I'm running into a problem with clang (both in XCode 4.4 and clang's<br>
Subversion repository, and maybe earlier versions too) which is a little<br>
strange. I've tried to dig around in clang, but there's just too much<br>
background knowledge I lack to solve this on my own. I'll try to explain<br>
it here the best I can, and maybe someone can point me in the right<br>
direction?<br>
<br>
Here's my problem:<br>
<br>
I've got a game built on an old version of the Unreal Engine, which does<br>
something strange/scary/interesting to support the engine's scripting<br>
language. The scripting language is bridged to C++, so that some objects<br>
can be used in either language, back and forth, transparently.<br>
<br>
To support this, the game malloc()'s a buffer for a scripted object,<br>
memcpy()'s a block of bytes over it that has bunch of default values for<br>
various data members specified in script, and then does a placement-new<br>
on that buffer, where the C++ constructor for that object might<br>
overwrite some of those default values, bubbling up through all the C++<br>
parent classes.<br>
<br>
There's a whole bunch of macro and template magic to make this system<br>
work. It's impressive and terrifying, but it has worked on several<br>
titles since 1997 or so, across various platforms, versions and targets<br>
for CodeWarrior, gcc, and Visual Studio.<br>
<br>
Here's the problem: some of the objects, between where we memcpy()'d the<br>
default values from script and where we call the C++ constructor via<br>
placement-new, has a memset() inserted that zeroes out the whole object.<br>
This memset() is being inserted by clang, not our code.<br>
<br>
This is happening around line 416 of clang's lib/CodeGen/CGExprCXX.cpp:<br>
<br>
   // Otherwise, just memset the whole thing to zero.  This is legal<br>
   // because in LLVM, all default initializers (other than the ones we just<br>
   // handled above) are guaranteed to have a bit pattern of all zeros.<br>
   CGF.Builder.CreateMemSet(DestPtr, CGF.Builder.getInt8(0), SizeVal,<br>
                            Align.getQuantity());<br>
<br>
<br>
All the scriptable objects get constructed with the same macro, but<br>
looking at the disassembly, only some of them get a memset() inserted.<br>
It's not clear to me why some do and some don't.<br>
<br>
The same macro magic that constructs the object is used in every C++<br>
class that is scriptable, but most of these classes are big and<br>
complicated beyond that piece of code. I could arrange for Apple to take<br>
a look at the source code off-list if that would be helpful, but it's<br>
not my code to hand out to the public.<br>
<br>
This isn't my area of expertise, but I need to get rid of that memset()<br>
reliably for Unreal's mechanism to function properly. What might make<br>
clang decide that a given C++ constructor would need to run through<br>
EmitNullBaseClassInitialization()? I wasn't able to find the right<br>
incantation of grep to figure out where semantic analysis makes this<br>
decision.<br>
<br>
(This is tested against clang on Mac OS X. The gcc-llvm that ships in<br>
Xcode 4.4 works for the game, but I'd rather ship this game with clang<br>
if possible, because clang is awesome.  :)  )<br>
<br>
Thanks,<br>
--ryan.<br>
<br>
_______________________________________________<br>
cfe-dev mailing list<br>
<a href="mailto:cfe-dev@cs.uiuc.edu">cfe-dev@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a><br>
</blockquote></div><br></div>