Skip to content

Commit 7c2d574

Browse files
committed
[CIR] Implement non-virtual thunk generation for multiple inheritance
This patch implements full support for generating non-virtual thunks in ClangIR to handle C++ multiple inheritance scenarios where the same virtual function must be accessible through different base class pointers with different this-pointer offsets. ghstack-source-id: 2ff8aae Pull-Request: #2029
1 parent 4037fb6 commit 7c2d574

19 files changed

+1355
-90
lines changed

clang/include/clang/CIR/Interfaces/CIROpInterfaces.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,33 @@ namespace cir {} // namespace cir
2626

2727
namespace cir {} // namespace cir
2828

29+
// Casting specializations for CIRGlobalValueInterface to concrete types.
30+
// OpInterfaces don't support casting to concrete types with reference return
31+
// types, so we specialize CastInfo to return by value instead.
32+
namespace llvm {
33+
template <typename To>
34+
struct CastInfo<
35+
To, cir::CIRGlobalValueInterface,
36+
std::enable_if_t<!std::is_same<To, cir::CIRGlobalValueInterface>::value>> {
37+
using CastReturnType = To;
38+
39+
static inline bool isPossible(cir::CIRGlobalValueInterface interface) {
40+
return llvm::isa<To>(interface.getOperation());
41+
}
42+
43+
static inline CastReturnType doCast(cir::CIRGlobalValueInterface interface) {
44+
return llvm::cast<To>(interface.getOperation());
45+
}
46+
47+
static inline CastReturnType castFailed() { return CastReturnType(nullptr); }
48+
49+
static inline CastReturnType
50+
doCastIfPossible(cir::CIRGlobalValueInterface interface) {
51+
if (!isPossible(interface))
52+
return castFailed();
53+
return doCast(interface);
54+
}
55+
};
56+
} // namespace llvm
57+
2958
#endif // MLIR_INTERFACES_CIR_OP_H_

clang/include/clang/CIR/Interfaces/CIROpInterfaces.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ let cppNamespace = "::cir" in {
146146
}]
147147
>,
148148
InterfaceMethod<"",
149+
"void", "setGlobalVisibility", (ins "cir::VisibilityKind":$val), [{}],
150+
/*defaultImplementation=*/[{
151+
$_op.setGlobalVisibility(val);
152+
}]
153+
>,
154+
InterfaceMethod<"",
155+
"void", "setLinkage", (ins "cir::GlobalLinkageKind":$val), [{}],
156+
/*defaultImplementation=*/[{
157+
$_op.setLinkage(val);
158+
}]
159+
>,
160+
InterfaceMethod<"",
149161
"bool", "isDSOLocal", (ins), [{}],
150162
/*defaultImplementation=*/[{
151163
return $_op.getDsoLocal();

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ struct MissingFeatures {
193193
// ABIInfo queries.
194194
static bool useTargetLoweringABIInfo() { return false; }
195195
static bool isEmptyFieldForLayout() { return false; }
196+
static bool ABIArgInfo() { return false; }
196197

197198
// Misc
198199
static bool cacheRecordLayouts() { return false; }
@@ -482,6 +483,8 @@ struct MissingFeatures {
482483
static bool skipTempCopy() { return false; }
483484

484485
static bool dataLayoutPtrHandlingBasedOnLangAS() { return false; }
486+
487+
static bool tailCall() { return false; }
485488
};
486489

487490
} // namespace cir

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,11 @@ bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *E) {
104104

105105
return E->getAllocatedType().isDestructedType();
106106
}
107+
108+
void CIRGenCXXABI::emitReturnFromThunk(CIRGenFunction &cgf, RValue rv,
109+
QualType resultType) {
110+
assert(!cgf.hasAggregateEvaluationKind(resultType) &&
111+
"cannot handle aggregates");
112+
auto loc = cgf.getBuilder().getUnknownLoc();
113+
cgf.emitReturnOfRValue(loc, rv, resultType);
114+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,28 @@ class CIRGenCXXABI {
234234
virtual void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD,
235235
bool ReturnAdjustment) = 0;
236236

237+
/// Perform adjustment on the this pointer for a thunk.
238+
/// Returns the adjusted this pointer value.
239+
virtual mlir::Value
240+
performThisAdjustment(CIRGenFunction &cgf, Address thisAddr,
241+
const CXXRecordDecl *unadjustedClass,
242+
const ThunkInfo &ti) = 0;
243+
244+
/// Perform adjustment on a return pointer for a thunk (covariant returns).
245+
/// Returns the adjusted return pointer value.
246+
virtual mlir::Value
247+
performReturnAdjustment(CIRGenFunction &cgf, Address ret,
248+
const CXXRecordDecl *unadjustedClass,
249+
const ReturnAdjustment &ra) = 0;
250+
251+
virtual void adjustCallArgsForDestructorThunk(CIRGenFunction &cgf,
252+
GlobalDecl globalDecl,
253+
CallArgList &callArgs) {}
254+
255+
/// Emit a return from a thunk.
256+
virtual void emitReturnFromThunk(CIRGenFunction &cgf, RValue rv,
257+
QualType resultType);
258+
237259
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
238260
QualType Ty) = 0;
239261
virtual CatchTypeInfo

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
765765
// Create a scope in the symbol table to hold variable declarations.
766766
SymTableScopeTy varScope(symbolTable);
767767
// Compiler synthetized functions might have invalid slocs...
768-
auto bSrcLoc = fd->getBody()->getBeginLoc();
769-
auto eSrcLoc = fd->getBody()->getEndLoc();
768+
auto bSrcLoc =
769+
(fd && fd->getBody()) ? fd->getBody()->getBeginLoc() : SourceLocation();
770+
auto eSrcLoc =
771+
(fd && fd->getBody()) ? fd->getBody()->getEndLoc() : SourceLocation();
770772
auto unknownLoc = builder.getUnknownLoc();
771773

772774
auto fnBeginLoc = bSrcLoc.isValid() ? getLoc(bSrcLoc) : unknownLoc;
@@ -1158,11 +1160,11 @@ void CIRGenFunction::StartFunction(GlobalDecl gd, QualType retTy,
11581160
llvm_unreachable("NYI");
11591161

11601162
// Apply xray attributes to the function (as a string, for now)
1161-
if (d->getAttr<XRayInstrumentAttr>()) {
1163+
if (d && d->getAttr<XRayInstrumentAttr>()) {
11621164
assert(!cir::MissingFeatures::xray());
11631165
}
11641166

1165-
if (ShouldXRayInstrumentFunction()) {
1167+
if (d && ShouldXRayInstrumentFunction()) {
11661168
assert(!cir::MissingFeatures::xray());
11671169
}
11681170

@@ -1365,12 +1367,15 @@ void CIRGenFunction::StartFunction(GlobalDecl gd, QualType retTy,
13651367

13661368
// Location of the store to the param storage tracked as beginning of
13671369
// the function body.
1368-
auto fnBodyBegin = getLoc(fd->getBody()->getBeginLoc());
1370+
auto fnBodyBegin = (fd && fd->getBody())
1371+
? getLoc(fd->getBody()->getBeginLoc())
1372+
: getLoc(Loc);
13691373
builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addr);
13701374
}
13711375
assert(builder.getInsertionBlock() && "Should be valid");
13721376

1373-
auto fnEndLoc = getLoc(fd->getBody()->getEndLoc());
1377+
auto fnEndLoc = (fd && fd->getBody()) ? getLoc(fd->getBody()->getEndLoc())
1378+
: getLoc(Loc);
13741379

13751380
// When the current function is not void, create an address to store the
13761381
// result value.

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,24 @@ class CIRGenFunction : public CIRGenTypeCache {
21312131

21322132
void emitDestructorBody(FunctionArgList &Args);
21332133

2134+
/// Generate a thunk for the given method.
2135+
void generateThunk(cir::FuncOp fn, const CIRGenFunctionInfo &fnInfo,
2136+
GlobalDecl gd, const ThunkInfo &thunk,
2137+
bool isUnprototyped);
2138+
2139+
void startThunk(cir::FuncOp fn, GlobalDecl gd,
2140+
const CIRGenFunctionInfo &fnInfo, bool isUnprototyped);
2141+
2142+
void emitCallAndReturnForThunk(cir::FuncOp callee, const ThunkInfo *thunk,
2143+
bool isUnprototyped);
2144+
2145+
/// Finish thunk generation.
2146+
void finishThunk();
2147+
2148+
/// Emit a musttail call for a thunk with a potentially adjusted this pointer.
2149+
void emitMustTailThunk(GlobalDecl gd, mlir::Value adjustedThisPtr,
2150+
cir::FuncOp callee);
2151+
21342152
mlir::LogicalResult emitDoStmt(const clang::DoStmt &S);
21352153

21362154
mlir::Value emitDynamicCast(Address ThisAddr, const CXXDynamicCastExpr *DCE);

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,19 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
239239
if (ForVTable && !Thunk.hasLocalLinkage())
240240
Thunk.setLinkage(cir::GlobalLinkageKind::AvailableExternallyLinkage);
241241
const auto *ND = cast<NamedDecl>(GD.getDecl());
242-
CGM.setGVProperties(Thunk.getOperation(), ND);
242+
CGM.setGVProperties(Thunk, ND);
243243
}
244244

245245
bool exportThunk() override { return true; }
246+
247+
mlir::Value performThisAdjustment(CIRGenFunction &cgf, Address thisAddr,
248+
const CXXRecordDecl *unadjustedClass,
249+
const ThunkInfo &ti) override;
250+
251+
mlir::Value performReturnAdjustment(CIRGenFunction &cgf, Address ret,
252+
const CXXRecordDecl *unadjustedClass,
253+
const ReturnAdjustment &ra) override;
254+
246255
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
247256
QualType Ty) override;
248257
bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
@@ -2144,6 +2153,63 @@ mlir::Attribute CIRGenItaniumCXXABI::getAddrOfRTTIDescriptor(mlir::Location loc,
21442153
return CIRGenItaniumRTTIBuilder(*this, CGM).BuildTypeInfo(loc, Ty);
21452154
}
21462155

2156+
static mlir::Value performTypeAdjustment(CIRGenFunction &cgf,
2157+
Address initialPtr,
2158+
const CXXRecordDecl *unadjustedClass,
2159+
int64_t nonVirtualAdjustment,
2160+
int64_t virtualAdjustment,
2161+
bool isReturnAdjustment) {
2162+
if (!nonVirtualAdjustment && !virtualAdjustment)
2163+
return initialPtr.getPointer();
2164+
2165+
auto &builder = cgf.getBuilder();
2166+
auto loc = builder.getUnknownLoc();
2167+
auto i8PtrTy = builder.getUInt8PtrTy();
2168+
mlir::Value v = builder.createBitcast(initialPtr.getPointer(), i8PtrTy);
2169+
2170+
// In a base-to-derived cast, the non-virtual adjustment is applied first.
2171+
if (nonVirtualAdjustment && !isReturnAdjustment) {
2172+
auto offsetConst = builder.getSInt64(nonVirtualAdjustment, loc);
2173+
v = builder.create<cir::PtrStrideOp>(loc, i8PtrTy, v, offsetConst);
2174+
}
2175+
2176+
// Perform the virtual adjustment if we have one.
2177+
mlir::Value resultPtr;
2178+
if (virtualAdjustment) {
2179+
llvm_unreachable("Virtual adjustment NYI - requires vtable offset lookup");
2180+
} else {
2181+
resultPtr = v;
2182+
}
2183+
2184+
// In a derived-to-base conversion, the non-virtual adjustment is
2185+
// applied second.
2186+
if (nonVirtualAdjustment && isReturnAdjustment) {
2187+
auto offsetConst = builder.getSInt64(nonVirtualAdjustment, loc);
2188+
resultPtr =
2189+
builder.create<cir::PtrStrideOp>(loc, i8PtrTy, resultPtr, offsetConst);
2190+
}
2191+
2192+
// Cast back to original pointer type
2193+
return builder.createBitcast(resultPtr, initialPtr.getType());
2194+
}
2195+
2196+
mlir::Value CIRGenItaniumCXXABI::performThisAdjustment(
2197+
CIRGenFunction &cgf, Address thisAddr, const CXXRecordDecl *unadjustedClass,
2198+
const ThunkInfo &ti) {
2199+
return performTypeAdjustment(cgf, thisAddr, unadjustedClass,
2200+
ti.This.NonVirtual,
2201+
ti.This.Virtual.Itanium.VCallOffsetOffset,
2202+
/*IsReturnAdjustment=*/false);
2203+
}
2204+
2205+
mlir::Value CIRGenItaniumCXXABI::performReturnAdjustment(
2206+
CIRGenFunction &cgf, Address ret, const CXXRecordDecl *unadjustedClass,
2207+
const ReturnAdjustment &ra) {
2208+
return performTypeAdjustment(cgf, ret, unadjustedClass, ra.NonVirtual,
2209+
ra.Virtual.Itanium.VBaseOffsetOffset,
2210+
/*IsReturnAdjustment=*/true);
2211+
}
2212+
21472213
void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &CGVT,
21482214
const CXXRecordDecl *RD) {
21492215
auto VTable = getAddrOfVTable(RD, CharUnits());

0 commit comments

Comments
 (0)