From: Eli Friedman Date: Fri, 6 May 2011 17:27:27 +0000 (+0000) Subject: Add an implementation of thunks for varargs methods. The implementation is a bit... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7dcdf5ba9324a9577461eae302e88fdd52e310c5;p=clang 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 . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130993 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 581467c537..c7ef9c7418 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -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 #include @@ -2636,6 +2637,131 @@ static bool similar(const ABIArgInfo &infoL, CanQualType typeL, } #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(GD.getDecl()); + const FunctionProtoType *FPT = MD->getType()->getAs(); + 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(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(I) && I->getOperand(0) == ThisPtr) { + ThisStore = cast(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(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 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn, // 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 @@ void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk, 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); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 169c576f1a..0745647b5e 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1157,6 +1157,9 @@ public: 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); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 4bf1e3ad3e..96aa725f56 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -793,14 +793,19 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD) { return; if (const CXXMethodDecl *Method = dyn_cast(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(Method)) + EmitCXXConstructor(CD, GD.getCtorType()); + else if (const CXXDestructorDecl *DD =dyn_cast(Method)) + EmitCXXDestructor(DD, GD.getDtorType()); + else + EmitGlobalFunctionDefinition(GD); + if (Method->isVirtual()) getVTables().EmitThunks(GD); - if (const CXXConstructorDecl *CD = dyn_cast(Method)) - return EmitCXXConstructor(CD, GD.getCtorType()); - - if (const CXXDestructorDecl *DD = dyn_cast(Method)) - return EmitCXXDestructor(DD, GD.getDtorType()); + return; } return EmitGlobalFunctionDefinition(GD); diff --git a/test/CodeGenCXX/thunks.cpp b/test/CodeGenCXX/thunks.cpp index a74cc053db..7c80b96b79 100644 --- a/test/CodeGenCXX/thunks.cpp +++ b/test/CodeGenCXX/thunks.cpp @@ -265,18 +265,42 @@ namespace Test11 { 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: