[PATCH] [ms-cxxabi] Use inalloca on win32 when passing non-trivial C++ objects

Reid Kleckner rnk at google.com
Tue Jan 28 18:02:29 PST 2014

Comment at: lib/CodeGen/CGCall.cpp:2276-2279
@@ +2275,6 @@
+    if (RD->hasNonTrivialDestructor()) {
+      // Create a no-op GEP between the placeholder and the cleanup so we can
+      // RAUW it successfully.  It also serves as a marker of the first
+      // instruction where the cleanup is active.
+      llvm::Value *Addr = Builder.CreateConstInBoundsGEP1_32(Slot.getAddr(), 0);
+      pushFullExprCleanup<DestroyUnpassedArg>(EHCleanup, Addr, type);
Richard Smith wrote:
> (For posterity...) As discussed off-line, we should defer the RAUW to after we're done emitting IR for the function rather than inserting a dummy GEP here.
Fine.  I'll have to add back the other unreachable placeholder, though.

Comment at: lib/CodeGen/CGDecl.cpp:1671
@@ -1666,1 +1670,3 @@
+  LValue lv = MakeAddrLValue(DeclPtr, Ty, Align);
+  if (hasScalarEvaluationKind(Ty)) {
     Qualifiers qs = Ty.getQualifiers();
Richard Smith wrote:
> You sometimes also call this on line 1655. Maybe avoid computing it twice on that path?

Comment at: lib/CodeGen/CodeGenFunction.cpp:602
@@ -595,1 +601,3 @@
+    llvm::Value *Addr = Builder.CreateStructGEP(EI, Idx);
+    ReturnValue = Builder.CreateLoad(Addr, "agg.result");
   } else {
Richard Smith wrote:
> I wonder if we can delay this load, or mark this memory as invariant. I'm worried that we won't be able to move the load to the end of the function (or wherever we initialize the return object) in some cases, and if we can't we'll end up wasting a register or a spill slot on it.
I'm inclined to live with that for now.  This is a special case of the things we lose once we move to the bag of bytes model.  Loads and stores can't be moved across captures of a single field of the larger alloca because of our aliasing rules.

Comment at: lib/CodeGen/CGCall.cpp:1933-1935
@@ +1932,5 @@
+    // FIXME: Either emit a copy constructor call, or figure out how to do
+    // guaranteed tail calls with perfect forwarding in LLVM.
+    CGM.ErrorUnsupported(param, "non-trivial argument copy for thunk");
+    EmitNullInitialization(Slot.getAddr(), type);
Richard Smith wrote:
> Reid Kleckner wrote:
> > Richard Smith wrote:
> > > Let's move ahead with this as-is, but... in every case where we emit a delegate call, we (should) have a code path which does the same thing but duplicates the function body instead (in order to support varargs functions), and we should probably use that codepath in any case where we have an inalloca argument.
> > > 
> > > I say "(should)" because this is slightly broken for vtable thunks, and entirely missing for lambda static invoker delegates.
> > This doesn't work in general because we don't always have the definition of the function we're trying to delegate to available in this TU.
> > 
> > A good example is a pointer to a virtual member function that takes something non-trivial by value.
> > 
> > When we emit a vtable, we might need to emit vtable thunks without having the definition, and we can't rely on the other TU to emit the thunk for us and still claim to be ABI compatible with MSVC.
> > 
> > Basically, duplicating the body isn't a complete solution.  The only complete solution is to add something to LLVM that guarantees a tail call with perfect argument forwarding without requiring fastcc.
> Oh, yuck. Is this even possible in the fully general case? Do we ever need to insert code after the call (for covariant return thunks maybe)?
It isn't always possible to delegate the call because the parameters might not have complete types.  Consider the thunk needed for this covariant return type:

  struct A { };
  struct B : virtual A {};
  struct C; // { C(); C(const C &o); ~C(); };
  struct D     { virtual A *f(C); };
  struct E : D { virtual B *f(C); };
  E e;

MSVC gives a diagnostic about requiring a complete type for C in a compiler generated function for f:

  t2.cpp(6) : error C2664: 'B *E::f(C)' : cannot convert parameter 1 from 'C' to 'C'
          Source or target has incomplete type
          This diagnostic occurred in the compiler generated function 'A *E::f(C)'


More information about the cfe-commits mailing list