Skip to content

Commit 7165cf3

Browse files
authored
[CIR] Fix structors for multidimensional arrrays (#159820)
This patchs implements array constructors and destructors for multidimensional arrays. This works by bitcasting the pointer to the first element to a one-dimensional array type of the same extent before lowering to a loop.
1 parent 1250095 commit 7165cf3

File tree

5 files changed

+171
-10
lines changed

5 files changed

+171
-10
lines changed

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -639,12 +639,22 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
639639
// are probably legitimate places where we could assume that this
640640
// doesn't happen, but it's not clear that it's worth it.
641641

642+
auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
643+
mlir::Type elementType = arrayTy.getElementType();
644+
645+
// This might be a multi-dimensional array. Find the innermost element type.
646+
while (auto maybeArrayTy = mlir::dyn_cast<cir::ArrayType>(elementType))
647+
elementType = maybeArrayTy.getElementType();
648+
cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
649+
642650
// Optimize for a constant count.
643651
if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) {
644652
if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>()) {
645653
// Just skip out if the constant count is zero.
646654
if (constIntAttr.getUInt() == 0)
647655
return;
656+
657+
arrayTy = cir::ArrayType::get(elementType, constIntAttr.getUInt());
648658
// Otherwise, emit the check.
649659
}
650660

@@ -655,10 +665,6 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
655665
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
656666
}
657667

658-
auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
659-
mlir::Type elementType = arrayTy.getElementType();
660-
cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
661-
662668
// Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of
663669
// LoweringPrepare.
664670

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -713,10 +713,11 @@ void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
713713
/// The array cannot be zero-length.
714714
///
715715
/// \param begin - a type* denoting the first element of the array
716-
/// \param end - a type* denoting one past the end of the array
716+
/// \param numElements - the number of elements in the array
717717
/// \param elementType - the element type of the array
718718
/// \param destroyer - the function to call to destroy elements
719-
void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end,
719+
void CIRGenFunction::emitArrayDestroy(mlir::Value begin,
720+
mlir::Value numElements,
720721
QualType elementType,
721722
CharUnits elementAlign,
722723
Destroyer *destroyer) {
@@ -727,9 +728,24 @@ void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end,
727728
mlir::Type cirElementType = convertTypeForMem(elementType);
728729
cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
729730

731+
uint64_t size = 0;
732+
733+
// Optimize for a constant array size.
734+
if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) {
735+
if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>())
736+
size = constIntAttr.getUInt();
737+
} else {
738+
cgm.errorNYI(begin.getDefiningOp()->getLoc(),
739+
"dynamic-length array expression");
740+
}
741+
742+
auto arrayTy = cir::ArrayType::get(cirElementType, size);
743+
mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy);
744+
730745
// Emit the dtor call that will execute for every array element.
731746
cir::ArrayDtor::create(
732-
builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) {
747+
builder, *currSrcLoc, arrayOp,
748+
[&](mlir::OpBuilder &b, mlir::Location loc) {
733749
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
734750
Address curAddr = Address(arg, cirElementType, elementAlign);
735751
assert(!cir::MissingFeatures::dtorCleanups());
@@ -772,8 +788,7 @@ void CIRGenFunction::emitDestroy(Address addr, QualType type,
772788
return;
773789

774790
mlir::Value begin = addr.getPointer();
775-
mlir::Value end; // This will be used for future non-constant counts.
776-
emitArrayDestroy(begin, end, type, elementAlign, destroyer);
791+
emitArrayDestroy(begin, length, type, elementAlign, destroyer);
777792

778793
// If the array destroy didn't use the length op, we can erase it.
779794
if (constantCount.use_empty())

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,7 @@ class CIRGenFunction : public CIRGenTypeCache {
10641064
/// even if no aggregate location is provided.
10651065
RValue emitAnyExprToTemp(const clang::Expr *e);
10661066

1067-
void emitArrayDestroy(mlir::Value begin, mlir::Value end,
1067+
void emitArrayDestroy(mlir::Value begin, mlir::Value numElements,
10681068
QualType elementType, CharUnits elementAlign,
10691069
Destroyer *destroyer);
10701070

clang/test/CIR/CodeGen/array-ctor.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,72 @@ void zero_sized() {
104104
// OGCG: alloca [0 x %struct.S]
105105
// OGCG-NOT: call void @_ZN1SC1Ev
106106
// OGCG: ret void
107+
108+
void multi_dimensional() {
109+
S s[3][5];
110+
}
111+
112+
// CIR-BEFORE-LPP: cir.func{{.*}} @_Z17multi_dimensionalv()
113+
// CIR-BEFORE-LPP: %[[S:.*]] = cir.alloca !cir.array<!cir.array<!rec_S x 5> x 3>, !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>, ["s", init]
114+
// CIR-BEFORE-LPP: %[[FLAT:.*]] = cir.cast(bitcast, %[[S]] : !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>), !cir.ptr<!cir.array<!rec_S x 15>>
115+
// CIR-BEFORE-LPP: cir.array.ctor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> {
116+
// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>):
117+
// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%[[ARG]]) : (!cir.ptr<!rec_S>) -> ()
118+
// CIR-BEFORE-LPP: cir.yield
119+
// CIR-BEFORE-LPP: }
120+
// CIR-BEFORE-LPP: cir.return
121+
122+
// CIR: cir.func{{.*}} @_Z17multi_dimensionalv()
123+
// CIR: %[[S:.*]] = cir.alloca !cir.array<!cir.array<!rec_S x 5> x 3>, !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>, ["s", init]
124+
// CIR: %[[CONST15:.*]] = cir.const #cir.int<15> : !u64i
125+
// CIR: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, {{.*}} : !cir.ptr<!cir.array<!rec_S x 15>>), !cir.ptr<!rec_S>
126+
// CIR: %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, %[[CONST15]] : !u64i), !cir.ptr<!rec_S>
127+
// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
128+
// CIR: cir.store %[[DECAY]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
129+
// CIR: cir.do {
130+
// CIR: %[[CURRENT:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
131+
// CIR: cir.call @_ZN1SC1Ev(%[[CURRENT]]) : (!cir.ptr<!rec_S>) -> ()
132+
// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
133+
// CIR: %[[NEXT:.*]] = cir.ptr_stride(%[[CURRENT]] : !cir.ptr<!rec_S>, %[[CONST1]] : !u64i), !cir.ptr<!rec_S>
134+
// CIR: cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
135+
// CIR: cir.yield
136+
// CIR: } while {
137+
// CIR: %[[CURRENT2:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
138+
// CIR: %[[CMP:.*]] = cir.cmp(ne, %[[CURRENT2]], %[[END_PTR]]) : !cir.ptr<!rec_S>, !cir.bool
139+
// CIR: cir.condition(%[[CMP]])
140+
// CIR: }
141+
// CIR: cir.return
142+
143+
// LLVM: define{{.*}} @_Z17multi_dimensionalv()
144+
// LLVM: %[[S:.*]] = alloca [3 x [5 x %struct.S]]
145+
// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[S]], i32 0
146+
// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 15
147+
// LLVM: %[[ITER:.*]] = alloca ptr
148+
// LLVM: store ptr %[[START]], ptr %[[ITER]]
149+
// LLVM: br label %[[LOOP:.*]]
150+
// LLVM: [[COND:.*]]:
151+
// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]]
152+
// LLVM: %[[DONE:.*]] = icmp ne ptr %[[CURRENT_CHECK]], %[[END]]
153+
// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]]
154+
// LLVM: [[LOOP]]:
155+
// LLVM: %[[CURRENT:.*]] = load ptr, ptr %[[ITER]]
156+
// LLVM: call void @_ZN1SC1Ev(ptr %[[CURRENT]])
157+
// LLVM: %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CURRENT]], i64 1
158+
// LLVM: store ptr %[[NEXT]], ptr %[[ITER]]
159+
// LLVM: br label %[[COND]]
160+
// LLVM: [[EXIT]]:
161+
// LLVM: ret void
162+
163+
// OGCG: define{{.*}} @_Z17multi_dimensionalv()
164+
// OGCG: %[[S:.*]] = alloca [3 x [5 x %struct.S]]
165+
// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}}
166+
// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 15
167+
// OGCG: br label %[[LOOP:.*]]
168+
// OGCG: [[LOOP]]:
169+
// OGCG: %[[CURRENT:.*]] = phi ptr [ %[[START]], %{{.*}} ], [ %[[NEXT:.*]], %[[LOOP]] ]
170+
// OGCG: call void @_ZN1SC1Ev(ptr{{.*}})
171+
// OGCG: %[[NEXT]] = getelementptr{{.*}} %struct.S{{.*}} i64 1
172+
// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]]
173+
// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
174+
// OGCG: [[EXIT]]:
175+
// OGCG: ret void

clang/test/CIR/CodeGen/array-dtor.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,74 @@ void test_cleanup_zero_length_array() {
102102
// OGCG: alloca [0 x %struct.S]
103103
// OGCG-NOT: call void @_ZN1SD1Ev
104104
// OGCG: ret void
105+
106+
void multi_dimensional() {
107+
S s[3][5];
108+
}
109+
110+
// CIR-BEFORE-LPP: cir.func{{.*}} @_Z17multi_dimensionalv()
111+
// CIR-BEFORE-LPP: %[[S:.*]] = cir.alloca !cir.array<!cir.array<!rec_S x 5> x 3>, !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>, ["s"]
112+
// CIR-BEFORE-LPP: %[[FLAT:.*]] = cir.cast(bitcast, %[[S]] : !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>), !cir.ptr<!cir.array<!rec_S x 15>>
113+
// CIR-BEFORE-LPP: cir.array.dtor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> {
114+
// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>):
115+
// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[ARG]]) nothrow : (!cir.ptr<!rec_S>) -> ()
116+
// CIR-BEFORE-LPP: cir.yield
117+
// CIR-BEFORE-LPP: }
118+
// CIR-BEFORE-LPP: cir.return
119+
120+
// CIR: cir.func{{.*}} @_Z17multi_dimensionalv()
121+
// CIR: %[[S:.*]] = cir.alloca !cir.array<!cir.array<!rec_S x 5> x 3>, !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>, ["s"]
122+
// CIR: %[[FLAT:.*]] = cir.cast(bitcast, %[[S]] : !cir.ptr<!cir.array<!cir.array<!rec_S x 5> x 3>>), !cir.ptr<!cir.array<!rec_S x 15>>
123+
// CIR: %[[CONST14:.*]] = cir.const #cir.int<14> : !u64i
124+
// CIR: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>>), !cir.ptr<!rec_S>
125+
// CIR: %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, %[[CONST14]] : !u64i), !cir.ptr<!rec_S>
126+
// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
127+
// CIR: cir.store %[[END_PTR]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
128+
// CIR: cir.do {
129+
// CIR: %[[CUR:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
130+
// CIR: cir.call @_ZN1SD1Ev(%[[CUR]]) nothrow : (!cir.ptr<!rec_S>) -> ()
131+
// CIR: %[[NEG1:.*]] = cir.const #cir.int<-1> : !s64i
132+
// CIR: %[[PREV:.*]] = cir.ptr_stride(%[[CUR]] : !cir.ptr<!rec_S>, %[[NEG1]] : !s64i), !cir.ptr<!rec_S>
133+
// CIR: cir.store %[[PREV]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
134+
// CIR: cir.yield
135+
// CIR: } while {
136+
// CIR: %[[CHK:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
137+
// CIR: %[[CMP:.*]] = cir.cmp(ne, %[[CHK]], %[[DECAY]])
138+
// CIR: cir.condition(%[[CMP]])
139+
// CIR: }
140+
// CIR: cir.return
141+
142+
// LLVM: define{{.*}} void @_Z17multi_dimensionalv()
143+
// LLVM: %[[S:.*]] = alloca [3 x [5 x %struct.S]]
144+
// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[S]], i32 0
145+
// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 14
146+
// LLVM: %[[ITER:.*]] = alloca ptr
147+
// LLVM: store ptr %[[END]], ptr %[[ITER]]
148+
// LLVM: br label %[[LOOP:.*]]
149+
// LLVM: [[COND:.*]]:
150+
// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]]
151+
// LLVM: %[[DONE:.*]] = icmp ne ptr %[[CURRENT_CHECK]], %[[START]]
152+
// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]]
153+
// LLVM: [[LOOP]]:
154+
// LLVM: %[[CUR:.*]] = load ptr, ptr %[[ITER]]
155+
// LLVM: call void @_ZN1SD1Ev(ptr %[[CUR]])
156+
// LLVM: %[[PREV:.*]] = getelementptr %struct.S, ptr %[[CUR]], i64 -1
157+
// LLVM: store ptr %[[PREV]], ptr %[[ITER]]
158+
// LLVM: br label %[[COND]]
159+
// LLVM: [[EXIT]]:
160+
// LLVM: ret void
161+
162+
// OGCG: define{{.*}} void @_Z17multi_dimensionalv()
163+
// OGCG: %[[ARRAY:.*]] = alloca [3 x [5 x %struct.S]]
164+
// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}}
165+
// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 15
166+
// OGCG: br label %[[LOOP:.*]]
167+
// OGCG: [[LOOP]]:
168+
// OGCG: %[[NEXT:.*]] = phi ptr [ %[[END]], %{{.*}} ], [ %[[LAST:.*]], %[[LOOP]] ]
169+
// OGCG: %[[LAST]] = getelementptr{{.*}} %struct.S{{.*}}, ptr %[[NEXT]], i64 -1
170+
// OGCG: call void @_ZN1SD1Ev(ptr{{.*}} %[[LAST]])
171+
// OGCG: %[[DONE:.*]] = icmp eq ptr %[[LAST]], %[[START]]
172+
// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
173+
// OGCG: [[EXIT]]:
174+
// OGCG: ret void
175+

0 commit comments

Comments
 (0)