@@ -210,6 +210,60 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
210210 return emitCall (fnInfo, callee, returnValue, args, nullptr , loc);
211211}
212212
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+
213267static mlir::Value emitCXXNewAllocSize (CIRGenFunction &cgf, const CXXNewExpr *e,
214268 unsigned minElements,
215269 mlir::Value &numElements,
@@ -332,6 +386,117 @@ static RValue emitNewDeleteCall(CIRGenFunction &cgf,
332386 return rv;
333387}
334388
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+
335500mlir::Value CIRGenFunction::emitCXXNewExpr (const CXXNewExpr *e) {
336501 // The element type being allocated.
337502 QualType allocType = getContext ().getBaseElementType (e->getAllocatedType ());
@@ -443,3 +608,53 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
443608 allocSizeWithoutCookie);
444609 return result.getPointer ();
445610}
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