14
14
#include " CIRGenFunction.h"
15
15
#include " CIRGenValue.h"
16
16
17
+ #include " clang/AST/EvaluatedExprVisitor.h"
17
18
#include " clang/AST/ExprCXX.h"
18
19
#include " clang/AST/RecordLayout.h"
19
20
#include " clang/AST/Type.h"
@@ -124,6 +125,32 @@ static bool isInitializerOfDynamicClass(const CXXCtorInitializer *baseInit) {
124
125
return baseClassDecl->isDynamicClass ();
125
126
}
126
127
128
+ namespace {
129
+ // / A visitor which checks whether an initializer uses 'this' in a
130
+ // / way which requires the vtable to be properly set.
131
+ struct DynamicThisUseChecker
132
+ : ConstEvaluatedExprVisitor<DynamicThisUseChecker> {
133
+ using super = ConstEvaluatedExprVisitor<DynamicThisUseChecker>;
134
+
135
+ bool usesThis = false ;
136
+
137
+ DynamicThisUseChecker (const ASTContext &c) : super(c) {}
138
+
139
+ // Black-list all explicit and implicit references to 'this'.
140
+ //
141
+ // Do we need to worry about external references to 'this' derived
142
+ // from arbitrary code? If so, then anything which runs arbitrary
143
+ // external code might potentially access the vtable.
144
+ void VisitCXXThisExpr (const CXXThisExpr *e) { usesThis = true ; }
145
+ };
146
+ } // end anonymous namespace
147
+
148
+ static bool baseInitializerUsesThis (ASTContext &c, const Expr *init) {
149
+ DynamicThisUseChecker checker (c);
150
+ checker.Visit (init);
151
+ return checker.usesThis ;
152
+ }
153
+
127
154
// / Gets the address of a direct base class within a complete object.
128
155
// / This should only be used for (1) non-virtual bases or (2) virtual bases
129
156
// / when the type is known to be complete (e.g. in complete destructors).
@@ -166,10 +193,8 @@ void CIRGenFunction::emitBaseInitializer(mlir::Location loc,
166
193
// If the initializer for the base (other than the constructor
167
194
// itself) accesses 'this' in any way, we need to initialize the
168
195
// vtables.
169
- if (classDecl->isDynamicClass ()) {
170
- cgm.errorNYI (loc, " emitBaseInitializer: dynamic class" );
171
- return ;
172
- }
196
+ if (baseInitializerUsesThis (getContext (), baseInit->getInit ()))
197
+ initializeVTablePointers (loc, classDecl);
173
198
174
199
// We can pretend to be a complete class because it only matters for
175
200
// virtual bases, and we only do virtual bases for complete ctors.
@@ -260,6 +285,34 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
260
285
}
261
286
}
262
287
288
+ static Address applyNonVirtualAndVirtualOffset (
289
+ mlir::Location loc, CIRGenFunction &cgf, Address addr,
290
+ CharUnits nonVirtualOffset, mlir::Value virtualOffset,
291
+ const CXXRecordDecl *derivedClass, const CXXRecordDecl *nearestVBase,
292
+ mlir::Type baseValueTy = {}, bool assumeNotNull = true ) {
293
+ // Assert that we have something to do.
294
+ assert (!nonVirtualOffset.isZero () || virtualOffset != nullptr );
295
+
296
+ // Compute the offset from the static and dynamic components.
297
+ if (!nonVirtualOffset.isZero ()) {
298
+ if (virtualOffset) {
299
+ cgf.cgm .errorNYI (
300
+ loc,
301
+ " applyNonVirtualAndVirtualOffset: virtual and non-virtual offset" );
302
+ return Address::invalid ();
303
+ } else {
304
+ assert (baseValueTy && " expected base type" );
305
+ // If no virtualOffset is present this is the final stop.
306
+ return cgf.getBuilder ().createBaseClassAddr (
307
+ loc, addr, baseValueTy, nonVirtualOffset.getQuantity (),
308
+ assumeNotNull);
309
+ }
310
+ }
311
+
312
+ cgf.cgm .errorNYI (loc, " applyNonVirtualAndVirtualOffset: virtual offset" );
313
+ return Address::invalid ();
314
+ }
315
+
263
316
void CIRGenFunction::initializeVTablePointer (mlir::Location loc,
264
317
const VPtr &vptr) {
265
318
// Compute the address point.
@@ -287,8 +340,9 @@ void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
287
340
// Apply the offsets.
288
341
Address classAddr = loadCXXThisAddress ();
289
342
if (!nonVirtualOffset.isZero () || virtualOffset) {
290
- cgm.errorNYI (loc,
291
- " initializeVTablePointer: non-virtual and virtual offset" );
343
+ classAddr = applyNonVirtualAndVirtualOffset (
344
+ loc, *this , classAddr, nonVirtualOffset, virtualOffset,
345
+ vptr.vtableClass , vptr.nearestVBase , baseValueTy);
292
346
}
293
347
294
348
// Finally, store the address point. Use the same CIR types as the field.
@@ -322,10 +376,11 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
322
376
CIRGenFunction::VPtrsVector
323
377
CIRGenFunction::getVTablePointers (const CXXRecordDecl *vtableClass) {
324
378
CIRGenFunction::VPtrsVector vptrsResult;
379
+ VisitedVirtualBasesSetTy vbases;
325
380
getVTablePointers (BaseSubobject (vtableClass, CharUnits::Zero ()),
326
381
/* NearestVBase=*/ nullptr ,
327
382
/* OffsetFromNearestVBase=*/ CharUnits::Zero (),
328
- /* BaseIsNonVirtualPrimaryBase=*/ false , vtableClass,
383
+ /* BaseIsNonVirtualPrimaryBase=*/ false , vtableClass, vbases,
329
384
vptrsResult);
330
385
return vptrsResult;
331
386
}
@@ -335,6 +390,7 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
335
390
CharUnits offsetFromNearestVBase,
336
391
bool baseIsNonVirtualPrimaryBase,
337
392
const CXXRecordDecl *vtableClass,
393
+ VisitedVirtualBasesSetTy &vbases,
338
394
VPtrsVector &vptrs) {
339
395
// If this base is a non-virtual primary base the address point has already
340
396
// been set.
@@ -346,8 +402,39 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
346
402
347
403
const CXXRecordDecl *rd = base.getBase ();
348
404
349
- if (rd->getNumBases ())
350
- cgm.errorNYI (rd->getSourceRange (), " getVTablePointers: traverse bases" );
405
+ for (const auto &nextBase : rd->bases ()) {
406
+ const auto *baseDecl =
407
+ cast<CXXRecordDecl>(
408
+ nextBase.getType ()->castAs <RecordType>()->getOriginalDecl ())
409
+ ->getDefinitionOrSelf ();
410
+
411
+ // Ignore classes without a vtable.
412
+ if (!baseDecl->isDynamicClass ())
413
+ continue ;
414
+
415
+ CharUnits baseOffset;
416
+ CharUnits baseOffsetFromNearestVBase;
417
+ bool baseDeclIsNonVirtualPrimaryBase;
418
+ const CXXRecordDecl *nextBaseDecl;
419
+
420
+ if (nextBase.isVirtual ()) {
421
+ cgm.errorNYI (rd->getSourceRange (), " getVTablePointers: virtual base" );
422
+ return ;
423
+ } else {
424
+ const ASTRecordLayout &layout = getContext ().getASTRecordLayout (rd);
425
+
426
+ nextBaseDecl = baseDecl;
427
+ baseOffset = base.getBaseOffset () + layout.getBaseClassOffset (baseDecl);
428
+ baseOffsetFromNearestVBase =
429
+ offsetFromNearestVBase + layout.getBaseClassOffset (baseDecl);
430
+ baseDeclIsNonVirtualPrimaryBase = layout.getPrimaryBase () == baseDecl;
431
+ }
432
+
433
+ getVTablePointers (BaseSubobject (baseDecl, baseOffset), nextBaseDecl,
434
+ baseOffsetFromNearestVBase,
435
+ baseDeclIsNonVirtualPrimaryBase, vtableClass, vbases,
436
+ vptrs);
437
+ }
351
438
}
352
439
353
440
Address CIRGenFunction::loadCXXThisAddress () {
0 commit comments