Skip to content

Commit a7092eb

Browse files
authored
Merge pull request #2212 from compnerd/20200714-inalloca-cherry-picks
Correct `inalloca` handling for Windows x86
2 parents 8b78af1 + c6aa45f commit a7092eb

File tree

4 files changed

+140
-81
lines changed

4 files changed

+140
-81
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionProtoType> FTP) {
198198
FTP);
199199
}
200200

201-
static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) {
201+
static CallingConv getCallingConventionForDecl(const ObjCMethodDecl *D,
202+
bool IsWindows) {
202203
// Set the appropriate calling convention for the Function.
203204
if (D->hasAttr<StdCallAttr>())
204205
return CC_X86StdCall;
@@ -3761,13 +3762,107 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
37613762
EmitCheck(std::make_pair(Cond, CheckKind), Handler, StaticData, None);
37623763
}
37633764

3765+
// Check if the call is going to use the inalloca convention. This needs to
3766+
// agree with CGFunctionInfo::usesInAlloca. The CGFunctionInfo is arranged
3767+
// later, so we can't check it directly.
3768+
static bool hasInAllocaArgs(CodeGenModule &CGM, CallingConv ExplicitCC,
3769+
ArrayRef<QualType> ArgTypes) {
3770+
// The Swift calling convention doesn't go through the target-specific
3771+
// argument classification, so it never uses inalloca.
3772+
// TODO: Consider limiting inalloca use to only calling conventions supported
3773+
// by MSVC.
3774+
if (ExplicitCC == CC_Swift)
3775+
return false;
3776+
if (!CGM.getTarget().getCXXABI().isMicrosoft())
3777+
return false;
3778+
return llvm::any_of(ArgTypes, [&](QualType Ty) {
3779+
return isInAllocaArgument(CGM.getCXXABI(), Ty);
3780+
});
3781+
}
3782+
3783+
#ifndef NDEBUG
3784+
// Determine whether the given argument is an Objective-C method
3785+
// that may have type parameters in its signature.
3786+
static bool isObjCMethodWithTypeParams(const ObjCMethodDecl *method) {
3787+
const DeclContext *dc = method->getDeclContext();
3788+
if (const ObjCInterfaceDecl *classDecl = dyn_cast<ObjCInterfaceDecl>(dc)) {
3789+
return classDecl->getTypeParamListAsWritten();
3790+
}
3791+
3792+
if (const ObjCCategoryDecl *catDecl = dyn_cast<ObjCCategoryDecl>(dc)) {
3793+
return catDecl->getTypeParamList();
3794+
}
3795+
3796+
return false;
3797+
}
3798+
#endif
3799+
3800+
/// EmitCallArgs - Emit call arguments for a function.
37643801
void CodeGenFunction::EmitCallArgs(
3765-
CallArgList &Args, ArrayRef<QualType> ArgTypes,
3802+
CallArgList &Args, PrototypeWrapper Prototype,
37663803
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
37673804
AbstractCallee AC, unsigned ParamsToSkip, EvaluationOrder Order) {
3805+
SmallVector<QualType, 16> ArgTypes;
3806+
3807+
assert((ParamsToSkip == 0 || Prototype.P) &&
3808+
"Can't skip parameters if type info is not provided");
3809+
3810+
// This variable only captures *explicitly* written conventions, not those
3811+
// applied by default via command line flags or target defaults, such as
3812+
// thiscall, aapcs, stdcall via -mrtd, etc. Computing that correctly would
3813+
// require knowing if this is a C++ instance method or being able to see
3814+
// unprototyped FunctionTypes.
3815+
CallingConv ExplicitCC = CC_C;
3816+
3817+
// First, if a prototype was provided, use those argument types.
3818+
bool IsVariadic = false;
3819+
if (Prototype.P) {
3820+
const auto *MD = Prototype.P.dyn_cast<const ObjCMethodDecl *>();
3821+
if (MD) {
3822+
IsVariadic = MD->isVariadic();
3823+
ExplicitCC = getCallingConventionForDecl(
3824+
MD, CGM.getTarget().getTriple().isOSWindows());
3825+
ArgTypes.assign(MD->param_type_begin() + ParamsToSkip,
3826+
MD->param_type_end());
3827+
} else {
3828+
const auto *FPT = Prototype.P.get<const FunctionProtoType *>();
3829+
IsVariadic = FPT->isVariadic();
3830+
ExplicitCC = FPT->getExtInfo().getCC();
3831+
ArgTypes.assign(FPT->param_type_begin() + ParamsToSkip,
3832+
FPT->param_type_end());
3833+
}
3834+
3835+
#ifndef NDEBUG
3836+
// Check that the prototyped types match the argument expression types.
3837+
bool isGenericMethod = MD && isObjCMethodWithTypeParams(MD);
3838+
CallExpr::const_arg_iterator Arg = ArgRange.begin();
3839+
for (QualType Ty : ArgTypes) {
3840+
assert(Arg != ArgRange.end() && "Running over edge of argument list!");
3841+
assert(
3842+
(isGenericMethod || Ty->isVariablyModifiedType() ||
3843+
Ty.getNonReferenceType()->isObjCRetainableType() ||
3844+
getContext()
3845+
.getCanonicalType(Ty.getNonReferenceType())
3846+
.getTypePtr() ==
3847+
getContext().getCanonicalType((*Arg)->getType()).getTypePtr()) &&
3848+
"type mismatch in call argument!");
3849+
++Arg;
3850+
}
3851+
3852+
// Either we've emitted all the call args, or we have a call to variadic
3853+
// function.
3854+
assert((Arg == ArgRange.end() || IsVariadic) &&
3855+
"Extra arguments in non-variadic function!");
3856+
#endif
3857+
}
3858+
3859+
// If we still have any arguments, emit them using the type of the argument.
3860+
for (auto *A : llvm::make_range(std::next(ArgRange.begin(), ArgTypes.size()),
3861+
ArgRange.end()))
3862+
ArgTypes.push_back(IsVariadic ? getVarArgType(A) : A->getType());
37683863
assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin()));
37693864

3770-
// We *have* to evaluate arguments from right to left in the MS C++ ABI,
3865+
// We must evaluate arguments from right to left in the MS C++ ABI,
37713866
// because arguments are destroyed left to right in the callee. As a special
37723867
// case, there are certain language constructs that require left-to-right
37733868
// evaluation, and in those cases we consider the evaluation order requirement
@@ -3800,15 +3895,10 @@ void CodeGenFunction::EmitCallArgs(
38003895
};
38013896

38023897
// Insert a stack save if we're going to need any inalloca args.
3803-
bool HasInAllocaArgs = false;
3804-
if (CGM.getTarget().getCXXABI().isMicrosoft()) {
3805-
for (ArrayRef<QualType>::iterator I = ArgTypes.begin(), E = ArgTypes.end();
3806-
I != E && !HasInAllocaArgs; ++I)
3807-
HasInAllocaArgs = isInAllocaArgument(CGM.getCXXABI(), *I);
3808-
if (HasInAllocaArgs) {
3809-
assert(getTarget().getTriple().getArch() == llvm::Triple::x86);
3810-
Args.allocateArgumentMemory(*this);
3811-
}
3898+
if (hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
3899+
assert(getTarget().getTriple().getArch() == llvm::Triple::x86 &&
3900+
"inalloca only supported on x86");
3901+
Args.allocateArgumentMemory(*this);
38123902
}
38133903

38143904
// Evaluate each argument in the appropriate order.

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
13291329
const CallExpr *TheCall,
13301330
bool IsDelete) {
13311331
CallArgList Args;
1332-
EmitCallArgs(Args, Type->getParamTypes(), TheCall->arguments());
1332+
EmitCallArgs(Args, Type, TheCall->arguments());
13331333
// Find the allocation or deallocation function that we're calling.
13341334
ASTContext &Ctx = getContext();
13351335
DeclarationName Name = Ctx.DeclarationNames

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 8 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4566,26 +4566,6 @@ class CodeGenFunction : public CodeGenTypeCache {
45664566
Address Loc);
45674567

45684568
public:
4569-
#ifndef NDEBUG
4570-
// Determine whether the given argument is an Objective-C method
4571-
// that may have type parameters in its signature.
4572-
static bool isObjCMethodWithTypeParams(const ObjCMethodDecl *method) {
4573-
const DeclContext *dc = method->getDeclContext();
4574-
if (const ObjCInterfaceDecl *classDecl= dyn_cast<ObjCInterfaceDecl>(dc)) {
4575-
return classDecl->getTypeParamListAsWritten();
4576-
}
4577-
4578-
if (const ObjCCategoryDecl *catDecl = dyn_cast<ObjCCategoryDecl>(dc)) {
4579-
return catDecl->getTypeParamList();
4580-
}
4581-
4582-
return false;
4583-
}
4584-
4585-
template<typename T>
4586-
static bool isObjCMethodWithTypeParams(const T *) { return false; }
4587-
#endif
4588-
45894569
enum class EvaluationOrder {
45904570
///! No language constraints on evaluation order.
45914571
Default,
@@ -4595,56 +4575,16 @@ class CodeGenFunction : public CodeGenTypeCache {
45954575
ForceRightToLeft
45964576
};
45974577

4598-
/// EmitCallArgs - Emit call arguments for a function.
4599-
template <typename T>
4600-
void EmitCallArgs(CallArgList &Args, const T *CallArgTypeInfo,
4601-
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
4602-
AbstractCallee AC = AbstractCallee(),
4603-
unsigned ParamsToSkip = 0,
4604-
EvaluationOrder Order = EvaluationOrder::Default) {
4605-
SmallVector<QualType, 16> ArgTypes;
4606-
CallExpr::const_arg_iterator Arg = ArgRange.begin();
4607-
4608-
assert((ParamsToSkip == 0 || CallArgTypeInfo) &&
4609-
"Can't skip parameters if type info is not provided");
4610-
if (CallArgTypeInfo) {
4611-
#ifndef NDEBUG
4612-
bool isGenericMethod = isObjCMethodWithTypeParams(CallArgTypeInfo);
4613-
#endif
4614-
4615-
// First, use the argument types that the type info knows about
4616-
for (auto I = CallArgTypeInfo->param_type_begin() + ParamsToSkip,
4617-
E = CallArgTypeInfo->param_type_end();
4618-
I != E; ++I, ++Arg) {
4619-
assert(Arg != ArgRange.end() && "Running over edge of argument list!");
4620-
assert((isGenericMethod ||
4621-
((*I)->isVariablyModifiedType() ||
4622-
(*I).getNonReferenceType()->isObjCRetainableType() ||
4623-
getContext()
4624-
.getCanonicalType((*I).getNonReferenceType())
4625-
.getTypePtr() ==
4626-
getContext()
4627-
.getCanonicalType((*Arg)->getType())
4628-
.getTypePtr())) &&
4629-
"type mismatch in call argument!");
4630-
ArgTypes.push_back(*I);
4631-
}
4632-
}
4633-
4634-
// Either we've emitted all the call args, or we have a call to variadic
4635-
// function.
4636-
assert((Arg == ArgRange.end() || !CallArgTypeInfo ||
4637-
CallArgTypeInfo->isVariadic()) &&
4638-
"Extra arguments in non-variadic function!");
4639-
4640-
// If we still have any arguments, emit them using the type of the argument.
4641-
for (auto *A : llvm::make_range(Arg, ArgRange.end()))
4642-
ArgTypes.push_back(CallArgTypeInfo ? getVarArgType(A) : A->getType());
4578+
// Wrapper for function prototype sources. Wraps either a FunctionProtoType or
4579+
// an ObjCMethodDecl.
4580+
struct PrototypeWrapper {
4581+
llvm::PointerUnion<const FunctionProtoType *, const ObjCMethodDecl *> P;
46434582

4644-
EmitCallArgs(Args, ArgTypes, ArgRange, AC, ParamsToSkip, Order);
4645-
}
4583+
PrototypeWrapper(const FunctionProtoType *FT) : P(FT) {}
4584+
PrototypeWrapper(const ObjCMethodDecl *MD) : P(MD) {}
4585+
};
46464586

4647-
void EmitCallArgs(CallArgList &Args, ArrayRef<QualType> ArgTypes,
4587+
void EmitCallArgs(CallArgList &Args, PrototypeWrapper Prototype,
46484588
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
46494589
AbstractCallee AC = AbstractCallee(),
46504590
unsigned ParamsToSkip = 0,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-windows -emit-llvm -target-cpu core2 -o - %s | FileCheck %s
2+
3+
#define SWIFTCALL __attribute__((swiftcall))
4+
#define OUT __attribute__((swift_indirect_result))
5+
#define ERROR __attribute__((swift_error_result))
6+
#define CONTEXT __attribute__((swift_context))
7+
8+
/*****************************************************************************/
9+
/****************************** PARAMETER ABIS *******************************/
10+
/*****************************************************************************/
11+
12+
// Swift doesn't use inalloca like windows x86 normally does.
13+
struct NonTrivial {
14+
NonTrivial();
15+
NonTrivial(const NonTrivial &);
16+
int o;
17+
};
18+
19+
SWIFTCALL int receiveNonTrivial(NonTrivial o) { return o.o; }
20+
21+
// CHECK-LABEL: define dso_local swiftcc i32 @"?receiveNonTrivial@@YSHUNonTrivial@@@Z"(%struct.NonTrivial* %o)
22+
23+
int passNonTrivial() {
24+
return receiveNonTrivial({});
25+
}
26+
27+
// CHECK-LABEL: define dso_local i32 @"?passNonTrivial@@YAHXZ"()
28+
// CHECK-NOT: stacksave
29+
// CHECK: call swiftcc i32 @"?receiveNonTrivial@@YSHUNonTrivial@@@Z"(%struct.NonTrivial* %{{.*}})

0 commit comments

Comments
 (0)