Skip to content

Commit 4b89bc0

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: eaf1735 Pull-Request: #2006
1 parent b577214 commit 4b89bc0

File tree

11 files changed

+488
-24
lines changed

11 files changed

+488
-24
lines changed

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,12 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
429429
builder.setInsertionPointToEnd(block);
430430
cir::YieldOp::create(builder, addr->getLoc());
431431
}
432+
433+
void CIRGenFunction::finishThunk() {
434+
// Clear these to restore the invariants expected by
435+
// StartFunction/FinishFunction.
436+
CurCodeDecl = nullptr;
437+
CurFuncDecl = nullptr;
438+
439+
finishFunction(SourceLocation());
440+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,10 @@ 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+
// Default implementation: just emit a normal return
111+
auto loc = CGF.getBuilder().getUnknownLoc();
112+
CGF.emitReturnOfRValue(loc, RV, ResultType);
113+
}

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 This,
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: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,8 @@ 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 = (fd && fd->getBody()) ? fd->getBody()->getBeginLoc() : SourceLocation();
769+
auto eSrcLoc = (fd && fd->getBody()) ? fd->getBody()->getEndLoc() : SourceLocation();
770770
auto unknownLoc = builder.getUnknownLoc();
771771

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

11601160
// Apply xray attributes to the function (as a string, for now)
1161-
if (d->getAttr<XRayInstrumentAttr>()) {
1161+
if (d && d->getAttr<XRayInstrumentAttr>()) {
11621162
assert(!cir::MissingFeatures::xray());
11631163
}
11641164

1165-
if (ShouldXRayInstrumentFunction()) {
1165+
if (d && ShouldXRayInstrumentFunction()) {
11661166
assert(!cir::MissingFeatures::xray());
11671167
}
11681168

@@ -1365,12 +1365,13 @@ void CIRGenFunction::StartFunction(GlobalDecl gd, QualType retTy,
13651365

13661366
// Location of the store to the param storage tracked as beginning of
13671367
// the function body.
1368-
auto fnBodyBegin = getLoc(fd->getBody()->getBeginLoc());
1368+
auto fnBodyBegin =
1369+
(fd && fd->getBody()) ? getLoc(fd->getBody()->getBeginLoc()) : getLoc(Loc);
13691370
builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addr);
13701371
}
13711372
assert(builder.getInsertionBlock() && "Should be valid");
13721373

1373-
auto fnEndLoc = getLoc(fd->getBody()->getEndLoc());
1374+
auto fnEndLoc = (fd && fd->getBody()) ? getLoc(fd->getBody()->getEndLoc()) : getLoc(Loc);
13741375

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

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,23 @@ class CIRGenFunction : public CIRGenTypeCache {
21302130

21312131
void emitDestructorBody(FunctionArgList &Args);
21322132

2133+
/// Generate a thunk for the given method.
2134+
void generateThunk(cir::FuncOp Fn, const CIRGenFunctionInfo &FnInfo,
2135+
GlobalDecl GD, const ThunkInfo &Thunk,
2136+
bool IsUnprototyped);
2137+
2138+
void startThunk(cir::FuncOp Fn, GlobalDecl GD,
2139+
const CIRGenFunctionInfo &FnInfo, bool IsUnprototyped);
2140+
2141+
void emitCallAndReturnForThunk(cir::FuncOp Callee, const ThunkInfo *Thunk,
2142+
bool IsUnprototyped);
2143+
2144+
/// Finish thunk generation.
2145+
void finishThunk();
2146+
2147+
void emitMustTailThunk(GlobalDecl GD, mlir::Value adjustedThisPtr,
2148+
cir::FuncOp Callee);
2149+
21332150
mlir::LogicalResult emitDoStmt(const clang::DoStmt &S);
21342151

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

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 87 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 This,
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,
@@ -2148,6 +2157,84 @@ mlir::Attribute CIRGenItaniumCXXABI::getAddrOfRTTIDescriptor(mlir::Location loc,
21482157
return CIRGenItaniumRTTIBuilder(*this, CGM).BuildTypeInfo(loc, Ty);
21492158
}
21502159

2160+
mlir::Value
2161+
CIRGenItaniumCXXABI::performThisAdjustment(CIRGenFunction &CGF, Address This,
2162+
const CXXRecordDecl *UnadjustedClass,
2163+
const ThunkInfo &TI) {
2164+
// Handle trivial case: no adjustment needed
2165+
if (!TI.This.NonVirtual && !TI.This.Virtual.Itanium.VCallOffsetOffset)
2166+
return This.getPointer();
2167+
2168+
auto &builder = CGF.getBuilder();
2169+
auto loc = builder.getUnknownLoc();
2170+
mlir::Value V = This.getPointer();
2171+
2172+
// Apply non-virtual adjustment first (for base-to-derived cast)
2173+
if (TI.This.NonVirtual) {
2174+
// Cast to i8* for byte-level pointer arithmetic
2175+
auto i8PtrTy = builder.getUInt8PtrTy();
2176+
V = builder.createBitcast(V, i8PtrTy);
2177+
2178+
// Create offset constant (NonVirtual is in bytes)
2179+
auto offsetConst = builder.getSInt64(TI.This.NonVirtual, loc);
2180+
2181+
// Perform pointer arithmetic: this_ptr + offset
2182+
V = builder.create<cir::PtrStrideOp>(loc, i8PtrTy, V, offsetConst);
2183+
2184+
// Cast back to original pointer type
2185+
V = builder.createBitcast(V, This.getType());
2186+
}
2187+
2188+
// Virtual adjustment (vcall offset lookup from vtable)
2189+
if (TI.This.Virtual.Itanium.VCallOffsetOffset) {
2190+
// TODO: Implement virtual adjustment - requires GetVTablePtr equivalent
2191+
llvm_unreachable(
2192+
"Virtual this adjustment NYI - requires vtable offset lookup");
2193+
}
2194+
2195+
return V;
2196+
}
2197+
2198+
mlir::Value CIRGenItaniumCXXABI::performReturnAdjustment(
2199+
CIRGenFunction &CGF, Address Ret, const CXXRecordDecl *UnadjustedClass,
2200+
const ReturnAdjustment &RA) {
2201+
// Handle trivial case: no adjustment needed
2202+
if (!RA.NonVirtual && !RA.Virtual.Itanium.VBaseOffsetOffset)
2203+
return Ret.getPointer();
2204+
2205+
auto &builder = CGF.getBuilder();
2206+
auto loc = builder.getUnknownLoc();
2207+
mlir::Value V = Ret.getPointer();
2208+
2209+
// For return adjustment, virtual adjustment is applied first,
2210+
// then non-virtual (opposite order from this adjustment)
2211+
2212+
// Virtual adjustment
2213+
if (RA.Virtual.Itanium.VBaseOffsetOffset) {
2214+
// TODO: Implement virtual return adjustment
2215+
llvm_unreachable(
2216+
"Virtual return adjustment NYI - requires vtable offset lookup");
2217+
}
2218+
2219+
// Apply non-virtual adjustment second (for derived-to-base cast)
2220+
if (RA.NonVirtual) {
2221+
// Cast to i8* for byte-level pointer arithmetic
2222+
auto i8PtrTy = builder.getUInt8PtrTy();
2223+
V = builder.createBitcast(V, i8PtrTy);
2224+
2225+
// Create offset constant
2226+
auto offsetConst = builder.getSInt64(RA.NonVirtual, loc);
2227+
2228+
// Perform pointer arithmetic
2229+
V = builder.create<cir::PtrStrideOp>(loc, i8PtrTy, V, offsetConst);
2230+
2231+
// Cast back to original pointer type
2232+
V = builder.createBitcast(V, Ret.getType());
2233+
}
2234+
2235+
return V;
2236+
}
2237+
21512238
void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &CGVT,
21522239
const CXXRecordDecl *RD) {
21532240
auto VTable = getAddrOfVTable(RD, CharUnits());

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 24 additions & 9 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
@@ -2710,10 +2718,12 @@ cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
27102718
// Some global emissions are triggered while emitting a function, e.g.
27112719
// void s() { x.method() }
27122720
//
2713-
// Be sure to insert a new function before a current one.
2721+
// Save the current function context for later insertion logic
27142722
auto *curCGF = getCurrCIRGenFun();
2715-
if (curCGF)
2716-
builder.setInsertionPoint(curCGF->CurFn);
2723+
2724+
// Clear insertion point to prevent auto-insertion by create()
2725+
// We'll manually insert at the correct location below
2726+
builder.clearInsertionPoint();
27172727

27182728
f = cir::FuncOp::create(builder, loc, name, ty);
27192729

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

2742-
if (!curCGF)
2752+
// Manually insert at the correct location
2753+
if (curCGF) {
2754+
// Insert before the current function being generated
2755+
theModule.insert(mlir::Block::iterator(curCGF->CurFn), f);
2756+
} else {
2757+
// Insert at end of module
27432758
theModule.push_back(f);
2759+
}
27442760
}
27452761
return f;
27462762
}
@@ -3058,7 +3074,6 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
30583074
// NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we
30593075
// represent them in dedicated ops. The correct attributes are ensured during
30603076
// translation to LLVM. Thus, we don't need to check for them here.
3061-
assert(!isThunk && "isThunk NYI");
30623077

30633078
if (!isIncompleteFunction) {
30643079
setCIRFunctionAttributes(globalDecl,

0 commit comments

Comments
 (0)