[cfe-commits] r130993 - in /cfe/trunk: lib/CodeGen/CGVTables.cpp lib/CodeGen/CodeGenFunction.h lib/CodeGen/CodeGenModule.cpp test/CodeGenCXX/thunks.cpp

Eli Friedman eli.friedman at gmail.com
Fri May 6 10:27:27 PDT 2011


Author: efriedma
Date: Fri May  6 12:27:27 2011
New Revision: 130993

URL: http://llvm.org/viewvc/llvm-project?rev=130993&view=rev
Log:
Add an implementation of thunks for varargs methods.  The implementation is a bit messy, but it is correct as long as the method in question doesn't use indirect gotos.  A couple of possible alternative implementations are outlined in FIXME's in this patch. rdar://problem/8077308 .


Modified:
    cfe/trunk/lib/CodeGen/CGVTables.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/CodeGen/CodeGenModule.cpp
    cfe/trunk/test/CodeGenCXX/thunks.cpp

Modified: cfe/trunk/lib/CodeGen/CGVTables.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGVTables.cpp?rev=130993&r1=130992&r2=130993&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGVTables.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGVTables.cpp Fri May  6 12:27:27 2011
@@ -21,6 +21,7 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Format.h"
+#include "llvm/Transforms/Utils/Cloning.h"
 #include <algorithm>
 #include <cstdio>
 
@@ -2636,6 +2637,131 @@
 }
 #endif
 
+static RValue PerformReturnAdjustment(CodeGenFunction &CGF,
+                                      QualType ResultType, RValue RV,
+                                      const ThunkInfo &Thunk) {
+  // Emit the return adjustment.
+  bool NullCheckValue = !ResultType->isReferenceType();
+  
+  llvm::BasicBlock *AdjustNull = 0;
+  llvm::BasicBlock *AdjustNotNull = 0;
+  llvm::BasicBlock *AdjustEnd = 0;
+  
+  llvm::Value *ReturnValue = RV.getScalarVal();
+
+  if (NullCheckValue) {
+    AdjustNull = CGF.createBasicBlock("adjust.null");
+    AdjustNotNull = CGF.createBasicBlock("adjust.notnull");
+    AdjustEnd = CGF.createBasicBlock("adjust.end");
+  
+    llvm::Value *IsNull = CGF.Builder.CreateIsNull(ReturnValue);
+    CGF.Builder.CreateCondBr(IsNull, AdjustNull, AdjustNotNull);
+    CGF.EmitBlock(AdjustNotNull);
+  }
+  
+  ReturnValue = PerformTypeAdjustment(CGF, ReturnValue, 
+                                      Thunk.Return.NonVirtual, 
+                                      Thunk.Return.VBaseOffsetOffset);
+  
+  if (NullCheckValue) {
+    CGF.Builder.CreateBr(AdjustEnd);
+    CGF.EmitBlock(AdjustNull);
+    CGF.Builder.CreateBr(AdjustEnd);
+    CGF.EmitBlock(AdjustEnd);
+  
+    llvm::PHINode *PHI = CGF.Builder.CreatePHI(ReturnValue->getType(), 2);
+    PHI->addIncoming(ReturnValue, AdjustNotNull);
+    PHI->addIncoming(llvm::Constant::getNullValue(ReturnValue->getType()), 
+                     AdjustNull);
+    ReturnValue = PHI;
+  }
+  
+  return RValue::get(ReturnValue);
+}
+
+// This function does roughly the same thing as GenerateThunk, but in a
+// very different way, so that va_start and va_end work correctly.
+// FIXME: This function assumes "this" is the first non-sret LLVM argument of
+//        a function, and that there is an alloca built in the entry block
+//        for all accesses to "this".
+// FIXME: This function assumes there is only one "ret" statement per function.
+// FIXME: Cloning isn't correct in the presence of indirect goto!
+// FIXME: This implementation of thunks bloats codesize by duplicating the
+//        function definition.  There are alternatives:
+//        1. Add some sort of stub support to LLVM for cases where we can
+//           do a this adjustment, then a sibcall.
+//        2. We could transform the definition to take a va_list instead of an
+//           actual variable argument list, then have the thunks (including a
+//           no-op thunk for the regular definition) call va_start/va_end.
+//           There's a bit of per-call overhead for this solution, but it's
+//           better for codesize if the definition is long.
+void CodeGenFunction::GenerateVarArgsThunk(
+                                      llvm::Function *Fn,
+                                      const CGFunctionInfo &FnInfo,
+                                      GlobalDecl GD, const ThunkInfo &Thunk) {
+  const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
+  const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
+  QualType ResultType = FPT->getResultType();
+
+  // Get the original function
+  const llvm::Type *Ty =
+    CGM.getTypes().GetFunctionType(FnInfo, /*IsVariadic*/true);
+  llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true);
+  llvm::Function *BaseFn = cast<llvm::Function>(Callee);
+
+  // Clone to thunk.
+  llvm::Function *NewFn = llvm::CloneFunction(BaseFn);
+  CGM.getModule().getFunctionList().push_back(NewFn);
+  Fn->replaceAllUsesWith(NewFn);
+  NewFn->takeName(Fn);
+  Fn->eraseFromParent();
+  Fn = NewFn;
+
+  // "Initialize" CGF (minimally).
+  CurFn = Fn;
+
+  // Get the "this" value
+  llvm::Function::arg_iterator AI = Fn->arg_begin();
+  if (CGM.ReturnTypeUsesSRet(FnInfo))
+    ++AI;
+
+  // Find the first store of "this", which will be to the alloca associated
+  // with "this".
+  llvm::Value *ThisPtr = &*AI;
+  llvm::BasicBlock *EntryBB = Fn->begin();
+  llvm::Instruction *ThisStore = 0;
+  for (llvm::BasicBlock::iterator I = EntryBB->begin(), E = EntryBB->end();
+       I != E; I++) {
+    if (isa<llvm::StoreInst>(I) && I->getOperand(0) == ThisPtr) {
+      ThisStore = cast<llvm::StoreInst>(I);
+      break;
+    }
+  }
+  assert(ThisStore && "Store of this should be in entry block?");
+  // Adjust "this", if necessary.
+  Builder.SetInsertPoint(ThisStore);
+  llvm::Value *AdjustedThisPtr = 
+    PerformTypeAdjustment(*this, ThisPtr, 
+                          Thunk.This.NonVirtual, 
+                          Thunk.This.VCallOffsetOffset);
+  ThisStore->setOperand(0, AdjustedThisPtr);
+
+  if (!Thunk.Return.isEmpty()) {
+    // Fix up the returned value, if necessary.
+    for (llvm::Function::iterator I = Fn->begin(), E = Fn->end(); I != E; I++) {
+      llvm::Instruction *T = I->getTerminator();
+      if (isa<llvm::ReturnInst>(T)) {
+        RValue RV = RValue::get(T->getOperand(0));
+        T->eraseFromParent();
+        Builder.SetInsertPoint(&*I);
+        RV = PerformReturnAdjustment(*this, ResultType, RV, Thunk);
+        Builder.CreateRet(RV.getScalarVal());
+        break;
+      }
+    }
+  }
+}
+
 void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
                                     const CGFunctionInfo &FnInfo,
                                     GlobalDecl GD, const ThunkInfo &Thunk) {
@@ -2715,45 +2841,8 @@
   // Now emit our call.
   RValue RV = EmitCall(FnInfo, Callee, Slot, CallArgs, MD);
   
-  if (!Thunk.Return.isEmpty()) {
-    // Emit the return adjustment.
-    bool NullCheckValue = !ResultType->isReferenceType();
-    
-    llvm::BasicBlock *AdjustNull = 0;
-    llvm::BasicBlock *AdjustNotNull = 0;
-    llvm::BasicBlock *AdjustEnd = 0;
-    
-    llvm::Value *ReturnValue = RV.getScalarVal();
-
-    if (NullCheckValue) {
-      AdjustNull = createBasicBlock("adjust.null");
-      AdjustNotNull = createBasicBlock("adjust.notnull");
-      AdjustEnd = createBasicBlock("adjust.end");
-    
-      llvm::Value *IsNull = Builder.CreateIsNull(ReturnValue);
-      Builder.CreateCondBr(IsNull, AdjustNull, AdjustNotNull);
-      EmitBlock(AdjustNotNull);
-    }
-    
-    ReturnValue = PerformTypeAdjustment(*this, ReturnValue, 
-                                        Thunk.Return.NonVirtual, 
-                                        Thunk.Return.VBaseOffsetOffset);
-    
-    if (NullCheckValue) {
-      Builder.CreateBr(AdjustEnd);
-      EmitBlock(AdjustNull);
-      Builder.CreateBr(AdjustEnd);
-      EmitBlock(AdjustEnd);
-    
-      llvm::PHINode *PHI = Builder.CreatePHI(ReturnValue->getType(), 2);
-      PHI->addIncoming(ReturnValue, AdjustNotNull);
-      PHI->addIncoming(llvm::Constant::getNullValue(ReturnValue->getType()), 
-                       AdjustNull);
-      ReturnValue = PHI;
-    }
-    
-    RV = RValue::get(ReturnValue);
-  }
+  if (!Thunk.Return.isEmpty())
+    RV = PerformReturnAdjustment(*this, ResultType, RV, Thunk);
 
   if (!ResultType->isVoidType() && Slot.isNull())
     CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType);
@@ -2823,8 +2912,18 @@
     return;
   }
 
-  // Actually generate the thunk body.
-  CodeGenFunction(CGM).GenerateThunk(ThunkFn, FnInfo, GD, Thunk);
+  if (ThunkFn->isVarArg()) {
+    // Varargs thunks are special; we can't just generate a call because
+    // we can't copy the varargs.  Our implementation is rather
+    // expensive/sucky at the moment, so don't generate the thunk unless
+    // we have to.
+    // FIXME: Do something better here; GenerateVarArgsThunk is extremely ugly.
+    if (!UseAvailableExternallyLinkage)
+      CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, FnInfo, GD, Thunk);
+  } else {
+    // Normal thunk body generation.
+    CodeGenFunction(CGM).GenerateThunk(ThunkFn, FnInfo, GD, Thunk);
+  }
 
   if (UseAvailableExternallyLinkage)
     ThunkFn->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=130993&r1=130992&r2=130993&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Fri May  6 12:27:27 2011
@@ -1157,6 +1157,9 @@
   void GenerateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo,
                      GlobalDecl GD, const ThunkInfo &Thunk);
 
+  void GenerateVarArgsThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo,
+                            GlobalDecl GD, const ThunkInfo &Thunk);
+
   void EmitCtorPrologue(const CXXConstructorDecl *CD, CXXCtorType Type,
                         FunctionArgList &Args);
 

Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=130993&r1=130992&r2=130993&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Fri May  6 12:27:27 2011
@@ -793,14 +793,19 @@
       return;
 
     if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+      // Make sure to emit the definition(s) before we emit the thunks.
+      // This is necessary for the generation of certain thunks.
+      if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(Method))
+        EmitCXXConstructor(CD, GD.getCtorType());
+      else if (const CXXDestructorDecl *DD =dyn_cast<CXXDestructorDecl>(Method))
+        EmitCXXDestructor(DD, GD.getDtorType());
+      else
+        EmitGlobalFunctionDefinition(GD);
+
       if (Method->isVirtual())
         getVTables().EmitThunks(GD);
 
-      if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(Method))
-        return EmitCXXConstructor(CD, GD.getCtorType());
-  
-      if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(Method))
-        return EmitCXXDestructor(DD, GD.getDtorType());
+      return;
     }
 
     return EmitGlobalFunctionDefinition(GD);

Modified: cfe/trunk/test/CodeGenCXX/thunks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/thunks.cpp?rev=130993&r1=130992&r2=130993&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/thunks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/thunks.cpp Fri May  6 12:27:27 2011
@@ -265,18 +265,42 @@
   struct C : B         { virtual C* f(); };
   C* C::f() { return 0; }
 
+  //  C::f itself.
+  // CHECK: define {{.*}} @_ZN6Test111C1fEv(
+
   //  The this-adjustment and return-adjustment thunk required when
   //  C::f appears in a vtable where A is at a nonzero offset from C.
   // CHECK: define {{.*}} @_ZTcv0_n24_v0_n32_N6Test111C1fEv(
 
-  //  C::f itself.
-  // CHECK: define {{.*}} @_ZN6Test111C1fEv(
-
   //  The return-adjustment thunk required when C::f appears in a vtable
   //  where A is at a zero offset from C.
   // CHECK: define {{.*}} @_ZTch0_v0_n32_N6Test111C1fEv(
 }
 
+// Varargs thunk test.
+namespace Test12 {
+  struct A {
+    virtual A* f(int x, ...);
+  };
+  struct B {
+    virtual B* f(int x, ...);
+  };
+  struct C : A, B {
+    virtual void c();
+    virtual C* f(int x, ...);
+  };
+  C* C::f(int x, ...) { return this; }
+
+  // C::f
+  // CHECK: define {{.*}} @_ZN6Test121C1fEiz
+
+  // Varargs thunk; check that both the this and covariant adjustments
+  // are generated.
+  // CHECK: define {{.*}} @_ZTchn8_h8_N6Test121C1fEiz
+  // CHECK: getelementptr inbounds i8* {{.*}}, i64 -8
+  // CHECK: getelementptr inbounds i8* {{.*}}, i64 8
+}
+
 /**** The following has to go at the end of the file ****/
 
 // This is from Test5:





More information about the cfe-commits mailing list