[flang-commits] [flang] 491f164 - [flang][hlfir] Prefer a constant-shape operand for elemental array results (#209791)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 27 07:48:40 PDT 2026
Author: Caroline Newcombe
Date: 2026-07-27T10:48:35-04:00
New Revision: 491f16472b0f08ed72481b671c1470c6de953fa0
URL: https://github.com/llvm/llvm-project/commit/491f16472b0f08ed72481b671c1470c6de953fa0
DIFF: https://github.com/llvm/llvm-project/commit/491f16472b0f08ed72481b671c1470c6de953fa0.diff
LOG: [flang][hlfir] Prefer a constant-shape operand for elemental array results (#209791)
Elemental operands are conformable, so their extents are identical and
the result shape may be taken from either operand. This patch prefers an
operand whose shape is a compile-time constant, producing a statically
shaped result. The left operand is checked first to avoid churn in lit
test expectations.
Added:
flang/test/Lower/HLFIR/elemental-array-constant-shape.f90
Modified:
flang/lib/Lower/ConvertExprToHLFIR.cpp
flang/test/Lower/HLFIR/matmul.f90
Removed:
################################################################################
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index b30a2dfcba90e..3ad32000b945a 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1774,10 +1774,22 @@ class HlfirBuilder {
Fortran::lower::getFIRType(builder.getContext(), R::category, R::kind,
/*params=*/{});
// TODO: "merge" shape, get cst shape from front-end if possible.
+ // Prefer a compile-time constant shape to get a statically shaped result.
+ auto hasConstantShape = [](hlfir::Entity entity) -> bool {
+ if (!entity.isArray())
+ return false;
+ auto seqTy =
+ mlir::dyn_cast<fir::SequenceType>(entity.getElementOrSequenceType());
+ return seqTy && !fir::sequenceWithNonConstantShape(seqTy);
+ };
mlir::Value shape;
- if (left.isArray()) {
+ if (hasConstantShape(left))
shape = hlfir::genShape(loc, builder, left);
- } else {
+ else if (hasConstantShape(right))
+ shape = hlfir::genShape(loc, builder, right);
+ else if (left.isArray())
+ shape = hlfir::genShape(loc, builder, left);
+ else {
assert(right.isArray() && "must have at least one array operand");
shape = hlfir::genShape(loc, builder, right);
}
diff --git a/flang/test/Lower/HLFIR/elemental-array-constant-shape.f90 b/flang/test/Lower/HLFIR/elemental-array-constant-shape.f90
new file mode 100644
index 0000000000000..42b7d400fb153
--- /dev/null
+++ b/flang/test/Lower/HLFIR/elemental-array-constant-shape.f90
@@ -0,0 +1,53 @@
+! Test that when a binary elemental operation has one operand with a
+! compile-time constant shape and another with only a dynamic (descriptor-based)
+! shape, lowering infers the constant shape for the operation result.
+! Conforming elemental operands have identical extents, so preferring the
+! constant shape is safe and yields a statically shaped, more precise result.
+
+! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s
+
+! Constant-shape operand on the right ("a" is assumed-shape/dynamic, "x" is
+! explicit-shape/constant): the result takes x's constant shape.
+subroutine const_right_operand(a)
+ real :: a(:)
+ real :: x(3)
+ a = a + x
+end subroutine
+! CHECK-LABEL: func.func @_QPconst_right_operand(
+! CHECK: %[[ELEM:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32>
+! CHECK: hlfir.assign %[[ELEM]] to %{{.*}} : !hlfir.expr<3xf32>, !fir.box<!fir.array<?xf32>>
+
+! Constant-shape operand on the left: the result still takes the constant shape.
+subroutine const_left_operand(a)
+ real :: a(:)
+ real :: x(3)
+ a = x + a
+end subroutine
+! CHECK-LABEL: func.func @_QPconst_left_operand(
+! CHECK: hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32>
+
+! No constant-shape operand: the result stays dynamically shaped.
+subroutine both_dynamic(a, b)
+ real :: a(:), b(:)
+ a = a + b
+end subroutine
+! CHECK-LABEL: func.func @_QPboth_dynamic(
+! CHECK: hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32>
+
+! Scalar operand with a constant-shape array: the scalar has no shape, so the
+! result takes the array's constant shape.
+subroutine scalar_times_const(a, s)
+ real :: a(:), s
+ real :: x(3)
+ a = s * x
+end subroutine
+! CHECK-LABEL: func.func @_QPscalar_times_const(
+! CHECK: hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<3xf32>
+
+! Scalar operand with a dynamic array: the result stays dynamically shaped.
+subroutine scalar_times_dyn(a, b, s)
+ real :: a(:), b(:), s
+ a = s * b
+end subroutine
+! CHECK-LABEL: func.func @_QPscalar_times_dyn(
+! CHECK: hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32>
diff --git a/flang/test/Lower/HLFIR/matmul.f90 b/flang/test/Lower/HLFIR/matmul.f90
index 298dd2b800636..70af715dfe660 100644
--- a/flang/test/Lower/HLFIR/matmul.f90
+++ b/flang/test/Lower/HLFIR/matmul.f90
@@ -43,18 +43,15 @@ subroutine matmul2(c)
! CHECK: fir.call @_QPfill
! CHECK: fir.call @_QPfill
! CHECK-NEXT: %[[B_BOX:.*]] = fir.load %[[B_BOX_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>
-! CHECK-NEXT: %[[C0:.*]] = arith.constant 0 : index
-! CHECK-NEXT: %[[B_DIMS_0:.*]]:3 = fir.box_dims %[[B_BOX]], %[[C0]]
-! CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
-! CHECK-NEXT: %[[B_DIMS_1:.*]]:3 = fir.box_dims %[[B_BOX]], %[[C1]]
-! CHECK-NEXT: %[[B_SHAPE:.*]] = fir.shape %[[B_DIMS_0]]#1, %[[B_DIMS_1]]#1
-! CHECK-NEXT: %[[ELEMENTAL:.*]] = hlfir.elemental %[[B_SHAPE]] unordered : (!fir.shape<2>) -> !hlfir.expr<?x?xi32> {
+! The elemental shape is taken from the constant-shape operand "x" (see above),
+! so "b - x" is inferred as a 4x4 array rather than a dynamically shaped one.
+! CHECK-NEXT: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<2>) -> !hlfir.expr<4x4xi32> {
! CHECK: }
! CHECK-NEXT: %[[A_BOX:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>
! The shapes in these types are what is being tested:
-! CHECK-NEXT: %[[MATMUL:.*]] = hlfir.matmul %[[A_BOX]] %[[ELEMENTAL]] {{.*}} : (!fir.box<!fir.heap<!fir.array<?x?xi32>>>, !hlfir.expr<?x?xi32>) -> !hlfir.expr<?x4xi32>
+! CHECK-NEXT: %[[MATMUL:.*]] = hlfir.matmul %[[A_BOX]] %[[ELEMENTAL]] {{.*}} : (!fir.box<!fir.heap<!fir.array<?x?xi32>>>, !hlfir.expr<4x4xi32>) -> !hlfir.expr<?x4xi32>
subroutine matmul3(lhs, rhs, res)
integer, allocatable :: lhs(:,:), rhs(:,:), res(:,:)
More information about the flang-commits
mailing list