Skip to content

Commit 98a979e

Browse files
committed
[CIR] Implement non-virtual thunk generation for multiple inheritance
This patch implements complete support for generating non-virtual thunks in ClangIR for multiple inheritance scenarios. Thunks are small adapter functions that adjust the 'this' pointer and forward calls to the actual target function when virtual functions are overridden in derived classes with multiple base classes. Implementation details: 1. **CIRGenVTables.cpp**: - Implemented `generateThunk()` to create thunk function bodies with proper scope management (SymTableScopeTy + LexicalScope) - Implemented `emitCallAndReturnForThunk()` to emit the call sequence - Implemented `setThunkProperties()` to set linkage and properties - Fixed VTable double-insertion bug in vtable emission 2. **CIRGenCXXABI.h/cpp**: - Added virtual methods `performThisAdjustment()` and `performReturnAdjustment()` to base class - Implemented `EmitReturnFromThunk()` default implementation 3. **CIRGenItaniumCXXABI.cpp**: - Implemented `performThisAdjustment()` for non-virtual adjustments using cir.ptr_stride operations - Implemented `performReturnAdjustment()` for covariant return types - Virtual adjustments marked as NYI with llvm_unreachable 4. **CIRGenFunction.h/cpp**: - Added `startThunk()`, `generateThunk()`, `emitCallAndReturnForThunk()`, `finishThunk()`, and `emitMustTailThunk()` methods - Implemented `finishThunk()` to properly cleanup after thunk generation 5. **CIRGenModule.cpp**: - Removed assertion blocking thunks in `setFunctionAttributes()` - Fixed insertion point management in vtable creation 6. **CIRGenCXX.cpp**: - Added null checks for GlobalDecl in XRay attributes for thunks 7. **Test Updates**: - Added `clang/test/CIR/CodeGen/vtable-thunk.cpp` for CIR generation - Added `clang/test/CIR/Lowering/vtable-thunk.cpp` for LLVM lowering - Fixed offset expectations (16 bytes for x86_64 layout) - Reordered FileCheck patterns to match actual CIR output order This implementation closely follows CodeGen's approach in CodeGenVTables.cpp and CodeGenItaniumCXXABI.cpp, with adaptations for CIR's MLIR-based infrastructure (e.g., location requirements, operation builders, scope management). Limitations: - Virtual adjustments (vcall offsets) not yet implemented - Musttail thunks for variadic functions not yet implemented - Return adjustments implemented but not extensively tested Test Plan: ``` bin/llvm-lit -v ../../clang/test/CIR/CodeGen/vtable-thunk.cpp bin/llvm-lit -v ../../clang/test/CIR/Lowering/vtable-thunk.cpp ``` Both tests pass, verifying: - CIR generation with correct thunk mangling and operations - Vtable contains thunk references - Lowering to LLVM IR produces correct getelementptr operations ghstack-source-id: eb03968 Pull-Request: #2006
1 parent 26199f0 commit 98a979e

15 files changed

+1012
-35
lines changed

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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,24 @@ 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+
/// Emit a return from a thunk.
252+
virtual void emitReturnFromThunk(CIRGenFunction &cgf, RValue rv,
253+
QualType resultType);
254+
237255
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
238256
QualType Ty) = 0;
239257
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: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,15 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
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());

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -826,14 +826,22 @@ cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm,
826826
// Some global emissions are triggered while emitting a function, e.g.
827827
// void s() { const char *s = "yolo"; ... }
828828
//
829-
// Be sure to insert global before the current function
829+
// Save the current function context for later insertion logic
830830
auto *curCGF = cgm.getCurrCIRGenFun();
831-
if (curCGF)
832-
builder.setInsertionPoint(curCGF->CurFn);
831+
832+
// Clear insertion point to prevent auto-insertion by create()
833+
// We'll manually insert at the correct location below
834+
builder.clearInsertionPoint();
833835

834836
g = cir::GlobalOp::create(builder, loc, name, t, isConstant, linkage,
835837
addrSpace);
836-
if (!curCGF) {
838+
839+
// Manually insert at the correct location
840+
if (curCGF) {
841+
// Insert before the current function being generated
842+
cgm.getModule().insert(mlir::Block::iterator(curCGF->CurFn), g);
843+
} else {
844+
// Insert at specified point or at end of module
837845
if (insertPoint)
838846
cgm.getModule().insert(insertPoint, g);
839847
else
@@ -2675,6 +2683,11 @@ void CIRGenModule::setDSOLocal(mlir::Operation *op) const {
26752683
}
26762684
}
26772685

2686+
void CIRGenModule::setGVProperties(mlir::Operation *op, GlobalDecl gd) const {
2687+
assert(!cir::MissingFeatures::setDLLImportDLLExport());
2688+
setGVPropertiesAux(op, dyn_cast<NamedDecl>(gd.getDecl()));
2689+
}
2690+
26782691
void CIRGenModule::setGVProperties(mlir::Operation *op,
26792692
const NamedDecl *d) const {
26802693
assert(!cir::MissingFeatures::setDLLImportDLLExport());
@@ -2710,10 +2723,12 @@ cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
27102723
// Some global emissions are triggered while emitting a function, e.g.
27112724
// void s() { x.method() }
27122725
//
2713-
// Be sure to insert a new function before a current one.
2726+
// Save the current function context for later insertion logic
27142727
auto *curCGF = getCurrCIRGenFun();
2715-
if (curCGF)
2716-
builder.setInsertionPoint(curCGF->CurFn);
2728+
2729+
// Clear insertion point to prevent auto-insertion by create()
2730+
// We'll manually insert at the correct location below
2731+
builder.clearInsertionPoint();
27172732

27182733
f = cir::FuncOp::create(builder, loc, name, ty);
27192734

@@ -2739,8 +2754,14 @@ cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
27392754
// Set the special member attribute for this function, if applicable.
27402755
setCXXSpecialMemberAttr(f, fd);
27412756

2742-
if (!curCGF)
2757+
// Manually insert at the correct location
2758+
if (curCGF) {
2759+
// Insert before the current function being generated
2760+
theModule.insert(mlir::Block::iterator(curCGF->CurFn), f);
2761+
} else {
2762+
// Insert at end of module
27432763
theModule.push_back(f);
2764+
}
27442765
}
27452766
return f;
27462767
}
@@ -3064,20 +3085,27 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
30643085
// NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we
30653086
// represent them in dedicated ops. The correct attributes are ensured during
30663087
// translation to LLVM. Thus, we don't need to check for them here.
3067-
assert(!isThunk && "isThunk NYI");
30683088

3069-
if (!isIncompleteFunction) {
3089+
const auto *funcDecl = dyn_cast<FunctionDecl>(globalDecl.getDecl());
3090+
3091+
if (!isIncompleteFunction)
30703092
setCIRFunctionAttributes(globalDecl,
30713093
getTypes().arrangeGlobalDeclaration(globalDecl),
30723094
func, isThunk);
3095+
3096+
// Add the Returned attribute for "this", except for iOS 5 and earlier
3097+
// where substantial code, including the libstdc++ dylib, was compiled with
3098+
// GCC and does not actually return "this".
3099+
if (!isThunk && getCXXABI().HasThisReturn(globalDecl) &&
3100+
!(getTriple().isiOS() && getTriple().isOSVersionLT(6))) {
3101+
llvm_unreachable("NYI");
30733102
}
30743103

30753104
// TODO(cir): Complete the remaining part of the function.
30763105
assert(!cir::MissingFeatures::setFunctionAttributes());
30773106

30783107
if (!isIncompleteFunction && func.isDeclaration())
3079-
getTargetCIRGenInfo().setTargetAttributes(globalDecl.getDecl(), func,
3080-
*this);
3108+
getTargetCIRGenInfo().setTargetAttributes(funcDecl, func, *this);
30813109

30823110
// TODO(cir): This needs a lot of work to better match CodeGen. That
30833111
// ultimately ends up in setGlobalVisibility, which already has the linkage of

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,9 @@ class CIRGenModule : public CIRGenTypeCache {
680680
void setDSOLocal(mlir::Operation *Op) const;
681681
/// Set visibility, dllimport/dllexport and dso_local.
682682
/// This must be called after dllimport/dllexport is set.
683-
void setGVProperties(mlir::Operation *Op, const NamedDecl *D) const;
684-
void setGVPropertiesAux(mlir::Operation *Op, const NamedDecl *D) const;
683+
void setGVProperties(mlir::Operation *op, GlobalDecl gd) const;
684+
void setGVProperties(mlir::Operation *op, const NamedDecl *d) const;
685+
void setGVPropertiesAux(mlir::Operation *op, const NamedDecl *d) const;
685686

686687
/// Set the TLS mode for the given global Op for the thread-local
687688
/// variable declaration D.
@@ -760,8 +761,8 @@ class CIRGenModule : public CIRGenTypeCache {
760761
void UpdateCompletedType(const clang::TagDecl *TD);
761762

762763
/// Set function attributes for a function declaration.
763-
void setFunctionAttributes(GlobalDecl GD, cir::FuncOp F,
764-
bool IsIncompleteFunction, bool IsThunk);
764+
void setFunctionAttributes(GlobalDecl globalDecl, cir::FuncOp func,
765+
bool isIncompleteFunction, bool isThunk);
765766

766767
/// Set the CIR function attributes (sext, zext, etc).
767768
void setCIRFunctionAttributes(GlobalDecl GD, const CIRGenFunctionInfo &info,

0 commit comments

Comments
 (0)