llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Morris Hafner (mmha) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/159820.diff 4 Files Affected: - (modified) clang/lib/CIR/CodeGen/CIRGenClass.cpp (+10-4) - (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+21-5) - (modified) clang/test/CIR/CodeGen/array-ctor.cpp (+69) - (modified) clang/test/CIR/CodeGen/array-dtor.cpp (+71) ``````````diff diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 0a8dc2b62fe21..be416f6f69eb0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -639,12 +639,22 @@ void CIRGenFunction::emitCXXAggrConstructorCall( // are probably legitimate places where we could assume that this // doesn't happen, but it's not clear that it's worth it. + auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType()); + mlir::Type elementType = arrayTy.getElementType(); + + // This might be a multi-dimensional array. Find the innermost element type. + while(auto maybeArrayTy = mlir::dyn_cast<cir::ArrayType>(elementType)) + elementType = maybeArrayTy.getElementType(); + cir::PointerType ptrToElmType = builder.getPointerTo(elementType); + // Optimize for a constant count. if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) { if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>()) { // Just skip out if the constant count is zero. if (constIntAttr.getUInt() == 0) return; + + arrayTy = cir::ArrayType::get(elementType, constIntAttr.getUInt()); // Otherwise, emit the check. } @@ -655,10 +665,6 @@ void CIRGenFunction::emitCXXAggrConstructorCall( cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression"); } - auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType()); - mlir::Type elementType = arrayTy.getElementType(); - cir::PointerType ptrToElmType = builder.getPointerTo(elementType); - // Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of // LoweringPrepare. diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index d028a98eaad02..65e3e71ad9852 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -713,10 +713,11 @@ void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr, /// The array cannot be zero-length. /// /// \param begin - a type* denoting the first element of the array -/// \param end - a type* denoting one past the end of the array +/// \param numElements - the number of elements in the array /// \param elementType - the element type of the array /// \param destroyer - the function to call to destroy elements -void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end, +void CIRGenFunction::emitArrayDestroy(mlir::Value begin, + mlir::Value numElements, QualType elementType, CharUnits elementAlign, Destroyer *destroyer) { @@ -727,9 +728,25 @@ void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end, mlir::Type cirElementType = convertTypeForMem(elementType); cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType); + uint64_t size = 0; + + // Optimize for a constant array size. + if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) { + if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>()) { + size = constIntAttr.getUInt(); + } + } else { + cgm.errorNYI(begin.getDefiningOp()->getLoc(), + "dynamic-length array expression"); + } + + auto arrayTy = cir::ArrayType::get(cirElementType, size); + mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy); + // Emit the dtor call that will execute for every array element. cir::ArrayDtor::create( - builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) { + builder, *currSrcLoc, arrayOp, + [&](mlir::OpBuilder &b, mlir::Location loc) { auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc); Address curAddr = Address(arg, cirElementType, elementAlign); assert(!cir::MissingFeatures::dtorCleanups()); @@ -772,8 +789,7 @@ void CIRGenFunction::emitDestroy(Address addr, QualType type, return; mlir::Value begin = addr.getPointer(); - mlir::Value end; // This will be used for future non-constant counts. - emitArrayDestroy(begin, end, type, elementAlign, destroyer); + emitArrayDestroy(begin, length, type, elementAlign, destroyer); // If the array destroy didn't use the length op, we can erase it. if (constantCount.use_empty()) diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp index c373acf0bff8c..bad4868ed8c34 100644 --- a/clang/test/CIR/CodeGen/array-ctor.cpp +++ b/clang/test/CIR/CodeGen/array-ctor.cpp @@ -104,3 +104,72 @@ void zero_sized() { // OGCG: alloca [0 x %struct.S] // OGCG-NOT: call void @_ZN1SC1Ev // OGCG: ret void + +void multi_dimensional() { + S s[3][5]; +} + +// CIR-BEFORE-LPP: cir.func{{.*}} @_Z17multi_dimensionalv() +// 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] +// 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>> +// CIR-BEFORE-LPP: cir.array.ctor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> { +// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>): +// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%[[ARG]]) : (!cir.ptr<!rec_S>) -> () +// CIR-BEFORE-LPP: cir.yield +// CIR-BEFORE-LPP: } +// CIR-BEFORE-LPP: cir.return + +// CIR: cir.func{{.*}} @_Z17multi_dimensionalv() +// 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] +// CIR: %[[CONST15:.*]] = cir.const #cir.int<15> : !u64i +// CIR: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, {{.*}} : !cir.ptr<!cir.array<!rec_S x 15>>), !cir.ptr<!rec_S> +// CIR: %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, %[[CONST15]] : !u64i), !cir.ptr<!rec_S> +// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"] +// CIR: cir.store %[[DECAY]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> +// CIR: cir.do { +// CIR: %[[CURRENT:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> +// CIR: cir.call @_ZN1SC1Ev(%[[CURRENT]]) : (!cir.ptr<!rec_S>) -> () +// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i +// CIR: %[[NEXT:.*]] = cir.ptr_stride(%[[CURRENT]] : !cir.ptr<!rec_S>, %[[CONST1]] : !u64i), !cir.ptr<!rec_S> +// CIR: cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> +// CIR: cir.yield +// CIR: } while { +// CIR: %[[CURRENT2:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> +// CIR: %[[CMP:.*]] = cir.cmp(ne, %[[CURRENT2]], %[[END_PTR]]) : !cir.ptr<!rec_S>, !cir.bool +// CIR: cir.condition(%[[CMP]]) +// CIR: } +// CIR: cir.return + +// LLVM: define{{.*}} @_Z17multi_dimensionalv() +// LLVM: %[[S:.*]] = alloca [3 x [5 x %struct.S]] +// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[S]], i32 0 +// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 15 +// LLVM: %[[ITER:.*]] = alloca ptr +// LLVM: store ptr %[[START]], ptr %[[ITER]] +// LLVM: br label %[[LOOP:.*]] +// LLVM: [[COND:.*]]: +// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]] +// LLVM: %[[DONE:.*]] = icmp ne ptr %[[CURRENT_CHECK]], %[[END]] +// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]] +// LLVM: [[LOOP]]: +// LLVM: %[[CURRENT:.*]] = load ptr, ptr %[[ITER]] +// LLVM: call void @_ZN1SC1Ev(ptr %[[CURRENT]]) +// LLVM: %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CURRENT]], i64 1 +// LLVM: store ptr %[[NEXT]], ptr %[[ITER]] +// LLVM: br label %[[COND]] +// LLVM: [[EXIT]]: +// LLVM: ret void + +// OGCG: define{{.*}} @_Z17multi_dimensionalv() +// OGCG: %[[S:.*]] = alloca [3 x [5 x %struct.S]] +// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}} +// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 15 +// OGCG: br label %[[LOOP:.*]] +// OGCG: [[LOOP]]: +// OGCG: %[[CURRENT:.*]] = phi ptr [ %[[START]], %{{.*}} ], [ %[[NEXT:.*]], %[[LOOP]] ] +// OGCG: call void @_ZN1SC1Ev(ptr{{.*}}) +// OGCG: %[[NEXT]] = getelementptr{{.*}} %struct.S{{.*}} i64 1 +// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]] +// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]] +// OGCG: [[EXIT]]: +// OGCG: ret void diff --git a/clang/test/CIR/CodeGen/array-dtor.cpp b/clang/test/CIR/CodeGen/array-dtor.cpp index 3edc6f1a6538d..36db265a6dfed 100644 --- a/clang/test/CIR/CodeGen/array-dtor.cpp +++ b/clang/test/CIR/CodeGen/array-dtor.cpp @@ -102,3 +102,74 @@ void test_cleanup_zero_length_array() { // OGCG: alloca [0 x %struct.S] // OGCG-NOT: call void @_ZN1SD1Ev // OGCG: ret void + +void multi_dimensional() { + S s[3][5]; +} + +// CIR-BEFORE-LPP: cir.func{{.*}} @_Z17multi_dimensionalv() +// 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"] +// 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>> +// CIR-BEFORE-LPP: cir.array.dtor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> { +// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>): +// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[ARG]]) nothrow : (!cir.ptr<!rec_S>) -> () +// CIR-BEFORE-LPP: cir.yield +// CIR-BEFORE-LPP: } +// CIR-BEFORE-LPP: cir.return + +// CIR: cir.func{{.*}} @_Z17multi_dimensionalv() +// 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"] +// 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>> +// CIR: %[[CONST14:.*]] = cir.const #cir.int<14> : !u64i +// CIR: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>>), !cir.ptr<!rec_S> +// CIR: %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, %[[CONST14]] : !u64i), !cir.ptr<!rec_S> +// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"] +// CIR: cir.store %[[END_PTR]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> +// CIR: cir.do { +// CIR: %[[CUR:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> +// CIR: cir.call @_ZN1SD1Ev(%[[CUR]]) nothrow : (!cir.ptr<!rec_S>) -> () +// CIR: %[[NEG1:.*]] = cir.const #cir.int<-1> : !s64i +// CIR: %[[PREV:.*]] = cir.ptr_stride(%[[CUR]] : !cir.ptr<!rec_S>, %[[NEG1]] : !s64i), !cir.ptr<!rec_S> +// CIR: cir.store %[[PREV]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> +// CIR: cir.yield +// CIR: } while { +// CIR: %[[CHK:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> +// CIR: %[[CMP:.*]] = cir.cmp(ne, %[[CHK]], %[[DECAY]]) +// CIR: cir.condition(%[[CMP]]) +// CIR: } +// CIR: cir.return + +// LLVM: define{{.*}} void @_Z17multi_dimensionalv() +// LLVM: %[[S:.*]] = alloca [3 x [5 x %struct.S]] +// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[S]], i32 0 +// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 14 +// LLVM: %[[ITER:.*]] = alloca ptr +// LLVM: store ptr %[[END]], ptr %[[ITER]] +// LLVM: br label %[[LOOP:.*]] +// LLVM: [[COND:.*]]: +// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]] +// LLVM: %[[DONE:.*]] = icmp ne ptr %[[CURRENT_CHECK]], %[[START]] +// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]] +// LLVM: [[LOOP]]: +// LLVM: %[[CUR:.*]] = load ptr, ptr %[[ITER]] +// LLVM: call void @_ZN1SD1Ev(ptr %[[CUR]]) +// LLVM: %[[PREV:.*]] = getelementptr %struct.S, ptr %[[CUR]], i64 -1 +// LLVM: store ptr %[[PREV]], ptr %[[ITER]] +// LLVM: br label %[[COND]] +// LLVM: [[EXIT]]: +// LLVM: ret void + +// OGCG: define{{.*}} void @_Z17multi_dimensionalv() +// OGCG: %[[ARRAY:.*]] = alloca [3 x [5 x %struct.S]] +// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}} +// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 15 +// OGCG: br label %[[LOOP:.*]] +// OGCG: [[LOOP]]: +// OGCG: %[[NEXT:.*]] = phi ptr [ %[[END]], %{{.*}} ], [ %[[LAST:.*]], %[[LOOP]] ] +// OGCG: %[[LAST]] = getelementptr{{.*}} %struct.S{{.*}}, ptr %[[NEXT]], i64 -1 +// OGCG: call void @_ZN1SD1Ev(ptr{{.*}} %[[LAST]]) +// OGCG: %[[DONE:.*]] = icmp eq ptr %[[LAST]], %[[START]] +// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]] +// OGCG: [[EXIT]]: +// OGCG: ret void + `````````` </details> https://github.com/llvm/llvm-project/pull/159820 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits