Skip to content
Merged
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
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ class CIRGenCXXABI {
/// this emits virtual table tables.
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;

virtual bool exportThunk() = 0;
virtual void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD,
bool ReturnAdjustment) = 0;

virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType Ty) = 0;
virtual CatchTypeInfo
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,14 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXMethodCall(
info, paramInfos, required);
}

const CIRGenFunctionInfo &
CIRGenTypes::arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md) {
assert(md->isVirtual() && "only methods have thunks");
CanQual<FunctionProtoType> FTP = GetFormalType(md);
CanQualType ArgTys[] = {DeriveThisType(md->getParent(), md)};
return arrangeCIRFunctionInfo(astContext.VoidTy, cir::FnInfoOpts::None,
ArgTys, FTP->getExtInfo(), {}, RequiredArgs(1));
}
/// Figure out the rules for calling a function with the given formal type using
/// the given arguments. The arguments are necessary because the function might
/// be unprototyped, in which case it's target-dependent in crazy ways.
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
void emitVTableDefinitions(CIRGenVTables &CGVT,
const CXXRecordDecl *RD) override;
void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;

void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD,
bool ReturnAdjustment) override {
if (ForVTable && !Thunk.hasLocalLinkage())
Thunk.setLinkage(cir::GlobalLinkageKind::AvailableExternallyLinkage);
const auto *ND = cast<NamedDecl>(GD.getDecl());
CGM.setGVProperties(Thunk.getOperation(), ND);
}

bool exportThunk() override { return true; }
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType Ty) override;
bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3065,6 +3065,7 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
// NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we
// represent them in dedicated ops. The correct attributes are ensured during
// translation to LLVM. Thus, we don't need to check for them here.
assert(!isThunk && "isThunk NYI");

if (!isIncompleteFunction) {
setCIRFunctionAttributes(globalDecl,
Expand Down Expand Up @@ -3100,8 +3101,6 @@ cir::FuncOp CIRGenModule::GetOrCreateCIRFunction(
StringRef mangledName, mlir::Type ty, GlobalDecl gd, bool forVTable,
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,
mlir::ArrayAttr extraAttrs) {
assert(!isThunk && "NYI");

const auto *d = gd.getDecl();

// Any attempts to use a MultiVersion function should result in retrieving the
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ class CIRGenModule : public CIRGenTypeCache {
cir::GlobalViewAttr
getAddrOfGlobalVarAttr(const VarDecl *D, mlir::Type Ty = {},
ForDefinition_t IsForDefinition = NotForDefinition);

cir::FuncOp getAddrOfThunk(StringRef name, mlir::Type fnTy, GlobalDecl gd);
/// Get a reference to the target of VD.
mlir::Operation *getWeakRefReference(const ValueDecl *VD);

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ class CIRGenTypes {
const clang::FunctionProtoType *type,
RequiredArgs required, unsigned numPrefixArgs);

const CIRGenFunctionInfo &
arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md);
/// C++ methods have some special rules and also have implicit parameters.
const CIRGenFunctionInfo &
arrangeCXXMethodDeclaration(const clang::CXXMethodDecl *MD);
Expand Down
146 changes: 142 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ using namespace clang::CIRGen;
CIRGenVTables::CIRGenVTables(CIRGenModule &CGM)
: CGM(CGM), VTContext(CGM.getASTContext().getVTableContext()) {}

cir::FuncOp CIRGenModule::getAddrOfThunk(StringRef name, mlir::Type fnTy,
GlobalDecl gd) {
return GetOrCreateCIRFunction(name, fnTy, gd, /*ForVTable=*/true,
/*DontDefer=*/true, /*IsThunk=*/true);
}

static void setThunkProperties(CIRGenModule &cgm, const ThunkInfo &thunk,
cir::FuncOp thunkFn, bool forVTable,
GlobalDecl gd) {
llvm_unreachable("NYI");
}

static bool UseRelativeLayout(const CIRGenModule &CGM) {
return CGM.getTarget().getCXXABI().isItaniumFamily() &&
CGM.getItaniumVTableContext().isRelativeLayout();
Expand Down Expand Up @@ -474,8 +486,6 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
auto r = shouldEmitAvailableExternallyVTable(*this, RD)
? cir::GlobalLinkageKind::AvailableExternallyLinkage
: cir::GlobalLinkageKind::ExternalLinkage;
assert(r == cir::GlobalLinkageKind::ExternalLinkage &&
"available external NYI");
return r;
}

Expand Down Expand Up @@ -644,6 +654,134 @@ void CIRGenVTables::emitVTTDefinition(cir::GlobalOp VTT,
assert(!cir::MissingFeatures::setComdat());
}
}
static bool shouldEmitVTableThunk(CIRGenModule &CGM, const CXXMethodDecl *MD,
bool IsUnprototyped, bool ForVTable) {
// Always emit thunks in the MS C++ ABI. We cannot rely on other TUs to
// provide thunks for us.
if (CGM.getTarget().getCXXABI().isMicrosoft())
return true;

// In the Itanium C++ ABI, vtable thunks are provided by TUs that provide
// definitions of the main method. Therefore, emitting thunks with the vtable
// is purely an optimization. Emit the thunk if optimizations are enabled and
// all of the parameter types are complete.
if (ForVTable)
return CGM.getCodeGenOpts().OptimizationLevel && !IsUnprototyped;

// Always emit thunks along with the method definition.
return true;
}

cir::FuncOp CIRGenVTables::maybeEmitThunk(GlobalDecl GD,
const ThunkInfo &ThunkAdjustments,
bool ForVTable) {
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
SmallString<256> Name;
MangleContext &MCtx = CGM.getCXXABI().getMangleContext();

llvm::raw_svector_ostream Out(Name);
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), ThunkAdjustments,
/* elideOverrideInfo */ false, Out);
} else
MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ false, Out);

if (CGM.getASTContext().useAbbreviatedThunkName(GD, Name.str())) {
Name = "";
if (const CXXDestructorDecl *dd = dyn_cast<CXXDestructorDecl>(MD))
MCtx.mangleCXXDtorThunk(dd, GD.getDtorType(), ThunkAdjustments,
/* elideOverrideInfo */ true, Out);
else
MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ true, Out);
}

cir::FuncType ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD);
cir::FuncOp Thunk = CGM.getAddrOfThunk(Name, ThunkVTableTy, GD);

// If we don't need to emit a definition, return this declaration as is.
bool IsUnprototyped = !CGM.getTypes().isFuncTypeConvertible(
MD->getType()->castAs<FunctionType>());
if (!shouldEmitVTableThunk(CGM, MD, IsUnprototyped, ForVTable))
return Thunk;

// Arrange a function prototype appropriate for a function definition. In some
// cases in the MS ABI, we may need to build an unprototyped musttail thunk.
const CIRGenFunctionInfo &FnInfo =
IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk(MD)
: CGM.getTypes().arrangeGlobalDeclaration(GD);
cir::FuncType ThunkFnTy = CGM.getTypes().GetFunctionType(FnInfo);

// This is to replace OG's casting to a function, keeping it here to
// streamline the 1-to-1 mapping from OG starting below
cir::FuncOp ThunkFn = Thunk;
if (Thunk.getFunctionType() != ThunkFnTy) {
cir::FuncOp OldThunkFn = ThunkFn;

assert(OldThunkFn.isDeclaration() && "Shouldn't replace non-declaration");

// Remove the name from the old thunk function and get a new thunk.
OldThunkFn.setName(StringRef());
auto thunkFn =
cir::FuncOp::create(CGM.getBuilder(), Thunk->getLoc(), Name.str(),
ThunkFnTy, cir::GlobalLinkageKind::ExternalLinkage);
CGM.setCIRFunctionAttributes(MD, FnInfo, thunkFn, /*IsThunk=*/false);

if (!OldThunkFn->use_empty()) {
OldThunkFn->replaceAllUsesWith(thunkFn);
}

// Remove the old thunk.
OldThunkFn->erase();
}
bool ABIHasKeyFunctions = CGM.getTarget().getCXXABI().hasKeyFunctions();
bool UseAvailableExternallyLinkage = ForVTable && ABIHasKeyFunctions;
// If the type of the underlying GlobalValue is wrong, we'll have to replace
// it. It should be a declaration.
if (!ThunkFn.isDeclaration()) {
if (!ABIHasKeyFunctions || UseAvailableExternallyLinkage) {
// There is already a thunk emitted for this function, do nothing.
return ThunkFn;
}

setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD);
return ThunkFn;
}
if (IsUnprototyped)
ThunkFn->setAttr("thunk", mlir::UnitAttr::get(&CGM.getMLIRContext()));

CGM.setCIRFunctionAttributesForDefinition(GD.getDecl(), ThunkFn);
//
// Thunks for variadic methods are special because in general variadic
// arguments cannot be perfectly forwarded. In the general case, clang
// implements such thunks by cloning the original function body. However, for
// thunks with no return adjustment on targets that support musttail, we can
// use musttail to perfectly forward the variadic arguments.
bool ShouldCloneVarArgs = false;
if (!IsUnprototyped && ThunkFn.getFunctionType().isVarArg()) {
ShouldCloneVarArgs = true;
if (ThunkAdjustments.Return.isEmpty()) {
switch (CGM.getTriple().getArch()) {
case llvm::Triple::x86_64:
case llvm::Triple::x86:
case llvm::Triple::aarch64:
ShouldCloneVarArgs = false;
break;
default:
break;
}
}
}
if (ShouldCloneVarArgs) {
if (UseAvailableExternallyLinkage)
return ThunkFn;
llvm_unreachable("NYI method, see OG GenerateVarArgsThunk");
} else {
llvm_unreachable("NYI method, see OG generateThunk");
}

setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD);
return ThunkFn;
}

void CIRGenVTables::emitThunks(GlobalDecl GD) {
const CXXMethodDecl *MD =
Expand All @@ -659,8 +797,8 @@ void CIRGenVTables::emitThunks(GlobalDecl GD) {
if (!ThunkInfoVector)
return;

for ([[maybe_unused]] const ThunkInfo &Thunk : *ThunkInfoVector)
llvm_unreachable("NYI");
for (const ThunkInfo &Thunk : *ThunkInfoVector)
maybeEmitThunk(GD, Thunk, /*ForVTable=*/false);
}

bool CIRGenModule::AlwaysHasLTOVisibilityPublic(const CXXRecordDecl *RD) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenVTables.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class CIRGenVTables {
/// Cache for the deleted virtual member call function.
cir::FuncOp DeletedVirtualFn = nullptr;

/// Get the address of a thunk and emit it if necessary.
cir::FuncOp maybeEmitThunk(GlobalDecl gd, const ThunkInfo &thunkAdjustments,
bool forVTable);

void addVTableComponent(ConstantArrayBuilder &builder,
const VTableLayout &layout, unsigned componentIndex,
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,
Expand Down
Loading