namespace {
+static const FunctionDecl *getStructor(const FunctionDecl *fn) {
+ if (const FunctionTemplateDecl *ftd = fn->getPrimaryTemplate())
+ return ftd->getTemplatedDecl();
+
+ return fn;
+}
+
/// MicrosoftCXXNameMangler - Manage the mangling of a single name for the
/// Microsoft Visual C++ ABI.
class MicrosoftCXXNameMangler {
MangleContext &Context;
raw_ostream &Out;
+ /// The "structor" is the top-level declaration being mangled, if
+ /// that's not a template specialization; otherwise it's the pattern
+ /// for that specialization.
+ const NamedDecl *Structor;
+ unsigned StructorType;
+
// FIXME: audit the performance of BackRefMap as it might do way too many
// copying of strings.
typedef std::map<std::string, unsigned> BackRefMap;
public:
MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_)
- : Context(C), Out(Out_), UseNameBackReferences(true) { }
+ : Context(C), Out(Out_),
+ Structor(0), StructorType(-1),
+ UseNameBackReferences(true) { }
+
+ MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_,
+ const CXXDestructorDecl *D, CXXDtorType Type)
+ : Context(C), Out(Out_),
+ Structor(getStructor(D)), StructorType(Type),
+ UseNameBackReferences(true) { }
raw_ostream &getStream() const { return Out; }
void mangleSourceName(const IdentifierInfo *II);
void manglePostfix(const DeclContext *DC, bool NoFunction=false);
void mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc);
+ void mangleCXXDtorType(CXXDtorType T);
void mangleQualifiers(Qualifiers Quals, bool IsMember);
void manglePointerQualifiers(Qualifiers Quals);
break;
case DeclarationName::CXXDestructorName:
- Out << "?1";
+ if (ND == Structor)
+ // If the named decl is the C++ destructor we're mangling,
+ // use the type we were given.
+ mangleCXXDtorType(static_cast<CXXDtorType>(StructorType));
+ else
+ // Otherwise, use the complete destructor name. This is relevant if a
+ // class with a destructor is declared within a destructor.
+ mangleCXXDtorType(Dtor_Complete);
break;
case DeclarationName::CXXConversionFunctionName:
}
}
+void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
+ switch (T) {
+ case Dtor_Deleting:
+ Out << "?_G";
+ return;
+ case Dtor_Base:
+ // FIXME: We should be asked to mangle base dtors.
+ // However, fixing this would require larger changes to the CodeGenModule.
+ // Please put llvm_unreachable here when CGM is changed.
+ // For now, just mangle a base dtor the same way as a complete dtor...
+ case Dtor_Complete:
+ Out << "?1";
+ return;
+ }
+ llvm_unreachable("Unsupported dtor type?");
+}
+
void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO,
SourceLocation Loc) {
switch (OO) {
// <return-type> ::= <type>
// ::= @ # structors (they have no declared return type)
- if (IsStructor)
+ if (IsStructor) {
+ if (isa<CXXDestructorDecl>(D) && D == Structor &&
+ StructorType == Dtor_Deleting) {
+ // The scalar deleting destructor takes an extra int argument.
+ // However, the FunctionType generated has 0 arguments.
+ // FIXME: This is a temporary hack.
+ // Maybe should fix the FunctionType creation instead?
+ Out << "PAXI@Z";
+ return;
+ }
Out << '@';
- else {
+ } else {
QualType Result = Proto->getResultType();
const Type* RT = Result.getTypePtr();
if (!RT->isAnyPointerType() && !RT->isReferenceType()) {
void MicrosoftMangleContext::mangleCXXDtor(const CXXDestructorDecl *D,
CXXDtorType Type,
raw_ostream & Out) {
- MicrosoftCXXNameMangler mangler(*this, Out);
+ MicrosoftCXXNameMangler mangler(*this, Out, D, Type);
mangler.mangle(D);
}
void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *VD,
break;
case VTableComponent::CK_DeletingDtorPointer:
// We've already added the thunk when we saw the complete dtor pointer.
+ // FIXME: check how this works in the Microsoft ABI
+ // while working on the multiple inheritance patch.
continue;
}
Components.push_back(VTableComponent::MakeCompleteDtor(DD));
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
} else {
- // Add only one destructor in MS mode.
- // FIXME: The virtual destructors are handled differently in MS ABI,
- // we should add such a support later. For now, put the complete
- // destructor into the vftable just to make its layout right.
- Components.push_back(VTableComponent::MakeCompleteDtor(DD));
+ // Add the scalar deleting destructor.
+ Components.push_back(VTableComponent::MakeDeletingDtor(DD));
}
} else {
// Add the return adjustment if necessary.
Out << DD->getQualifiedNameAsString();
if (IsComplete)
Out << "() [complete]";
+ else if (isMicrosoftABI())
+ Out << "() [scalar deleting]";
else
Out << "() [deleting]";
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))]
= MethodName + " [deleting]";
} else {
- IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Complete))]
- = MethodName;
+ IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))]
+ = MethodName + " [scalar deleting]";
}
} else {
IndicesMap[VTables.getMethodVTableIndex(MD)] = MethodName;
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] =
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting));
} else {
- MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
- getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Complete));
+ // Add the scalar deleting destructor.
+ MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] =
+ getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting));
}
} else {
MethodVTableIndices[MD] = getMethodVTableIndex(OverriddenMD);
// Add the deleting dtor.
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++;
} else {
- // Add only the deleting dtor.
- // FIXME: The virtual destructors are handled differently in MS ABI,
- // we should add such a support later. For now, put the complete
- // destructor into the vftable indices.
- MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++;
+ // Add the scalar deleting dtor.
+ MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++;
}
} else {
// Add the entry.
return CGF.CXXABIThisValue;
}
+ // FIXME: Every place that calls getVTT{Decl,Value} is something
+ // that needs to be abstracted properly.
ImplicitParamDecl *&getVTTDecl(CodeGenFunction &CGF) {
- return CGF.CXXVTTDecl;
+ return CGF.CXXStructorImplicitParamDecl;
}
llvm::Value *&getVTTValue(CodeGenFunction &CGF) {
- return CGF.CXXVTTValue;
+ return CGF.CXXStructorImplicitParamValue;
+ }
+
+ ImplicitParamDecl *&getStructorImplicitParamDecl(CodeGenFunction &CGF) {
+ return CGF.CXXStructorImplicitParamDecl;
+ }
+ llvm::Value *&getStructorImplicitParamValue(CodeGenFunction &CGF) {
+ return CGF.CXXStructorImplicitParamValue;
}
/// Build a parameter variable suitable for 'this'.
}
};
+ struct CallDtorDeleteConditional : EHScopeStack::Cleanup {
+ llvm::Value *ShouldDeleteCondition;
+ public:
+ CallDtorDeleteConditional(llvm::Value *ShouldDeleteCondition)
+ : ShouldDeleteCondition(ShouldDeleteCondition) {
+ assert(ShouldDeleteCondition != NULL);
+ }
+
+ void Emit(CodeGenFunction &CGF, Flags flags) {
+ llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
+ llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
+ llvm::Value *ShouldCallDelete
+ = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
+ CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
+
+ CGF.EmitBlock(callDeleteBB);
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
+ const CXXRecordDecl *ClassDecl = Dtor->getParent();
+ CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
+ CGF.getContext().getTagDeclType(ClassDecl));
+ CGF.Builder.CreateBr(continueBB);
+
+ CGF.EmitBlock(continueBB);
+ }
+ };
+
class DestroyField : public EHScopeStack::Cleanup {
const FieldDecl *field;
CodeGenFunction::Destroyer *destroyer;
if (DtorType == Dtor_Deleting) {
assert(DD->getOperatorDelete() &&
"operator delete missing - EmitDtorEpilogue");
- EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ if (CXXStructorImplicitParamValue) {
+ // If there is an implicit param to the deleting dtor, it's a boolean
+ // telling whether we should call delete at the end of the dtor.
+ EHStack.pushCleanup<CallDtorDeleteConditional>(
+ NormalAndEHCleanup, CXXStructorImplicitParamValue);
+ } else {
+ EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ }
return;
}
// FIXME: Provide a source location here.
EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This,
- VTT, ArgBeg, ArgEnd);
+ VTT, getContext().getPointerType(getContext().VoidPtrTy),
+ ArgBeg, ArgEnd);
}
void
// FIXME: Provide a source location here.
EmitCXXMemberCall(DD, SourceLocation(), Callee, ReturnValueSlot(), This,
- VTT, 0, 0);
+ VTT, getContext().getPointerType(getContext().VoidPtrTy),
+ 0, 0);
}
namespace {
llvm::Value *Callee,
ReturnValueSlot ReturnValue,
llvm::Value *This,
- llvm::Value *VTT,
+ llvm::Value *ImplicitParam,
+ QualType ImplicitParamTy,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd) {
assert(MD->isInstance() &&
// Push the this ptr.
Args.add(RValue::get(This), MD->getThisType(getContext()));
- // If there is a VTT parameter, emit it.
- if (VTT) {
- QualType T = getContext().getPointerType(getContext().VoidPtrTy);
- Args.add(RValue::get(VTT), T);
+ // If there is an implicit parameter (e.g. VTT), emit it.
+ if (ImplicitParam) {
+ Args.add(RValue::get(ImplicitParam), ImplicitParamTy);
}
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
}
return EmitCXXMemberCall(MD, CE->getExprLoc(), Callee, ReturnValue, This,
- /*VTT=*/0, CE->arg_begin(), CE->arg_end());
+ /*ImplicitParam=*/0, QualType(),
+ CE->arg_begin(), CE->arg_end());
}
RValue
llvm::Value *Callee = EmitCXXOperatorMemberCallee(E, MD, This);
return EmitCXXMemberCall(MD, E->getExprLoc(), Callee, ReturnValue, This,
- /*VTT=*/0, E->arg_begin() + 1, E->arg_end());
+ /*ImplicitParam=*/0, QualType(),
+ E->arg_begin() + 1, E->arg_end());
}
RValue CodeGenFunction::EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E,
Ptr, Ty);
// FIXME: Provide a source location here.
CGF.EmitCXXMemberCall(Dtor, SourceLocation(), Callee, ReturnValueSlot(),
- Ptr, /*VTT=*/0, 0, 0);
+ Ptr, /*ImplicitParam=*/0, QualType(), 0, 0);
if (UseGlobalDelete) {
CGF.PopCleanupBlock();
FirstBlockInfo(0), EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0),
DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false),
IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0),
- CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0), CXXVTTDecl(0),
- CXXVTTValue(0), OutermostConditional(0), TerminateLandingPad(0),
+ CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0),
+ CXXStructorImplicitParamDecl(0), CXXStructorImplicitParamValue(0),
+ OutermostConditional(0), TerminateLandingPad(0),
TerminateHandler(0), TrapBB(0) {
if (!suppressNewContext)
CGM.getCXXABI().getMangleContext().startNewFunction();
llvm::Value *CXXABIThisValue;
llvm::Value *CXXThisValue;
- /// CXXVTTDecl - When generating code for a base object constructor or
- /// base object destructor with virtual bases, this will hold the implicit
- /// VTT parameter.
- ImplicitParamDecl *CXXVTTDecl;
- llvm::Value *CXXVTTValue;
+ /// CXXStructorImplicitParamDecl - When generating code for a constructor or
+ /// destructor, this will hold the implicit argument (e.g. VTT).
+ ImplicitParamDecl *CXXStructorImplicitParamDecl;
+ llvm::Value *CXXStructorImplicitParamValue;
/// OutermostConditional - Points to the outermost active
/// conditional control. This is used so that we know if a
/// LoadCXXVTT - Load the VTT parameter to base constructors/destructors have
/// virtual bases.
+ // FIXME: Every place that calls LoadCXXVTT is something
+ // that needs to be abstracted properly.
llvm::Value *LoadCXXVTT() {
- assert(CXXVTTValue && "no VTT value for this function");
- return CXXVTTValue;
+ assert(CXXStructorImplicitParamValue && "no VTT value for this function");
+ return CXXStructorImplicitParamValue;
+ }
+
+ /// LoadCXXStructorImplicitParam - Load the implicit parameter
+ /// for a constructor/destructor.
+ llvm::Value *LoadCXXStructorImplicitParam() {
+ assert(CXXStructorImplicitParamValue &&
+ "no implicit argument value for this function");
+ return CXXStructorImplicitParamValue;
}
/// GetAddressOfBaseOfCompleteClass - Convert the given pointer to a
llvm::Value *Callee,
ReturnValueSlot ReturnValue,
llvm::Value *This,
- llvm::Value *VTT,
+ llvm::Value *ImplicitParam,
+ QualType ImplicitParamTy,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd);
RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E,
void BuildDestructorSignature(const CXXDestructorDecl *Ctor,
CXXDtorType Type,
CanQualType &ResTy,
- SmallVectorImpl<CanQualType> &ArgTys) {
- // 'this' is already in place
- // TODO: 'for base' flag
- }
+ SmallVectorImpl<CanQualType> &ArgTys);
void BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
ResTy = ArgTys[0];
}
+void MicrosoftCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor,
+ CXXDtorType Type,
+ CanQualType &ResTy,
+ SmallVectorImpl<CanQualType> &ArgTys) {
+ // 'this' is already in place
+ // TODO: 'for base' flag
+
+ if (Type == Dtor_Deleting) {
+ // The scalar deleting destructor takes an implicit bool parameter.
+ ArgTys.push_back(CGM.getContext().BoolTy);
+ }
+}
+
+static bool IsDeletingDtor(GlobalDecl GD) {
+ const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
+ if (isa<CXXDestructorDecl>(MD)) {
+ return GD.getDtorType() == Dtor_Deleting;
+ }
+ return false;
+}
+
void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) {
if (needThisReturn(CGF.CurGD)) {
ResTy = Params[0]->getType();
}
+ if (IsDeletingDtor(CGF.CurGD)) {
+ ASTContext &Context = getContext();
+
+ ImplicitParamDecl *ShouldDelete
+ = ImplicitParamDecl::Create(Context, 0,
+ CGF.CurGD.getDecl()->getLocation(),
+ &Context.Idents.get("should_call_delete"),
+ Context.BoolTy);
+ Params.push_back(ShouldDelete);
+ getStructorImplicitParamDecl(CGF) = ShouldDelete;
+ }
}
void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
if (needThisReturn(CGF.CurGD)) {
CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue);
}
+ if (IsDeletingDtor(CGF.CurGD)) {
+ assert(getStructorImplicitParamDecl(CGF) &&
+ "no implicit parameter for a deleting destructor?");
+ getStructorImplicitParamValue(CGF)
+ = CGF.Builder.CreateLoad(
+ CGF.GetAddrOfLocalVar(getStructorImplicitParamDecl(CGF)),
+ "should_call_delete");
+ }
}
bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
-// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 -fno-rtti > %t 2>&1
+// RUN: FileCheck %s < %t
+// Using a different check prefix as the inline destructors might be placed
+// anywhere in the output.
+// RUN: FileCheck --check-prefix=DTORS %s < %t
class A {
public:
}
struct B {
- virtual ~B();
+ virtual ~B() {
+// Complete destructor first:
+// DTORS: define {{.*}} x86_thiscallcc void @"\01??1B@@UAE@XZ"(%struct.B* %this)
+//
+// Then, the scalar deleting destructor (used in the vtable):
+// DTORS: define {{.*}} x86_thiscallcc void @"\01??_GB@@UAEPAXI@Z"(%struct.B* %this, i1 zeroext %should_call_delete)
+// DTORS: %0 = icmp eq i8 %should_call_delete{{.*}}, 0
+// DTORS-NEXT: br i1 %0, label %dtor.continue, label %dtor.call_delete
+// DTORS: dtor.call_delete:
+// DTORS-NEXT: %1 = bitcast %struct.B* %this1 to i8*
+// DTORS-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %1) nounwind
+// DTORS-NEXT: br label %dtor.continue
+// DTORS: dtor.continue:
+// DTORS-NEXT: ret void
+ }
virtual void foo();
};
+// Emits the vftable in the output.
+void B::foo() {}
+
void check_vftable_offset() {
B b;
// The vftable pointer should point at the beginning of the vftable.
// CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %struct.B* {{.*}} to i8***
// CHECK: store i8** getelementptr inbounds ([2 x i8*]* @"\01??_7B@@6B@", i64 0, i64 0), i8*** [[THIS_PTR]]
}
+
+// FIXME: Enable the following block and add expectations when calls
+// to virtual complete dtor are supported.
+#if 0
+void call_complete_dtor(B *obj_ptr) {
+ obj_ptr->~B();
+}
+#endif
+
+void call_deleting_dtor(B *obj_ptr) {
+// FIXME: Add CHECKs when calls to virtual deleting dtor are generated properly.
+ delete obj_ptr;
+}
+
+struct C {
+ static int foo();
+
+ C() {
+ static int ctor_static = foo();
+ // CHECK that the static in the ctor gets mangled correctly:
+ // CHECK: @"\01?ctor_static@?1???0C@@QAE@XZ@4HA"
+ }
+ ~C() {
+ static int dtor_static = foo();
+ // CHECK that the static in the dtor gets mangled correctly:
+ // CHECK: @"\01?dtor_static@?1???1C@@QAE@XZ@4HA"
+ }
+};
+
+void use_C() { C c; }
struct C {
// CHECK-C: Vtable for 'C' (2 entries)
- // CHECK-C-NEXT: 0 | C::~C()
+ // CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: VTable indices for 'C' (2 entries).
- // CHECK-C-NEXT: 0 | C::~C()
+ // CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// Never used, so doesn't emit a vtable.
virtual ~C();
struct D {
// CHECK-D: Vtable for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
- // CHECK-D-NEXT: 1 | D::~D()
+ // CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
// EMITS-VTABLE: @"\01??_7D@@6B@" = unnamed_addr constant [2 x i8*]
virtual void f();
// CHECK-E-NEXT: 0 | void A::f()
// CHECK-E-NEXT: 1 | void A::g()
// CHECK-E-NEXT: 2 | void A::h()
- // CHECK-E-NEXT: 3 | E::~E()
+ // CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// CHECK-E: VTable indices for 'E' (2 entries).
- // CHECK-E-NEXT: 3 | E::~E()
+ // CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// Never used, so doesn't emit a vtable.
// CHECK-F-NEXT: 1 | void A::g()
// CHECK-F-NEXT: 2 | void A::h()
// CHECK-F-NEXT: 3 | void F::i()
- // CHECK-F-NEXT: 4 | F::~F()
+ // CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
// CHECK-F: VTable indices for 'F' (2 entries).
// CHECK-F-NEXT: 3 | void F::i()
- // CHECK-F-NEXT: 4 | F::~F()
+ // CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
// EMITS-VTABLE: @"\01??_7F@@6B@" = unnamed_addr constant [5 x i8*]
virtual void i();
virtual ~F();
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 1 | void A::g()
// CHECK-G-NEXT: 2 | void A::h()
- // CHECK-G-NEXT: 3 | G::~G()
+ // CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 4 | void E::i()
// CHECK-G-NEXT: 5 | void G::j()
// CHECK-G: VTable indices for 'G' (3 entries).
// CHECK-G-NEXT: 0 | void G::f()
- // CHECK-G-NEXT: 3 | G::~G()
+ // CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 5 | void G::j()
// Never used, so doesn't emit a vtable.
virtual void f(); // overrides A::f()