diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index b03c5bb9c4218..0df2e494234da 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -1252,6 +1252,45 @@ struct FreeMemOpConversion : public FIROpConversion { }; } // namespace +// Convert subcomponent array indices from column-major to row-major ordering. +static llvm::SmallVector +convertSubcomponentIndices(mlir::Location loc, mlir::Type eleTy, + mlir::ValueRange indices, + mlir::Type *retTy = nullptr) { + llvm::SmallVector result; + llvm::SmallVector arrayIndices; + + auto appendArrayIndices = [&] { + if (arrayIndices.empty()) + return; + std::reverse(arrayIndices.begin(), arrayIndices.end()); + result.append(arrayIndices.begin(), arrayIndices.end()); + arrayIndices.clear(); + }; + + for (mlir::Value index : indices) { + // Component indices can be field index to select a component, or array + // index, to select an element in an array component. + if (auto structTy = mlir::dyn_cast(eleTy)) { + std::int64_t cstIndex = getConstantIntValue(index); + assert(cstIndex < (int64_t)structTy.getBody().size() && + "out-of-bounds struct field index"); + eleTy = structTy.getBody()[cstIndex]; + appendArrayIndices(); + result.push_back(index); + } else if (auto arrayTy = + mlir::dyn_cast(eleTy)) { + eleTy = arrayTy.getElementType(); + arrayIndices.push_back(index); + } else + fir::emitFatalError(loc, "Unexpected subcomponent type"); + } + appendArrayIndices(); + if (retTy) + *retTy = eleTy; + return result; +} + /// Common base class for embox to descriptor conversion. template struct EmboxCommonConversion : public FIROpConversion { @@ -1556,21 +1595,9 @@ struct EmboxCommonConversion : public FIROpConversion { resultTy = arrayTy.getElementType(); gepArgs.push_back(interiorIndex); } - for (mlir::Value componentIndex : componentIndices) { - // Component indices can be field index to select a component, or array - // index, to select an element in an array component. - if (auto structTy = resultTy.dyn_cast()) { - std::int64_t cstIndex = getConstantIntValue(componentIndex); - resultTy = structTy.getBody()[cstIndex]; - } else if (auto arrayTy = - resultTy.dyn_cast()) { - resultTy = arrayTy.getElementType(); - } else { - fir::emitFatalError(loc, "corrupted component GEP generated being " - "generated in fir.embox/fir.rebox"); - } - gepArgs.push_back(componentIndex); - } + llvm::SmallVector gepIndices = + convertSubcomponentIndices(loc, resultTy, componentIndices, &resultTy); + gepArgs.append(gepIndices.begin(), gepIndices.end()); if (substringOffset) { if (auto arrayTy = resultTy.dyn_cast()) { gepArgs.push_back(*substringOffset); @@ -2407,10 +2434,11 @@ struct XArrayCoorOpConversion // as below, as the LLVM struct type cannot be statically defined. TODO(loc, "derived type with type parameters"); } - // TODO: array offset subcomponents must be converted to LLVM's - // row-major layout here. - for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i) - args.push_back(operands[i]); + llvm::SmallVector indices = convertSubcomponentIndices( + loc, elementType, + operands.slice(coor.subcomponentOffset(), + coor.getSubcomponent().size())); + args.append(indices.begin(), indices.end()); rewriter.replaceOpWithNewOp(coor, ty, casted, args); return mlir::success(); } @@ -2437,7 +2465,10 @@ struct XArrayCoorOpConversion } // Cast the base address to a pointer to T. base = rewriter.create(loc, ty, operands[0]); - } else { + } + + llvm::SmallVector args = {offset}; + if (!coor.getSubcomponent().empty()) { // Operand #0 must have a pointer type. For subcomponent slicing, we // want to cast away the array type and have a plain struct type. mlir::Type ty0 = operands[0].getType(); @@ -2448,10 +2479,12 @@ struct XArrayCoorOpConversion eleTy = arrTy.getElementType(); auto newTy = mlir::LLVM::LLVMPointerType::get(eleTy); base = rewriter.create(loc, newTy, operands[0]); + llvm::SmallVector indices = convertSubcomponentIndices( + loc, eleTy, + operands.slice(coor.subcomponentOffset(), + coor.getSubcomponent().size())); + args.append(indices.begin(), indices.end()); } - llvm::SmallVector args = {offset}; - for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i) - args.push_back(operands[i]); rewriter.replaceOpWithNewOp(coor, ty, base, args); return mlir::success(); } diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir index 14cd58a6148b1..d0c154fb0376e 100644 --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -1988,6 +1988,27 @@ func.func private @_QPtest_dt_callee(%arg0: !fir.box>) // CHECK: llvm.store %[[BOX10]], %[[ALLOCA]] : !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i64>>)>> // CHECK: llvm.call @_QPtest_dt_callee(%1) : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i64>>)>>) -> () +// Conversion with a subcomponent that indexes a 2d array field in a derived type. + +func.func @_QPtest_dt_slice2(%arg0: !fir.ref}>>>) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %c2 = arith.constant 2 : index + %1 = fir.field_index a, !fir.type<_QPtest_dt_slice2Tt{a:!fir.array<2x3xi32>}> + %2 = fircg.ext_embox %arg0(%c2)[%c1, %c2, %c1] path %1, %c0, %c1 : (!fir.ref}>>>, index, index, index, index, !fir.field, index, index) -> !fir.box> + return +} + +// CHECK-LABEL: llvm.func @_QPtest_dt_slice2( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr>)>>>) { +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : index) : i64 +// CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : index) : i64 +// CHECK: %[[C2:.*]] = llvm.mlir.constant(2 : index) : i64 +// CHECK: %[[C0_2:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %{{.*}} = llvm.getelementptr %[[ARG0]][%[[C0_2]], {{.*}}, 0, %[[C1]], %[[C0]]] : (!llvm.ptr>)>>>, i64, i64, i64, i64) -> !llvm.ptr +// CHECK: return +// CHECK: } + // ----- // Test `fircg.ext_array_coor` conversion. @@ -2191,6 +2212,21 @@ func.func @ext_array_coor_dt_slice(%arg0: !fir.ref}>>>, %idx1 : index, %idx2 : index, %idx3 : index, %idx4 : index, %idx5 : index, %idx6 : index, %idx7 : index) { + %1 = fir.field_index a, !fir.type<_QFtest_dt_sliceT2t{a:!fir.array<2x3xi32>}> + %2 = fircg.ext_array_coor %arg0(%idx1)[%idx2, %idx3, %idx4] path %1, %idx5, %idx6 <%idx7> : (!fir.ref}>>>, index, index, index, index, !fir.field, index, index, index) -> !fir.ref + return +} + +// CHECK-LABEL: llvm.func @ext_array_coor_dt_slice2( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr>)>>>, %[[IDX1:.*]]: i64, %[[IDX2:.*]]: i64, %[[IDX3:.*]]: i64, %[[IDX4:.*]]: i64, %[[IDX5:.*]]: i64, %[[IDX6:.*]]: i64, %[[IDX7:.*]]: i64) { +// CHECK: %[[PTR:.*]] = llvm.bitcast %[[ARG0]] : !llvm.ptr>)>>> to !llvm.ptr>)>> +// CHECK: %{{.*}} = llvm.getelementptr %[[PTR]][%{{.*}}, 0, %[[IDX6]], %[[IDX5]]] : (!llvm.ptr>)>>, i64, i64, i64) -> !llvm.ptr +// CHECK: llvm.return +// CHECK: } + // ----- // Check `fircg.ext_rebox` conversion to LLVM IR dialect