[CIR] Add abstract delete operation without AST attribute (#185538)
This introduces the cir.delete_array operation, adds code to emit that operation during CIR codegen, and adds lowering of the operation to the CXXABILowering pass. In order to handle possible variations in the delete representation, we add the name of the delete function, the usual delete parameters, and, optionally, the name of the element destructor function. During the CXXABILoweringPass, the cir.delete_array operation is expanded to call the delete function. This will be extended in a future change to handle reading the array cookie, if required, and calling element destructors.
This commit is contained in:
parent
7a104955e7
commit
9e611e82b8
@ -1357,6 +1357,30 @@ def CIR_StaticLocalGuardAttr : CIR_Attr<"StaticLocalGuard",
|
||||
let assemblyFormat = "`<` $name `>`";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// UsualDeleteParamsAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_UsualDeleteParamsAttr
|
||||
: CIR_Attr<"UsualDeleteParams", "usual_delete_params"> {
|
||||
let summary = "Parameters describing the usual operator delete signature";
|
||||
let description = [{
|
||||
Captures the properties of the usual deallocation function associated with
|
||||
an operator delete. These mirror the fields of `clang::UsualDeleteParams`.
|
||||
}];
|
||||
|
||||
let parameters = (ins
|
||||
DefaultValuedParameter<"bool", "false">:$size,
|
||||
DefaultValuedParameter<"bool", "false">:$alignment,
|
||||
DefaultValuedParameter<"bool", "false">:$type_aware_delete,
|
||||
DefaultValuedParameter<"bool", "false">:$destroying_delete
|
||||
);
|
||||
|
||||
let assemblyFormat = [{
|
||||
`<` struct($size, $alignment, $type_aware_delete, $destroying_delete) `>`
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AST Wrappers
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -3601,6 +3601,47 @@ def CIR_LLVMIntrinsicCallOp : CIR_Op<"call_llvm_intrinsic"> {
|
||||
];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DeleteArrayOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_DeleteArrayOp : CIR_Op<"delete_array"> {
|
||||
let summary = "Delete address representing an array";
|
||||
let description = [{
|
||||
`cir.delete_array` operation deletes an array. For example, `delete[] ptr;`
|
||||
will be translated to `cir.delete_array %ptr`.
|
||||
|
||||
The `delete_fn` attribute specifies the operator delete function to call.
|
||||
The `delete_params` attribute describes the parameters needed by the
|
||||
operator delete call.
|
||||
The `element_dtor` attribute, when present, specifies the destructor to call
|
||||
on each array element before deallocation.
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
CIR_PointerType:$address,
|
||||
FlatSymbolRefAttr:$delete_fn,
|
||||
CIR_UsualDeleteParamsAttr:$delete_params,
|
||||
OptionalAttr<FlatSymbolRefAttr>:$element_dtor
|
||||
);
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "mlir::Value":$address,
|
||||
"mlir::FlatSymbolRefAttr":$delete_fn,
|
||||
"cir::UsualDeleteParamsAttr":$delete_params), [{
|
||||
build($_builder, $_state, address, delete_fn, delete_params,
|
||||
/*element_dtor=*/mlir::FlatSymbolRefAttr{});
|
||||
}]>
|
||||
];
|
||||
|
||||
let assemblyFormat = [{
|
||||
$address `:` qualified(type($address)) attr-dict
|
||||
}];
|
||||
|
||||
let hasLLVMLowering = false;
|
||||
let hasCXXABILowering = true;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CallOp and TryCallOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "mlir/IR/Attributes.h"
|
||||
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
|
||||
/// Include the generated interface declarations.
|
||||
|
||||
@ -1222,9 +1222,23 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) {
|
||||
}
|
||||
|
||||
if (e->isArrayForm()) {
|
||||
assert(!cir::MissingFeatures::deleteArray());
|
||||
cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: array delete");
|
||||
return;
|
||||
// To handle this for cases that require array cookie, we will need to
|
||||
// add target-specific handling during the lowering of delete_array in
|
||||
// CXXABILowering, but we can emit a better diagnostic here.
|
||||
if (e->doesUsualArrayDeleteWantSize() || deleteTy.isDestructedType()) {
|
||||
cgm.errorNYI(e->getSourceRange(),
|
||||
"emitCXXDeleteExpr: array delete requires cookies");
|
||||
}
|
||||
const FunctionDecl *operatorDelete = e->getOperatorDelete();
|
||||
cir::FuncOp operatorDeleteFn = cgm.getAddrOfFunction(operatorDelete);
|
||||
auto deleteFn =
|
||||
mlir::FlatSymbolRefAttr::get(operatorDeleteFn.getSymNameAttr());
|
||||
UsualDeleteParams udp = operatorDelete->getUsualDeleteParams();
|
||||
auto deleteParams = cir::UsualDeleteParamsAttr::get(
|
||||
builder.getContext(), udp.Size, isAlignedAllocation(udp.Alignment),
|
||||
isTypeAwareAllocation(udp.TypeAwareDelete), udp.DestroyingDelete);
|
||||
cir::DeleteArrayOp::create(builder, ptr.getPointer().getLoc(),
|
||||
ptr.getPointer(), deleteFn, deleteParams);
|
||||
} else {
|
||||
emitObjectDelete(*this, e, ptr, deleteTy);
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ public:
|
||||
mlir::ConversionPatternRewriter &rewriter) const override {
|
||||
// Do not match on operations that have dedicated ABI lowering rewrite rules
|
||||
if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::BaseMethodOp,
|
||||
cir::CastOp, cir::CmpOp, cir::ConstantOp,
|
||||
cir::CastOp, cir::CmpOp, cir::ConstantOp, cir::DeleteArrayOp,
|
||||
cir::DerivedDataMemberOp, cir::DerivedMethodOp, cir::FuncOp,
|
||||
cir::GetMethodOp, cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
|
||||
return mlir::failure();
|
||||
@ -320,6 +320,30 @@ mlir::LogicalResult CIRBaseMethodOpABILowering::matchAndRewrite(
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite(
|
||||
cir::DeleteArrayOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
mlir::FlatSymbolRefAttr deleteFn = op.getDeleteFnAttr();
|
||||
mlir::Location loc = op->getLoc();
|
||||
mlir::Value loweredAddress = adaptor.getAddress();
|
||||
auto voidPtrTy =
|
||||
cir::PointerType::get(cir::VoidType::get(rewriter.getContext()));
|
||||
mlir::Value deletePtr = cir::CastOp::create(
|
||||
rewriter, loc, voidPtrTy, cir::CastKind::bitcast, loweredAddress);
|
||||
|
||||
cir::UsualDeleteParamsAttr deleteParams = op.getDeleteParams();
|
||||
if (deleteParams.getSize() || deleteParams.getTypeAwareDelete() ||
|
||||
deleteParams.getDestroyingDelete() || deleteParams.getAlignment())
|
||||
return rewriter.notifyMatchFailure(
|
||||
op,
|
||||
"sized, type-aware, destroying, or aligned delete not yet supported");
|
||||
|
||||
llvm::SmallVector<mlir::Value> callArgs{deletePtr};
|
||||
cir::CallOp::create(rewriter, loc, deleteFn, cir::VoidType(), callArgs);
|
||||
rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
|
||||
cir::DerivedDataMemberOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
@ -454,6 +478,9 @@ populateCXXABIConversionTarget(mlir::ConversionTarget &target,
|
||||
[&typeConverter](cir::GlobalOp op) {
|
||||
return typeConverter.isLegal(op.getSymType());
|
||||
});
|
||||
// Operations that do not use any special types must be explicitly marked as
|
||||
// illegal to trigger processing here.
|
||||
target.addIllegalOp<cir::DeleteArrayOp>();
|
||||
target.addIllegalOp<cir::DynamicCastOp>();
|
||||
target.addIllegalOp<cir::VTableGetTypeInfoOp>();
|
||||
}
|
||||
|
||||
64
clang/test/CIR/CodeGen/delete-array.cpp
Normal file
64
clang/test/CIR/CodeGen/delete-array.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir
|
||||
// RUN: FileCheck --input-file=%t-before.cir -check-prefix=CIR-BEFORE %s
|
||||
// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t-cir.ll
|
||||
// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t.ll
|
||||
// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s
|
||||
|
||||
void test_delete_array(int *ptr) {
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
// CIR-BEFORE: cir.func {{.*}} @_Z17test_delete_arrayPi
|
||||
// CIR-BEFORE: %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
|
||||
// CIR-BEFORE: cir.delete_array %[[PTR]] : !cir.ptr<!s32i> {delete_fn = @_ZdaPv, delete_params = #cir.usual_delete_params<>
|
||||
|
||||
// CIR: cir.func {{.*}} @_Z17test_delete_arrayPi
|
||||
// CIR: %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
|
||||
// CIR: %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
|
||||
// CIR: cir.call @_ZdaPv(%[[VOID_PTR]])
|
||||
|
||||
// LLVM: define {{.*}} void @_Z17test_delete_arrayPi
|
||||
// LLVM: %[[PTR:.*]] = load ptr, ptr %{{.*}}
|
||||
// LLVM: call void @_ZdaPv(ptr %[[PTR]])
|
||||
|
||||
// OGCG: define {{.*}} void @_Z17test_delete_arrayPi
|
||||
// OGCG: %[[PTR:.*]] = load ptr, ptr %{{.*}}
|
||||
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR]], null
|
||||
// OGCG: br i1 %[[IS_NULL]], label %[[DELETE_END:.*]], label %[[DELETE_NOT_NULL:.*]]
|
||||
// OGCG: [[DELETE_NOT_NULL]]:
|
||||
// OGCG: call void @_ZdaPv(ptr {{.*}} %[[PTR]])
|
||||
// OGCG: br label %[[DELETE_END]]
|
||||
// OGCG: [[DELETE_END]]:
|
||||
// OGCG: ret void
|
||||
|
||||
struct SimpleArrDelete {
|
||||
void operator delete[](void *);
|
||||
int member;
|
||||
};
|
||||
void test_simple_delete_array(SimpleArrDelete *ptr) {
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
// CIR-BEFORE: cir.func {{.*}} @_Z24test_simple_delete_arrayP15SimpleArrDelete
|
||||
// CIR-BEFORE: %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
|
||||
// CIR-BEFORE: cir.delete_array %[[PTR]] : !cir.ptr<!rec_SimpleArrDelete> {delete_fn = @_ZN15SimpleArrDeletedaEPv, delete_params = #cir.usual_delete_params<>
|
||||
|
||||
// CIR: cir.func {{.*}} @_Z24test_simple_delete_arrayP15SimpleArrDelete
|
||||
// CIR: %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
|
||||
// CIR: %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!rec_SimpleArrDelete> -> !cir.ptr<!void>
|
||||
// CIR: cir.call @_ZN15SimpleArrDeletedaEPv(%[[VOID_PTR]])
|
||||
|
||||
// LLVM: define {{.*}} void @_Z24test_simple_delete_arrayP15SimpleArrDelete
|
||||
// LLVM: %[[PTR:.*]] = load ptr, ptr %{{.*}}
|
||||
// LLVM: call void @_ZN15SimpleArrDeletedaEPv(ptr %[[PTR]])
|
||||
|
||||
// OGCG: define {{.*}} void @_Z24test_simple_delete_arrayP15SimpleArrDelete
|
||||
// OGCG: %[[PTR:.*]] = load ptr, ptr %{{.*}}
|
||||
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR]], null
|
||||
// OGCG: br i1 %[[IS_NULL]], label %[[ARR_DELETE_END:.*]], label %[[ARR_DELETE_NOT_NULL:.*]]
|
||||
// OGCG: [[ARR_DELETE_NOT_NULL]]:
|
||||
// OGCG: call void @_ZN15SimpleArrDeletedaEPv(ptr {{.*}} %[[PTR]])
|
||||
// OGCG: br label %[[ARR_DELETE_END]]
|
||||
// OGCG: [[ARR_DELETE_END]]:
|
||||
Loading…
x
Reference in New Issue
Block a user