[flang-commits] [flang] d0ddae6 - [flang] Fix segfault in CSHIFT/EOSHIFT with dynamically optional DIM (#184431)
via flang-commits
flang-commits at lists.llvm.org
Sun Mar 8 22:24:29 PDT 2026
Author: Sairudra More
Date: 2026-03-09T10:54:25+05:30
New Revision: d0ddae678e3f634c988542dfbdde1dd0afe66344
URL: https://github.com/llvm/llvm-project/commit/d0ddae678e3f634c988542dfbdde1dd0afe66344
DIFF: https://github.com/llvm/llvm-project/commit/d0ddae678e3f634c988542dfbdde1dd0afe66344.diff
LOG: [flang] Fix segfault in CSHIFT/EOSHIFT with dynamically optional DIM (#184431)
When `DIM` is passed as an optional dummy argument and is absent at
runtime, the HLFIR lowering for the `CSHIFT` and `EOSHIFT` intrinsics
treated it as unconditionally present. This resulted in an unconditional
load of the `DIM` reference, causing a null pointer dereference and a
runtime segmentation fault when absent.
The underlying issue was that the `dim` argument for `cshift` and
`eoshift` was not marked with `handleDynamicOptional` during intrinsic
argument lowering setup. As a result, the `isPresent` state was never
populated, and the lowering implementation incorrectly fell through to
an unconditional scalar load.
This patch resolves the issue by:
1. Updating the `dim` entries for `cshift` and `eoshift` in
`IntrinsicCall.cpp` to use `handleDynamicOptional`. This enables
`getOperandVector()` to appropriately emit a guarded load (via
`loadOptionalValue()`) that safely returns a 0 placeholder when `DIM` is
absent.
2. Updating the HLFIR lowering implementations (`HlfirCShiftLowering`
and `HlfirEOShiftLowering`) to use the loaded scalar. It now employs an
`arith.select` to substitute the 0 placeholder with the
standard-mandated default of 1 when `isPresent` is false, matching the
Fortran requirements (§16.9.68 and §16.9.77) without generating
redundant `fir.if` or `fir.load` operations. Explicit `DIM=0` calls
still correctly forward the 0 value to flang-rt to trigger the
appropriate runtime bounds check error.
Co-authored-by: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Added:
flang/test/Lower/HLFIR/cshift-optional-dim.f90
Modified:
flang/lib/Lower/HlfirIntrinsics.cpp
flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Removed:
################################################################################
diff --git a/flang/lib/Lower/HlfirIntrinsics.cpp b/flang/lib/Lower/HlfirIntrinsics.cpp
index f63628d7824ff..9ee30e52af697 100644
--- a/flang/lib/Lower/HlfirIntrinsics.cpp
+++ b/flang/lib/Lower/HlfirIntrinsics.cpp
@@ -489,6 +489,14 @@ mlir::Value HlfirCShiftLowering::lowerImpl(
if (!dim) {
// If DIM is not present, drop the last element which is a null Value.
operands.truncate(2);
+ } else if (loweredActuals[2] && loweredActuals[2]->handleDynamicOptional()) {
+ // Use getIsPresent() to select between a present DIM value or
+ // the default 1 per Fortran 16.9.68.
+ mlir::Value isPresent = loweredActuals[2]->getIsPresent();
+ mlir::Type dimType = dim.getType();
+ mlir::Value one = builder.createIntegerConstant(loc, dimType, 1);
+ dim = mlir::arith::SelectOp::create(builder, loc, isPresent, dim, one);
+ operands[2] = dim;
} else {
// If DIM is present, then dereference it if it is a ref.
dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});
@@ -509,9 +517,17 @@ mlir::Value HlfirEOShiftLowering::lowerImpl(
mlir::Value shift = operands[1];
mlir::Value boundary = operands[2];
mlir::Value dim = operands[3];
- // If DIM is present, then dereference it if it is a ref.
- if (dim)
+ if (loweredActuals[3] && loweredActuals[3]->handleDynamicOptional()) {
+ // Use getIsPresent() to select between a present DIM value or
+ // the default 1 per Fortran 16.9.77.
+ mlir::Value isPresent = loweredActuals[3]->getIsPresent();
+ mlir::Type dimType = dim.getType();
+ mlir::Value one = builder.createIntegerConstant(loc, dimType, 1);
+ dim = mlir::arith::SelectOp::create(builder, loc, isPresent, dim, one);
+ } else if (dim) {
+ // If DIM is statically present, dereference it if it is a ref.
dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});
+ }
mlir::Type resultType = computeResultType(array, stmtResultType);
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 9036d65f138d3..d67eebdd8c93c 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -254,7 +254,9 @@ static constexpr IntrinsicHandler handlers[]{
/*isElemental=*/false},
{"cshift",
&I::genCshift,
- {{{"array", asAddr}, {"shift", asAddr}, {"dim", asValue}}},
+ {{{"array", asAddr},
+ {"shift", asAddr},
+ {"dim", asValue, handleDynamicOptional}}},
/*isElemental=*/false},
{"date_and_time",
&I::genDateAndTime,
@@ -281,7 +283,7 @@ static constexpr IntrinsicHandler handlers[]{
{{{"array", asBox},
{"shift", asAddr},
{"boundary", asBox, handleDynamicOptional},
- {"dim", asValue}}},
+ {"dim", asValue, handleDynamicOptional}}},
/*isElemental=*/false},
{"erfc_scaled", &I::genErfcScaled},
{"etime",
diff --git a/flang/test/Lower/HLFIR/cshift-optional-dim.f90 b/flang/test/Lower/HLFIR/cshift-optional-dim.f90
new file mode 100644
index 0000000000000..8f72b40910359
--- /dev/null
+++ b/flang/test/Lower/HLFIR/cshift-optional-dim.f90
@@ -0,0 +1,50 @@
+! Test lowering of CSHIFT/EOSHIFT with a dynamically optional DIM argument.
+! When DIM is an optional dummy argument that is absent at runtime, it must
+! default to 1 (Fortran standard 16.9.68, 16.9.77). Prior to the fix, the
+! absent case caused a segmentation fault because the optional reference was
+! unconditionally dereferenced without checking for presence.
+! RUN: bbc -emit-hlfir -o - -I nowhere %s 2>&1 | FileCheck %s
+
+! CSHIFT 2D with optional DIM - DIM may be absent at runtime (defaults to 1).
+subroutine cshift_optional_dim(a, sh, dim)
+ integer :: a(:,:), sh(:)
+ integer, optional :: dim
+ a = CSHIFT(a, sh, dim)
+end subroutine
+! CHECK-LABEL: func.func @_QPcshift_optional_dim(
+! CHECK-SAME: {{.*}}: !fir.ref<i32> {fir.bindc_name = "dim", fir.optional})
+! CHECK: %[[DECL_DIM:.*]]:2 = hlfir.declare {{.*}} {fortran_attrs = #fir.var_attrs<optional>
+! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL_DIM]]#0 : (!fir.ref<i32>) -> i1
+! CHECK: %[[DIM_LOADED:.*]] = fir.if %[[IS_PRESENT]] -> (i32) {
+! CHECK: %[[LOADED:.*]] = fir.load %[[DECL_DIM]]#0 : !fir.ref<i32>
+! CHECK: fir.result %[[LOADED]] : i32
+! CHECK: } else {
+! CHECK: arith.constant 0 : i32
+! CHECK: fir.result
+! CHECK: }
+! CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+! CHECK: %[[DIM_VAL:.*]] = arith.select %[[IS_PRESENT]], %[[DIM_LOADED]], %[[ONE]] : i32
+! CHECK-NOT: arith.cmpi
+! CHECK: hlfir.cshift {{.*}} dim %[[DIM_VAL]] :
+
+! EOSHIFT 2D with optional DIM - DIM may be absent at runtime (defaults to 1).
+subroutine eoshift_optional_dim(a, sh, dim)
+ integer :: a(:,:), sh(:)
+ integer, optional :: dim
+ a = EOSHIFT(a, sh, dim=dim)
+end subroutine
+! CHECK-LABEL: func.func @_QPeoshift_optional_dim(
+! CHECK-SAME: {{.*}}: !fir.ref<i32> {fir.bindc_name = "dim", fir.optional})
+! CHECK: %[[DECL_DIM2:.*]]:2 = hlfir.declare {{.*}} {fortran_attrs = #fir.var_attrs<optional>
+! CHECK: %[[IS_PRESENT2:.*]] = fir.is_present %[[DECL_DIM2]]#0 : (!fir.ref<i32>) -> i1
+! CHECK: %[[DIM_LOADED2:.*]] = fir.if %[[IS_PRESENT2]] -> (i32) {
+! CHECK: %[[LOADED2:.*]] = fir.load %[[DECL_DIM2]]#0 : !fir.ref<i32>
+! CHECK: fir.result %[[LOADED2]] : i32
+! CHECK: } else {
+! CHECK: arith.constant 0 : i32
+! CHECK: fir.result
+! CHECK: }
+! CHECK: %[[ONE2:.*]] = arith.constant 1 : i32
+! CHECK: %[[DIM_VAL2:.*]] = arith.select %[[IS_PRESENT2]], %[[DIM_LOADED2]], %[[ONE2]] : i32
+! CHECK-NOT: arith.cmpi
+! CHECK: hlfir.eoshift {{.*}} dim %[[DIM_VAL2]] :
More information about the flang-commits
mailing list