From: Reid Kleckner Date: Tue, 17 Dec 2013 19:46:40 +0000 (+0000) Subject: [ms-cxxabi] The 'most derived' ctor parameter usually comes last X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=395336de591e50191eb5c41d4ca1f3b68517a014;p=clang [ms-cxxabi] The 'most derived' ctor parameter usually comes last Unlike Itanium's VTTs, the 'most derived' boolean or bitfield is the last parameter for non-variadic constructors, rather than the second. For variadic constructors, the 'most derived' parameter comes after the 'this' parameter. This affects constructor calls and constructor decls in a variety of places. Reviewers: timurrrr Differential Revision: http://llvm-reviews.chandlerc.com/D2405 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@197518 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 412b27814a..e9e34b0a1a 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -116,7 +116,7 @@ bool CGCXXABI::isZeroInitializable(const MemberPointerType *MPT) { return true; } -void CGCXXABI::BuildThisParam(CodeGenFunction &CGF, FunctionArgList ¶ms) { +void CGCXXABI::buildThisParam(CodeGenFunction &CGF, FunctionArgList ¶ms) { const CXXMethodDecl *MD = cast(CGF.CurGD.getDecl()); // FIXME: I'm not entirely sure I like using a fake decl just for code diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index cbb120f576..6c89143c5f 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -67,11 +67,8 @@ protected: return CGF.CXXStructorImplicitParamValue; } - /// Build a parameter variable suitable for 'this'. - void BuildThisParam(CodeGenFunction &CGF, FunctionArgList &Params); - /// Perform prolog initialization of the parameter variable suitable - /// for 'this' emitted by BuildThisParam. + /// for 'this' emitted by buildThisParam. void EmitThisParam(CodeGenFunction &CGF); ASTContext &getContext() const { return CGM.getContext(); } @@ -270,16 +267,18 @@ public: return This; } - /// Build the ABI-specific portion of the parameter list for a - /// function. This generally involves a 'this' parameter and - /// possibly some extra data for constructors and destructors. + /// Build a parameter variable suitable for 'this'. + void buildThisParam(CodeGenFunction &CGF, FunctionArgList &Params); + + /// Insert any ABI-specific implicit parameters into the parameter list for a + /// function. This generally involves extra data for constructors and + /// destructors. /// /// ABIs may also choose to override the return type, which has been /// initialized with the type of 'this' if HasThisReturn(CGF.CurGD) is true or /// the formal return type of the function otherwise. - virtual void BuildInstanceFunctionParams(CodeGenFunction &CGF, - QualType &ResTy, - FunctionArgList &Params) = 0; + virtual void addImplicitStructorParams(CodeGenFunction &CGF, QualType &ResTy, + FunctionArgList &Params) = 0; /// Perform ABI-specific "this" parameter adjustment in a virtual function /// prologue. @@ -291,14 +290,14 @@ public: /// Emit the ABI-specific prolog for the function. virtual void EmitInstanceFunctionProlog(CodeGenFunction &CGF) = 0; - /// Emit the constructor call. - virtual void EmitConstructorCall(CodeGenFunction &CGF, - const CXXConstructorDecl *D, - CXXCtorType Type, - bool ForVirtualBase, bool Delegating, - llvm::Value *This, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd) = 0; + /// Add any ABI-specific implicit arguments needed to call a constructor. + /// + /// \return The number of args added to the call, which is typically zero or + /// one. + virtual unsigned + addImplicitConstructorArgs(CodeGenFunction &CGF, const CXXConstructorDecl *D, + CXXCtorType Type, bool ForVirtualBase, + bool Delegating, CallArgList &Args) = 0; /// Emit the destructor call. virtual void EmitDestructorCall(CodeGenFunction &CGF, diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 726e808ed0..50cbdb1b07 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -202,16 +202,17 @@ CodeGenTypes::arrangeCXXConstructorDeclaration(const CXXConstructorDecl *D, CanQualType resultType = TheCXXABI.HasThisReturn(GD) ? argTypes.front() : Context.VoidTy; - TheCXXABI.BuildConstructorSignature(D, ctorKind, resultType, argTypes); - CanQual FTP = GetFormalType(D); - RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, argTypes.size()); - // Add the formal parameters. for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) argTypes.push_back(FTP->getArgType(i)); + TheCXXABI.BuildConstructorSignature(D, ctorKind, resultType, argTypes); + + RequiredArgs required = + (D->isVariadic() ? RequiredArgs(argTypes.size()) : RequiredArgs::All); + FunctionType::ExtInfo extInfo = FTP->getExtInfo(); return arrangeLLVMFunctionInfo(resultType, argTypes, extInfo, required); } diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 7a0391b9b9..20ff2a46de 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1682,9 +1682,32 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, return; } - // Non-trivial constructors are handled in an ABI-specific manner. - CGM.getCXXABI().EmitConstructorCall(*this, D, Type, ForVirtualBase, - Delegating, This, ArgBeg, ArgEnd); + // C++11 [class.mfct.non-static]p2: + // If a non-static member function of a class X is called for an object that + // is not of type X, or of a type derived from X, the behavior is undefined. + // FIXME: Provide a source location here. + EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, SourceLocation(), This, + getContext().getRecordType(D->getParent())); + + CallArgList Args; + + // Push the this ptr. + Args.add(RValue::get(This), D->getThisType(getContext())); + + // Add the rest of the user-supplied arguments. + const FunctionProtoType *FPT = D->getType()->castAs(); + EmitCallArgs(Args, FPT, ArgBeg, ArgEnd); + + // Insert any ABI-specific implicit constructor arguments. + unsigned ExtraArgs = CGM.getCXXABI().addImplicitConstructorArgs( + *this, D, Type, ForVirtualBase, Delegating, Args); + + // Emit the call. + llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(D, Type); + RequiredArgs Required = RequiredArgs::forPrototypePlus(FPT, 1 + ExtraArgs); + const CGFunctionInfo &Info = + CGM.getTypes().arrangeCXXMethodCall(Args, FPT, Required); + EmitCall(Info, Callee, ReturnValueSlot(), Args, D); } void diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 894e81ca29..a5967fddf2 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -253,7 +253,7 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, FunctionArgList FunctionArgs; // Create the implicit 'this' parameter declaration. - CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResultType, FunctionArgs); + CGM.getCXXABI().buildThisParam(*this, FunctionArgs); // Add the rest of the parameters. for (FunctionDecl::param_const_iterator I = MD->param_begin(), @@ -261,6 +261,9 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, I != E; ++I) FunctionArgs.push_back(*I); + if (isa(MD)) + CGM.getCXXABI().addImplicitStructorParams(*this, ResultType, FunctionArgs); + // Start defining the function. StartFunction(GlobalDecl(), ResultType, Fn, FnInfo, FunctionArgs, SourceLocation()); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index ce2c9b05c4..46e2edd916 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -694,16 +694,19 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, QualType ResTy = FD->getResultType(); CurGD = GD; - const CXXMethodDecl *MD; - if ((MD = dyn_cast(FD)) && MD->isInstance()) { + const CXXMethodDecl *MD = dyn_cast(FD); + if (MD && MD->isInstance()) { if (CGM.getCXXABI().HasThisReturn(GD)) ResTy = MD->getThisType(getContext()); - CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResTy, Args); + CGM.getCXXABI().buildThisParam(*this, Args); } for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) Args.push_back(FD->getParamDecl(i)); + if (MD && (isa(MD) || isa(MD))) + CGM.getCXXABI().addImplicitStructorParams(*this, ResTy, Args); + SourceRange BodyRange; if (Stmt *Body = FD->getBody()) BodyRange = Body->getSourceRange(); CurEHLocation = BodyRange.getEnd(); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 2ddb1b9460..6c00b2a6b4 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2492,6 +2492,7 @@ private: std::string &ConstraintStr, SourceLocation Loc); +public: /// EmitCallArgs - Emit call arguments for a function. template void EmitCallArgs(CallArgList &Args, const T *CallArgTypeInfo, @@ -2565,6 +2566,7 @@ private: CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd, bool ForceColumnInfo); +private: const TargetCodeGenInfo &getTargetHooks() const { return CGM.getTargetCodeGenInfo(); } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index bb8b536202..b5dc819511 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -136,18 +136,15 @@ public: void EmitCXXDestructors(const CXXDestructorDecl *D); - void BuildInstanceFunctionParams(CodeGenFunction &CGF, - QualType &ResTy, - FunctionArgList &Params); + void addImplicitStructorParams(CodeGenFunction &CGF, QualType &ResTy, + FunctionArgList &Params); void EmitInstanceFunctionProlog(CodeGenFunction &CGF); - void EmitConstructorCall(CodeGenFunction &CGF, - const CXXConstructorDecl *D, CXXCtorType Type, - bool ForVirtualBase, bool Delegating, - llvm::Value *This, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd); + unsigned addImplicitConstructorArgs(CodeGenFunction &CGF, + const CXXConstructorDecl *D, + CXXCtorType Type, bool ForVirtualBase, + bool Delegating, CallArgList &Args); void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, @@ -801,18 +798,19 @@ ItaniumCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF, /// The generic ABI passes 'this', plus a VTT if it's initializing a /// base subobject. -void ItaniumCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor, - CXXCtorType Type, - CanQualType &ResTy, - SmallVectorImpl &ArgTys) { +void +ItaniumCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor, + CXXCtorType Type, CanQualType &ResTy, + SmallVectorImpl &ArgTys) { ASTContext &Context = getContext(); - // 'this' parameter is already there, as well as 'this' return if - // HasThisReturn(GlobalDecl(Ctor, Type)) is true + // All parameters are already in place except VTT, which goes after 'this'. + // These are Clang types, so we don't need to worry about sret yet. // Check if we need to add a VTT parameter (which has type void **). if (Type == Ctor_Base && Ctor->getParent()->getNumVBases() != 0) - ArgTys.push_back(Context.getPointerType(Context.VoidPtrTy)); + ArgTys.insert(ArgTys.begin() + 1, + Context.getPointerType(Context.VoidPtrTy)); } void ItaniumCXXABI::EmitCXXConstructors(const CXXConstructorDecl *D) { @@ -863,14 +861,11 @@ void ItaniumCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) { CGM.EmitGlobal(GlobalDecl(D, Dtor_Deleting)); } -void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, - QualType &ResTy, - FunctionArgList &Params) { - /// Create the 'this' variable. - BuildThisParam(CGF, Params); - +void ItaniumCXXABI::addImplicitStructorParams(CodeGenFunction &CGF, + QualType &ResTy, + FunctionArgList &Params) { const CXXMethodDecl *MD = cast(CGF.CurGD.getDecl()); - assert(MD->isInstance()); + assert(isa(MD) || isa(MD)); // Check if we need a VTT parameter as well. if (NeedsVTTParameter(CGF.CurGD)) { @@ -881,7 +876,7 @@ void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, ImplicitParamDecl *VTTDecl = ImplicitParamDecl::Create(Context, 0, MD->getLocation(), &Context.Idents.get("vtt"), T); - Params.push_back(VTTDecl); + Params.insert(Params.begin() + 1, VTTDecl); getStructorImplicitParamDecl(CGF) = VTTDecl; } } @@ -908,21 +903,19 @@ void ItaniumCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue); } -void ItaniumCXXABI::EmitConstructorCall(CodeGenFunction &CGF, - const CXXConstructorDecl *D, - CXXCtorType Type, - bool ForVirtualBase, bool Delegating, - llvm::Value *This, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd) { - llvm::Value *VTT = CGF.GetVTTParameter(GlobalDecl(D, Type), ForVirtualBase, - Delegating); - QualType VTTTy = getContext().getPointerType(getContext().VoidPtrTy); - llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(D, Type); +unsigned ItaniumCXXABI::addImplicitConstructorArgs( + CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, + bool ForVirtualBase, bool Delegating, CallArgList &Args) { + if (!NeedsVTTParameter(GlobalDecl(D, Type))) + return 0; - // FIXME: Provide a source location here. - CGF.EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This, - VTT, VTTTy, ArgBeg, ArgEnd); + // Insert the implicit 'vtt' argument as the second argument. + llvm::Value *VTT = + CGF.GetVTTParameter(GlobalDecl(D, Type), ForVirtualBase, Delegating); + QualType VTTTy = getContext().getPointerType(getContext().VoidPtrTy); + Args.insert(Args.begin() + 1, + CallArg(RValue::get(VTT), VTTTy, /*needscopy=*/false)); + return 1; // Added one arg. } void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF, diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 46a9256a32..62b7f43022 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -140,21 +140,18 @@ public: GlobalDecl GD, llvm::Value *This); - void BuildInstanceFunctionParams(CodeGenFunction &CGF, - QualType &ResTy, - FunctionArgList &Params); + void addImplicitStructorParams(CodeGenFunction &CGF, QualType &ResTy, + FunctionArgList &Params); llvm::Value *adjustThisParameterInVirtualFunctionPrologue( CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This); void EmitInstanceFunctionProlog(CodeGenFunction &CGF); - void EmitConstructorCall(CodeGenFunction &CGF, - const CXXConstructorDecl *D, CXXCtorType Type, - bool ForVirtualBase, bool Delegating, - llvm::Value *This, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd); + unsigned addImplicitConstructorArgs(CodeGenFunction &CGF, + const CXXConstructorDecl *D, + CXXCtorType Type, bool ForVirtualBase, + bool Delegating, CallArgList &Args); void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, @@ -450,16 +447,20 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const { return isa(GD.getDecl()); } -void MicrosoftCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor, - CXXCtorType Type, - CanQualType &ResTy, - SmallVectorImpl &ArgTys) { - // 'this' parameter and 'this' return are already in place +void MicrosoftCXXABI::BuildConstructorSignature( + const CXXConstructorDecl *Ctor, CXXCtorType Type, CanQualType &ResTy, + SmallVectorImpl &ArgTys) { + + // All parameters are already in place except is_most_derived, which goes + // after 'this' if it's variadic and last if it's not. const CXXRecordDecl *Class = Ctor->getParent(); + const FunctionProtoType *FPT = Ctor->getType()->castAs(); if (Class->getNumVBases()) { - // Constructors of classes with virtual bases take an implicit parameter. - ArgTys.push_back(CGM.getContext().IntTy); + if (FPT->isVariadic()) + ArgTys.insert(ArgTys.begin() + 1, CGM.getContext().IntTy); + else + ArgTys.push_back(CGM.getContext().IntTy); } } @@ -682,20 +683,25 @@ static bool IsDeletingDtor(GlobalDecl GD) { return false; } -void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, - QualType &ResTy, - FunctionArgList &Params) { - BuildThisParam(CGF, Params); - +void MicrosoftCXXABI::addImplicitStructorParams(CodeGenFunction &CGF, + QualType &ResTy, + FunctionArgList &Params) { ASTContext &Context = getContext(); const CXXMethodDecl *MD = cast(CGF.CurGD.getDecl()); + assert(isa(MD) || isa(MD)); if (isa(MD) && MD->getParent()->getNumVBases()) { ImplicitParamDecl *IsMostDerived = ImplicitParamDecl::Create(Context, 0, CGF.CurGD.getDecl()->getLocation(), &Context.Idents.get("is_most_derived"), Context.IntTy); - Params.push_back(IsMostDerived); + // The 'most_derived' parameter goes second if the ctor is variadic and last + // if it's not. Dtors can't be variadic. + const FunctionProtoType *FPT = MD->getType()->castAs(); + if (FPT->isVariadic()) + Params.insert(Params.begin() + 1, IsMostDerived); + else + Params.push_back(IsMostDerived); getStructorImplicitParamDecl(CGF) = IsMostDerived; } else if (IsDeletingDtor(CGF.CurGD)) { ImplicitParamDecl *ShouldDelete @@ -788,27 +794,29 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { } } -void MicrosoftCXXABI::EmitConstructorCall(CodeGenFunction &CGF, - const CXXConstructorDecl *D, - CXXCtorType Type, - bool ForVirtualBase, - bool Delegating, - llvm::Value *This, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd) { +unsigned MicrosoftCXXABI::addImplicitConstructorArgs( + CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, + bool ForVirtualBase, bool Delegating, CallArgList &Args) { assert(Type == Ctor_Complete || Type == Ctor_Base); - llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(D, Ctor_Complete); - llvm::Value *ImplicitParam = 0; - QualType ImplicitParamTy; - if (D->getParent()->getNumVBases()) { - ImplicitParam = llvm::ConstantInt::get(CGM.Int32Ty, Type == Ctor_Complete); - ImplicitParamTy = getContext().IntTy; + // Check if we need a 'most_derived' parameter. + if (!D->getParent()->getNumVBases()) + return 0; + + // Add the 'most_derived' argument second if we are variadic or last if not. + const FunctionProtoType *FPT = D->getType()->castAs(); + llvm::Value *MostDerivedArg = + llvm::ConstantInt::get(CGM.Int32Ty, Type == Ctor_Complete); + RValue RV = RValue::get(MostDerivedArg); + if (MostDerivedArg) { + if (FPT->isVariadic()) + Args.insert(Args.begin() + 1, + CallArg(RV, getContext().IntTy, /*needscopy=*/false)); + else + Args.add(RV, getContext().IntTy); } - // FIXME: Provide a source location here. - CGF.EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This, - ImplicitParam, ImplicitParamTy, ArgBeg, ArgEnd); + return 1; // Added one arg. } void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, diff --git a/test/CodeGenCXX/microsoft-abi-structors.cpp b/test/CodeGenCXX/microsoft-abi-structors.cpp index 71cc31d9ae..9199096f5f 100644 --- a/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -300,6 +300,37 @@ void call_nv_deleting_dtor(D *d) { } +namespace test1 { +struct A { }; +struct B : virtual A { + B(int *a); + B(const char *a, ...); + __cdecl B(short *a); +}; +B::B(int *a) {} +B::B(const char *a, ...) {} +B::B(short *a) {} +// CHECK: define x86_thiscallcc %"struct.test1::B"* @"\01??0B@test1@@QAE@PAH@Z" +// CHECK: (%"struct.test1::B"* returned %this, i32* %a, i32 %is_most_derived) +// CHECK: define %"struct.test1::B"* @"\01??0B@test1@@QAA@PBDZZ" +// CHECK: (%"struct.test1::B"* returned %this, i32 %is_most_derived, i8* %a, ...) + +// FIXME: This should be x86_thiscallcc. MSVC ignores explicit CCs on structors. +// CHECK: define %"struct.test1::B"* @"\01??0B@test1@@QAA@PAF@Z" +// CHECK: (%"struct.test1::B"* returned %this, i16* %a, i32 %is_most_derived) + +void construct_b() { + int a; + B b1(&a); + B b2("%d %d", 1, 2); +} +// CHECK-LABEL: define void @"\01?construct_b@test1@@YAXXZ"() +// CHECK: call x86_thiscallcc %"struct.test1::B"* @"\01??0B@test1@@QAE@PAH@Z" +// CHECK: (%"struct.test1::B"* {{.*}}, i32* {{.*}}, i32 1) +// CHECK: call %"struct.test1::B"* (%"struct.test1::B"*, i32, i8*, ...)* @"\01??0B@test1@@QAA@PBDZZ" +// CHECK: (%"struct.test1::B"* {{.*}}, i32 1, i8* {{.*}}, i32 1, i32 2) +} + // Dtor thunks for classes in anonymous namespaces should be internal, not // linkonce_odr. namespace {