129 changes: 122 additions & 7 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ class MicrosoftCXXABI : public CGCXXABI {
return Fn;
}

llvm::Function *getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD);

llvm::Constant *getCatchableType(QualType T,
uint32_t NVOffset = 0,
int32_t VBPtrOffset = -1,
Expand Down Expand Up @@ -3220,6 +3222,103 @@ void MicrosoftCXXABI::emitCXXStructor(const CXXMethodDecl *MD,
emitCXXDestructor(CGM, cast<CXXDestructorDecl>(MD), Type);
}

llvm::Function *
MicrosoftCXXABI::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
// Calculate the mangled name.
SmallString<256> ThunkName;
llvm::raw_svector_ostream Out(ThunkName);
getMangleContext().mangleCXXCtor(CD, Ctor_CopyingClosure, Out);
Out.flush();

// If the thunk has been generated previously, just return it.
if (llvm::GlobalValue *GV = CGM.getModule().getNamedValue(ThunkName))
return cast<llvm::Function>(GV);

// Create the llvm::Function.
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeMSCopyCtorClosure(CD);
llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo);
const CXXRecordDecl *RD = CD->getParent();
QualType RecordTy = getContext().getRecordType(RD);
llvm::Function *ThunkFn = llvm::Function::Create(
ThunkTy, getLinkageForRTTI(RecordTy), ThunkName.str(), &CGM.getModule());

// Start codegen.
CodeGenFunction CGF(CGM);
CGF.CurGD = GlobalDecl(CD, Ctor_Complete);

// Build FunctionArgs.
FunctionArgList FunctionArgs;

// A copy constructor always starts with a 'this' pointer as its first
// argument.
buildThisParam(CGF, FunctionArgs);

// Following the 'this' pointer is a reference to the source object that we
// are copying from.
ImplicitParamDecl SrcParam(
getContext(), nullptr, SourceLocation(), &getContext().Idents.get("src"),
getContext().getLValueReferenceType(RecordTy,
/*SpelledAsLValue=*/true));
FunctionArgs.push_back(&SrcParam);

// Copy constructors for classes which utilize virtual bases have an
// additional parameter which indicates whether or not it is being delegated
// to by a more derived constructor.
ImplicitParamDecl IsMostDerived(getContext(), nullptr, SourceLocation(),
&getContext().Idents.get("is_most_derived"),
getContext().IntTy);
// Only add the parameter to the list if thie class has virtual bases.
if (RD->getNumVBases() > 0)
FunctionArgs.push_back(&IsMostDerived);

// Start defining the function.
CGF.StartFunction(GlobalDecl(), FnInfo.getReturnType(), ThunkFn, FnInfo,
FunctionArgs, CD->getLocation(), SourceLocation());
EmitThisParam(CGF);
llvm::Value *This = getThisValue(CGF);

llvm::Value *SrcVal =
CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(&SrcParam), "src");

CallArgList Args;

// Push the this ptr.
Args.add(RValue::get(This), CD->getThisType(getContext()));

// Push the src ptr.
Args.add(RValue::get(SrcVal), SrcParam.getType());

// Add the rest of the default arguments.
std::vector<Stmt *> ArgVec;
for (unsigned I = 1, E = CD->getNumParams(); I != E; ++I)
ArgVec.push_back(getContext().getDefaultArgExprForConstructor(CD, I));

CodeGenFunction::RunCleanupsScope Cleanups(CGF);

const auto *FPT = CD->getType()->castAs<FunctionProtoType>();
ConstExprIterator ArgBegin(ArgVec.data()), ArgEnd(&*ArgVec.end());
CGF.EmitCallArgs(Args, FPT, ArgBegin, ArgEnd, CD, 1);

// Insert any ABI-specific implicit constructor arguments.
unsigned ExtraArgs = addImplicitConstructorArgs(CGF, CD, Ctor_Complete,
/*ForVirtualBase=*/false,
/*Delegating=*/false, Args);

// Call the destructor with our arguments.
llvm::Value *CalleeFn = CGM.getAddrOfCXXStructor(CD, StructorType::Complete);
const CGFunctionInfo &CalleeInfo = CGM.getTypes().arrangeCXXConstructorCall(
Args, CD, Ctor_Complete, ExtraArgs);
CGF.EmitCall(CalleeInfo, CalleeFn, ReturnValueSlot(), Args, CD);

Cleanups.ForceCleanup();

// Emit the ret instruction, remove any temporary instructions created for the
// aid of CodeGen.
CGF.FinishFunction(SourceLocation());

return ThunkFn;
}

llvm::Constant *MicrosoftCXXABI::getCatchableType(QualType T,
uint32_t NVOffset,
int32_t VBPtrOffset,
Expand All @@ -3229,27 +3328,43 @@ llvm::Constant *MicrosoftCXXABI::getCatchableType(QualType T,
CXXRecordDecl *RD = T->getAsCXXRecordDecl();
const CXXConstructorDecl *CD =
RD ? CGM.getContext().getCopyConstructorForExceptionObject(RD) : nullptr;
CXXCtorType CT = Ctor_Complete;
if (CD) {
CallingConv ExpectedCallingConv = getContext().getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/true);
CallingConv ActualCallingConv =
CD->getType()->getAs<FunctionProtoType>()->getCallConv();
if (ExpectedCallingConv != ActualCallingConv || CD->getNumParams() != 1)
CT = Ctor_CopyingClosure;
}

uint32_t Size = getContext().getTypeSizeInChars(T).getQuantity();
SmallString<256> MangledName;
{
llvm::raw_svector_ostream Out(MangledName);
getMangleContext().mangleCXXCatchableType(T, CD, Size, NVOffset,
getMangleContext().mangleCXXCatchableType(T, CD, CT, Size, NVOffset,
VBPtrOffset, VBIndex, Out);
}
if (llvm::GlobalVariable *GV = CGM.getModule().getNamedGlobal(MangledName))
return getImageRelativeConstant(GV);

// The TypeDescriptor is used by the runtime to determine of a catch handler
// The TypeDescriptor is used by the runtime to determine if a catch handler
// is appropriate for the exception object.
llvm::Constant *TD = getImageRelativeConstant(getAddrOfRTTIDescriptor(T));

// The runtime is responsible for calling the copy constructor if the
// exception is caught by value.
llvm::Constant *CopyCtor =
CD ? llvm::ConstantExpr::getBitCast(
CGM.getAddrOfCXXStructor(CD, StructorType::Complete),
CGM.Int8PtrTy)
: llvm::Constant::getNullValue(CGM.Int8PtrTy);
llvm::Constant *CopyCtor;
if (CD) {
if (CT == Ctor_CopyingClosure)
CopyCtor = getAddrOfCXXCopyCtorClosure(CD);
else
CopyCtor = CGM.getAddrOfCXXStructor(CD, StructorType::Complete);

CopyCtor = llvm::ConstantExpr::getBitCast(CopyCtor, CGM.Int8PtrTy);
} else {
CopyCtor = llvm::Constant::getNullValue(CGM.Int8PtrTy);
}
CopyCtor = getImageRelativeConstant(CopyCtor);

bool IsScalar = !RD;
Expand Down
45 changes: 41 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,14 +782,51 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E,
}
}

// The MSVC ABI creates a list of all types which can catch the exception
// object. This list also references the appropriate copy constructor to call
// if the object is caught by value and has a non-trivial copy constructor.
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
// We are only interested in the public, unambiguous bases contained within
// the exception object. Bases which are ambiguous or otherwise
// inaccessible are not catchable types.
llvm::SmallVector<CXXRecordDecl *, 2> UnambiguousPublicSubobjects;
getUnambiguousPublicSubobjects(RD, UnambiguousPublicSubobjects);

for (CXXRecordDecl *Subobject : UnambiguousPublicSubobjects) {
if (CXXConstructorDecl *CD = LookupCopyingConstructor(Subobject, 0)) {
MarkFunctionReferenced(E->getExprLoc(), CD);
if (!CD->isTrivial())
Context.addCopyConstructorForExceptionObject(Subobject, CD);
// Attempt to lookup the copy constructor. Various pieces of machinery
// will spring into action, like template instantiation, which means this
// cannot be a simple walk of the class's decls. Instead, we must perform
// lookup and overload resolution.
CXXConstructorDecl *CD = LookupCopyingConstructor(Subobject, 0);
if (!CD)
continue;

// Mark the constructor referenced as it is used by this throw expression.
MarkFunctionReferenced(E->getExprLoc(), CD);

// Skip this copy constructor if it is trivial, we don't need to record it
// in the catchable type data.
if (CD->isTrivial())
continue;

// The copy constructor is non-trivial, create a mapping from this class
// type to this constructor.
// N.B. The selection of copy constructor is not sensitive to this
// particular throw-site. Lookup will be performed at the catch-site to
// ensure that the copy constructor is, in fact, accessible (via
// friendship or any other means).
Context.addCopyConstructorForExceptionObject(Subobject, CD);

// We don't keep the instantiated default argument expressions around so
// we must rebuild them here.
for (unsigned I = 1, E = CD->getNumParams(); I != E; ++I) {
// Skip any default arguments that we've already instantiated.
if (Context.getDefaultArgExprForConstructor(CD, I))
continue;

Expr *DefaultArg =
BuildCXXDefaultArgExpr(ThrowLoc, CD, CD->getParamDecl(I)).get();
Context.addDefaultArgExprForConstructor(CD, I, DefaultArg);
}
}
}
Expand Down
54 changes: 53 additions & 1 deletion clang/test/CodeGenCXX/microsoft-abi-throw.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -emit-llvm -o - -triple=i386-pc-win32 %s -fcxx-exceptions | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -o - -triple=i386-pc-win32 -std=c++11 %s -fcxx-exceptions | FileCheck %s

// CHECK-DAG: @"\01??_R0?AUY@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUY@@\00" }, comdat
// CHECK-DAG: @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUY@@@8" to i8*), i32 0, i32 -1, i32 0, i32 8, i8* bitcast (%struct.Y* (%struct.Y*, %struct.Y*, i32)* @"\01??0Y@@QAE@ABU0@@Z" to i8*) }, section ".xdata", comdat
Expand All @@ -12,6 +12,9 @@
// CHECK-DAG: @"_CT??_R0?AUV@@@81044" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUV@@@8" to i8*), i32 0, i32 4, i32 4, i32 1, i8* null }, section ".xdata", comdat
// CHECK-DAG: @"_CTA5?AUY@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.5 { i32 5, [5 x %eh.CatchableType*] [%eh.CatchableType* @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8", %eh.CatchableType* @"_CT??_R0?AUZ@@@81", %eh.CatchableType* @"_CT??_R0?AUW@@@8??0W@@QAE@ABU0@@Z44", %eh.CatchableType* @"_CT??_R0?AUM@@@818", %eh.CatchableType* @"_CT??_R0?AUV@@@81044"] }, section ".xdata", comdat
// CHECK-DAG: @"_TI5?AUY@@" = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i8* bitcast (void (%struct.Y*)* @"\01??_DY@@QAE@XZ" to i8*), i8* null, i8* bitcast (%eh.CatchableTypeArray.5* @"_CTA5?AUY@@" to i8*) }, section ".xdata", comdat
// CHECK-DAG: @"_CT??_R0?AUDefault@@@8??_ODefault@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor13* @"\01??_R0?AUDefault@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.Default*, %struct.Default*)* @"\01??_ODefault@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat
// CHECK-DAG: @"_CT??_R0?AUVariadic@@@8??_OVariadic@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor14* @"\01??_R0?AUVariadic@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.Variadic*, %struct.Variadic*)* @"\01??_OVariadic@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat
// CHECK-DAG: @"_CT??_R0?AUTemplateWithDefault@@@8??$?_OH@TemplateWithDefault@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor25* @"\01??_R0?AUTemplateWithDefault@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.TemplateWithDefault*, %struct.TemplateWithDefault*)* @"\01??$?_OH@TemplateWithDefault@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat


struct N { ~N(); };
Expand All @@ -35,3 +38,52 @@ void g(const int *const *y) {
// CHECK: call void @_CxxThrowException(i8* %{{.*}}, %eh.ThrowInfo* @_TIC2PAPBH)
throw y;
}

struct Default {
Default(Default &, int = 42);
};

// CHECK-LABEL: @"\01??_ODefault@@QAEXAAU0@@Z"
// CHECK: %[[src_addr:.*]] = alloca
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store {{.*}} %src, {{.*}} %[[src_addr]], align 4
// CHECK: store {{.*}} %this, {{.*}} %[[this_addr]], align 4
// CHECK: %[[this:.*]] = load {{.*}} %[[this_addr]]
// CHECK: %[[src:.*]] = load {{.*}} %[[src_addr]]
// CHECK: call x86_thiscallcc {{.*}} @"\01??0Default@@QAE@AAU0@H@Z"({{.*}} %[[this]], {{.*}} %[[src]], i32 42)
// CHECK: ret void

void h(Default &d) {
throw d;
}

struct Variadic {
Variadic(Variadic &, ...);
};

void i(Variadic &v) {
throw v;
}

// CHECK-LABEL: @"\01??_OVariadic@@QAEXAAU0@@Z"
// CHECK: %[[src_addr:.*]] = alloca
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store {{.*}} %src, {{.*}} %[[src_addr:.*]], align
// CHECK: store {{.*}} %this, {{.*}} %[[this_addr:.*]], align
// CHECK: %[[this:.*]] = load {{.*}} %[[this_addr]]
// CHECK: %[[src:.*]] = load {{.*}} %[[src_addr]]
// CHECK: %call = call {{.*}} @"\01??0Variadic@@QAA@AAU0@ZZ"({{.*}} %[[this]], {{.*}} %[[src]])
// CHECK: ret void

struct TemplateWithDefault {
template <typename T>
static int f() {
return 0;
}
template <typename T = int>
TemplateWithDefault(TemplateWithDefault &, T = f<T>());
};

void j(TemplateWithDefault &twd) {
throw twd;
}