-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[CIR] Upstream handling for delete array #165225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-clangir Author: Shawn K (kimsh02) ChangesUpstream Full diff: https://github.com/llvm/llvm-project/pull/165225.diff 4 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2b361ed0982c6..b7bdfe7d9f5b6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4089,6 +4089,24 @@ def CIR_PrefetchOp : CIR_Op<"prefetch"> {
}];
}
+//===----------------------------------------------------------------------===//
+// 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`.
+ }];
+
+ let arguments = (ins CIR_PointerType:$address);
+
+ let assemblyFormat = [{
+ $address `:` type($address) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// PtrDiffOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 7a35382e79a93..42d066e88225c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -626,8 +626,8 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) {
ptr = ptr.withElementType(builder, convertTypeForMem(deleteTy));
if (e->isArrayForm()) {
- assert(!cir::MissingFeatures::deleteArray());
- cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: array delete");
+ cir::DeleteArrayOp::create(builder, ptr.getPointer().getLoc(),
+ ptr.getPointer());
return;
} else {
emitObjectDelete(*this, e, ptr, deleteTy);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5a6193fa8d840..aba182e2c9952 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1704,6 +1704,43 @@ mlir::LogicalResult CIRToLLVMPrefetchOpLowering::matchAndRewrite(
return mlir::success();
}
+// Make sure the LLVM function we are about to create a call for actually
+// exists, if not create one. Returns a function
+static void getOrCreateLLVMFuncOp(mlir::ConversionPatternRewriter &rewriter,
+ mlir::Operation *srcOp,
+ llvm::StringRef fnName, mlir::Type fnTy) {
+ auto modOp = srcOp->getParentOfType<mlir::ModuleOp>();
+ auto enclosingFnOp = srcOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
+ mlir::Operation *sourceSymbol =
+ mlir::SymbolTable::lookupSymbolIn(modOp, fnName);
+ if (!sourceSymbol) {
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPoint(enclosingFnOp);
+ mlir::LLVM::LLVMFuncOp::create(rewriter, srcOp->getLoc(), fnName, fnTy);
+ }
+}
+
+mlir::LogicalResult CIRToLLVMDeleteArrayOpLowering::matchAndRewrite(
+ cir::DeleteArrayOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ StringRef fnName = "_ZdaPv";
+
+ auto voidTy = rewriter.getType<mlir::LLVM::LLVMVoidType>();
+ mlir::Type llvmPtrTy =
+ mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+
+ mlir::Type fnTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {llvmPtrTy},
+ /*isVarArg=*/false);
+
+ getOrCreateLLVMFuncOp(rewriter, op, fnName, fnTy);
+
+ // Replace the operation with a call to _ZdaPv with the pointer argument
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
+ op, mlir::TypeRange{}, fnName, mlir::ValueRange{adaptor.getAddress()});
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
cir::PtrDiffOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/delete-array.cpp b/clang/test/CIR/CodeGen/delete-array.cpp
new file mode 100644
index 0000000000000..0ff08be61ef45
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delete-array.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+void test_delete_array(int *ptr) {
+ delete[] ptr;
+}
+
+// CIR: cir.delete.array
+
+// LLVM: call void @_ZdaPv{{(m)?}}(ptr
+
+// OGCG: call void @_ZdaPv{{(m)?}}(ptr
|
|
@llvm/pr-subscribers-clang Author: Shawn K (kimsh02) ChangesUpstream Full diff: https://github.com/llvm/llvm-project/pull/165225.diff 4 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2b361ed0982c6..b7bdfe7d9f5b6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4089,6 +4089,24 @@ def CIR_PrefetchOp : CIR_Op<"prefetch"> {
}];
}
+//===----------------------------------------------------------------------===//
+// 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`.
+ }];
+
+ let arguments = (ins CIR_PointerType:$address);
+
+ let assemblyFormat = [{
+ $address `:` type($address) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// PtrDiffOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 7a35382e79a93..42d066e88225c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -626,8 +626,8 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) {
ptr = ptr.withElementType(builder, convertTypeForMem(deleteTy));
if (e->isArrayForm()) {
- assert(!cir::MissingFeatures::deleteArray());
- cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: array delete");
+ cir::DeleteArrayOp::create(builder, ptr.getPointer().getLoc(),
+ ptr.getPointer());
return;
} else {
emitObjectDelete(*this, e, ptr, deleteTy);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5a6193fa8d840..aba182e2c9952 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1704,6 +1704,43 @@ mlir::LogicalResult CIRToLLVMPrefetchOpLowering::matchAndRewrite(
return mlir::success();
}
+// Make sure the LLVM function we are about to create a call for actually
+// exists, if not create one. Returns a function
+static void getOrCreateLLVMFuncOp(mlir::ConversionPatternRewriter &rewriter,
+ mlir::Operation *srcOp,
+ llvm::StringRef fnName, mlir::Type fnTy) {
+ auto modOp = srcOp->getParentOfType<mlir::ModuleOp>();
+ auto enclosingFnOp = srcOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
+ mlir::Operation *sourceSymbol =
+ mlir::SymbolTable::lookupSymbolIn(modOp, fnName);
+ if (!sourceSymbol) {
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPoint(enclosingFnOp);
+ mlir::LLVM::LLVMFuncOp::create(rewriter, srcOp->getLoc(), fnName, fnTy);
+ }
+}
+
+mlir::LogicalResult CIRToLLVMDeleteArrayOpLowering::matchAndRewrite(
+ cir::DeleteArrayOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ StringRef fnName = "_ZdaPv";
+
+ auto voidTy = rewriter.getType<mlir::LLVM::LLVMVoidType>();
+ mlir::Type llvmPtrTy =
+ mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+
+ mlir::Type fnTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {llvmPtrTy},
+ /*isVarArg=*/false);
+
+ getOrCreateLLVMFuncOp(rewriter, op, fnName, fnTy);
+
+ // Replace the operation with a call to _ZdaPv with the pointer argument
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
+ op, mlir::TypeRange{}, fnName, mlir::ValueRange{adaptor.getAddress()});
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
cir::PtrDiffOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/delete-array.cpp b/clang/test/CIR/CodeGen/delete-array.cpp
new file mode 100644
index 0000000000000..0ff08be61ef45
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delete-array.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+void test_delete_array(int *ptr) {
+ delete[] ptr;
+}
+
+// CIR: cir.delete.array
+
+// LLVM: call void @_ZdaPv{{(m)?}}(ptr
+
+// OGCG: call void @_ZdaPv{{(m)?}}(ptr
|
|
I referenced https://godbolt.org/z/63K4779ej . Please let me know if the checks need fixing. |
andykaylor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid this is an instance where the implementation in the incubator is incorrect. In particular, this doesn't handle the case of deleting an array of objects that require destruction.
https://godbolt.org/z/bTsqWzbPG
I think this needs to be fixed in the incubator (using the test case I linked above as a reference) before it is upstreamed. If you want to take that on, you'll need to use the behavior in classic codegen as a guide.
Because this implementation (when done correctly) involves reading the array cookie, which is target-specific, it would be nice to have a high-level CIR abstraction like cir.delete.array, but that will eventually need to incorporate array cookie handling when it is lowered, and it needs to get the name of the delete function from the AST, because _ZdaPv (which is hard-coded in the incubator implementation and this PR) isn't always the correct function to call.
I would suggest starting with a lower-level implementation (in the incubator) which follows classic codegen more closely and calls the delete function directly. This will be analogous to the way we are currently handling array new. Later, after this is working, we can introduce higher level cir.array.new and cir.array.delete operations to remove the ABI-specific details.
@wizardengineer is starting to look into this kind of upleveling of the CIR implementation, and I think it's important to have a correctly working implementation as the basis of the upleveling work. It seems cir.delete.array is an example of an operation where we were too hasty in pushing to the higher level representation and missed some important details in the process.
|
@andykaylor Okay, thanks for the description of the issue. I'll start taking a look at it. It may take me a while because I'm very inexperienced and don't know much of anything 😅 |
+1 |
xlauko
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add also print/parse roudn-trip test for the operation to test/CIR/IR
| // DeleteArrayOp | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| def CIR_DeleteArrayOp : CIR_Op<"delete.array"> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| def CIR_DeleteArrayOp : CIR_Op<"delete.array"> { | |
| def CIR_DeleteArrayOp : CIR_Op<"delete_array"> { |
| 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;` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| `cir.delete.array` operation deletes an array. For example, `delete[] ptr;` | |
| `cir.delete_array` operation deletes an array. For example, `delete[] ptr;` |
| let arguments = (ins CIR_PointerType:$address); | ||
|
|
||
| let assemblyFormat = [{ | ||
| $address `:` type($address) attr-dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $address `:` type($address) attr-dict | |
| $address `:` qualified(type($address)) attr-dict |
| // CIR: cir.delete.array | ||
|
|
||
| // LLVM: call void @_ZdaPv{{(m)?}}(ptr | ||
|
|
||
| // OGCG: call void @_ZdaPv{{(m)?}}(ptr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make more explicit checks
Upstream
DeleteArrayOpandclang/test/CIR/CodeGen/delete-array.cpp