diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h index c586ac0ec08e3..48a72d73c03bd 100644 --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -208,6 +208,11 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener { return createRealConstant(loc, realType, 0u); } + /// Create a real constant of type \p realType with value one. + mlir::Value createRealOneConstant(mlir::Location loc, mlir::Type realType) { + return createRealConstant(loc, realType, 1u); + } + /// Create a slot for a local on the stack. Besides the variable's type and /// shape, it may be given name, pinned, or target attributes. mlir::Value allocateLocal(mlir::Location loc, mlir::Type ty, @@ -856,6 +861,11 @@ mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value createZeroValue(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type type); +/// Create a one value of a given numerical or logical \p type (`true` +/// for logical types). +mlir::Value createOneValue(fir::FirOpBuilder &builder, mlir::Location loc, + mlir::Type type); + /// Get the integer constants of triplet and compute the extent. std::optional getExtentFromTriplet(mlir::Value lb, mlir::Value ub, mlir::Value stride); diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp index 73ce1dc1174a0..003883e49a2f2 100644 --- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp +++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp @@ -1671,6 +1671,26 @@ mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder, "numeric or logical type"); } +mlir::Value fir::factory::createOneValue(fir::FirOpBuilder &builder, + mlir::Location loc, mlir::Type type) { + mlir::Type i1 = builder.getIntegerType(1); + if (mlir::isa(type) || type == i1) + return builder.createConvert(loc, type, builder.createBool(loc, true)); + if (fir::isa_integer(type)) + return builder.createIntegerConstant(loc, type, 0); + if (fir::isa_real(type)) + return builder.createRealOneConstant(loc, type); + if (fir::isa_complex(type)) { + fir::factory::Complex complexHelper(builder, loc); + mlir::Type partType = complexHelper.getComplexPartType(type); + mlir::Value realPart = builder.createRealOneConstant(loc, partType); + mlir::Value imagPart = builder.createRealZeroConstant(loc, partType); + return complexHelper.createComplex(type, realPart, imagPart); + } + fir::emitFatalError(loc, "internal: trying to generate one value of non " + "numeric or logical type"); +} + std::optional fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub, mlir::Value stride) { diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp index ce8ebaa803f47..4fa8103687e02 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp @@ -931,6 +931,37 @@ class SumAsElementalConverter mlir::Value genScalarAdd(mlir::Value value1, mlir::Value value2); }; +/// Reduction converter for Product. +class ProductAsElementalConverter + : public NumericReductionAsElementalConverterBase { + using Base = NumericReductionAsElementalConverterBase; + +public: + ProductAsElementalConverter(hlfir::ProductOp op, + mlir::PatternRewriter &rewriter) + : Base{op, rewriter} {} + +private: + virtual llvm::SmallVector genReductionInitValues( + [[maybe_unused]] mlir::ValueRange oneBasedIndices, + [[maybe_unused]] const llvm::SmallVectorImpl &extents) + final { + return {fir::factory::createOneValue(builder, loc, getResultElementType())}; + } + virtual llvm::SmallVector + reduceOneElement(const llvm::SmallVectorImpl ¤tValue, + hlfir::Entity array, + mlir::ValueRange oneBasedIndices) final { + checkReductions(currentValue); + hlfir::Entity elementValue = + hlfir::loadElementAt(loc, builder, array, oneBasedIndices); + return {genScalarMult(currentValue[0], elementValue)}; + } + + // Generate scalar multiplication of the two values (of the same data type). + mlir::Value genScalarMult(mlir::Value value1, mlir::Value value2); +}; + /// Base class for logical reductions like ALL, ANY, COUNT. /// They do not have MASK and FastMathFlags. template @@ -1194,6 +1225,20 @@ mlir::Value SumAsElementalConverter::genScalarAdd(mlir::Value value1, llvm_unreachable("unsupported SUM reduction type"); } +mlir::Value ProductAsElementalConverter::genScalarMult(mlir::Value value1, + mlir::Value value2) { + mlir::Type ty = value1.getType(); + assert(ty == value2.getType() && "reduction values' types do not match"); + if (mlir::isa(ty)) + return mlir::arith::MulFOp::create(builder, loc, value1, value2); + else if (mlir::isa(ty)) + return fir::MulcOp::create(builder, loc, value1, value2); + else if (mlir::isa(ty)) + return mlir::arith::MulIOp::create(builder, loc, value1, value2); + + llvm_unreachable("unsupported MUL reduction type"); +} + mlir::Value ReductionAsElementalConverter::genMaskValue( mlir::Value mask, mlir::Value isPresentPred, mlir::ValueRange indices) { mlir::OpBuilder::InsertionGuard guard(builder); @@ -1265,6 +1310,9 @@ class ReductionConversion : public mlir::OpRewritePattern { } else if constexpr (std::is_same_v) { SumAsElementalConverter converter{op, rewriter}; return converter.convert(); + } else if constexpr (std::is_same_v) { + ProductAsElementalConverter converter{op, rewriter}; + return converter.convert(); } return rewriter.notifyMatchFailure(op, "unexpected reduction operation"); } @@ -3158,6 +3206,7 @@ class SimplifyHLFIRIntrinsics mlir::RewritePatternSet patterns(context); patterns.insert(context); patterns.insert>(context); + patterns.insert>(context); patterns.insert>(context); patterns.insert>(context); patterns.insert(context); diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-product.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-product.fir new file mode 100644 index 0000000000000..aadba65760687 --- /dev/null +++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-product.fir @@ -0,0 +1,449 @@ +// RUN: fir-opt --simplify-hlfir-intrinsics %s | FileCheck %s + +// box with known extents +func.func @product_box_known_extents(%arg0: !fir.box>) -> !hlfir.expr<2xi32> { + %cst = arith.constant 2 : i32 + %res = hlfir.product %arg0 dim %cst : (!fir.box>, i32) -> !hlfir.expr<2xi32> + return %res : !hlfir.expr<2xi32> +} +// CHECK-LABEL: func.func @product_box_known_extents( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>) -> !hlfir.expr<2xi32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 0 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 0 : i32 +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 2 : index +// CHECK: %[[CONSTANT_4:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_3]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<2xi32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_1]] to %[[CONSTANT_4]] step %[[CONSTANT_1]] unordered iter_args(%[[VAL_2:.*]] = %[[CONSTANT_2]]) -> (i32) { +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_0]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[BOX_DIMS_1:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_1]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_0]]#0, %[[CONSTANT_1]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_0]], %[[SUBI_0]] : index +// CHECK: %[[SUBI_1:.*]] = arith.subi %[[BOX_DIMS_1]]#0, %[[CONSTANT_1]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[VAL_1]], %[[SUBI_1]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG0]] (%[[ADDI_0]], %[[ADDI_1]]) : (!fir.box>, index, index) -> !fir.ref +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref +// CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_2]], %[[LOAD_0]] : i32 +// CHECK: fir.result %[[MULI_0]] : i32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : i32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<2xi32> +// CHECK: } + +// expr with known extents +func.func @product_expr_known_extents(%arg0: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst : (!hlfir.expr<2x3xi32>, i32) -> !hlfir.expr<3xi32> + return %res : !hlfir.expr<3xi32> +} +// CHECK-LABEL: func.func @product_expr_known_extents( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 0 : i32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 2 : index +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_3]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xi32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_2]] step %[[CONSTANT_0]] unordered iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (i32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_1]], %[[VAL_0]] : (!hlfir.expr<2x3xi32>, index, index) -> i32 +// CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_2]], %[[APPLY_0]] : i32 +// CHECK: fir.result %[[MULI_0]] : i32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : i32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xi32> +// CHECK: } + +// box with unknown extent +func.func @product_box_unknown_extent1(%arg0: !fir.box>>) -> !hlfir.expr<3xcomplex> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst : (!fir.box>>, i32) -> !hlfir.expr<3xcomplex> + return %res : !hlfir.expr<3xcomplex> +} +// CHECK-LABEL: func.func @product_box_unknown_extent1( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>>) -> !hlfir.expr<3xcomplex> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 0.000000e+00 : f64 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 1.000000e+00 : f64 +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 3 : index +// CHECK: %[[CONSTANT_4:.*]] = arith.constant 0 : index +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_4]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_3]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xcomplex> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[UNDEFINED_0:.*]] = fir.undefined complex +// CHECK: %[[INSERT_VALUE_0:.*]] = fir.insert_value %[[UNDEFINED_0]], %[[CONSTANT_2]], [0 : index] : (complex, f64) -> complex +// CHECK: %[[INSERT_VALUE_1:.*]] = fir.insert_value %[[INSERT_VALUE_0]], %[[CONSTANT_1]], [1 : index] : (complex, f64) -> complex +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[BOX_DIMS_0]]#1 step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[INSERT_VALUE_1]]) -> (complex) { +// CHECK: %[[BOX_DIMS_1:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_4]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[BOX_DIMS_2:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_0]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_1]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_1]], %[[SUBI_0]] : index +// CHECK: %[[SUBI_1:.*]] = arith.subi %[[BOX_DIMS_2]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[VAL_0]], %[[SUBI_1]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG0]] (%[[ADDI_0]], %[[ADDI_1]]) : (!fir.box>>, index, index) -> !fir.ref> +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref> +// CHECK: %[[MULC_0:.*]] = fir.mulc %[[VAL_2]], %[[LOAD_0]] : complex +// CHECK: fir.result %[[MULC_0]] : complex +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : complex +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xcomplex> +// CHECK: } + +func.func @product_box_unknown_extent2(%arg0: !fir.box>>) -> !hlfir.expr> { + %cst = arith.constant 2 : i32 + %res = hlfir.product %arg0 dim %cst : (!fir.box>>, i32) -> !hlfir.expr> + return %res : !hlfir.expr> +} +// CHECK-LABEL: func.func @product_box_unknown_extent2( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>>) -> !hlfir.expr> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 0.000000e+00 : f64 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 1.000000e+00 : f64 +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 3 : index +// CHECK: %[[CONSTANT_4:.*]] = arith.constant 0 : index +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_4]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[BOX_DIMS_0]]#1 : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[UNDEFINED_0:.*]] = fir.undefined complex +// CHECK: %[[INSERT_VALUE_0:.*]] = fir.insert_value %[[UNDEFINED_0]], %[[CONSTANT_2]], [0 : index] : (complex, f64) -> complex +// CHECK: %[[INSERT_VALUE_1:.*]] = fir.insert_value %[[INSERT_VALUE_0]], %[[CONSTANT_1]], [1 : index] : (complex, f64) -> complex +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_3]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[INSERT_VALUE_1]]) -> (complex) { +// CHECK: %[[BOX_DIMS_1:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_4]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[BOX_DIMS_2:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_0]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_1]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_0]], %[[SUBI_0]] : index +// CHECK: %[[SUBI_1:.*]] = arith.subi %[[BOX_DIMS_2]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[VAL_1]], %[[SUBI_1]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG0]] (%[[ADDI_0]], %[[ADDI_1]]) : (!fir.box>>, index, index) -> !fir.ref> +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref> +// CHECK: %[[MULC_0:.*]] = fir.mulc %[[VAL_2]], %[[LOAD_0]] : complex +// CHECK: fir.result %[[MULC_0]] : complex +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : complex +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr> +// CHECK: } + +// expr with unknown extent +func.func @product_expr_unknown_extent1(%arg0: !hlfir.expr) -> !hlfir.expr<3xf32> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst : (!hlfir.expr, i32) -> !hlfir.expr<3xf32> + return %res : !hlfir.expr<3xf32> +} +// CHECK-LABEL: func.func @product_expr_unknown_extent1( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr) -> !hlfir.expr<3xf32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_2]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[GET_EXTENT_0]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_1]], %[[VAL_0]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xf32> +// CHECK: } + +func.func @product_expr_unknown_extent2(%arg0: !hlfir.expr) -> !hlfir.expr { + %cst = arith.constant 2 : i32 + %res = hlfir.product %arg0 dim %cst : (!hlfir.expr, i32) -> !hlfir.expr + return %res : !hlfir.expr +} +// CHECK-LABEL: func.func @product_expr_unknown_extent2( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr) -> !hlfir.expr { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[GET_EXTENT_0]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_2]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr +// CHECK: } + +// scalar mask +func.func @product_scalar_mask(%arg0: !hlfir.expr, %mask: !fir.ref>) -> !hlfir.expr<3xf32> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst mask %mask : (!hlfir.expr, i32, !fir.ref>) -> !hlfir.expr<3xf32> + return %res : !hlfir.expr<3xf32> +} +// CHECK-LABEL: func.func @product_scalar_mask( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr, +// CHECK-SAME: %[[ARG1:.*]]: !fir.ref>) -> !hlfir.expr<3xf32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_2]] : (index) -> !fir.shape<1> +// CHECK: %[[LOAD_0:.*]] = fir.load %[[ARG1]] : !fir.ref> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[GET_EXTENT_0]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[CONVERT_0:.*]] = fir.convert %[[LOAD_0]] : (!fir.logical<1>) -> i1 +// CHECK: %[[IF_0:.*]] = fir.if %[[CONVERT_0]] -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_1]], %[[VAL_0]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } else { +// CHECK: fir.result %[[VAL_2]] : f32 +// CHECK: } +// CHECK: fir.result %[[IF_0]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xf32> +// CHECK: } + +// scalar boxed mask +func.func @product_scalar_boxed_mask(%arg0: !hlfir.expr, %mask: !fir.box>) -> !hlfir.expr<3xf32> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst mask %mask : (!hlfir.expr, i32, !fir.box>) -> !hlfir.expr<3xf32> + return %res : !hlfir.expr<3xf32> +} +// CHECK-LABEL: func.func @product_scalar_boxed_mask( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr, +// CHECK-SAME: %[[ARG1:.*]]: !fir.box>) -> !hlfir.expr<3xf32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant true +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_3]] : (index) -> !fir.shape<1> +// CHECK: %[[IS_PRESENT_0:.*]] = fir.is_present %[[ARG1]] : (!fir.box>) -> i1 +// CHECK: %[[IF_0:.*]] = fir.if %[[IS_PRESENT_0]] -> (!fir.logical<1>) { +// CHECK: %[[BOX_ADDR_0:.*]] = fir.box_addr %[[ARG1]] : (!fir.box>) -> !fir.ref> +// CHECK: %[[LOAD_0:.*]] = fir.load %[[BOX_ADDR_0]] : !fir.ref> +// CHECK: fir.result %[[LOAD_0]] : !fir.logical<1> +// CHECK: } else { +// CHECK: %[[CONVERT_0:.*]] = fir.convert %[[CONSTANT_2]] : (i1) -> !fir.logical<1> +// CHECK: fir.result %[[CONVERT_0]] : !fir.logical<1> +// CHECK: } +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[GET_EXTENT_0]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[CONVERT_1:.*]] = fir.convert %[[IF_0]] : (!fir.logical<1>) -> i1 +// CHECK: %[[IF_1:.*]] = fir.if %[[CONVERT_1]] -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_1]], %[[VAL_0]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } else { +// CHECK: fir.result %[[VAL_2]] : f32 +// CHECK: } +// CHECK: fir.result %[[IF_1]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xf32> +// CHECK: } + +// array mask +func.func @product_array_mask(%arg0: !hlfir.expr, %mask: !fir.box>>) -> !hlfir.expr { + %cst = arith.constant 2 : i32 + %res = hlfir.product %arg0 dim %cst mask %mask : (!hlfir.expr, i32, !fir.box>>) -> !hlfir.expr + return %res : !hlfir.expr +} +// CHECK-LABEL: func.func @product_array_mask( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr, +// CHECK-SAME: %[[ARG1:.*]]: !fir.box>>) -> !hlfir.expr { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant true +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 0 : index +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_4:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[GET_EXTENT_0]] : (index) -> !fir.shape<1> +// CHECK: %[[IS_PRESENT_0:.*]] = fir.is_present %[[ARG1]] : (!fir.box>>) -> i1 +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_2]] to %[[CONSTANT_4]] step %[[CONSTANT_2]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_3]]) -> (f32) { +// CHECK: %[[IF_0:.*]] = fir.if %[[IS_PRESENT_0]] -> (!fir.logical<1>) { +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG1]], %[[CONSTANT_1]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[BOX_DIMS_1:.*]]:3 = fir.box_dims %[[ARG1]], %[[CONSTANT_2]] : (!fir.box>>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_0]]#0, %[[CONSTANT_2]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_0]], %[[SUBI_0]] : index +// CHECK: %[[SUBI_1:.*]] = arith.subi %[[BOX_DIMS_1]]#0, %[[CONSTANT_2]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[VAL_1]], %[[SUBI_1]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG1]] (%[[ADDI_0]], %[[ADDI_1]]) : (!fir.box>>, index, index) -> !fir.ref> +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref> +// CHECK: fir.result %[[LOAD_0]] : !fir.logical<1> +// CHECK: } else { +// CHECK: %[[CONVERT_0:.*]] = fir.convert %[[CONSTANT_0]] : (i1) -> !fir.logical<1> +// CHECK: fir.result %[[CONVERT_0]] : !fir.logical<1> +// CHECK: } +// CHECK: %[[CONVERT_1:.*]] = fir.convert %[[IF_0]] : (!fir.logical<1>) -> i1 +// CHECK: %[[IF_1:.*]] = fir.if %[[CONVERT_1]] -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } else { +// CHECK: fir.result %[[VAL_2]] : f32 +// CHECK: } +// CHECK: fir.result %[[IF_1]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr +// CHECK: } + +// array expr mask +func.func @product_array_expr_mask(%arg0: !hlfir.expr, %mask: !hlfir.expr>) -> !hlfir.expr { + %cst = arith.constant 2 : i32 + %res = hlfir.product %arg0 dim %cst mask %mask : (!hlfir.expr, i32, !hlfir.expr>) -> !hlfir.expr + return %res : !hlfir.expr +} +// CHECK-LABEL: func.func @product_array_expr_mask( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr, +// CHECK-SAME: %[[ARG1:.*]]: !hlfir.expr>) -> !hlfir.expr { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_OF_0:.*]] = hlfir.shape_of %[[ARG0]] : (!hlfir.expr) -> !fir.shape<2> +// CHECK: %[[GET_EXTENT_0:.*]] = hlfir.get_extent %[[SHAPE_OF_0]] {dim = 0 : index} : (!fir.shape<2>) -> index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[GET_EXTENT_0]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_2]] step %[[CONSTANT_0]] iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG1]], %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr>, index, index) -> !fir.logical<1> +// CHECK: %[[CONVERT_0:.*]] = fir.convert %[[APPLY_0]] : (!fir.logical<1>) -> i1 +// CHECK: %[[IF_0:.*]] = fir.if %[[CONVERT_0]] -> (f32) { +// CHECK: %[[APPLY_1:.*]] = hlfir.apply %[[ARG0]], %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_1]] : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } else { +// CHECK: fir.result %[[VAL_2]] : f32 +// CHECK: } +// CHECK: fir.result %[[IF_0]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr +// CHECK: } + +// unordered floating point reduction +func.func @product_unordered_reduction(%arg0: !hlfir.expr<2x3xf32>) -> !hlfir.expr<3xf32> { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst {fastmath = #arith.fastmath} : (!hlfir.expr<2x3xf32>, i32) -> !hlfir.expr<3xf32> + return %res : !hlfir.expr<3xf32> +} +// CHECK-LABEL: func.func @product_unordered_reduction( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr<2x3xf32>) -> !hlfir.expr<3xf32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 2 : index +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 3 : index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_3]] : (index) -> !fir.shape<1> +// CHECK: %[[ELEMENTAL_0:.*]] = hlfir.elemental %[[SHAPE_0]] unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32> { +// CHECK: ^bb0(%[[VAL_0:.*]]: index): +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_1:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_2]] step %[[CONSTANT_0]] unordered iter_args(%[[VAL_2:.*]] = %[[CONSTANT_1]]) -> (f32) { +// CHECK: %[[APPLY_0:.*]] = hlfir.apply %[[ARG0]], %[[VAL_1]], %[[VAL_0]] : (!hlfir.expr<2x3xf32>, index, index) -> f32 +// CHECK: %[[MULF_0:.*]] = arith.mulf %[[VAL_2]], %[[APPLY_0]] fastmath : f32 +// CHECK: fir.result %[[MULF_0]] : f32 +// CHECK: } +// CHECK: hlfir.yield_element %[[DO_LOOP_0]] : f32 +// CHECK: } +// CHECK: return %[[ELEMENTAL_0]] : !hlfir.expr<3xf32> +// CHECK: } + +// total 1d reduction +func.func @product_total_1d_reduction(%arg0: !fir.box>) -> i32 { + %cst = arith.constant 1 : i32 + %res = hlfir.product %arg0 dim %cst : (!fir.box>, i32) -> i32 + return %res : i32 +} +// CHECK-LABEL: func.func @product_total_1d_reduction( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>) -> i32 { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 0 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 3 : index +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 0 : i32 +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 1 : index +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_0:.*]] = %[[CONSTANT_3]] to %[[CONSTANT_1]] step %[[CONSTANT_3]] unordered iter_args(%[[VAL_1:.*]] = %[[CONSTANT_2]]) -> (i32) { +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_0]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_0]]#0, %[[CONSTANT_3]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_0]], %[[SUBI_0]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG0]] (%[[ADDI_0]]) : (!fir.box>, index) -> !fir.ref +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref +// CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_1]], %[[LOAD_0]] : i32 +// CHECK: fir.result %[[MULI_0]] : i32 +// CHECK: } +// CHECK: return %[[DO_LOOP_0]] : i32 +// CHECK: } + +// total 2d reduction +func.func @product_total_2d_reduction(%arg0: !fir.box>) -> i32 { + %res = hlfir.product %arg0 : (!fir.box>) -> i32 + return %res : i32 +} +// CHECK-LABEL: func.func @product_total_2d_reduction( +// CHECK-SAME: %[[ARG0:.*]]: !fir.box>) -> i32 { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 0 : i32 +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 3 : index +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 0 : index +// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_3]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_0:.*]] = %[[CONSTANT_0]] to %[[CONSTANT_2]] step %[[CONSTANT_0]] unordered iter_args(%[[VAL_1:.*]] = %[[CONSTANT_1]]) -> (i32) { +// CHECK: %[[DO_LOOP_1:.*]] = fir.do_loop %[[VAL_2:.*]] = %[[CONSTANT_0]] to %[[BOX_DIMS_0]]#1 step %[[CONSTANT_0]] unordered iter_args(%[[VAL_3:.*]] = %[[VAL_1]]) -> (i32) { +// CHECK: %[[BOX_DIMS_1:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_3]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[BOX_DIMS_2:.*]]:3 = fir.box_dims %[[ARG0]], %[[CONSTANT_0]] : (!fir.box>, index) -> (index, index, index) +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[BOX_DIMS_1]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[VAL_2]], %[[SUBI_0]] : index +// CHECK: %[[SUBI_1:.*]] = arith.subi %[[BOX_DIMS_2]]#0, %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[VAL_0]], %[[SUBI_1]] : index +// CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[ARG0]] (%[[ADDI_0]], %[[ADDI_1]]) : (!fir.box>, index, index) -> !fir.ref +// CHECK: %[[LOAD_0:.*]] = fir.load %[[DESIGNATE_0]] : !fir.ref +// CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_3]], %[[LOAD_0]] : i32 +// CHECK: fir.result %[[MULI_0]] : i32 +// CHECK: } +// CHECK: fir.result %[[DO_LOOP_1]] : i32 +// CHECK: } +// CHECK: return %[[DO_LOOP_0]] : i32 +// CHECK: } + +// negative: invalid dim==0 +func.func @product_invalid_dim0(%arg0: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { + %cst = arith.constant 0 : i32 + %res = hlfir.product %arg0 dim %cst : (!hlfir.expr<2x3xi32>, i32) -> !hlfir.expr<3xi32> + return %res : !hlfir.expr<3xi32> +} +// CHECK-LABEL: func.func @product_invalid_dim0( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 0 : i32 +// CHECK: %[[PRODUCT_0:.*]] = hlfir.product %[[ARG0]] dim %[[CONSTANT_0]] : (!hlfir.expr<2x3xi32>, i32) -> !hlfir.expr<3xi32> +// CHECK: return %[[PRODUCT_0]] : !hlfir.expr<3xi32> +// CHECK: } + +// negative: invalid dim>rank +func.func @product_invalid_dim_big(%arg0: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { + %cst = arith.constant 3 : i32 + %res = hlfir.product %arg0 dim %cst : (!hlfir.expr<2x3xi32>, i32) -> !hlfir.expr<3xi32> + return %res : !hlfir.expr<3xi32> +} +// CHECK-LABEL: func.func @product_invalid_dim_big( +// CHECK-SAME: %[[ARG0:.*]]: !hlfir.expr<2x3xi32>) -> !hlfir.expr<3xi32> { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 3 : i32 +// CHECK: %[[PRODUCT_0:.*]] = hlfir.product %[[ARG0]] dim %[[CONSTANT_0]] : (!hlfir.expr<2x3xi32>, i32) -> !hlfir.expr<3xi32> +// CHECK: return %[[PRODUCT_0]] : !hlfir.expr<3xi32> +// CHECK: } \ No newline at end of file