@@ -210,6 +210,60 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
210
210
return emitCall (fnInfo, callee, returnValue, args, nullptr , loc);
211
211
}
212
212
213
+ namespace {
214
+ // / The parameters to pass to a usual operator delete.
215
+ struct UsualDeleteParams {
216
+ TypeAwareAllocationMode typeAwareDelete = TypeAwareAllocationMode::No;
217
+ bool destroyingDelete = false ;
218
+ bool size = false ;
219
+ AlignedAllocationMode alignment = AlignedAllocationMode::No;
220
+ };
221
+ } // namespace
222
+
223
+ // FIXME(cir): this should be shared with LLVM codegen
224
+ static UsualDeleteParams getUsualDeleteParams (const FunctionDecl *fd) {
225
+ UsualDeleteParams params;
226
+
227
+ const FunctionProtoType *fpt = fd->getType ()->castAs <FunctionProtoType>();
228
+ auto ai = fpt->param_type_begin (), ae = fpt->param_type_end ();
229
+
230
+ if (fd->isTypeAwareOperatorNewOrDelete ()) {
231
+ params.typeAwareDelete = TypeAwareAllocationMode::Yes;
232
+ assert (ai != ae);
233
+ ++ai;
234
+ }
235
+
236
+ // The first argument after the type-identity parameter (if any) is
237
+ // always a void* (or C* for a destroying operator delete for class
238
+ // type C).
239
+ ++ai;
240
+
241
+ // The next parameter may be a std::destroying_delete_t.
242
+ if (fd->isDestroyingOperatorDelete ()) {
243
+ params.destroyingDelete = true ;
244
+ assert (ai != ae);
245
+ ++ai;
246
+ }
247
+
248
+ // Figure out what other parameters we should be implicitly passing.
249
+ if (ai != ae && (*ai)->isIntegerType ()) {
250
+ params.size = true ;
251
+ ++ai;
252
+ } else {
253
+ assert (!isTypeAwareAllocation (params.typeAwareDelete ));
254
+ }
255
+
256
+ if (ai != ae && (*ai)->isAlignValT ()) {
257
+ params.alignment = AlignedAllocationMode::Yes;
258
+ ++ai;
259
+ } else {
260
+ assert (!isTypeAwareAllocation (params.typeAwareDelete ));
261
+ }
262
+
263
+ assert (ai == ae && " unexpected usual deallocation function parameter" );
264
+ return params;
265
+ }
266
+
213
267
static mlir::Value emitCXXNewAllocSize (CIRGenFunction &cgf, const CXXNewExpr *e,
214
268
unsigned minElements,
215
269
mlir::Value &numElements,
@@ -332,6 +386,117 @@ static RValue emitNewDeleteCall(CIRGenFunction &cgf,
332
386
return rv;
333
387
}
334
388
389
+ namespace {
390
+ // / Calls the given 'operator delete' on a single object.
391
+ struct CallObjectDelete final : EHScopeStack::Cleanup {
392
+ mlir::Value ptr;
393
+ const FunctionDecl *operatorDelete;
394
+ QualType elementType;
395
+
396
+ CallObjectDelete (mlir::Value ptr, const FunctionDecl *operatorDelete,
397
+ QualType elementType)
398
+ : ptr(ptr), operatorDelete(operatorDelete), elementType(elementType) {}
399
+
400
+ void emit (CIRGenFunction &cgf) override {
401
+ cgf.emitDeleteCall (operatorDelete, ptr, elementType);
402
+ }
403
+
404
+ // This is a placeholder until EHCleanupScope is implemented.
405
+ size_t getSize () const override {
406
+ assert (!cir::MissingFeatures::ehCleanupScope ());
407
+ return sizeof (CallObjectDelete);
408
+ }
409
+ };
410
+ } // namespace
411
+
412
+ // / Emit the code for deleting a single object.
413
+ static void emitObjectDelete (CIRGenFunction &cgf, const CXXDeleteExpr *de,
414
+ Address ptr, QualType elementType) {
415
+ // C++11 [expr.delete]p3:
416
+ // If the static type of the object to be deleted is different from its
417
+ // dynamic type, the static type shall be a base class of the dynamic type
418
+ // of the object to be deleted and the static type shall have a virtual
419
+ // destructor or the behavior is undefined.
420
+ assert (!cir::MissingFeatures::emitTypeCheck ());
421
+
422
+ const FunctionDecl *operatorDelete = de->getOperatorDelete ();
423
+ assert (!operatorDelete->isDestroyingOperatorDelete ());
424
+
425
+ // Find the destructor for the type, if applicable. If the
426
+ // destructor is virtual, we'll just emit the vcall and return.
427
+ const CXXDestructorDecl *dtor = nullptr ;
428
+ if (const auto *rd = elementType->getAsCXXRecordDecl ()) {
429
+ if (rd->hasDefinition () && !rd->hasTrivialDestructor ()) {
430
+ dtor = rd->getDestructor ();
431
+
432
+ if (dtor->isVirtual ()) {
433
+ cgf.cgm .errorNYI (de->getSourceRange (),
434
+ " emitObjectDelete: virtual destructor" );
435
+ }
436
+ }
437
+ }
438
+
439
+ // Make sure that we call delete even if the dtor throws.
440
+ // This doesn't have to a conditional cleanup because we're going
441
+ // to pop it off in a second.
442
+ cgf.ehStack .pushCleanup <CallObjectDelete>(
443
+ NormalAndEHCleanup, ptr.getPointer (), operatorDelete, elementType);
444
+
445
+ if (dtor) {
446
+ cgf.emitCXXDestructorCall (dtor, Dtor_Complete,
447
+ /* ForVirtualBase=*/ false ,
448
+ /* Delegating=*/ false , ptr, elementType);
449
+ } else if (elementType.getObjCLifetime ()) {
450
+ assert (!cir::MissingFeatures::objCLifetime ());
451
+ cgf.cgm .errorNYI (de->getSourceRange (), " emitObjectDelete: ObjCLifetime" );
452
+ }
453
+
454
+ // In traditional LLVM codegen null checks are emitted to save a delete call.
455
+ // In CIR we optimize for size by default, the null check should be added into
456
+ // this function callers.
457
+ assert (!cir::MissingFeatures::emitNullCheckForDeleteCalls ());
458
+
459
+ cgf.popCleanupBlock ();
460
+ }
461
+
462
+ void CIRGenFunction::emitCXXDeleteExpr (const CXXDeleteExpr *e) {
463
+ const Expr *arg = e->getArgument ();
464
+ Address ptr = emitPointerWithAlignment (arg);
465
+
466
+ // Null check the pointer.
467
+ //
468
+ // We could avoid this null check if we can determine that the object
469
+ // destruction is trivial and doesn't require an array cookie; we can
470
+ // unconditionally perform the operator delete call in that case. For now, we
471
+ // assume that deleted pointers are null rarely enough that it's better to
472
+ // keep the branch. This might be worth revisiting for a -O0 code size win.
473
+ //
474
+ // CIR note: emit the code size friendly by default for now, such as mentioned
475
+ // in `emitObjectDelete`.
476
+ assert (!cir::MissingFeatures::emitNullCheckForDeleteCalls ());
477
+ QualType deleteTy = e->getDestroyedType ();
478
+
479
+ // A destroying operator delete overrides the entire operation of the
480
+ // delete expression.
481
+ if (e->getOperatorDelete ()->isDestroyingOperatorDelete ()) {
482
+ cgm.errorNYI (e->getSourceRange (),
483
+ " emitCXXDeleteExpr: destroying operator delete" );
484
+ return ;
485
+ }
486
+
487
+ // We might be deleting a pointer to array.
488
+ deleteTy = getContext ().getBaseElementType (deleteTy);
489
+ ptr = ptr.withElementType (builder, convertTypeForMem (deleteTy));
490
+
491
+ if (e->isArrayForm ()) {
492
+ assert (!cir::MissingFeatures::deleteArray ());
493
+ cgm.errorNYI (e->getSourceRange (), " emitCXXDeleteExpr: array delete" );
494
+ return ;
495
+ } else {
496
+ emitObjectDelete (*this , e, ptr, deleteTy);
497
+ }
498
+ }
499
+
335
500
mlir::Value CIRGenFunction::emitCXXNewExpr (const CXXNewExpr *e) {
336
501
// The element type being allocated.
337
502
QualType allocType = getContext ().getBaseElementType (e->getAllocatedType ());
@@ -443,3 +608,53 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
443
608
allocSizeWithoutCookie);
444
609
return result.getPointer ();
445
610
}
611
+
612
+ void CIRGenFunction::emitDeleteCall (const FunctionDecl *deleteFD,
613
+ mlir::Value ptr, QualType deleteTy) {
614
+ assert (!cir::MissingFeatures::deleteArray ());
615
+
616
+ const auto *deleteFTy = deleteFD->getType ()->castAs <FunctionProtoType>();
617
+ CallArgList deleteArgs;
618
+
619
+ UsualDeleteParams params = getUsualDeleteParams (deleteFD);
620
+ auto paramTypeIt = deleteFTy->param_type_begin ();
621
+
622
+ // Pass std::type_identity tag if present
623
+ if (isTypeAwareAllocation (params.typeAwareDelete ))
624
+ cgm.errorNYI (deleteFD->getSourceRange (),
625
+ " emitDeleteCall: type aware delete" );
626
+
627
+ // Pass the pointer itself.
628
+ QualType argTy = *paramTypeIt++;
629
+ mlir::Value deletePtr =
630
+ builder.createBitcast (ptr.getLoc (), ptr, convertType (argTy));
631
+ deleteArgs.add (RValue::get (deletePtr), argTy);
632
+
633
+ // Pass the std::destroying_delete tag if present.
634
+ if (params.destroyingDelete )
635
+ cgm.errorNYI (deleteFD->getSourceRange (),
636
+ " emitDeleteCall: destroying delete" );
637
+
638
+ // Pass the size if the delete function has a size_t parameter.
639
+ if (params.size ) {
640
+ QualType sizeType = *paramTypeIt++;
641
+ CharUnits deleteTypeSize = getContext ().getTypeSizeInChars (deleteTy);
642
+ assert (mlir::isa<cir::IntType>(convertType (sizeType)) &&
643
+ " expected cir::IntType" );
644
+ cir::ConstantOp size = builder.getConstInt (
645
+ *currSrcLoc, convertType (sizeType), deleteTypeSize.getQuantity ());
646
+
647
+ deleteArgs.add (RValue::get (size), sizeType);
648
+ }
649
+
650
+ // Pass the alignment if the delete function has an align_val_t parameter.
651
+ if (isAlignedAllocation (params.alignment ))
652
+ cgm.errorNYI (deleteFD->getSourceRange (),
653
+ " emitDeleteCall: aligned allocation" );
654
+
655
+ assert (paramTypeIt == deleteFTy->param_type_end () &&
656
+ " unknown parameter to usual delete function" );
657
+
658
+ // Emit the call to delete.
659
+ emitNewDeleteCall (*this , deleteFD, deleteFTy, deleteArgs);
660
+ }
0 commit comments