Skip to content

Commit ab2090d

Browse files
committed
MS ABI: Use musttail for vtable thunks that pass arguments by value
This moves some memptr specific code into the generic thunk emission codepath. Fixes PR20053. Reviewers: majnemer Differential Revision: http://reviews.llvm.org/D4613 llvm-svn: 214004
1 parent 3f76ac7 commit ab2090d

File tree

7 files changed

+205
-58
lines changed

7 files changed

+205
-58
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,19 +2045,8 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args,
20452045
return args.add(RValue::get(Builder.CreateLoad(local)), type);
20462046
}
20472047

2048-
if (isInAllocaArgument(CGM.getCXXABI(), type)) {
2049-
AggValueSlot Slot = createPlaceholderSlot(*this, type);
2050-
Slot.setExternallyDestructed();
2051-
2052-
// FIXME: Either emit a copy constructor call, or figure out how to do
2053-
// guaranteed tail calls with perfect forwarding in LLVM.
2054-
CGM.ErrorUnsupported(param, "non-trivial argument copy for thunk");
2055-
EmitNullInitialization(Slot.getAddr(), type);
2056-
2057-
RValue RV = Slot.asRValue();
2058-
args.add(RV, type);
2059-
return;
2060-
}
2048+
assert(!isInAllocaArgument(CGM.getCXXABI(), type) &&
2049+
"cannot emit delegate call arguments for inalloca arguments!");
20612050

20622051
args.add(convertTempToRValue(local, type, loc), type);
20632052
}

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,18 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Value *Callee,
236236
*this, LoadCXXThis(), Thunk->This)
237237
: LoadCXXThis();
238238

239+
if (CurFnInfo->usesInAlloca()) {
240+
// We don't handle return adjusting thunks, because they require us to call
241+
// the copy constructor. For now, fall through and pretend the return
242+
// adjustment was empty so we don't crash.
243+
if (Thunk && !Thunk->Return.isEmpty()) {
244+
CGM.ErrorUnsupported(
245+
MD, "non-trivial argument copy for return-adjusting thunk");
246+
}
247+
EmitMustTailThunk(MD, AdjustedThisPtr, Callee);
248+
return;
249+
}
250+
239251
// Start building CallArgs.
240252
CallArgList CallArgs;
241253
QualType ThisType = MD->getThisType(getContext());
@@ -278,8 +290,9 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Value *Callee,
278290
Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified());
279291

280292
// Now emit our call.
281-
RValue RV = EmitCall(*CurFnInfo, Callee, Slot, CallArgs, MD);
282-
293+
llvm::Instruction *CallOrInvoke;
294+
RValue RV = EmitCall(*CurFnInfo, Callee, Slot, CallArgs, MD, &CallOrInvoke);
295+
283296
// Consider return adjustment if we have ThunkInfo.
284297
if (Thunk && !Thunk->Return.isEmpty())
285298
RV = PerformReturnAdjustment(*this, ResultType, RV, *Thunk);
@@ -294,6 +307,62 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Value *Callee,
294307
FinishFunction();
295308
}
296309

310+
void CodeGenFunction::EmitMustTailThunk(const CXXMethodDecl *MD,
311+
llvm::Value *AdjustedThisPtr,
312+
llvm::Value *Callee) {
313+
// Emitting a musttail call thunk doesn't use any of the CGCall.cpp machinery
314+
// to translate AST arguments into LLVM IR arguments. For thunks, we know
315+
// that the caller prototype more or less matches the callee prototype with
316+
// the exception of 'this'.
317+
SmallVector<llvm::Value *, 8> Args;
318+
for (llvm::Argument &A : CurFn->args())
319+
Args.push_back(&A);
320+
321+
// Set the adjusted 'this' pointer.
322+
const ABIArgInfo &ThisAI = CurFnInfo->arg_begin()->info;
323+
if (ThisAI.isDirect()) {
324+
const ABIArgInfo &RetAI = CurFnInfo->getReturnInfo();
325+
int ThisArgNo = RetAI.isIndirect() && !RetAI.isSRetAfterThis() ? 1 : 0;
326+
llvm::Type *ThisType = Args[ThisArgNo]->getType();
327+
if (ThisType != AdjustedThisPtr->getType())
328+
AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr, ThisType);
329+
Args[ThisArgNo] = AdjustedThisPtr;
330+
} else {
331+
assert(ThisAI.isInAlloca() && "this is passed directly or inalloca");
332+
llvm::Value *ThisAddr = GetAddrOfLocalVar(CXXABIThisDecl);
333+
llvm::Type *ThisType =
334+
cast<llvm::PointerType>(ThisAddr->getType())->getElementType();
335+
if (ThisType != AdjustedThisPtr->getType())
336+
AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr, ThisType);
337+
Builder.CreateStore(AdjustedThisPtr, ThisAddr);
338+
}
339+
340+
// Emit the musttail call manually. Even if the prologue pushed cleanups, we
341+
// don't actually want to run them.
342+
llvm::CallInst *Call = Builder.CreateCall(Callee, Args);
343+
Call->setTailCallKind(llvm::CallInst::TCK_MustTail);
344+
345+
// Apply the standard set of call attributes.
346+
unsigned CallingConv;
347+
CodeGen::AttributeListType AttributeList;
348+
CGM.ConstructAttributeList(*CurFnInfo, MD, AttributeList, CallingConv,
349+
/*AttrOnCallSite=*/true);
350+
llvm::AttributeSet Attrs =
351+
llvm::AttributeSet::get(getLLVMContext(), AttributeList);
352+
Call->setAttributes(Attrs);
353+
Call->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
354+
355+
if (Call->getType()->isVoidTy())
356+
Builder.CreateRetVoid();
357+
else
358+
Builder.CreateRet(Call);
359+
360+
// Finish the function to maintain CodeGenFunction invariants.
361+
// FIXME: Don't emit unreachable code.
362+
EmitBlock(createBasicBlock());
363+
FinishFunction();
364+
}
365+
297366
void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
298367
const CGFunctionInfo &FnInfo,
299368
GlobalDecl GD, const ThunkInfo &Thunk) {

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,10 @@ class CodeGenFunction : public CodeGenTypeCache {
12101210

12111211
void EmitCallAndReturnForThunk(llvm::Value *Callee, const ThunkInfo *Thunk);
12121212

1213+
/// Emit a musttail call for a thunk with a potentially adjusted this pointer.
1214+
void EmitMustTailThunk(const CXXMethodDecl *MD, llvm::Value *AdjustedThisPtr,
1215+
llvm::Value *Callee);
1216+
12131217
/// GenerateThunk - Generate a thunk for the given method.
12141218
void GenerateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo,
12151219
GlobalDecl GD, const ThunkInfo &Thunk);

clang/lib/CodeGen/MicrosoftCXXABI.cpp

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,31 +1467,7 @@ llvm::Function *MicrosoftCXXABI::EmitVirtualMemPtrThunk(
14671467
CGF.Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
14681468
llvm::Value *Callee = CGF.Builder.CreateLoad(VFuncPtr);
14691469

1470-
unsigned CallingConv;
1471-
CodeGen::AttributeListType AttributeList;
1472-
CGM.ConstructAttributeList(FnInfo, MD, AttributeList, CallingConv, true);
1473-
llvm::AttributeSet Attrs =
1474-
llvm::AttributeSet::get(CGF.getLLVMContext(), AttributeList);
1475-
1476-
// Do a musttail call with perfect argument forwarding. Any inalloca argument
1477-
// will be forwarded in place without any copy.
1478-
SmallVector<llvm::Value *, 8> Args;
1479-
for (llvm::Argument &A : ThunkFn->args())
1480-
Args.push_back(&A);
1481-
llvm::CallInst *Call = CGF.Builder.CreateCall(Callee, Args);
1482-
Call->setTailCallKind(llvm::CallInst::TCK_MustTail);
1483-
Call->setAttributes(Attrs);
1484-
Call->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
1485-
1486-
if (Call->getType()->isVoidTy())
1487-
CGF.Builder.CreateRetVoid();
1488-
else
1489-
CGF.Builder.CreateRet(Call);
1490-
1491-
// Finish the function to maintain CodeGenFunction invariants.
1492-
// FIXME: Don't emit unreachable code.
1493-
CGF.EmitBlock(CGF.createBasicBlock());
1494-
CGF.FinishFunction();
1470+
CGF.EmitCallAndReturnForThunk(Callee, 0);
14951471

14961472
return ThunkFn;
14971473
}
Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %clang_cc1 %s -fno-rtti -triple=i686-pc-win32 -emit-llvm -o /dev/null 2>&1 | FileCheck --check-prefix=CHECK32 %s
1+
// RUN: %clang_cc1 %s -fno-rtti -triple=i686-pc-win32 -emit-llvm -o - | FileCheck --check-prefix=CHECK32 %s
22
// RUN: %clang_cc1 %s -fno-rtti -triple=x86_64-pc-win32 -emit-llvm -o - | FileCheck --check-prefix=CHECK64 %s
33

44
namespace byval_thunk {
@@ -11,15 +11,103 @@ struct Agg {
1111

1212
struct A { virtual void foo(Agg x); };
1313
struct B { virtual void foo(Agg x); };
14-
struct C : A, B { virtual void foo(Agg x); };
15-
C c;
14+
struct C : A, B { C(); virtual void foo(Agg x); };
15+
C::C() {} // force emission
1616

17-
// CHECK32: cannot compile this non-trivial argument copy for thunk yet
17+
// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01?foo@C@byval_thunk@@W3AEXUAgg@2@@Z"
18+
// CHECK32: (%"struct.byval_thunk::C"* %this, <{ %"struct.byval_thunk::Agg" }>* inalloca)
19+
// CHECK32: %2 = getelementptr i8* %{{.*}}, i32 -4
20+
// CHECK32: musttail call x86_thiscallcc void @"\01?foo@C@byval_thunk@@UAEXUAgg@2@@Z"
21+
// CHECK32: (%"struct.byval_thunk::C"* %{{.*}}, <{ %"struct.byval_thunk::Agg" }>* inalloca %0)
22+
// CHECK32-NEXT: ret void
1823

1924
// CHECK64-LABEL: define linkonce_odr void @"\01?foo@C@byval_thunk@@W7EAAXUAgg@2@@Z"
2025
// CHECK64: (%"struct.byval_thunk::C"* %this, %"struct.byval_thunk::Agg"* %x)
2126
// CHECK64: getelementptr i8* %{{.*}}, i32 -8
22-
// CHECK64: call void @"\01?foo@C@byval_thunk@@UEAAXUAgg@2@@Z"(%"struct.byval_thunk::C"* %{{.*}}, %"struct.byval_thunk::Agg"* %x)
27+
// CHECK64: call void @"\01?foo@C@byval_thunk@@UEAAXUAgg@2@@Z"
28+
// CHECK64: (%"struct.byval_thunk::C"* %{{.*}}, %"struct.byval_thunk::Agg"* %x)
2329
// CHECK64-NOT: call
2430
// CHECK64: ret void
2531
}
32+
33+
namespace stdcall_thunk {
34+
struct Agg {
35+
Agg();
36+
Agg(const Agg &);
37+
~Agg();
38+
int x;
39+
};
40+
41+
struct A { virtual void __stdcall foo(Agg x); };
42+
struct B { virtual void __stdcall foo(Agg x); };
43+
struct C : A, B { C(); virtual void __stdcall foo(Agg x); };
44+
C::C() {} // force emission
45+
46+
// CHECK32-LABEL: define linkonce_odr x86_stdcallcc void @"\01?foo@C@stdcall_thunk@@W3AGXUAgg@2@@Z"
47+
// CHECK32: (<{ %"struct.stdcall_thunk::C"*, %"struct.stdcall_thunk::Agg" }>* inalloca)
48+
// CHECK32: %[[this_slot:[^ ]*]] = getelementptr inbounds <{ %"struct.stdcall_thunk::C"*, %"struct.stdcall_thunk::Agg" }>* %0, i32 0, i32 0
49+
// CHECK32: load %"struct.stdcall_thunk::C"** %[[this_slot]]
50+
// CHECK32: getelementptr i8* %{{.*}}, i32 -4
51+
// CHECK32: store %"struct.stdcall_thunk::C"* %{{.*}}, %"struct.stdcall_thunk::C"** %[[this_slot]]
52+
// CHECK32: musttail call x86_stdcallcc void @"\01?foo@C@stdcall_thunk@@UAGXUAgg@2@@Z"
53+
// CHECK32: (<{ %"struct.stdcall_thunk::C"*, %"struct.stdcall_thunk::Agg" }>* inalloca %0)
54+
// CHECK32-NEXT: ret void
55+
56+
// CHECK64-LABEL: define linkonce_odr void @"\01?foo@C@stdcall_thunk@@W7EAAXUAgg@2@@Z"
57+
// CHECK64: (%"struct.stdcall_thunk::C"* %this, %"struct.stdcall_thunk::Agg"* %x)
58+
// CHECK64: getelementptr i8* %{{.*}}, i32 -8
59+
// CHECK64: call void @"\01?foo@C@stdcall_thunk@@UEAAXUAgg@2@@Z"
60+
// CHECK64: (%"struct.stdcall_thunk::C"* %{{.*}}, %"struct.stdcall_thunk::Agg"* %x)
61+
// CHECK64-NOT: call
62+
// CHECK64: ret void
63+
}
64+
65+
namespace sret_thunk {
66+
struct Agg {
67+
Agg();
68+
Agg(const Agg &);
69+
~Agg();
70+
int x;
71+
};
72+
73+
struct A { virtual Agg __cdecl foo(Agg x); };
74+
struct B { virtual Agg __cdecl foo(Agg x); };
75+
struct C : A, B { C(); virtual Agg __cdecl foo(Agg x); };
76+
C::C() {} // force emission
77+
78+
// CHECK32-LABEL: define linkonce_odr %"struct.sret_thunk::Agg"* @"\01?foo@C@sret_thunk@@W3AA?AUAgg@2@U32@@Z"
79+
// CHECK32: (<{ %"struct.sret_thunk::C"*, %"struct.sret_thunk::Agg"*, %"struct.sret_thunk::Agg" }>* inalloca)
80+
// CHECK32: %[[this_slot:[^ ]*]] = getelementptr inbounds <{ %"struct.sret_thunk::C"*, %"struct.sret_thunk::Agg"*, %"struct.sret_thunk::Agg" }>* %0, i32 0, i32 0
81+
// CHECK32: load %"struct.sret_thunk::C"** %[[this_slot]]
82+
// CHECK32: getelementptr i8* %{{.*}}, i32 -4
83+
// CHECK32: store %"struct.sret_thunk::C"* %{{.*}}, %"struct.sret_thunk::C"** %[[this_slot]]
84+
// CHECK32: %[[rv:[^ ]*]] = musttail call %"struct.sret_thunk::Agg"* @"\01?foo@C@sret_thunk@@UAA?AUAgg@2@U32@@Z"
85+
// CHECK32: (<{ %"struct.sret_thunk::C"*, %"struct.sret_thunk::Agg"*, %"struct.sret_thunk::Agg" }>* inalloca %0)
86+
// CHECK32-NEXT: ret %"struct.sret_thunk::Agg"* %[[rv]]
87+
88+
// CHECK64-LABEL: define linkonce_odr void @"\01?foo@C@sret_thunk@@W7EAA?AUAgg@2@U32@@Z"
89+
// CHECK64: (%"struct.sret_thunk::C"* %this, %"struct.sret_thunk::Agg"* noalias sret %agg.result, %"struct.sret_thunk::Agg"* %x)
90+
// CHECK64: getelementptr i8* %{{.*}}, i32 -8
91+
// CHECK64: call void @"\01?foo@C@sret_thunk@@UEAA?AUAgg@2@U32@@Z"
92+
// CHECK64: (%"struct.sret_thunk::C"* %{{.*}}, %"struct.sret_thunk::Agg"* sret %agg.result, %"struct.sret_thunk::Agg"* %x)
93+
// CHECK64-NOT: call
94+
// CHECK64: ret void
95+
}
96+
97+
#if 0
98+
// FIXME: When we extend LLVM IR to allow forwarding of varargs through musttail
99+
// calls, use this test.
100+
namespace variadic_thunk {
101+
struct Agg {
102+
Agg();
103+
Agg(const Agg &);
104+
~Agg();
105+
int x;
106+
};
107+
108+
struct A { virtual void foo(Agg x, ...); };
109+
struct B { virtual void foo(Agg x, ...); };
110+
struct C : A, B { C(); virtual void foo(Agg x, ...); };
111+
C::C() {} // force emission
112+
}
113+
#endif

clang/test/CodeGenCXX/microsoft-abi-nontrivial-covariant-thunk.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct B {
1818
struct C : A, B {
1919
C();
2020
int c;
21-
virtual C *clone(A); // expected-error {{cannot compile this non-trivial argument copy for thunk yet}}
21+
virtual C *clone(A); // expected-error {{cannot compile this non-trivial argument copy for return-adjusting thunk yet}}
2222
};
2323
B::B() {} // force emission
2424
C::C() {} // force emission

0 commit comments

Comments
 (0)