Skip to content

Commit ecb3678

Browse files
authored
[CIR] Add implementation and support skeleton for maybeEmitThunks (#1905)
This PR adds to the implementation of `maybeEmitThunks` in `clang/lib/CIR/CodeGen/CIRGenVTables.cpp` . Newly declared/defined functions are ported from OG. Some missings are `Type::canLosslesslyBitCastTo` and `setDLLStorageClass`. No tests are added since the implementation is not finished yet.
1 parent f64b31b commit ecb3678

File tree

8 files changed

+172
-7
lines changed

8 files changed

+172
-7
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class CIRGenCXXABI {
230230
/// this emits virtual table tables.
231231
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;
232232

233+
virtual bool exportThunk() = 0;
234+
virtual void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD,
235+
bool ReturnAdjustment) = 0;
236+
233237
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
234238
QualType Ty) = 0;
235239
virtual CatchTypeInfo

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,14 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXMethodCall(
12961296
info, paramInfos, required);
12971297
}
12981298

1299+
const CIRGenFunctionInfo &
1300+
CIRGenTypes::arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md) {
1301+
assert(md->isVirtual() && "only methods have thunks");
1302+
CanQual<FunctionProtoType> FTP = GetFormalType(md);
1303+
CanQualType ArgTys[] = {DeriveThisType(md->getParent(), md)};
1304+
return arrangeCIRFunctionInfo(astContext.VoidTy, cir::FnInfoOpts::None,
1305+
ArgTys, FTP->getExtInfo(), {}, RequiredArgs(1));
1306+
}
12991307
/// Figure out the rules for calling a function with the given formal type using
13001308
/// the given arguments. The arguments are necessary because the function might
13011309
/// be unprototyped, in which case it's target-dependent in crazy ways.

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
227227
void emitVTableDefinitions(CIRGenVTables &CGVT,
228228
const CXXRecordDecl *RD) override;
229229
void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
230+
231+
void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD,
232+
bool ReturnAdjustment) override {
233+
if (ForVTable && !Thunk.hasLocalLinkage())
234+
Thunk.setLinkage(cir::GlobalLinkageKind::AvailableExternallyLinkage);
235+
const auto *ND = cast<NamedDecl>(GD.getDecl());
236+
CGM.setGVProperties(Thunk.getOperation(), ND);
237+
}
238+
239+
bool exportThunk() override { return true; }
230240
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
231241
QualType Ty) override;
232242
bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,7 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
30653065
// NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we
30663066
// represent them in dedicated ops. The correct attributes are ensured during
30673067
// translation to LLVM. Thus, we don't need to check for them here.
3068+
assert(!isThunk && "isThunk NYI");
30683069

30693070
if (!isIncompleteFunction) {
30703071
setCIRFunctionAttributes(globalDecl,
@@ -3100,8 +3101,6 @@ cir::FuncOp CIRGenModule::GetOrCreateCIRFunction(
31003101
StringRef mangledName, mlir::Type ty, GlobalDecl gd, bool forVTable,
31013102
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,
31023103
mlir::ArrayAttr extraAttrs) {
3103-
assert(!isThunk && "NYI");
3104-
31053104
const auto *d = gd.getDecl();
31063105

31073106
// Any attempts to use a MultiVersion function should result in retrieving the

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ class CIRGenModule : public CIRGenTypeCache {
359359
cir::GlobalViewAttr
360360
getAddrOfGlobalVarAttr(const VarDecl *D, mlir::Type Ty = {},
361361
ForDefinition_t IsForDefinition = NotForDefinition);
362-
362+
cir::FuncOp getAddrOfThunk(StringRef name, mlir::Type fnTy, GlobalDecl gd);
363363
/// Get a reference to the target of VD.
364364
mlir::Operation *getWeakRefReference(const ValueDecl *VD);
365365

clang/lib/CIR/CodeGen/CIRGenTypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ class CIRGenTypes {
245245
const clang::FunctionProtoType *type,
246246
RequiredArgs required, unsigned numPrefixArgs);
247247

248+
const CIRGenFunctionInfo &
249+
arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md);
248250
/// C++ methods have some special rules and also have implicit parameters.
249251
const CIRGenFunctionInfo &
250252
arrangeCXXMethodDeclaration(const clang::CXXMethodDecl *MD);

clang/lib/CIR/CodeGen/CIRGenVTables.cpp

Lines changed: 142 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ using namespace clang::CIRGen;
3535
CIRGenVTables::CIRGenVTables(CIRGenModule &CGM)
3636
: CGM(CGM), VTContext(CGM.getASTContext().getVTableContext()) {}
3737

38+
cir::FuncOp CIRGenModule::getAddrOfThunk(StringRef name, mlir::Type fnTy,
39+
GlobalDecl gd) {
40+
return GetOrCreateCIRFunction(name, fnTy, gd, /*ForVTable=*/true,
41+
/*DontDefer=*/true, /*IsThunk=*/true);
42+
}
43+
44+
static void setThunkProperties(CIRGenModule &cgm, const ThunkInfo &thunk,
45+
cir::FuncOp thunkFn, bool forVTable,
46+
GlobalDecl gd) {
47+
llvm_unreachable("NYI");
48+
}
49+
3850
static bool UseRelativeLayout(const CIRGenModule &CGM) {
3951
return CGM.getTarget().getCXXABI().isItaniumFamily() &&
4052
CGM.getItaniumVTableContext().isRelativeLayout();
@@ -474,8 +486,6 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
474486
auto r = shouldEmitAvailableExternallyVTable(*this, RD)
475487
? cir::GlobalLinkageKind::AvailableExternallyLinkage
476488
: cir::GlobalLinkageKind::ExternalLinkage;
477-
assert(r == cir::GlobalLinkageKind::ExternalLinkage &&
478-
"available external NYI");
479489
return r;
480490
}
481491

@@ -644,6 +654,134 @@ void CIRGenVTables::emitVTTDefinition(cir::GlobalOp VTT,
644654
assert(!cir::MissingFeatures::setComdat());
645655
}
646656
}
657+
static bool shouldEmitVTableThunk(CIRGenModule &CGM, const CXXMethodDecl *MD,
658+
bool IsUnprototyped, bool ForVTable) {
659+
// Always emit thunks in the MS C++ ABI. We cannot rely on other TUs to
660+
// provide thunks for us.
661+
if (CGM.getTarget().getCXXABI().isMicrosoft())
662+
return true;
663+
664+
// In the Itanium C++ ABI, vtable thunks are provided by TUs that provide
665+
// definitions of the main method. Therefore, emitting thunks with the vtable
666+
// is purely an optimization. Emit the thunk if optimizations are enabled and
667+
// all of the parameter types are complete.
668+
if (ForVTable)
669+
return CGM.getCodeGenOpts().OptimizationLevel && !IsUnprototyped;
670+
671+
// Always emit thunks along with the method definition.
672+
return true;
673+
}
674+
675+
cir::FuncOp CIRGenVTables::maybeEmitThunk(GlobalDecl GD,
676+
const ThunkInfo &ThunkAdjustments,
677+
bool ForVTable) {
678+
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
679+
SmallString<256> Name;
680+
MangleContext &MCtx = CGM.getCXXABI().getMangleContext();
681+
682+
llvm::raw_svector_ostream Out(Name);
683+
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
684+
MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), ThunkAdjustments,
685+
/* elideOverrideInfo */ false, Out);
686+
} else
687+
MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ false, Out);
688+
689+
if (CGM.getASTContext().useAbbreviatedThunkName(GD, Name.str())) {
690+
Name = "";
691+
if (const CXXDestructorDecl *dd = dyn_cast<CXXDestructorDecl>(MD))
692+
MCtx.mangleCXXDtorThunk(dd, GD.getDtorType(), ThunkAdjustments,
693+
/* elideOverrideInfo */ true, Out);
694+
else
695+
MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ true, Out);
696+
}
697+
698+
cir::FuncType ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD);
699+
cir::FuncOp Thunk = CGM.getAddrOfThunk(Name, ThunkVTableTy, GD);
700+
701+
// If we don't need to emit a definition, return this declaration as is.
702+
bool IsUnprototyped = !CGM.getTypes().isFuncTypeConvertible(
703+
MD->getType()->castAs<FunctionType>());
704+
if (!shouldEmitVTableThunk(CGM, MD, IsUnprototyped, ForVTable))
705+
return Thunk;
706+
707+
// Arrange a function prototype appropriate for a function definition. In some
708+
// cases in the MS ABI, we may need to build an unprototyped musttail thunk.
709+
const CIRGenFunctionInfo &FnInfo =
710+
IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk(MD)
711+
: CGM.getTypes().arrangeGlobalDeclaration(GD);
712+
cir::FuncType ThunkFnTy = CGM.getTypes().GetFunctionType(FnInfo);
713+
714+
// This is to replace OG's casting to a function, keeping it here to
715+
// streamline the 1-to-1 mapping from OG starting below
716+
cir::FuncOp ThunkFn = Thunk;
717+
if (Thunk.getFunctionType() != ThunkFnTy) {
718+
cir::FuncOp OldThunkFn = ThunkFn;
719+
720+
assert(OldThunkFn.isDeclaration() && "Shouldn't replace non-declaration");
721+
722+
// Remove the name from the old thunk function and get a new thunk.
723+
OldThunkFn.setName(StringRef());
724+
auto thunkFn =
725+
cir::FuncOp::create(CGM.getBuilder(), Thunk->getLoc(), Name.str(),
726+
ThunkFnTy, cir::GlobalLinkageKind::ExternalLinkage);
727+
CGM.setCIRFunctionAttributes(MD, FnInfo, thunkFn, /*IsThunk=*/false);
728+
729+
if (!OldThunkFn->use_empty()) {
730+
OldThunkFn->replaceAllUsesWith(thunkFn);
731+
}
732+
733+
// Remove the old thunk.
734+
OldThunkFn->erase();
735+
}
736+
bool ABIHasKeyFunctions = CGM.getTarget().getCXXABI().hasKeyFunctions();
737+
bool UseAvailableExternallyLinkage = ForVTable && ABIHasKeyFunctions;
738+
// If the type of the underlying GlobalValue is wrong, we'll have to replace
739+
// it. It should be a declaration.
740+
if (!ThunkFn.isDeclaration()) {
741+
if (!ABIHasKeyFunctions || UseAvailableExternallyLinkage) {
742+
// There is already a thunk emitted for this function, do nothing.
743+
return ThunkFn;
744+
}
745+
746+
setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD);
747+
return ThunkFn;
748+
}
749+
if (IsUnprototyped)
750+
ThunkFn->setAttr("thunk", mlir::UnitAttr::get(&CGM.getMLIRContext()));
751+
752+
CGM.setCIRFunctionAttributesForDefinition(GD.getDecl(), ThunkFn);
753+
//
754+
// Thunks for variadic methods are special because in general variadic
755+
// arguments cannot be perfectly forwarded. In the general case, clang
756+
// implements such thunks by cloning the original function body. However, for
757+
// thunks with no return adjustment on targets that support musttail, we can
758+
// use musttail to perfectly forward the variadic arguments.
759+
bool ShouldCloneVarArgs = false;
760+
if (!IsUnprototyped && ThunkFn.getFunctionType().isVarArg()) {
761+
ShouldCloneVarArgs = true;
762+
if (ThunkAdjustments.Return.isEmpty()) {
763+
switch (CGM.getTriple().getArch()) {
764+
case llvm::Triple::x86_64:
765+
case llvm::Triple::x86:
766+
case llvm::Triple::aarch64:
767+
ShouldCloneVarArgs = false;
768+
break;
769+
default:
770+
break;
771+
}
772+
}
773+
}
774+
if (ShouldCloneVarArgs) {
775+
if (UseAvailableExternallyLinkage)
776+
return ThunkFn;
777+
llvm_unreachable("NYI method, see OG GenerateVarArgsThunk");
778+
} else {
779+
llvm_unreachable("NYI method, see OG generateThunk");
780+
}
781+
782+
setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD);
783+
return ThunkFn;
784+
}
647785

648786
void CIRGenVTables::emitThunks(GlobalDecl GD) {
649787
const CXXMethodDecl *MD =
@@ -659,8 +797,8 @@ void CIRGenVTables::emitThunks(GlobalDecl GD) {
659797
if (!ThunkInfoVector)
660798
return;
661799

662-
for ([[maybe_unused]] const ThunkInfo &Thunk : *ThunkInfoVector)
663-
llvm_unreachable("NYI");
800+
for (const ThunkInfo &Thunk : *ThunkInfoVector)
801+
maybeEmitThunk(GD, Thunk, /*ForVTable=*/false);
664802
}
665803

666804
bool CIRGenModule::AlwaysHasLTOVisibilityPublic(const CXXRecordDecl *RD) {

clang/lib/CIR/CodeGen/CIRGenVTables.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ class CIRGenVTables {
5757
/// Cache for the deleted virtual member call function.
5858
cir::FuncOp DeletedVirtualFn = nullptr;
5959

60+
/// Get the address of a thunk and emit it if necessary.
61+
cir::FuncOp maybeEmitThunk(GlobalDecl gd, const ThunkInfo &thunkAdjustments,
62+
bool forVTable);
63+
6064
void addVTableComponent(ConstantArrayBuilder &builder,
6165
const VTableLayout &layout, unsigned componentIndex,
6266
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,

0 commit comments

Comments
 (0)