[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