Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions clang/include/clang/CodeGenShared/CXXABIShared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===----- CXXABIShared.h - Shared C++ ABI Base Class -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides a base class for C++ ABI functionality that can be shared
// between LLVM IR codegen and CIR codegen.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CODEGENSHARED_CXXABISHARED_H
#define LLVM_CLANG_CODEGENSHARED_CXXABISHARED_H

#include "clang/AST/DeclCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/ABI.h"

namespace clang {
class ASTContext;

/// Implements C++ ABI functionality that can be shared between LLVM IR codegen
/// and CIR codegen.
class CXXABIShared {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: how about 'CXXABICommon' (or maybe just 'CXXABIBase')? IMO "Shared" might give extra room for interpretation given other idiomatic c++ stuff (e.g. shared pointers, etc)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would swear I had made a comment to this effect but apparently saving comments is too much to ask of myself.

I just did a quick grep of the clang tree and it looks like *Base is what is used most consistently

protected:
ASTContext &Context;
std::unique_ptr<MangleContext> MangleCtx;

CXXABIShared(ASTContext &Context)
: Context(Context), MangleCtx(Context.createMangleContext()) {}

public:
virtual ~CXXABIShared() = default;

/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
struct AddedStructorArgCounts {
unsigned Prefix = 0;
unsigned Suffix = 0;
AddedStructorArgCounts() = default;
AddedStructorArgCounts(unsigned P, unsigned S) : Prefix(P), Suffix(S) {}
static AddedStructorArgCounts prefix(unsigned N) { return {N, 0}; }
static AddedStructorArgCounts suffix(unsigned N) { return {0, N}; }
};

/// Get the AST context.
ASTContext &getContext() const { return Context; }

/// Gets the mangle context.
MangleContext &getMangleContext() { return *MangleCtx; }

/// Determine whether there's something special about the rules of
/// the ABI tell us that 'this' is a complete object within the
/// given function. Obvious common logic like being defined on a
/// final class will have been taken care of by the caller.
virtual bool isThisCompleteObject(GlobalDecl GD) const = 0;

/// Returns true if the most-derived return value should be returned.
virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; }

/// Return whether the given global decl needs a VTT parameter.
virtual bool NeedsVTTParameter(GlobalDecl GD) const { return false; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed there's a mismatch of style among the methods, they should be consistent. Not sure which one we want to stick with given clang vs MLIR differences, but I'd vote for camelBack for this!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should match the style of the rest of clang - I'm somewhat concerned about the current divergence from the clang naming style (although I do really hate the style, it is the style clang is meant to be using)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, this is method names, not variables - I think we should match the clang style, but at this point I don't know what that actually is for method names :D

(@AaronBallman what is the correct case convention?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not saying this is the right thing to do, but what I intended for this PR was to keep the names the same as they were in the LLVM IR codegen implementation. A few of those names were out-of-date with the latest coding style. Maybe this is a good time to update them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think given this movement it is entirely reasonable to update to the current style


/// Returns true if the given constructor or destructor is one of the
/// kinds that the ABI says returns 'this' (only applies when called
/// non-virtually for destructors).
///
/// There currently is no way to indicate if a destructor returns 'this'
/// when called virtually, and code generation does not support the case.
/// Returns true if the given constructor or destructor is one of the
/// kinds that the ABI says returns 'this' (only applies when called
/// non-virtually for destructors).
///
/// There currently is no way to indicate if a destructor returns 'this'
/// when called virtually, and code generation does not support the case.
virtual bool HasThisReturn(GlobalDecl GD) const {
if (isa<CXXConstructorDecl>(GD.getDecl()) ||
(isa<CXXDestructorDecl>(GD.getDecl()) &&
GD.getDtorType() != Dtor_Deleting))
return constructorsAndDestructorsReturnThis();
return false;
}

/// Returns true if the given destructor type should be emitted as a linkonce
/// delegating thunk, regardless of whether the dtor is defined in this TU or
/// not.
virtual bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
CXXDtorType DT) const = 0;

protected:
virtual bool constructorsAndDestructorsReturnThis() const = 0;
};

} // namespace clang

#endif // LLVM_CLANG_CODEGENSHARED_CXXABISHARED_H
118 changes: 118 additions & 0 deletions clang/include/clang/CodeGenShared/ItaniumCXXABIShared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//===--- ItaniumCXXABIShared.h - Itanium C++ ABI Shared Base ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides a base class for Itanium C++ ABI implementations that can
// be shared between LLVM IR codegen and CIR codegen.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CODEGENSHARED_ITANIUMCXXABISHARED_H
#define LLVM_CLANG_CODEGENSHARED_ITANIUMCXXABISHARED_H

#include "clang/AST/DeclCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Basic/ABI.h"
#include <utility>

namespace clang {

template <typename BaseT> class ItaniumCXXABIShared : public BaseT {
protected:
/// Constructor forwarding to the base ABI class.
template <typename... Args>
ItaniumCXXABIShared(Args &&...args) : BaseT(std::forward<Args>(args)...) {}

public:
/// Determine whether there's something special about the rules of
/// the ABI tell us that 'this' is a complete object within the
/// given function. Obvious common logic like being defined on a
/// final class will have been taken care of by the caller.
bool isThisCompleteObject(GlobalDecl GD) const override {
// The Itanium ABI has separate complete-object vs. base-object
// variants of both constructors and destructors.
if (isa<CXXDestructorDecl>(GD.getDecl())) {
switch (GD.getDtorType()) {
case Dtor_Complete:
case Dtor_Deleting:
return true;

case Dtor_Base:
return false;

case Dtor_Comdat:
llvm_unreachable("emitting dtor comdat as function?");
case Dtor_Unified:
llvm_unreachable("emitting unified dtor as function?");
}
llvm_unreachable("bad dtor kind");
}
if (isa<CXXConstructorDecl>(GD.getDecl())) {
switch (GD.getCtorType()) {
case Ctor_Complete:
return true;

case Ctor_Base:
return false;

case Ctor_CopyingClosure:
case Ctor_DefaultClosure:
llvm_unreachable("closure ctors in Itanium ABI?");

case Ctor_Comdat:
llvm_unreachable("emitting ctor comdat as function?");

case Ctor_Unified:
llvm_unreachable("emitting unified ctor as function?");
}
llvm_unreachable("bad ctor kind");
}

// No other kinds.
return false;
}

bool NeedsVTTParameter(GlobalDecl GD) const override {
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());

// We don't have any virtual bases, just return early.
if (!MD->getParent()->getNumVBases())
return false;

// Check if we have a base constructor.
if (isa<CXXConstructorDecl>(MD) && GD.getCtorType() == Ctor_Base)
return true;

// Check if we have a base destructor.
if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base)
return true;

return false;
}

/// Returns true if the given destructor type should be emitted as a linkonce
/// delegating thunk, regardless of whether the dtor is defined in this TU or
/// not.
///
/// Itanium does not emit any destructor variant as an inline thunk.
/// Delegating may occur as an optimization, but all variants are either
/// emitted with external linkage or as linkonce if they are inline and used.
bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
CXXDtorType DT) const override {
return false;
}

/// Returns true if this ABI initializes vptrs in constructors/destructors.
/// For Itanium, this is always true.
bool doStructorsInitializeVPtrs(const CXXRecordDecl *VTableClass) override {
return true;
}
};

} // namespace clang

#endif // LLVM_CLANG_CODEGENSHARED_ITANIUMCXXABISHARED_H
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
// mismatch.
const CXXRecordDecl *record = type->getAsCXXRecordDecl();
bool canRegisterDestructor =
record && (!cgm.getCXXABI().hasThisReturn(
record && (!cgm.getCXXABI().HasThisReturn(
GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
cgm.getCXXABI().canCallMismatchedFunctionType());

Expand Down
52 changes: 9 additions & 43 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@
#include "CIRGenFunction.h"
#include "CIRGenModule.h"

#include "clang/AST/Mangle.h"
#include "clang/CodeGenShared/CXXABIShared.h"

namespace clang::CIRGen {

/// Implements C++ ABI-specific code generation functions.
class CIRGenCXXABI {
class CIRGenCXXABI : public CXXABIShared {
protected:
CIRGenModule &cgm;
std::unique_ptr<clang::MangleContext> mangleContext;

CIRGenCXXABI(CIRGenModule &cgm)
: CXXABIShared(cgm.getASTContext()), cgm(cgm) {}

virtual bool requiresArrayCookie(const CXXNewExpr *e);

bool constructorsAndDestructorsReturnThis() const override {
return cgm.getCodeGenOpts().CtorDtorReturnThis;
}

public:
// TODO(cir): make this protected when target-specific CIRGenCXXABIs are
// implemented.
CIRGenCXXABI(CIRGenModule &cgm)
: cgm(cgm), mangleContext(cgm.getASTContext().createMangleContext()) {}
virtual ~CIRGenCXXABI();

void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
Expand All @@ -62,17 +64,6 @@ class CIRGenCXXABI {
bool isRefCast, Address src) = 0;

public:
/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
struct AddedStructorArgCounts {
unsigned prefix = 0;
unsigned suffix = 0;
AddedStructorArgCounts() = default;
AddedStructorArgCounts(unsigned p, unsigned s) : prefix(p), suffix(s) {}
static AddedStructorArgCounts withPrefix(unsigned n) { return {n, 0}; }
static AddedStructorArgCounts withSuffix(unsigned n) { return {0, n}; }
};

/// Additional implicit arguments to add to the beginning (Prefix) and end
/// (Suffix) of a constructor / destructor arg list.
///
Expand Down Expand Up @@ -138,10 +129,6 @@ class CIRGenCXXABI {
return md->getParent();
}

/// Return whether the given global decl needs a VTT (virtual table table)
/// parameter.
virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; }

/// Perform ABI-specific "this" argument adjustment required prior to
/// a call of a virtual function.
/// The "VirtualCall" argument is true iff the call itself is virtual.
Expand Down Expand Up @@ -214,12 +201,6 @@ class CIRGenCXXABI {
/// this emits virtual table tables.
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *rd) = 0;

/// Returns true if the given destructor type should be emitted as a linkonce
/// delegating thunk, regardless of whether the dtor is defined in this TU or
/// not.
virtual bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
CXXDtorType dt) const = 0;

virtual cir::GlobalLinkageKind
getCXXDestructorLinkage(GVALinkage linkage, const CXXDestructorDecl *dtor,
CXXDtorType dt) const;
Expand Down Expand Up @@ -262,18 +243,6 @@ class CIRGenCXXABI {
virtual bool
doStructorsInitializeVPtrs(const clang::CXXRecordDecl *vtableClass) = 0;

/// Returns true if the given constructor or destructor is one of the kinds
/// that the ABI says returns 'this' (only applies when called non-virtually
/// for destructors).
///
/// There currently is no way to indicate if a destructor returns 'this' when
/// called virtually, and CIR generation does not support this case.
virtual bool hasThisReturn(clang::GlobalDecl gd) const { return false; }

virtual bool hasMostDerivedReturn(clang::GlobalDecl gd) const {
return false;
}

/// Returns true if the target allows calling a function through a pointer
/// with a different signature than the actual function (or equivalently,
/// bitcasting a function or function pointer to a different function type).
Expand All @@ -284,9 +253,6 @@ class CIRGenCXXABI {
/// for all calls.
virtual bool canCallMismatchedFunctionType() const { return true; }

/// Gets the mangle context.
clang::MangleContext &getMangleContext() { return *mangleContext; }

clang::ImplicitParamDecl *&getStructorImplicitParamDecl(CIRGenFunction &cgf) {
return cgf.cxxStructorImplicitParamDecl;
}
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) {
(passParams && md->isVariadic() ? RequiredArgs(argTypes.size())
: RequiredArgs::All);

CanQualType resultType = theCXXABI.hasThisReturn(gd) ? argTypes.front()
CanQualType resultType = theCXXABI.HasThisReturn(gd) ? argTypes.front()
: theCXXABI.hasMostDerivedReturn(gd)
? astContext.VoidPtrTy
: astContext.VoidTy;

assert(!theCXXABI.hasThisReturn(gd) &&
assert(!theCXXABI.HasThisReturn(gd) &&
"Please send PR with a test and remove this");

assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
Expand Down Expand Up @@ -350,7 +350,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXConstructorCall(
: RequiredArgs::All;

GlobalDecl gd(d, ctorKind);
if (theCXXABI.hasThisReturn(gd))
if (theCXXABI.HasThisReturn(gd))
cgm.errorNYI(d->getSourceRange(),
"arrangeCXXConstructorCall: hasThisReturn");
if (theCXXABI.hasMostDerivedReturn(gd))
Expand Down
Loading