[CodeGen] Fix ExtParameterInfo bugs in C++ CodeGen code.

This patch makes use of the prefix/suffix ABI argument distinction that
was introduced in r295870, so that we now emit ExtParameterInfo at the
correct offset for member calls that have added ABI arguments. I don't
see a good way to test the generated param info, since we don't actually
seem to use it in CGFunctionInfo outside of Swift. Any
suggestions/thoughts for how to better test this are welcome. :)

This patch also fixes a small bug with inheriting constructors: if we
decide not to pass args into an base class ctor, we would still
generate ExtParameterInfo as though we did. The added test-case is for
that behavior.

llvm-svn: 296024
This commit is contained in:
George Burgess IV 2017-02-23 22:07:35 +00:00
parent 66b4e21534
commit d0a9e807f3
7 changed files with 88 additions and 26 deletions

View File

@ -362,18 +362,31 @@ getExtParameterInfosForCall(const FunctionProtoType *proto,
} }
/// Arrange a call to a C++ method, passing the given arguments. /// Arrange a call to a C++ method, passing the given arguments.
///
/// ExtraPrefixArgs is the number of ABI-specific args passed after the `this`
/// parameter.
/// ExtraSuffixArgs is the number of ABI-specific args passed at the end of
/// args.
/// PassProtoArgs indicates whether `args` has args for the parameters in the
/// given CXXConstructorDecl.
const CGFunctionInfo & const CGFunctionInfo &
CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args, CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
const CXXConstructorDecl *D, const CXXConstructorDecl *D,
CXXCtorType CtorKind, CXXCtorType CtorKind,
unsigned ExtraArgs) { unsigned ExtraPrefixArgs,
unsigned ExtraSuffixArgs,
bool PassProtoArgs) {
// FIXME: Kill copy. // FIXME: Kill copy.
SmallVector<CanQualType, 16> ArgTypes; SmallVector<CanQualType, 16> ArgTypes;
for (const auto &Arg : args) for (const auto &Arg : args)
ArgTypes.push_back(Context.getCanonicalParamType(Arg.Ty)); ArgTypes.push_back(Context.getCanonicalParamType(Arg.Ty));
// +1 for implicit this, which should always be args[0].
unsigned TotalPrefixArgs = 1 + ExtraPrefixArgs;
CanQual<FunctionProtoType> FPT = GetFormalType(D); CanQual<FunctionProtoType> FPT = GetFormalType(D);
RequiredArgs Required = RequiredArgs::forPrototypePlus(FPT, 1 + ExtraArgs, D); RequiredArgs Required =
RequiredArgs::forPrototypePlus(FPT, TotalPrefixArgs + ExtraSuffixArgs, D);
GlobalDecl GD(D, CtorKind); GlobalDecl GD(D, CtorKind);
CanQualType ResultType = TheCXXABI.HasThisReturn(GD) CanQualType ResultType = TheCXXABI.HasThisReturn(GD)
? ArgTypes.front() ? ArgTypes.front()
@ -382,8 +395,14 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
: Context.VoidTy; : Context.VoidTy;
FunctionType::ExtInfo Info = FPT->getExtInfo(); FunctionType::ExtInfo Info = FPT->getExtInfo();
auto ParamInfos = getExtParameterInfosForCall(FPT.getTypePtr(), 1 + ExtraArgs, llvm::SmallVector<FunctionProtoType::ExtParameterInfo, 16> ParamInfos;
ArgTypes.size()); // If the prototype args are elided, we should only have ABI-specific args,
// which never have param info.
if (PassProtoArgs && FPT->hasExtParameterInfos()) {
// ABI-specific suffix arguments are treated the same as variadic arguments.
addExtParameterInfosForCall(ParamInfos, FPT.getTypePtr(), TotalPrefixArgs,
ArgTypes.size());
}
return arrangeLLVMFunctionInfo(ResultType, /*instanceMethod=*/true, return arrangeLLVMFunctionInfo(ResultType, /*instanceMethod=*/true,
/*chainCall=*/false, ArgTypes, Info, /*chainCall=*/false, ArgTypes, Info,
ParamInfos, Required); ParamInfos, Required);
@ -627,15 +646,20 @@ CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType,
} }
/// Arrange a call to a C++ method, passing the given arguments. /// Arrange a call to a C++ method, passing the given arguments.
///
/// numPrefixArgs is the number of ABI-specific prefix arguments we have. It
/// does not count `this`.
const CGFunctionInfo & const CGFunctionInfo &
CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args,
const FunctionProtoType *proto, const FunctionProtoType *proto,
RequiredArgs required) { RequiredArgs required,
unsigned numRequiredArgs = unsigned numPrefixArgs) {
(proto->isVariadic() ? required.getNumRequiredArgs() : args.size()); assert(numPrefixArgs + 1 <= args.size() &&
unsigned numPrefixArgs = numRequiredArgs - proto->getNumParams(); "Emitting a call with less args than the required prefix?");
// Add one to account for `this`. It's a bit awkward here, but we don't count
// `this` in similar places elsewhere.
auto paramInfos = auto paramInfos =
getExtParameterInfosForCall(proto, numPrefixArgs, args.size()); getExtParameterInfosForCall(proto, numPrefixArgs + 1, args.size());
// FIXME: Kill copy. // FIXME: Kill copy.
auto argTypes = getArgTypesForCall(Context, args); auto argTypes = getArgTypesForCall(Context, args);

View File

@ -1979,7 +1979,7 @@ static bool canEmitDelegateCallArgs(CodeGenFunction &CGF,
// Likewise if they're inalloca. // Likewise if they're inalloca.
const CGFunctionInfo &Info = const CGFunctionInfo &Info =
CGF.CGM.getTypes().arrangeCXXConstructorCall(Args, Ctor, Type, 0); CGF.CGM.getTypes().arrangeCXXConstructorCall(Args, Ctor, Type, 0, 0);
if (Info.usesInAlloca()) if (Info.usesInAlloca())
return false; return false;
} }
@ -2021,10 +2021,11 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
return; return;
} }
bool PassPrototypeArgs = true;
// Check whether we can actually emit the constructor before trying to do so. // Check whether we can actually emit the constructor before trying to do so.
if (auto Inherited = D->getInheritedConstructor()) { if (auto Inherited = D->getInheritedConstructor()) {
if (getTypes().inheritingCtorHasParams(Inherited, Type) && PassPrototypeArgs = getTypes().inheritingCtorHasParams(Inherited, Type);
!canEmitDelegateCallArgs(*this, D, Type, Args)) { if (PassPrototypeArgs && !canEmitDelegateCallArgs(*this, D, Type, Args)) {
EmitInlinedInheritingCXXConstructorCall(D, Type, ForVirtualBase, EmitInlinedInheritingCXXConstructorCall(D, Type, ForVirtualBase,
Delegating, Args); Delegating, Args);
return; return;
@ -2040,7 +2041,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
llvm::Constant *CalleePtr = llvm::Constant *CalleePtr =
CGM.getAddrOfCXXStructor(D, getFromCtorType(Type)); CGM.getAddrOfCXXStructor(D, getFromCtorType(Type));
const CGFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall( const CGFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall(
Args, D, Type, ExtraArgs.Prefix + ExtraArgs.Suffix); Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix, PassPrototypeArgs);
CGCallee Callee = CGCallee::forDirect(CalleePtr, D); CGCallee Callee = CGCallee::forDirect(CalleePtr, D);
EmitCall(Info, Callee, ReturnValueSlot(), Args); EmitCall(Info, Callee, ReturnValueSlot(), Args);

View File

@ -24,7 +24,15 @@
using namespace clang; using namespace clang;
using namespace CodeGen; using namespace CodeGen;
static RequiredArgs namespace {
struct MemberCallInfo {
RequiredArgs ReqArgs;
// Number of prefix arguments for the call. Ignores the `this` pointer.
unsigned PrefixSize;
};
}
static MemberCallInfo
commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD, commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
llvm::Value *This, llvm::Value *ImplicitParam, llvm::Value *This, llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *CE, QualType ImplicitParamTy, const CallExpr *CE,
@ -48,6 +56,7 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, Args.size(), MD); RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, Args.size(), MD);
unsigned PrefixSize = Args.size() - 1;
// And the rest of the call args. // And the rest of the call args.
if (RtlArgs) { if (RtlArgs) {
@ -65,7 +74,7 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
FPT->getNumParams() == 0 && FPT->getNumParams() == 0 &&
"No CallExpr specified for function with non-zero number of arguments"); "No CallExpr specified for function with non-zero number of arguments");
} }
return required; return {required, PrefixSize};
} }
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
@ -75,9 +84,10 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
const CallExpr *CE, CallArgList *RtlArgs) { const CallExpr *CE, CallArgList *RtlArgs) {
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
CallArgList Args; CallArgList Args;
RequiredArgs required = commonEmitCXXMemberOrOperatorCall( MemberCallInfo CallInfo = commonEmitCXXMemberOrOperatorCall(
*this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs); *this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs);
auto &FnInfo = CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required); auto &FnInfo = CGM.getTypes().arrangeCXXMethodCall(
Args, FPT, CallInfo.ReqArgs, CallInfo.PrefixSize);
return EmitCall(FnInfo, Callee, ReturnValue, Args); return EmitCall(FnInfo, Callee, ReturnValue, Args);
} }
@ -425,7 +435,8 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E,
// And the rest of the call args // And the rest of the call args
EmitCallArgs(Args, FPT, E->arguments()); EmitCallArgs(Args, FPT, E->arguments());
return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required,
/*PrefixSize=*/0),
Callee, ReturnValue, Args); Callee, ReturnValue, Args);
} }

View File

@ -284,6 +284,7 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Constant *CalleePtr,
if (isa<CXXDestructorDecl>(MD)) if (isa<CXXDestructorDecl>(MD))
CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, CurGD, CallArgs); CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, CurGD, CallArgs);
unsigned PrefixArgs = CallArgs.size() - 1;
// Add the rest of the arguments. // Add the rest of the arguments.
for (const ParmVarDecl *PD : MD->parameters()) for (const ParmVarDecl *PD : MD->parameters())
EmitDelegateCallArg(CallArgs, PD, SourceLocation()); EmitDelegateCallArg(CallArgs, PD, SourceLocation());
@ -292,7 +293,7 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Constant *CalleePtr,
#ifndef NDEBUG #ifndef NDEBUG
const CGFunctionInfo &CallFnInfo = CGM.getTypes().arrangeCXXMethodCall( const CGFunctionInfo &CallFnInfo = CGM.getTypes().arrangeCXXMethodCall(
CallArgs, FPT, RequiredArgs::forPrototypePlus(FPT, 1, MD)); CallArgs, FPT, RequiredArgs::forPrototypePlus(FPT, 1, MD), PrefixArgs);
assert(CallFnInfo.getRegParm() == CurFnInfo->getRegParm() && assert(CallFnInfo.getRegParm() == CurFnInfo->getRegParm() &&
CallFnInfo.isNoReturn() == CurFnInfo->isNoReturn() && CallFnInfo.isNoReturn() == CurFnInfo->isNoReturn() &&
CallFnInfo.getCallingConvention() == CurFnInfo->getCallingConvention()); CallFnInfo.getCallingConvention() == CurFnInfo->getCallingConvention());

View File

@ -303,11 +303,14 @@ public:
const CGFunctionInfo &arrangeCXXConstructorCall(const CallArgList &Args, const CGFunctionInfo &arrangeCXXConstructorCall(const CallArgList &Args,
const CXXConstructorDecl *D, const CXXConstructorDecl *D,
CXXCtorType CtorKind, CXXCtorType CtorKind,
unsigned ExtraArgs); unsigned ExtraPrefixArgs,
unsigned ExtraSuffixArgs,
bool PassProtoArgs = true);
const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args, const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args,
const FunctionProtoType *type, const FunctionProtoType *type,
RequiredArgs required); RequiredArgs required,
unsigned numPrefixArgs);
const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD);
const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD, const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD,
CXXCtorType CT); CXXCtorType CT);

View File

@ -3934,7 +3934,7 @@ MicrosoftCXXABI::getAddrOfCXXCtorClosure(const CXXConstructorDecl *CD,
CGM.getAddrOfCXXStructor(CD, StructorType::Complete); CGM.getAddrOfCXXStructor(CD, StructorType::Complete);
CGCallee Callee = CGCallee::forDirect(CalleePtr, CD); CGCallee Callee = CGCallee::forDirect(CalleePtr, CD);
const CGFunctionInfo &CalleeInfo = CGM.getTypes().arrangeCXXConstructorCall( const CGFunctionInfo &CalleeInfo = CGM.getTypes().arrangeCXXConstructorCall(
Args, CD, Ctor_Complete, ExtraArgs.Prefix + ExtraArgs.Suffix); Args, CD, Ctor_Complete, ExtraArgs.Prefix, ExtraArgs.Suffix);
CGF.EmitCall(CalleeInfo, Callee, ReturnValueSlot(), Args); CGF.EmitCall(CalleeInfo, Callee, ReturnValueSlot(), Args);
Cleanups.ForceCleanup(); Cleanups.ForceCleanup();

View File

@ -1,8 +1,8 @@
// RUN: %clang_cc1 -triple x86_64-apple -emit-llvm -fobjc-arc -o - %s // RUN: %clang_cc1 -triple x86_64-apple -emit-llvm -fobjc-arc -o - %s -std=c++11 | FileCheck %s --check-prefix=ITANIUM
// RUN: %clang_cc1 -triple x86_64-windows -emit-llvm -fobjc-arc -o - %s // RUN: %clang_cc1 -triple x86_64-windows -emit-llvm -fobjc-arc -o - %s -std=c++11
// //
// Test caess where we weren't properly adding parameter infos declarations, // Test cases where we weren't properly adding extended parameter info, which
// which caused assertions to fire. Hence, no CHECKs. // caused assertions to fire. Hence, minimal CHECKs.
struct VirtualBase { struct VirtualBase {
VirtualBase(__attribute__((ns_consumed)) id x); VirtualBase(__attribute__((ns_consumed)) id x);
@ -13,3 +13,25 @@ struct WithVirtualBase : virtual VirtualBase {
WithVirtualBase::WithVirtualBase(__attribute__((ns_consumed)) id x) WithVirtualBase::WithVirtualBase(__attribute__((ns_consumed)) id x)
: VirtualBase(x) {} : VirtualBase(x) {}
struct VirtualBase2 {
VirtualBase2(__attribute__((ns_consumed)) id x, void *y);
};
// In this case, we don't actually end up passing the `id` param from
// WithVirtualBaseLast's ctor to WithVirtualBaseMid's. So, we shouldn't emit
// ext param info for `id` to `Mid`. Itanium-only check since MSABI seems to
// emit the construction code inline.
struct WithVirtualBaseMid : virtual VirtualBase2 {
// Ensure we only pass in `this` and a vtable. Otherwise this test is useless.
// ITANIUM: define {{.*}} void @_ZN18WithVirtualBaseMidCI212VirtualBase2EP11objc_objectPv({{.*}}, {{.*}})
using VirtualBase2::VirtualBase2;
};
struct WithVirtualBaseLast : WithVirtualBaseMid {
using WithVirtualBaseMid::WithVirtualBaseMid;
};
void callLast(__attribute__((ns_consumed)) id x) {
WithVirtualBaseLast{x, (void*)0};
}