[clang] [CIR] Upstream support for array new with empty initializer list (PR #178806)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 08:52:43 PST 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/178806
>From 284889dcb1164cafcc86b9b0d1b8e7ed19c766d2 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 29 Jan 2026 17:05:23 -0800
Subject: [PATCH 1/4] [CIR] Upstream support for array new with empty
initializer list
This adds CIR support for array new with an empty initializer list for
zero-initializable types.
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 35 ++++++
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 6 +
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 114 +++++++++++++++++-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 ++
clang/test/CIR/CodeGen/new.cpp | 66 ++++++----
6 files changed, 208 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ee84df93b4933..b6db00e11c639 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3540,6 +3540,41 @@ def CIR_MemCpyOp : CIR_MemOp<"libc.memcpy"> {
// TODO: MemMoveOp
+//===----------------------------------------------------------------------===//
+// MemSetOp
+//===----------------------------------------------------------------------===//
+
+def CIR_MemSetOp : CIR_Op<"libc.memset"> {
+ let summary = "Equivalent to libc's `memset`";
+ let description = [{
+ Given the CIR pointer, `dst`, `cir.libc.memset` will set the first `len`
+ bytes of the memory pointed by `dst` to the specified `val`.
+
+ Examples:
+
+ ```mlir
+ // Set 2 bytes from a record to 0:
+ %2 = cir.const #cir.int<2> : !u32i
+ %3 = cir.const #cir.int<0> : !u32i
+ %zero = cir.cast integral %3 : !s32i -> !u8i
+ cir.libc.memset %2 bytes from %record set to %zero : !cir.ptr<!void>,
+ !s32i, !u64i
+ ```
+ }];
+
+ let arguments = (ins
+ Arg<CIR_VoidPtrType, "", [MemWrite]>:$dst,
+ CIR_SInt32:$val,
+ CIR_AnyFundamentalUIntType:$len
+ );
+
+ let assemblyFormat = [{
+ $len `bytes` `from` $dst `set` `to` $val attr-dict
+ `:` qualified(type($dst)) `,` type($val) `,` type($len)
+ }];
+}
+
+
//===----------------------------------------------------------------------===//
// ReturnAddrOp and FrameAddrOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index cdd9fb950b8b2..12575f51366fa 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -238,6 +238,7 @@ struct MissingFeatures {
static bool cleanupAfterErrorDiags() { return false; }
static bool cleanupAppendInsts() { return false; }
static bool cleanupBranchThrough() { return false; }
+ static bool cleanupDeactivationScope() { return false; }
static bool cleanupIndexAndBIAdjustment() { return false; }
static bool cleanupWithPreservedValues() { return false; }
static bool cleanupsToDeactivate() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index dedb369bf3f67..51c5a238cf258 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -197,6 +197,12 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
mlir::Value src, mlir::Value len) {
return cir::MemCpyOp::create(*this, loc, dst, src, len);
}
+
+ cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst,
+ mlir::Value val, mlir::Value len) {
+ val = createIntCast(val, cir::IntType::get(getContext(), 32, true));
+ return cir::MemSetOp::create(*this, loc, dst, val, len);
+ }
// ---------------------------
cir::DataMemberAttr getDataMemberAttr(cir::DataMemberType ty,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 98cf75f0d69e0..087607f19e332 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/MissingFeatures.h"
@@ -568,13 +569,111 @@ void CIRGenFunction::emitNewArrayInitializer(
if (!e->hasInitializer())
return;
+ Address curPtr = beginPtr;
+
unsigned initListElements = 0;
const Expr *init = e->getInitializer();
+ Address endOfInit = Address::invalid();
+ QualType::DestructionKind dtorKind = elementType.isDestructedType();
+ assert(!cir::MissingFeatures::cleanupDeactivationScope());
+
+ // Attempt to perform zero-initialization using memset.
+ auto tryMemsetInitialization = [&]() -> bool {
+ mlir::Location loc = numElements.getLoc();
+
+ // FIXME: If the type is a pointer-to-data-member under the Itanium ABI,
+ // we can initialize with a memset to -1.
+ if (!cgm.getTypes().isZeroInitializable(elementType))
+ return false;
+
+ // Optimization: since zero initialization will just set the memory
+ // to all zeroes, generate a single memset to do it in one shot.
+
+ // Subtract out the size of any elements we've already initialized.
+ auto remainingSize = allocSizeWithoutCookie;
+ if (initListElements) {
+ // We know this can't overflow; we check this when doing the allocation.
+ unsigned initializedSize =
+ getContext().getTypeSizeInChars(elementType).getQuantity() *
+ initListElements;
+ cir::ConstantOp initSizeOp =
+ builder.getConstInt(loc, remainingSize.getType(), initializedSize);
+ remainingSize = builder.createSub(loc, remainingSize, initSizeOp);
+ }
+
+ // Create the memset.
+ mlir::Value castOp =
+ builder.createPtrBitcast(curPtr.getPointer(), cgm.voidTy);
+ builder.createMemSet(loc, castOp, builder.getConstInt(loc, cgm.sInt32Ty, 0),
+ remainingSize);
+ return true;
+ };
+
const InitListExpr *ile = dyn_cast<InitListExpr>(init);
- if (ile) {
- cgm.errorNYI(ile->getSourceRange(), "emitNewArrayInitializer: init list");
- return;
+ const CXXParenListInitExpr *cplie = nullptr;
+ const StringLiteral *sl = nullptr;
+ const ObjCEncodeExpr *ocee = nullptr;
+ const Expr *ignoreParen = nullptr;
+ if (!ile) {
+ ignoreParen = init->IgnoreParenImpCasts();
+ cplie = dyn_cast<CXXParenListInitExpr>(ignoreParen);
+ sl = dyn_cast<StringLiteral>(ignoreParen);
+ ocee = dyn_cast<ObjCEncodeExpr>(ignoreParen);
+ }
+ // If the initializer is an initializer list, first do the explicit elements.
+ if (ile || cplie || sl || ocee) {
+ // Initializing from a (braced) string literal is a special case; the init
+ // list element does not initialize a (single) array element.
+ if ((ile && ile->isStringLiteralInit()) || sl || ocee) {
+ cgm.errorNYI(ile->getSourceRange(),
+ "emitNewArrayInitializer: string literal init");
+ return;
+ }
+
+ ArrayRef<const Expr *> initExprs =
+ ile ? ile->inits() : cplie->getInitExprs();
+ initListElements = initExprs.size();
+
+ // If this is a multi-dimensional array new, we will initialize multiple
+ // elements with each init list element.
+ QualType allocType = e->getAllocatedType();
+ if (const ConstantArrayType *cat = dyn_cast_or_null<ConstantArrayType>(
+ allocType->getAsArrayTypeUnsafe())) {
+ cgm.errorNYI(ile->getSourceRange(),
+ "emitNewArrayInitializer: constant array init");
+ return;
+ }
+
+ // Enter a partial-destruction Cleanup if necessary.
+ if (dtorKind) {
+ cgm.errorNYI(ile->getSourceRange(),
+ "emitNewArrayInitializer: init requires dtor");
+ return;
+ }
+
+ if (!initExprs.empty()) {
+ cgm.errorNYI(ile->getSourceRange(),
+ "emitNewArrayInitializer: non-empty init list");
+ return;
+ }
+
+ // The remaining elements are filled with the array filler expression.
+ init = ile ? ile->getArrayFiller() : cplie->getArrayFiller();
+
+ // Extract the initializer for the individual array elements by pulling
+ // out the array filler from all the nested initializer lists. This avoids
+ // generating a nested loop for the initialization.
+ while (init && init->getType()->isConstantArrayType()) {
+ auto *subIle = dyn_cast<InitListExpr>(init);
+ if (!subIle)
+ break;
+ assert(subIle->getNumInits() == 0 && "explicit inits in array filler?");
+ init = subIle->getArrayFiller();
+ }
+
+ // Switch back to initializing one base element at a time.
+ curPtr = curPtr.withElementType(builder, beginPtr.getElementType());
}
// If all elements have already been initialized, skip any further
@@ -609,6 +708,15 @@ void CIRGenFunction::emitNewArrayInitializer(
return;
}
+ // If this is value-initialization, we can usually use memset.
+ if (isa<ImplicitValueInitExpr>(init)) {
+ if (tryMemsetInitialization())
+ return;
+ cgm.errorNYI(init->getSourceRange(),
+ "emitNewArrayInitializer: implicit value init");
+ return;
+ }
+
cgm.errorNYI(init->getSourceRange(),
"emitNewArrayInitializer: unsupported initializer");
return;
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 091489c404642..871eb2920d170 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -196,6 +196,18 @@ mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMMemSetOpLowering::matchAndRewrite(
+ cir::MemSetOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto converted = mlir::LLVM::TruncOp::create(
+ rewriter, op.getLoc(), mlir::IntegerType::get(op.getContext(), 8),
+ adaptor.getVal());
+ rewriter.replaceOpWithNewOp<mlir::LLVM::MemsetOp>(op, adaptor.getDst(),
+ converted, adaptor.getLen(),
+ /*isVolatile=*/false);
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMSqrtOpLowering::matchAndRewrite(
cir::SqrtOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 80a4b476226af..c097d88c9114d 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -187,8 +187,8 @@ void t_new_constant_size() {
// CHECK: cir.func{{.*}} @_Z19t_new_constant_sizev()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
-// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<128> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<128> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.double>
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
// CHECK: cir.return
@@ -215,14 +215,14 @@ void t_constant_size_nontrivial() {
// CHECK: cir.func{{.*}} @_Z26t_constant_size_nontrivialv()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["p", init] {alignment = 8 : i64}
-// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
-// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<11> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<11> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
-// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
-// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
-// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
+// CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
+// CHECK: %[[COOKIE_SIZE:.*]] = cir.const #cir.int<8> : !s32i
+// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_C>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>
@@ -255,14 +255,14 @@ void t_constant_size_nontrivial2() {
// CHECK: cir.func{{.*}} @_Z27t_constant_size_nontrivial2v()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["p", init] {alignment = 8 : i64}
-// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
-// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<20> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<20> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
-// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
-// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
-// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
+// CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
+// CHECK: %[[COOKIE_SIZE:.*]] = cir.const #cir.int<8> : !s32i
+// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_D>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>
@@ -287,16 +287,16 @@ void t_align16_nontrivial() {
// CHECK: cir.func{{.*}} @_Z20t_align16_nontrivialv()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>, ["p", init] {alignment = 8 : i64}
-// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<2> : !u64i
-// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<48> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<2> : !u64i
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<48> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_OFFSET:.*]] = cir.const #cir.int<8> : !s32i
// CHECK: %[[COOKIE_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_OFFSET]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
-// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
-// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
-// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
+// CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
+// CHECK: %[[COOKIE_SIZE:.*]] = cir.const #cir.int<16> : !s32i
+// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_E>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>
@@ -324,10 +324,10 @@ void t_new_multidim_constant_size() {
auto p = new double[2][3][4];
}
-// CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev()
+// CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>, ["p", init] {alignment = 8 : i64}
-// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<192> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<192> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>
// CHECK: }
@@ -341,3 +341,23 @@ void t_new_multidim_constant_size() {
// OGCG: %[[P_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 192)
// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
+
+void t_constant_size_memset_init() {
+ auto p = new int[16] {};
+}
+
+// CHECK: cir.func {{.*}} @_Z27t_constant_size_memset_initv()
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
+// CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
+// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes from %[[VOID_PTR]] set to %[[ZERO]] : !cir.ptr<!void>, !s32i, !u64i
+
+// LLVM: define {{.*}} void @_Z27t_constant_size_memset_initv()
+// LLVM: %[[P:.*]] = call ptr @_Znam(i64 64)
+// LLVM: call void @llvm.memset.p0.i64(ptr %[[P]], i8 0, i64 64, i1 false)
+
+// OGCG: define {{.*}} void @_Z27t_constant_size_memset_initv()
+// OGCG: %[[P:.*]] = call{{.*}} ptr @_Znam(i64{{.*}} 64)
+// OGCG: call void @llvm.memset.p0.i64(ptr{{.*}} %[[P]], i8 0, i64 64, i1 false)
>From c4d56169dedf9fbb638c0c8aa8bbe76ef800196d Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 29 Jan 2026 18:01:52 -0800
Subject: [PATCH 2/4] Fix unused variables
---
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 087607f19e332..7f87d9b0f73b7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -574,7 +574,6 @@ void CIRGenFunction::emitNewArrayInitializer(
unsigned initListElements = 0;
const Expr *init = e->getInitializer();
- Address endOfInit = Address::invalid();
QualType::DestructionKind dtorKind = elementType.isDestructedType();
assert(!cir::MissingFeatures::cleanupDeactivationScope());
@@ -640,6 +639,7 @@ void CIRGenFunction::emitNewArrayInitializer(
QualType allocType = e->getAllocatedType();
if (const ConstantArrayType *cat = dyn_cast_or_null<ConstantArrayType>(
allocType->getAsArrayTypeUnsafe())) {
+ (void)cat;
cgm.errorNYI(ile->getSourceRange(),
"emitNewArrayInitializer: constant array init");
return;
>From a57c4cbf5d03311cdb63c445b53b14ce0a4cc974 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 30 Jan 2026 11:01:30 -0800
Subject: [PATCH 3/4] Address review feedback
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 16 +++++++---------
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 2 +-
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 2 +-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 9 +++------
clang/test/CIR/CodeGen/new.cpp | 4 ++--
5 files changed, 14 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index b6db00e11c639..344f2d20bdf9c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3553,28 +3553,26 @@ def CIR_MemSetOp : CIR_Op<"libc.memset"> {
Examples:
```mlir
- // Set 2 bytes from a record to 0:
- %2 = cir.const #cir.int<2> : !u32i
- %3 = cir.const #cir.int<0> : !u32i
- %zero = cir.cast integral %3 : !s32i -> !u8i
- cir.libc.memset %2 bytes from %record set to %zero : !cir.ptr<!void>,
- !s32i, !u64i
+ // Set 2 bytes in a record to 0:
+ %len = cir.const #cir.int<2> : !u32i
+ %zero = cir.const #cir.int<0> : !u8i
+ cir.libc.memset %len bytes at %record to %zero : !cir.ptr<!void>,
+ !s32i, !u64i
```
}];
let arguments = (ins
Arg<CIR_VoidPtrType, "", [MemWrite]>:$dst,
- CIR_SInt32:$val,
+ CIR_UInt8:$val,
CIR_AnyFundamentalUIntType:$len
);
let assemblyFormat = [{
- $len `bytes` `from` $dst `set` `to` $val attr-dict
+ $len `bytes` `at` $dst `to` $val attr-dict
`:` qualified(type($dst)) `,` type($val) `,` type($len)
}];
}
-
//===----------------------------------------------------------------------===//
// ReturnAddrOp and FrameAddrOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 51c5a238cf258..af9c4fcdeb8c7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -200,7 +200,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst,
mlir::Value val, mlir::Value len) {
- val = createIntCast(val, cir::IntType::get(getContext(), 32, true));
+ val = createIntCast(val, getUInt8Ty());
return cir::MemSetOp::create(*this, loc, dst, val, len);
}
// ---------------------------
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 7f87d9b0f73b7..3731773cffa45 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -604,7 +604,7 @@ void CIRGenFunction::emitNewArrayInitializer(
// Create the memset.
mlir::Value castOp =
builder.createPtrBitcast(curPtr.getPointer(), cgm.voidTy);
- builder.createMemSet(loc, castOp, builder.getConstInt(loc, cgm.sInt32Ty, 0),
+ builder.createMemSet(loc, castOp, builder.getConstInt(loc, cgm.uInt8Ty, 0),
remainingSize);
return true;
};
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 871eb2920d170..485f61d3d6a9d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -199,12 +199,9 @@ mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMMemSetOpLowering::matchAndRewrite(
cir::MemSetOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- auto converted = mlir::LLVM::TruncOp::create(
- rewriter, op.getLoc(), mlir::IntegerType::get(op.getContext(), 8),
- adaptor.getVal());
- rewriter.replaceOpWithNewOp<mlir::LLVM::MemsetOp>(op, adaptor.getDst(),
- converted, adaptor.getLen(),
- /*isVolatile=*/false);
+ rewriter.replaceOpWithNewOp<mlir::LLVM::MemsetOp>(
+ op, adaptor.getDst(), adaptor.getVal(), adaptor.getLen(),
+ /*isVolatile=*/false);
return mlir::success();
}
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index c097d88c9114d..9f454770e3960 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -351,8 +351,8 @@ void t_constant_size_memset_init() {
// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
-// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
-// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes from %[[VOID_PTR]] set to %[[ZERO]] : !cir.ptr<!void>, !s32i, !u64i
+// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
+// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes at %[[VOID_PTR]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: define {{.*}} void @_Z27t_constant_size_memset_initv()
// LLVM: %[[P:.*]] = call ptr @_Znam(i64 64)
>From f46ce89d21563ef580c65f2e415af71d88453b3c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 30 Jan 2026 12:08:06 -0800
Subject: [PATCH 4/4] Replace cast with assert
---
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index af9c4fcdeb8c7..99a8152e7d365 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -200,7 +200,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst,
mlir::Value val, mlir::Value len) {
- val = createIntCast(val, getUInt8Ty());
+ assert(val.getType() == getUInt8Ty());
return cir::MemSetOp::create(*this, loc, dst, val, len);
}
// ---------------------------
More information about the cfe-commits
mailing list