[flang-commits] [flang] [llvm] [flang] Check allocation status in inlined ALLOCATE lowering (PR #223287)

via flang-commits flang-commits at lists.llvm.org
Mon Sep 21 01:26:58 PDT 2026


https://github.com/shivaramaarao updated https://github.com/llvm/llvm-project/pull/223287

>From 6bb15f09d3068522181f3c5a41379eb4886036e6 Mon Sep 17 00:00:00 2001
From: Shivarama Rao <shivarama.rao at amd.com>
Date: Mon, 14 Sep 2026 07:32:56 +0530
Subject: [PATCH] [flang] Check allocation status in inlined ALLOCATE lowering

The inlined ALLOCATE path lowered the allocation without testing
whether the object was already allocated. This commit Introduces
fir.assert as a non-speculatable write of a non-addressable
FortranRuntimeResource. That keeps the check from being DCE'd or CSEd
without aliasing program memory, so unrelated store-to-load forwarding
still works.

Uses it in the inlined ALLOCATE path instead of a fir.if plus
_FortranAReportFatalUserError, and lower fir.assert through cf.assert.
Teach Flang LICM not to hoist non-speculatable ops across a preceding
assert, since the runtime write does not conflict in alias analysis.

Assisted by Cursor in updating existing tests and adding new tests.
---
 .../Driver/allocate-already-allocated.f90     | 14 +++++++
 .../flang/Optimizer/Dialect/FIRAttr.td        |  3 ++
 .../include/flang/Optimizer/Dialect/FIROps.h  |  8 ++++
 .../include/flang/Optimizer/Dialect/FIROps.td | 37 +++++++++++++++++++
 flang/lib/Lower/Allocatable.cpp               |  6 +++
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       | 32 +++++++++++-----
 .../Transforms/LoopInvariantCodeMotion.cpp    | 29 +++++++++++++++
 flang/test/Fir/convert-to-llvm.fir            | 20 ++++++++++
 flang/test/Fir/fir-ops.fir                    | 11 ++++++
 .../OpenMP/parallel-reduction-mixed.f90       |  5 +++
 flang/test/Lower/HLFIR/allocatable-return.f90 |  4 ++
 flang/test/Lower/Intrinsics/len.f90           |  1 +
 .../test/Lower/OpenACC/acc-data-operands.f90  |  1 +
 .../Lower/OpenMP/allocatable-array-bounds.f90 |  3 ++
 .../Lower/OpenMP/lastprivate-allocatable.f90  |  1 +
 .../target-enter-data-default-openmp52.f90    |  4 +-
 ...oop-reduction-allocatable-array-minmax.f90 |  4 ++
 flang/test/Lower/allocatable-return.f90       |  4 ++
 flang/test/Lower/allocatables.f90             |  2 +-
 .../test/Lower/allocate-already-allocated.f90 | 19 ++++++++++
 flang/test/Lower/assigned-goto.f90            |  1 +
 flang/test/Lower/intentout-deallocate.f90     |  1 +
 flang/test/Lower/io-implied-do-fixes.f90      |  2 +
 .../Transforms/DoConcurrent/allocatable.f90   |  1 +
 .../licm-non-addressable-resource.mlir        | 24 ++++++++++++
 25 files changed, 225 insertions(+), 12 deletions(-)
 create mode 100644 flang-rt/test/Driver/allocate-already-allocated.f90
 create mode 100644 flang/test/Lower/allocate-already-allocated.f90

diff --git a/flang-rt/test/Driver/allocate-already-allocated.f90 b/flang-rt/test/Driver/allocate-already-allocated.f90
new file mode 100644
index 0000000000000..9ee8eed44db2b
--- /dev/null
+++ b/flang-rt/test/Driver/allocate-already-allocated.f90
@@ -0,0 +1,14 @@
+! UNSUPPORTED: offload-cuda
+
+! RUN: %flang %isysroot -L"%libdir" %s -o %t
+! RUN: not --crash env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t
+
+! Double ALLOCATE of the same object must abort. The assert message is
+! checked in Lower/allocate-already-allocated.f90.
+
+program allocate_twice
+  integer, allocatable :: array(:)
+
+  allocate(array(5))
+  allocate(array(8))
+end program
diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
index 88d7964768b5a..5b0f59bb36b3c 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
@@ -189,6 +189,9 @@ def fir_ReduceAttr : fir_Attr<"Reduce"> {
 // mlir::SideEffects::Resource for modelling operations which add debugging information
 def DebuggingResource : Resource<"::fir::DebuggingResource">;
 
+// mlir::SideEffects::Resource for modelling Fortran runtime-owned state.
+def FortranRuntimeResource : Resource<"::fir::FortranRuntimeResource">;
+
 def fir_LowerBoundModifierAttribute : I32EnumAttr<
     "LowerBoundModifierAttribute",
     "Describes how to modify lower bounds",
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index 9d771b1e60604..49b2426121830 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.h
@@ -54,6 +54,14 @@ struct DebuggingResource
   bool isAddressable() const override { return false; }
 };
 
+/// Model interaction with Fortran runtime state that is not program memory
+/// (error reporting, abort, and similar runtime-owned resources).
+struct FortranRuntimeResource
+    : public mlir::SideEffects::Resource::Base<FortranRuntimeResource> {
+  mlir::StringRef getName() const final { return "FortranRuntimeResource"; }
+  bool isAddressable() const override { return false; }
+};
+
 /// Model operations which read from/write to volatile memory
 struct VolatileMemoryResource
     : public mlir::SideEffects::Resource::Base<VolatileMemoryResource> {
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index faefdbd37b76f..fd236af72d8f4 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -839,6 +839,43 @@ def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> {
 
 }
 
+def fir_AssertOp : fir_Op<"assert",
+    [ConditionallySpeculatable,
+     MemoryEffects<[MemWrite<FortranRuntimeResource>]>]> {
+  let summary = "assert a condition at runtime";
+
+  let description = [{
+    Assert that a condition is true at runtime. If the condition is false,
+    program execution is terminated and the diagnostic message may be reported
+    to the user via the Fortran runtime so that formatting and abort behavior
+    stay consistent with other runtime aborts.
+
+    This operation is not speculatable. It writes a non-addressable
+    `FortranRuntimeResource` so that it cannot be CSEd or DCE'd, while still
+    allowing store-to-load forwarding of unrelated program memory across it.
+
+    It is not a terminator. MLIR CFG regions treat every operation in a block
+    as having the same reachability, so this write does not by itself prevent
+    a later non-speculatable op in the same block from being moved before it.
+    Passes should not speculate such ops; they must consult
+    `ConditionallySpeculatable` rather than alias analysis alone.
+
+    ```
+      fir.assert %condition, "expected condition to hold"
+    ```
+  }];
+
+  let arguments = (ins I1:$condition, StrAttr:$message);
+
+  let assemblyFormat = "$condition `,` $message attr-dict";
+
+  let extraClassDeclaration = [{
+    ::mlir::Speculation::Speculatability getSpeculatability() {
+      return ::mlir::Speculation::NotSpeculatable;
+    }
+  }];
+}
+
 def fir_FirEndOp : fir_Op<"end", [Terminator, NoMemoryEffect]> {
   let summary = "the end instruction";
 
diff --git a/flang/lib/Lower/Allocatable.cpp b/flang/lib/Lower/Allocatable.cpp
index 5f10319ab0101..aa0384d27b84f 100644
--- a/flang/lib/Lower/Allocatable.cpp
+++ b/flang/lib/Lower/Allocatable.cpp
@@ -446,6 +446,12 @@ class AllocateStmtHelper {
   /// Only for intrinsic types. No coarrays, no polymorphism. No error recovery.
   void genInlinedAllocation(const Allocation &alloc,
                             const fir::MutableBoxValue &box) {
+    mlir::Value isNotAllocated =
+        fir::factory::genIsNotAllocatedOrAssociatedTest(builder, loc, box);
+    fir::AssertOp::create(builder, loc, isNotAllocated,
+                          "The object '" + alloc.getSymbol().name().ToString() +
+                              "' is already allocated");
+
     llvm::SmallVector<mlir::Value> lbounds;
     llvm::SmallVector<mlir::Value> extents;
     Fortran::lower::StatementContext stmtCtx;
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 1fafee2e90714..44060c26b0a7e 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -51,6 +51,7 @@
 #include "mlir/Conversion/UBToLLVM/UBToLLVM.h"
 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
 #include "mlir/Dialect/DLTI/DLTI.h"
 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
@@ -4347,6 +4348,19 @@ struct UnreachableOpConversion
   }
 };
 
+/// `fir.assert` --> `cf.assert`
+struct AssertOpConversion : public fir::FIROpConversion<fir::AssertOp> {
+  using FIROpConversion::FIROpConversion;
+
+  llvm::LogicalResult
+  matchAndRewrite(fir::AssertOp assertOp, OpAdaptor adaptor,
+                  mlir::ConversionPatternRewriter &rewriter) const override {
+    rewriter.replaceOpWithNewOp<mlir::cf::AssertOp>(
+        assertOp, adaptor.getCondition(), assertOp.getMessageAttr());
+    return mlir::success();
+  }
+};
+
 /// `fir.is_present` -->
 /// ```
 ///  %0 = llvm.mlir.constant(0 : i64)
@@ -5100,15 +5114,15 @@ void fir::populateFIRToLLVMConversionPatterns(
   patterns.insert<
       AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
       AllocaOpConversion, AllocMemOpConversion, BitcastOpConversion,
-      BoxAddrOpConversion, BoxCharLenOpConversion, BoxDimsOpConversion,
-      BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
-      BoxIsPtrOpConversion, AssumedSizeExtentOpConversion,
-      IsAssumedSizeExtentOpConversion, BoxOffsetOpConversion,
-      BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeCodeOpConversion,
-      BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion,
-      VolatileCastOpConversion, ConvertOpConversion, CoordinateOpConversion,
-      CopyOpConversion, DTEntryOpConversion, DeclareOpConversion,
-      DeclareValueOpConversion,
+      AssertOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
+      BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
+      BoxIsArrayOpConversion, BoxIsPtrOpConversion,
+      AssumedSizeExtentOpConversion, IsAssumedSizeExtentOpConversion,
+      BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
+      BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
+      CmpcOpConversion, VolatileCastOpConversion, ConvertOpConversion,
+      CoordinateOpConversion, CopyOpConversion, DTEntryOpConversion,
+      DeclareOpConversion, DeclareValueOpConversion,
       DoConcurrentSpecifierOpConversion<fir::LocalitySpecifierOp>,
       DoConcurrentSpecifierOpConversion<fir::DeclareReductionOp>,
       CreateBoxOpConversion, DivcOpConversion, EmboxOpConversion,
diff --git a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
index 4003f1cb30b18..0b81b9ad8cde1 100644
--- a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
+++ b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
@@ -14,6 +14,7 @@
 
 #include "flang/Optimizer/Analysis/AliasAnalysis.h"
 #include "flang/Optimizer/Dialect/FIROperationMoveOpInterface.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
@@ -240,6 +241,24 @@ static bool canHoistLoad(Operation *op, LoopLikeOpInterface loopLike,
   return false;
 }
 
+/// Return true if moving \p op out of \p loopLike would move it before a
+/// fir.assert that precedes it in one of its enclosing blocks. Since
+/// fir.assert may terminate execution, doing so would speculate \p op on the
+/// assertion failure path.
+static bool isAfterAssert(Operation *op, LoopLikeOpInterface loopLike) {
+  Operation *current = op;
+  while (current != loopLike.getOperation()) {
+    for (Operation *previous = current->getPrevNode(); previous;
+         previous = previous->getPrevNode())
+      if (isa<fir::AssertOp>(previous))
+        return true;
+    current = current->getParentOp();
+    if (!current)
+      return false;
+  }
+  return false;
+}
+
 /// Returns true iff hoisting \p op out of a nested region is expected to be
 /// inexpensive. This is a cost heuristic only; the safety of the hoisting is
 /// established separately.
@@ -341,6 +360,16 @@ void LoopInvariantCodeMotion::runOnOperation() {
           return true;
         }
 
+        // fir.assert may terminate execution. Do not move an operation that
+        // cannot be speculated to a point where it executes before an assert
+        // that currently guards it. The assert's non-addressable runtime
+        // effect intentionally does not alias program memory, so alias
+        // analysis alone cannot enforce this ordering.
+        if (!isSpeculatable(op) && isAfterAssert(op, loopLike)) {
+          LDBG() << "Cannot speculate operation before fir.assert: " << *op;
+          return false;
+        }
+
         // Handle RecursivelySpeculatable operations that have
         // RecursiveMemoryEffects by checking if all their
         // nested operations can be hoisted.
diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir
index d00aae198bf13..4c2154fec64e2 100644
--- a/flang/test/Fir/convert-to-llvm.fir
+++ b/flang/test/Fir/convert-to-llvm.fir
@@ -326,6 +326,26 @@ func.func @test_unreachable() {
 
 // -----
 
+// Verify that fir.assert is transformed to an LLVM conditional branch that
+// reports the message and aborts when the condition is false.
+
+func.func @test_assert(%condition: i1) {
+  fir.assert %condition, "expected condition to hold"
+  return
+}
+
+// CHECK: llvm.mlir.global private constant @assert_msg
+// CHECK: llvm.func @test_assert(%[[CONDITION:.*]]: i1) {
+// CHECK:   llvm.cond_br %[[CONDITION]], ^[[CONTINUE:.*]], ^[[FAILURE:.*]]
+// CHECK: ^[[CONTINUE]]:
+// CHECK:   llvm.return
+// CHECK: ^[[FAILURE]]:
+// CHECK:   llvm.call @puts
+// CHECK:   llvm.call @abort()
+// CHECK:   llvm.unreachable
+
+// -----
+
 // Test fir.extract_value operation conversion with derived type.
 
 func.func @extract_derived_type() -> f32 {
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 06d4d2206b74f..128c10fcc66bb 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -1,6 +1,7 @@
 // Test the FIR operations
 
 // RUN: fir-opt %s | fir-opt | FileCheck %s
+// RUN: fir-opt --canonicalize %s | FileCheck %s --check-prefix=ASSERT
 
 // CHECK-LABEL: func private @it1() -> !fir.int<4>
 // CHECK: func private @box1() -> !fir.boxchar<2>
@@ -11,6 +12,16 @@ func.func private @box1() -> !fir.boxchar<2>
 func.func private @box2() -> !fir.boxproc<(i32, i32) -> i64>
 func.func private @box3() -> !fir.box<!fir.type<derived3{f:f32}>>
 
+// CHECK-LABEL: func.func @test_assert(
+// CHECK-SAME: %[[CONDITION:.*]]: i1
+// CHECK: fir.assert %[[CONDITION]], "expected condition to hold"
+// ASSERT-LABEL: func.func @test_assert(
+// ASSERT: fir.assert
+func.func @test_assert(%condition: i1) {
+  fir.assert %condition, "expected condition to hold"
+  return
+}
+
 // Fortran SUBROUTINE and FUNCTION
 // CHECK-LABEL: func private @print_index3(index, index, index)
 // CHECK: func private @user_i64(i64)
diff --git a/flang/test/Integration/OpenMP/parallel-reduction-mixed.f90 b/flang/test/Integration/OpenMP/parallel-reduction-mixed.f90
index 8f25a6a92d7e8..c25e926a2eccd 100644
--- a/flang/test/Integration/OpenMP/parallel-reduction-mixed.f90
+++ b/flang/test/Integration/OpenMP/parallel-reduction-mixed.f90
@@ -43,6 +43,11 @@ end subroutine proc
 !CHECK:  br label %[[MALLOC_BB:.*]]
 
 !CHECK: [[MALLOC_BB]]:
+!! ALLOCATE first reports an error if the object is already allocated.
+!CHECK:  %[[IS_NOT_ALLOCATED:.*]] = icmp eq i64 %{{.*}}, 0
+!CHECK:  br i1 %[[IS_NOT_ALLOCATED]], label %[[ALLOC_BB:.*]], label %{{.*}}
+
+!CHECK: [[ALLOC_BB]]:
 !CHECK-NOT: omp.par.{{.*}}:
 !POSIX: call ptr @aligned_alloc(i{{(32)|(64)}} 64, i{{(32)|(64)}} 128)
 !WINDOWS: call ptr @malloc(i{{(32)|(64)}} 80)
diff --git a/flang/test/Lower/HLFIR/allocatable-return.f90 b/flang/test/Lower/HLFIR/allocatable-return.f90
index b6e8df5d07fa6..3ef5f87344a81 100644
--- a/flang/test/Lower/HLFIR/allocatable-return.f90
+++ b/flang/test/Lower/HLFIR/allocatable-return.f90
@@ -10,6 +10,7 @@ end function test_alloc_return_scalar
 ! CHECK-LABEL:   func.func @_QPtest_alloc_return_scalar() -> !fir.box<!fir.heap<f32>> {
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "test_alloc_return_scalar", uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"}
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+! CHECK:           fir.store %{{.*}} to %[[VAL_3]]#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
 ! CHECK:           %[[VAL_6:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
 ! CHECK:           return %[[VAL_6]] : !fir.box<!fir.heap<f32>>
 ! CHECK:         }
@@ -21,6 +22,7 @@ end function test_alloc_return_array
 ! CHECK-LABEL:   func.func @_QPtest_alloc_return_array() -> !fir.box<!fir.heap<!fir.array<?xf32>>> {
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "test_alloc_return_array", uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"}
 ! CHECK:           %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>)
+! CHECK:           fir.store %{{.*}} to %[[VAL_5]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
 ! CHECK:           %[[VAL_19:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
 ! CHECK:           %[[VAL_20:.*]] = arith.constant 1 : index
 ! CHECK:           %[[VAL_21:.*]] = fir.shift %[[VAL_20]] : (index) -> !fir.shift<1>
@@ -36,6 +38,7 @@ end function test_alloc_return_char_scalar
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,3>>> {bindc_name = "test_alloc_return_char_scalar", uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"}
 ! CHECK:           %[[VAL_1:.*]] = arith.constant 3 : index
 ! CHECK:           %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>, index) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>)
+! CHECK:           fir.store %{{.*}} to %[[VAL_4]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
 ! CHECK:           %[[VAL_7:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
 ! CHECK:           return %[[VAL_7]] : !fir.box<!fir.heap<!fir.char<1,3>>>
 ! CHECK:         }
@@ -48,6 +51,7 @@ end function test_alloc_return_char_array
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {bindc_name = "test_alloc_return_char_array", uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"}
 ! CHECK:           %[[VAL_1:.*]] = arith.constant 3 : index
 ! CHECK:           %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>, index) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>)
+! CHECK:           fir.store %{{.*}} to %[[VAL_6]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
 ! CHECK:           %[[VAL_20:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
 ! CHECK:           %[[VAL_21:.*]] = arith.constant 1 : index
 ! CHECK:           %[[VAL_22:.*]] = fir.shift %[[VAL_21]] : (index) -> !fir.shift<1>
diff --git a/flang/test/Lower/Intrinsics/len.f90 b/flang/test/Lower/Intrinsics/len.f90
index c0ca30a6401dd..075c9738da164 100644
--- a/flang/test/Lower/Intrinsics/len.f90
+++ b/flang/test/Lower/Intrinsics/len.f90
@@ -65,6 +65,7 @@ subroutine len_test_array_local_alloc(i)
 ! CHECK:  %[[I:.*]]:2 = hlfir.declare %[[VAL_0]]
 ! CHECK:  %[[C10:.*]] = arith.constant 10 : i32
   allocate(character(10):: c(100))
+! CHECK:  fir.store %{{.*}} to %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
 ! CHECK:  %[[C_LOADED:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
 ! CHECK:  %[[ELESIZE:.*]] = fir.box_elesize %[[C_LOADED]] : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>) -> index
 ! CHECK:  %[[RESULT:.*]] = fir.convert %[[ELESIZE]] : (index) -> i32
diff --git a/flang/test/Lower/OpenACC/acc-data-operands.f90 b/flang/test/Lower/OpenACC/acc-data-operands.f90
index bd65518797f2a..ad33c3a1ee9d3 100644
--- a/flang/test/Lower/OpenACC/acc-data-operands.f90
+++ b/flang/test/Lower/OpenACC/acc-data-operands.f90
@@ -116,6 +116,7 @@ subroutine acc_operand_array_section_allocatable()
 ! CHECK-LABEL: func.func @_QMacc_data_operandPacc_operand_array_section_allocatable() {
 ! CHECK: %[[A:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", uniq_name = "_QMacc_data_operandFacc_operand_array_section_allocatableEa"}
 ! CHECK: %[[DECLA:.*]]:2 = hlfir.declare %[[A]] {fortran_attrs = #fir.var_attrs<allocatable>
+! CHECK: fir.allocmem !fir.array<?xf32>
 ! CHECK: %[[LOAD_BOX_A_1:.*]] = fir.load %[[DECLA]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
 ! CHECK: %[[C0:.*]] = arith.constant 0 : index
 ! CHECK: %[[DIMS0_0:.*]]:3 = fir.box_dims %[[LOAD_BOX_A_1]], %[[C0]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>, index) -> (index, index, index)
diff --git a/flang/test/Lower/OpenMP/allocatable-array-bounds.f90 b/flang/test/Lower/OpenMP/allocatable-array-bounds.f90
index 1aa4780a47517..76becf3bf53fc 100644
--- a/flang/test/Lower/OpenMP/allocatable-array-bounds.f90
+++ b/flang/test/Lower/OpenMP/allocatable-array-bounds.f90
@@ -8,6 +8,9 @@
 !HOST: %[[ALLOCA_2:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>> {bindc_name = "sp_write", uniq_name = "_QFread_write_sectionEsp_write"}
 !HOST: %[[DECLARE_2:.*]]:2 = hlfir.declare %[[ALLOCA_2]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFread_write_sectionEsp_write"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
 
+!HOST: fir.allocmem !fir.array<?xi32>, %{{.*}}uniq_name = "_QFread_write_sectionEsp_read.alloc"}
+!HOST: fir.allocmem !fir.array<?xi32>, %{{.*}}uniq_name = "_QFread_write_sectionEsp_write.alloc"}
+
 !HOST: %[[LOAD_1:.*]] = fir.load %[[DECLARE_1]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
 !HOST: %[[LOAD_2:.*]] = fir.load %[[DECLARE_1]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
 !HOST: %[[CONSTANT_1:.*]] = arith.constant 0 : index
diff --git a/flang/test/Lower/OpenMP/lastprivate-allocatable.f90 b/flang/test/Lower/OpenMP/lastprivate-allocatable.f90
index c2626e14b51c7..6e49ffc8304e0 100644
--- a/flang/test/Lower/OpenMP/lastprivate-allocatable.f90
+++ b/flang/test/Lower/OpenMP/lastprivate-allocatable.f90
@@ -49,6 +49,7 @@ program lastprivate_allocatable
 ! CHECK:            omp.sections {
 ! CHECK:              omp.section {
 ! CHECK:                fir.load
+! CHECK:                fir.allocmem !fir.array<?xcomplex<f32>>
 ! CHECK:                %[[TEMP:.*]] = fir.load %[[A_PRIV:.*]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xcomplex<f32>>>>>
 ! CHECK:                hlfir.assign %[[TEMP]] to %[[A]]#0 realloc : !fir.box<!fir.heap<!fir.array<?xcomplex<f32>>>>,
 ! CHECK-SAME:             !fir.ref<!fir.box<!fir.heap<!fir.array<?xcomplex<f32>>>>>
diff --git a/flang/test/Lower/OpenMP/target-enter-data-default-openmp52.f90 b/flang/test/Lower/OpenMP/target-enter-data-default-openmp52.f90
index 086866cb8682b..7ba17ea8b3643 100644
--- a/flang/test/Lower/OpenMP/target-enter-data-default-openmp52.f90
+++ b/flang/test/Lower/OpenMP/target-enter-data-default-openmp52.f90
@@ -10,8 +10,8 @@ module test
   subroutine initialize()
   allocate(A)
   !$omp target enter data map(A)
-  !CHECK-52: omp.map.info var_ptr(%2 : !fir.ref<!fir.box<!fir.heap<f32>>>, !fir.box<!fir.heap<f32>>) map_clauses(to) capture(ByRef) var_ptr_ptr(%5 : !fir.llvm_ptr<!fir.ref<f32>>, f32) name("") -> !fir.llvm_ptr<!fir.ref<f32>>
-  !CHECK-52: omp.map.info var_ptr(%2 : !fir.ref<!fir.box<!fir.heap<f32>>>, !fir.box<!fir.heap<f32>>) map_clauses(always, to) capture(ByRef) members(%6 : [0] : !fir.llvm_ptr<!fir.ref<f32>>) name("a") -> !fir.ref<!fir.box<!fir.heap<f32>>>
+  !CHECK-52: %[[MEMBER:.*]] = omp.map.info var_ptr(%[[A:.*]] : !fir.ref<!fir.box<!fir.heap<f32>>>, !fir.box<!fir.heap<f32>>) map_clauses(to) capture(ByRef) var_ptr_ptr(%{{[0-9]+}} : !fir.llvm_ptr<!fir.ref<f32>>, f32) name("") -> !fir.llvm_ptr<!fir.ref<f32>>
+  !CHECK-52: omp.map.info var_ptr(%[[A]] : !fir.ref<!fir.box<!fir.heap<f32>>>, !fir.box<!fir.heap<f32>>) map_clauses(always, to) capture(ByRef) members(%[[MEMBER]] : [0] : !fir.llvm_ptr<!fir.ref<f32>>) name("a") -> !fir.ref<!fir.box<!fir.heap<f32>>>
   !CHECK-51: to and alloc map types are permitted
 
   end subroutine initialize
diff --git a/flang/test/Lower/OpenMP/wsloop-reduction-allocatable-array-minmax.f90 b/flang/test/Lower/OpenMP/wsloop-reduction-allocatable-array-minmax.f90
index 0c96d741c1992..e8c81aef3ff79 100644
--- a/flang/test/Lower/OpenMP/wsloop-reduction-allocatable-array-minmax.f90
+++ b/flang/test/Lower/OpenMP/wsloop-reduction-allocatable-array-minmax.f90
@@ -165,6 +165,8 @@ program reduce15
 ! CHECK:           %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]] {fortran_attrs = {{.*}}<allocatable>, uniq_name = "_QFEmins"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
 ! CHECK:           %[[VAL_8:.*]] = fir.address_of(@_QFECsize) : !fir.ref<i32>
 ! CHECK:           %[[VAL_9:.*]]:2 = hlfir.declare %[[VAL_8]] {fortran_attrs = {{.*}}<parameter>, uniq_name = "_QFECsize"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! Each ALLOCATE first reports an error if the object is already allocated.
+! CHECK:           fir.assert {{.*}}, "The object 'arr' is already allocated"
 ! CHECK:           %[[VAL_10:.*]] = arith.constant 10 : i32
 ! CHECK:           %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (i32) -> index
 ! CHECK:           %[[VAL_12:.*]] = arith.constant 0 : index
@@ -174,6 +176,7 @@ program reduce15
 ! CHECK:           %[[VAL_16:.*]] = fir.shape %[[VAL_14]] : (index) -> !fir.shape<1>
 ! CHECK:           %[[VAL_17:.*]] = fir.embox %[[VAL_15]](%[[VAL_16]]) : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xi32>>>
 ! CHECK:           fir.store %[[VAL_17]] to %[[VAL_1]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:           fir.assert {{.*}}, "The object 'maxes' is already allocated"
 ! CHECK:           %[[VAL_18:.*]] = arith.constant 10 : i32
 ! CHECK:           %[[VAL_19:.*]] = fir.convert %[[VAL_18]] : (i32) -> index
 ! CHECK:           %[[VAL_20:.*]] = arith.constant 0 : index
@@ -183,6 +186,7 @@ program reduce15
 ! CHECK:           %[[VAL_24:.*]] = fir.shape %[[VAL_22]] : (index) -> !fir.shape<1>
 ! CHECK:           %[[VAL_25:.*]] = fir.embox %[[VAL_23]](%[[VAL_24]]) : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xi32>>>
 ! CHECK:           fir.store %[[VAL_25]] to %[[VAL_5]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:           fir.assert {{.*}}, "The object 'mins' is already allocated"
 ! CHECK:           %[[VAL_26:.*]] = arith.constant 10 : i32
 ! CHECK:           %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (i32) -> index
 ! CHECK:           %[[VAL_28:.*]] = arith.constant 0 : index
diff --git a/flang/test/Lower/allocatable-return.f90 b/flang/test/Lower/allocatable-return.f90
index f00b31a03f54d..b17b83fda4802 100644
--- a/flang/test/Lower/allocatable-return.f90
+++ b/flang/test/Lower/allocatable-return.f90
@@ -10,6 +10,7 @@ end function test_alloc_return_scalar
 ! CHECK-LABEL:   func.func @_QPtest_alloc_return_scalar() -> !fir.box<!fir.heap<f32>> {
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "test_alloc_return_scalar", uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"}
 ! CHECK:           %[[VAL_DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}
+! CHECK:           fir.store %{{.*}} to %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
 ! CHECK:           %[[VAL:.*]] = fir.load %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
 ! CHECK:           return %[[VAL]] : !fir.box<!fir.heap<f32>>
 ! CHECK:         }
@@ -21,6 +22,7 @@ end function test_alloc_return_array
 ! CHECK-LABEL:   func.func @_QPtest_alloc_return_array() -> !fir.box<!fir.heap<!fir.array<?xf32>>> {
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "test_alloc_return_array", uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"}
 ! CHECK:           %[[VAL_DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}
+! CHECK:           fir.store %{{.*}} to %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
 ! CHECK:           %[[VAL:.*]] = fir.load %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
 ! CHECK:           %[[C1:.*]] = arith.constant 1 : index
 ! CHECK:           %[[SHIFT:.*]] = fir.shift %[[C1]] : (index) -> !fir.shift<1>
@@ -36,6 +38,7 @@ end function test_alloc_return_char_scalar
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,3>>> {bindc_name = "test_alloc_return_char_scalar", uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"}
 ! CHECK:           %[[LEN:.*]] = arith.constant 3 : index
 ! CHECK:           %[[VAL_DECL:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[LEN]] {{.*}}
+! CHECK:           fir.store %{{.*}} to %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
 ! CHECK:           %[[VAL:.*]] = fir.load %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
 ! CHECK:           return %[[VAL]] : !fir.box<!fir.heap<!fir.char<1,3>>>
 ! CHECK:         }
@@ -48,6 +51,7 @@ end function test_alloc_return_char_array
 ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {bindc_name = "test_alloc_return_char_array", uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"}
 ! CHECK:           %[[LEN:.*]] = arith.constant 3 : index
 ! CHECK:           %[[VAL_DECL:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[LEN]] {{.*}}
+! CHECK:           fir.store %{{.*}} to %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
 ! CHECK:           %[[VAL:.*]] = fir.load %[[VAL_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
 ! CHECK:           %[[C1:.*]] = arith.constant 1 : index
 ! CHECK:           %[[SHIFT:.*]] = fir.shift %[[C1]] : (index) -> !fir.shift<1>
diff --git a/flang/test/Lower/allocatables.f90 b/flang/test/Lower/allocatables.f90
index 2600162eee8c7..06e4aeb2793af 100644
--- a/flang/test/Lower/allocatables.f90
+++ b/flang/test/Lower/allocatables.f90
@@ -80,7 +80,7 @@ subroutine char_deferred(n)
   character(:), allocatable :: c
   ! CHECK: %[[cAddrVar:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,?>>> {{{.*}}uniq_name = "_QFchar_deferredEc"}
   allocate(character(10):: c)
-  ! CHECK: %[[c10:.]] = fir.convert %c10_i32 : (i32) -> index
+  ! CHECK: %[[c10:.*]] = fir.convert %c10_i32 : (i32) -> index
   ! CHECK: %[[alloc:.*]] = fir.allocmem !fir.char<1,?>(%[[c10]] : index) {{{.*}}uniq_name = "_QFchar_deferredEc.alloc"}
   ! CHECK: %[[box:.*]] = fir.embox %[[alloc]] typeparams %[[c10]]
   ! CHECK: fir.store %[[box]] to %{{.*}}
diff --git a/flang/test/Lower/allocate-already-allocated.f90 b/flang/test/Lower/allocate-already-allocated.f90
new file mode 100644
index 0000000000000..8ae6449b5d1b2
--- /dev/null
+++ b/flang/test/Lower/allocate-already-allocated.f90
@@ -0,0 +1,19 @@
+! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
+
+! Verify that the inlined allocation path checks the allocation status before
+! calling the actual allocation function
+
+! CHECK-LABEL: func.func @_QPallocate_twice()
+subroutine allocate_twice()
+  integer, allocatable :: array(:)
+
+  ! CHECK: %[[IS_NOT_ALLOCATED_1:.*]] = arith.cmpi eq
+  ! CHECK: fir.assert %[[IS_NOT_ALLOCATED_1]], "The object 'array' is already allocated"
+  ! CHECK: fir.allocmem
+  allocate(array(5))
+
+  ! CHECK: %[[IS_NOT_ALLOCATED_2:.*]] = arith.cmpi eq
+  ! CHECK: fir.assert %[[IS_NOT_ALLOCATED_2]], "The object 'array' is already allocated"
+  ! CHECK: fir.allocmem
+  allocate(array(8))
+end subroutine
diff --git a/flang/test/Lower/assigned-goto.f90 b/flang/test/Lower/assigned-goto.f90
index 15bc8a9c95962..a060dcaa87b3f 100644
--- a/flang/test/Lower/assigned-goto.f90
+++ b/flang/test/Lower/assigned-goto.f90
@@ -40,6 +40,7 @@ subroutine allocated
     integer :: V
  13 V = 1
     allocate(L)
+    ! CHECK: fir.allocmem i32
     ! CHECK: %[[N0:.+]] = fir.box_addr %{{.+}}
     ! CHECK: fir.store %c31{{.*}} to %[[N0]]
     assign 31 to L
diff --git a/flang/test/Lower/intentout-deallocate.f90 b/flang/test/Lower/intentout-deallocate.f90
index fc49fa83523b9..d7ea1defb3c1a 100644
--- a/flang/test/Lower/intentout-deallocate.f90
+++ b/flang/test/Lower/intentout-deallocate.f90
@@ -76,6 +76,7 @@ subroutine sub2()
 
 ! CHECK-LABEL: func.func @_QMmod1Psub2(
 ! CHECK: %[[ARG0:.*]]:2 = hlfir.declare {{.*}}"_QMmod1Fsub2Ea"
+! CHECK: fir.allocmem !fir.array<?xi32>
 ! CHECK: %[[BOX:.*]] = fir.load %[[ARG0]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
 ! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
 ! CHECK: %[[BOX_ADDR_PTR:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.heap<!fir.array<?xi32>>) -> i64
diff --git a/flang/test/Lower/io-implied-do-fixes.f90 b/flang/test/Lower/io-implied-do-fixes.f90
index 97647b0ef3f3c..e9c939a569e11 100644
--- a/flang/test/Lower/io-implied-do-fixes.f90
+++ b/flang/test/Lower/io-implied-do-fixes.f90
@@ -23,6 +23,7 @@ subroutine ido1
 ! CHECK-LABEL: func @_QPido2
 ! CHECK: %[[IPTR_BOX_ADDR:.*]] = fir.alloca !fir.box<!fir.heap<i32>> {bindc_name = "iptr", uniq_name = "_QFido2Eiptr"}
 ! CHECK: %[[IPTR_DECL:.*]]:2 = hlfir.declare %[[IPTR_BOX_ADDR]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFido2Eiptr"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
+! CHECK: fir.allocmem i32
 ! CHECK: %[[IPTR_BOX:.*]] = fir.load %[[IPTR_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
 ! CHECK: %[[IPTR_ADDR:.*]] = fir.box_addr %[[IPTR_BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
 ! CHECK: %[[J_VAL_FINAL:.*]] = fir.do_loop %[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}} -> index {
@@ -41,6 +42,7 @@ subroutine ido2
 ! CHECK-LABEL: func @_QPido3
 ! CHECK: %[[J_BOX_ADDR:.*]] = fir.alloca !fir.box<!fir.heap<i32>> {bindc_name = "j", uniq_name = "_QFido3Ej"}
 ! CHECK: %[[J_DECL:.*]]:2 = hlfir.declare %[[J_BOX_ADDR]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFido3Ej"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
+! CHECK: fir.allocmem i32
 ! CHECK: %[[J_BOX:.*]] = fir.load %[[J_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
 ! CHECK: %[[J_ADDR:.*]] = fir.box_addr %[[J_BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
 ! CHECK: %[[J_VAL_FINAL:.*]]:2 = fir.iterate_while (%[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}}) and (%[[OK:.*]] = {{.*}}) -> (index, i1) {
diff --git a/flang/test/Transforms/DoConcurrent/allocatable.f90 b/flang/test/Transforms/DoConcurrent/allocatable.f90
index 03962f150eb95..e6a9597afc40a 100644
--- a/flang/test/Transforms/DoConcurrent/allocatable.f90
+++ b/flang/test/Transforms/DoConcurrent/allocatable.f90
@@ -20,6 +20,7 @@ program main
 end program main
 
 ! CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEy"}
+! CHECK: fir.allocmem !fir.array<?xf32>
 ! CHECK: %[[Y_VAL:.*]] = fir.load %[[Y_DECL]]#0
 ! CHECK: %[[Y_DIM0:.*]]:3 = fir.box_dims %[[Y_VAL]], %{{c0_.*}}
 ! CHECK: %[[Y_LB:.*]] = arith.constant 0 : index
diff --git a/flang/test/Transforms/licm-non-addressable-resource.mlir b/flang/test/Transforms/licm-non-addressable-resource.mlir
index 07df4582b687c..299c921bf9364 100644
--- a/flang/test/Transforms/licm-non-addressable-resource.mlir
+++ b/flang/test/Transforms/licm-non-addressable-resource.mlir
@@ -23,3 +23,27 @@ func.func @_QPtest(%arg0: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "x"}, %
   }
   return
 }
+
+// -----
+
+// A fir.assert may terminate execution, so a non-speculatable load after it
+// must not be hoisted onto the assertion failure path. The assert writes a
+// non-addressable resource, therefore this ordering must be enforced
+// independently of alias analysis.
+// CHECK-LABEL:   func.func @assert_guards_load(
+// CHECK:           fir.do_loop {{.*}} {
+// CHECK:             fir.assert %{{.*}}, "valid pointer"
+// CHECK-NEXT:        %[[LOAD:.*]] = fir.load %{{.*}} : !fir.ref<f32>
+// CHECK:             fir.store %[[LOAD]] to %{{.*}} : !fir.ref<f32>
+func.func @assert_guards_load(%arg0: !fir.ref<!fir.array<10xf32>>, %arg1: !fir.ref<f32>, %is_valid: i1) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  fir.do_loop %i = %c1 to %c10 step %c1 {
+    fir.assert %is_valid, "valid pointer"
+    %value = fir.load %arg1 : !fir.ref<f32>
+    %element = fir.array_coor %arg0(%shape) %i : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+    fir.store %value to %element : !fir.ref<f32>
+  }
+  return
+}



More information about the flang-commits mailing list