[flang-commits] [flang] [flang] Fix volatile attribute propagation on allocatables (PR #139183)
Asher Mancinelli via flang-commits
flang-commits at lists.llvm.org
Mon May 12 07:11:04 PDT 2025
https://github.com/ashermancinelli updated https://github.com/llvm/llvm-project/pull/139183
>From d8a31d1c81bcebdb900c546a12b4c2291d467ed8 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Thu, 8 May 2025 17:30:04 -0700
Subject: [PATCH 1/3] [flang] Fix volatile attr propagation on allocatables
Ensure volatility is reflected not just on the reference of an allocatable,
but on the box, too. When we designate a volatile allocatable, we now get
a volatile reference to a volatile box.
Some related cleanups:
* SELECT TYPE constructs properly handle volatility by checking the
selector's type for volatility when creating the target type.
* Refine the verifier for fir.convert. In general, I think it should be
ok to implicitly drop volatility in any ptr-to-int conversion because
it means we are in codegen or we are calling an external function, and
it's okay to drop volatility from the Fir type system in these cases.
* An allocatable test that was XFAILed is now passing.
* I noticed a runtime function was missing the fir.runtime attribute. Fix that.
---
flang/lib/Lower/Bridge.cpp | 12 +++--
flang/lib/Optimizer/Dialect/FIROps.cpp | 47 +++++++++++++++----
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 15 ++++--
.../Transforms/PolymorphicOpConversion.cpp | 11 +++--
flang/test/Fir/invalid.fir | 4 +-
flang/test/Lower/volatile-allocatable.f90 | 21 +++++----
flang/test/Lower/volatile-allocatable1.f90 | 17 ++++++-
7 files changed, 91 insertions(+), 36 deletions(-)
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 43375e84f21fa..d28c01ed16cbf 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -3842,6 +3842,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
bool hasLocalScope = false;
llvm::SmallVector<const Fortran::semantics::Scope *> typeCaseScopes;
+ const auto selectorIsVolatile = [&selector]() {
+ return fir::isa_volatile_type(fir::getBase(selector).getType());
+ };
+
const auto &typeCaseList =
std::get<std::list<Fortran::parser::SelectTypeConstruct::TypeCase>>(
selectTypeConstruct.t);
@@ -3995,7 +3999,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
addrTy = fir::HeapType::get(addrTy);
if (std::holds_alternative<Fortran::parser::IntrinsicTypeSpec>(
typeSpec->u)) {
- mlir::Type refTy = fir::ReferenceType::get(addrTy);
+ mlir::Type refTy = fir::ReferenceType::get(addrTy, selectorIsVolatile());
if (isPointer || isAllocatable)
refTy = addrTy;
exactValue = builder->create<fir::BoxAddrOp>(
@@ -4004,7 +4008,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
typeSpec->declTypeSpec->AsIntrinsic();
if (isArray) {
mlir::Value exact = builder->create<fir::ConvertOp>(
- loc, fir::BoxType::get(addrTy), fir::getBase(selector));
+ loc, fir::BoxType::get(addrTy, selectorIsVolatile()), fir::getBase(selector));
addAssocEntitySymbol(selectorBox->clone(exact));
} else if (intrinsic->category() ==
Fortran::common::TypeCategory::Character) {
@@ -4019,7 +4023,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
} else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
typeSpec->u)) {
exactValue = builder->create<fir::ConvertOp>(
- loc, fir::BoxType::get(addrTy), fir::getBase(selector));
+ loc, fir::BoxType::get(addrTy, selectorIsVolatile()), fir::getBase(selector));
addAssocEntitySymbol(selectorBox->clone(exactValue));
}
} else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
@@ -4037,7 +4041,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
addrTy = fir::PointerType::get(addrTy);
if (isAllocatable)
addrTy = fir::HeapType::get(addrTy);
- mlir::Type classTy = fir::ClassType::get(addrTy);
+ mlir::Type classTy = fir::ClassType::get(addrTy, selectorIsVolatile());
if (classTy == baseTy) {
addAssocEntitySymbol(selector);
} else {
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 332cca1ab9f95..9b58578e55474 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1536,20 +1536,47 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
areRecordsCompatible(inType, outType);
}
+// In general, ptrtoint-like conversions are allowed to lose volatility information
+// because they are either:
+//
+// 1. passing an entity to an external function and there's nothing we can do
+// about volatility after that happens, or
+// 2. for code generation, at which point we represent volatility with attributes
+// on the LLVM instructions and intrinsics.
+//
+// For all other cases, volatility ought to match exactly.
+static mlir::LogicalResult verifyVolatility(mlir::Type inType, mlir::Type outType) {
+ const bool toLLVMPointer = mlir::isa<mlir::LLVM::LLVMPointerType>(outType);
+ const bool toInteger = fir::isa_integer(outType);
+
+ // When converting references to classes or allocatables into boxes for runtime arguments,
+ // we cast away all the volatility information and pass a box<none>. This is allowed.
+ const bool isBoxNoneLike = [&]() {
+ if (fir::isBoxNone(outType))
+ return true;
+ if (auto referenceType = mlir::dyn_cast<fir::ReferenceType>(outType)) {
+ if (fir::isBoxNone(referenceType.getElementType())) {
+ return true;
+ }
+ }
+ return false;
+ }();
+
+ const bool isPtrToIntLike = toLLVMPointer || toInteger || isBoxNoneLike;
+ if (isPtrToIntLike) {
+ return mlir::success();
+ }
+
+ // In all other cases, we need to check for an exact volatility match.
+ return mlir::success(fir::isa_volatile_type(inType) == fir::isa_volatile_type(outType));
+}
+
llvm::LogicalResult fir::ConvertOp::verify() {
mlir::Type inType = getValue().getType();
mlir::Type outType = getType();
- // If we're converting to an LLVM pointer type or an integer, we don't
- // need to check for volatility mismatch - volatility will be handled by the
- // memory operations themselves in llvm code generation and ptr-to-int can't
- // represent volatility.
- const bool toLLVMPointer = mlir::isa<mlir::LLVM::LLVMPointerType>(outType);
- const bool toInteger = fir::isa_integer(outType);
if (fir::useStrictVolatileVerification()) {
- if (fir::isa_volatile_type(inType) != fir::isa_volatile_type(outType) &&
- !toLLVMPointer && !toInteger) {
- return emitOpError("cannot convert between volatile and non-volatile "
- "types, use fir.volatile_cast instead ")
+ if (failed(verifyVolatility(inType, outType))) {
+ return emitOpError("this conversion does not preserve volatility: ")
<< inType << " / " << outType;
}
}
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 711d5d1461b08..52517eef2890d 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -207,20 +207,25 @@ static bool hasExplicitLowerBounds(mlir::Value shape) {
mlir::isa<fir::ShapeShiftType, fir::ShiftType>(shape.getType());
}
-static std::pair<mlir::Type, mlir::Value> updateDeclareInputTypeWithVolatility(
+static std::pair<mlir::Type, mlir::Value> updateDeclaredInputTypeWithVolatility(
mlir::Type inputType, mlir::Value memref, mlir::OpBuilder &builder,
fir::FortranVariableFlagsAttr fortran_attrs) {
if (fortran_attrs &&
bitEnumContainsAny(fortran_attrs.getFlags(),
fir::FortranVariableFlagsEnum::fortran_volatile)) {
+ // A volatile pointer's pointee is volatile.
const bool isPointer = bitEnumContainsAny(
fortran_attrs.getFlags(), fir::FortranVariableFlagsEnum::pointer);
+ // An allocatable's inner type's volatility matches that of the reference.
+ const bool isAllocatable = bitEnumContainsAny(
+ fortran_attrs.getFlags(), fir::FortranVariableFlagsEnum::allocatable);
auto updateType = [&](auto t) {
using FIRT = decltype(t);
- // A volatile pointer's pointee is volatile.
auto elementType = t.getEleTy();
- const bool elementTypeIsVolatile =
- isPointer || fir::isa_volatile_type(elementType);
+ const bool elementTypeIsBox = mlir::isa<fir::BoxType>(elementType);
+ const bool elementTypeIsVolatile = isPointer || isAllocatable ||
+ elementTypeIsBox ||
+ fir::isa_volatile_type(elementType);
auto newEleTy =
fir::updateTypeWithVolatility(elementType, elementTypeIsVolatile);
inputType = FIRT::get(newEleTy, true);
@@ -243,7 +248,7 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
auto nameAttr = builder.getStringAttr(uniq_name);
mlir::Type inputType = memref.getType();
bool hasExplicitLbs = hasExplicitLowerBounds(shape);
- std::tie(inputType, memref) = updateDeclareInputTypeWithVolatility(
+ std::tie(inputType, memref) = updateDeclaredInputTypeWithVolatility(
inputType, memref, builder, fortran_attrs);
mlir::Type hlfirVariableType =
getHLFIRVariableType(inputType, hasExplicitLbs);
diff --git a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
index 0c78a878cdc53..309e557e409c0 100644
--- a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
@@ -401,10 +401,13 @@ llvm::LogicalResult SelectTypeConv::genTypeLadderStep(
{
// Since conversion is done in parallel for each fir.select_type
// operation, the runtime function insertion must be threadsafe.
- callee =
- fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
- rewriter.getFunctionType({descNoneTy, typeDescTy},
- rewriter.getI1Type()));
+ auto runtimeAttr =
+ mlir::NamedAttribute(fir::FIROpsDialect::getFirRuntimeAttrName(),
+ mlir::UnitAttr::get(rewriter.getContext()));
+ callee = fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
+ rewriter.getFunctionType({descNoneTy, typeDescTy},
+ rewriter.getI1Type()),
+ {runtimeAttr});
}
cmp = rewriter
.create<fir::CallOp>(loc, callee,
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 1de48b87365b3..834eea7df8ebe 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -1260,7 +1260,7 @@ func.func @dc_invalid_reduction(%arg0: index, %arg1: index) {
// Should fail when volatility changes from a fir.convert
func.func @bad_convert_volatile(%arg0: !fir.ref<i32>) -> !fir.ref<i32, volatile> {
- // expected-error at +1 {{'fir.convert' op cannot convert between volatile and non-volatile types, use fir.volatile_cast instead}}
+ // expected-error at +1 {{op this conversion does not preserve volatilit}}
%0 = fir.convert %arg0 : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
return %0 : !fir.ref<i32, volatile>
}
@@ -1269,7 +1269,7 @@ func.func @bad_convert_volatile(%arg0: !fir.ref<i32>) -> !fir.ref<i32, volatile>
// Should fail when volatility changes from a fir.convert
func.func @bad_convert_volatile2(%arg0: !fir.ref<i32, volatile>) -> !fir.ref<i32> {
- // expected-error at +1 {{'fir.convert' op cannot convert between volatile and non-volatile types, use fir.volatile_cast instead}}
+ // expected-error at +1 {{op this conversion does not preserve volatility}}
%0 = fir.convert %arg0 : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
return %0 : !fir.ref<i32>
}
diff --git a/flang/test/Lower/volatile-allocatable.f90 b/flang/test/Lower/volatile-allocatable.f90
index 5f75a5425422a..e182fe8a4d9c9 100644
--- a/flang/test/Lower/volatile-allocatable.f90
+++ b/flang/test/Lower/volatile-allocatable.f90
@@ -119,10 +119,10 @@ subroutine test_unlimited_polymorphic()
end subroutine
! CHECK-LABEL: func.func @_QPtest_scalar_volatile() {
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEc1"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>, volatile>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv1"} : (!fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>>, volatile>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv2"} : (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv3"} : (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEc1"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv1"} : (!fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv2"} : (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_scalar_volatileEv3"} : (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.type<{{.*}}>>, volatile>, volatile>)
! CHECK: fir.call @_FortranAAllocatableInitDerivedForAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i32, i32) -> ()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i64, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: fir.call @_FortranAAllocatableInitDerivedForAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i32, i32) -> ()
@@ -140,8 +140,8 @@ subroutine test_unlimited_polymorphic()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocatePolymorphic(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK-LABEL: func.func @_QPtest_volatile_asynchronous() {
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, asynchronous, volatile>, uniq_name = "_QFtest_volatile_asynchronousEi1"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>, volatile>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, asynchronous, volatile>, uniq_name = "_QFtest_volatile_asynchronousEv1"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, asynchronous, volatile>, uniq_name = "_QFtest_volatile_asynchronousEi1"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, asynchronous, volatile>, uniq_name = "_QFtest_volatile_asynchronousEv1"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>)
! CHECK: fir.call @_FortranAAllocatableInitDerivedForAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i32, i32) -> ()
! CHECK: fir.call @_FortranAAllocatableSetBounds(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i64, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
@@ -151,10 +151,11 @@ subroutine test_unlimited_polymorphic()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocatePolymorphic(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocatePolymorphic(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK-LABEL: func.func @_QPtest_select_base_type_volatile() {
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_select_base_type_volatileEv"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_select_base_type_volatileEv"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>)
! CHECK: fir.call @_FortranAAllocatableInitDerivedForAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i32, i32) -> ()
! CHECK: fir.call @_FortranAAllocatableSetBounds(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i64, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
+! CHECK: %{{.+}} = fir.call @_FortranAClassIs(%{{.+}}, %{{.+}}) : (!fir.box<none>, !fir.ref<none>) -> i1
! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}}(%{{.+}}) {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFtest_select_base_type_volatileEv"} : (!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, !fir.shift<1>) -> (!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, !fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>)
! CHECK: %{{.+}} = hlfir.designate %{{.+}}#0 (%{{.+}}) : (!fir.class<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, index) -> !fir.class<!fir.type<{{.*}}>, volatile>
! CHECK: %{{.+}} = hlfir.designate %{{.+}}{"i"} : (!fir.class<!fir.type<{{.*}}>, volatile>) -> !fir.ref<i32, volatile>
@@ -162,7 +163,7 @@ subroutine test_unlimited_polymorphic()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocatePolymorphic(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, !fir.ref<none>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK-LABEL: func.func @_QPtest_mold_allocation() {
! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {uniq_name = "_QFtest_mold_allocationEtemplate"} : (!fir.ref<!fir.type<{{.*}}>>) -> (!fir.ref<!fir.type<{{.*}}>>, !fir.ref<!fir.type<{{.*}}>>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_mold_allocationEv"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_mold_allocationEv"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<{{.*}}>>>, volatile>, volatile>)
! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} typeparams %{{.+}} {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQclX6D6F6C642074657374"} : (!fir.ref<!fir.char<1,9>>, index) -> (!fir.ref<!fir.char<1,9>>, !fir.ref<!fir.char<1,9>>)
! CHECK: %{{.+}} = hlfir.designate %{{.+}}#0{"str"} typeparams %{{.+}} : (!fir.ref<!fir.type<{{.*}}>>, index) -> !fir.ref<!fir.char<1,10>>
! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}}(%{{.+}}) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQro.2xi4.2"} : (!fir.ref<!fir.array<2xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xi32>>, !fir.ref<!fir.array<2xi32>>)
@@ -173,8 +174,8 @@ subroutine test_unlimited_polymorphic()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableDeallocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK-LABEL: func.func @_QPtest_unlimited_polymorphic() {
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_unlimited_polymorphicEup"} : (!fir.ref<!fir.class<!fir.heap<none>>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<none>>, volatile>, !fir.ref<!fir.class<!fir.heap<none>>, volatile>)
-! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_unlimited_polymorphicEupa"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_unlimited_polymorphicEup"} : (!fir.ref<!fir.class<!fir.heap<none>, volatile>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<none>, volatile>, volatile>, !fir.ref<!fir.class<!fir.heap<none>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFtest_unlimited_polymorphicEupa"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>, volatile>, volatile>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>, volatile>, volatile>, !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>, volatile>, volatile>)
! CHECK: fir.call @_FortranAAllocatableInitIntrinsicForAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i32, i32, i32, i32) -> ()
! CHECK: %{{.+}} = fir.call @_FortranAAllocatableAllocate(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i64, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFtest_unlimited_polymorphicEup"} : (!fir.heap<i32>) -> (!fir.heap<i32>, !fir.heap<i32>)
diff --git a/flang/test/Lower/volatile-allocatable1.f90 b/flang/test/Lower/volatile-allocatable1.f90
index a21359c3b4225..d2a07c8763885 100644
--- a/flang/test/Lower/volatile-allocatable1.f90
+++ b/flang/test/Lower/volatile-allocatable1.f90
@@ -1,7 +1,6 @@
! RUN: bbc --strict-fir-volatile-verifier %s -o - | FileCheck %s
! Requires correct propagation of volatility for allocatable nested types.
-! XFAIL: *
function allocatable_udt()
type :: base_type
@@ -15,3 +14,19 @@ function allocatable_udt()
allocate(v2(2,3))
allocatable_udt = v2(1,1)%i
end function
+! CHECK-LABEL: func.func @_QPallocatable_udt() -> i32 {
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} typeparams %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.n.i"} : (!fir.ref<!fir.char<1>>, index) -> (!fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.di.base_type.i"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} typeparams %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.n.base_type"} : (!fir.ref<!fir.char<1,9>>, index) -> (!fir.ref<!fir.char<1,9>>, !fir.ref<!fir.char<1,9>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} typeparams %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.n.j"} : (!fir.ref<!fir.char<1>>, index) -> (!fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.di.ext_type.j"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} typeparams %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.n.ext_type"} : (!fir.ref<!fir.char<1,8>>, index) -> (!fir.ref<!fir.char<1,8>>, !fir.ref<!fir.char<1,8>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {uniq_name = "_QFallocatable_udtEallocatable_udt"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<allocatable, volatile>, uniq_name = "_QFallocatable_udtEv2"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.type<{{.*}}>>>, volatile>, volatile>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.type<{{.*}}>>>, volatile>, volatile>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.type<{{.*}}>>>, volatile>, volatile>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}}(%{{.+}}) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.c.base_type"} : (!fir.ref<!fir.array<1x!fir.type<{{.*}}>>>, !fir.shapeshift<1>) -> (!fir.box<!fir.array<1x!fir.type<{{.*}}>>>, !fir.ref<!fir.array<1x!fir.type<{{.*}}>>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.dt.base_type"} : (!fir.ref<!fir.type<{{.*}}>>) -> (!fir.ref<!fir.type<{{.*}}>>, !fir.ref<!fir.type<{{.*}}>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.dt.ext_type"} : (!fir.ref<!fir.type<{{.*}}>>) -> (!fir.ref<!fir.type<{{.*}}>>, !fir.ref<!fir.type<{{.*}}>>)
+! CHECK: %{{.+}}:2 = hlfir.declare %{{.+}}(%{{.+}}) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFallocatable_udtE.c.ext_type"} : (!fir.ref<!fir.array<2x!fir.type<{{.*}}>>>, !fir.shapeshift<1>) -> (!fir.box<!fir.array<2x!fir.type<{{.*}}>>>, !fir.ref<!fir.array<2x!fir.type<{{.*}}>>>)
+! CHECK: %{{.+}} = hlfir.designate %{{.+}} (%{{.+}}, %{{.+}}) : (!fir.box<!fir.heap<!fir.array<?x?x!fir.type<{{.*}}>>>, volatile>, index, index) -> !fir.ref<!fir.type<{{.*}}>, volatile>
+! CHECK: %{{.+}} = hlfir.designate %{{.+}}{"base_type"} : (!fir.ref<!fir.type<{{.*}}>, volatile>) -> !fir.ref<!fir.type<{{.*}}>, volatile>
+! CHECK: %{{.+}} = hlfir.designate %{{.+}}{"i"} : (!fir.ref<!fir.type<{{.*}}>, volatile>) -> !fir.ref<i32, volatile>
>From aeed3ec3318ea0b0fdfa813868ce3a888671ed34 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Thu, 8 May 2025 17:44:10 -0700
Subject: [PATCH 2/3] format
---
flang/lib/Lower/Bridge.cpp | 12 +++++++----
flang/lib/Optimizer/Dialect/FIROps.cpp | 20 +++++++++++--------
.../Transforms/PolymorphicOpConversion.cpp | 9 +++++----
3 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index d28c01ed16cbf..169a2780ea14d 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -3999,7 +3999,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
addrTy = fir::HeapType::get(addrTy);
if (std::holds_alternative<Fortran::parser::IntrinsicTypeSpec>(
typeSpec->u)) {
- mlir::Type refTy = fir::ReferenceType::get(addrTy, selectorIsVolatile());
+ mlir::Type refTy =
+ fir::ReferenceType::get(addrTy, selectorIsVolatile());
if (isPointer || isAllocatable)
refTy = addrTy;
exactValue = builder->create<fir::BoxAddrOp>(
@@ -4008,7 +4009,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
typeSpec->declTypeSpec->AsIntrinsic();
if (isArray) {
mlir::Value exact = builder->create<fir::ConvertOp>(
- loc, fir::BoxType::get(addrTy, selectorIsVolatile()), fir::getBase(selector));
+ loc, fir::BoxType::get(addrTy, selectorIsVolatile()),
+ fir::getBase(selector));
addAssocEntitySymbol(selectorBox->clone(exact));
} else if (intrinsic->category() ==
Fortran::common::TypeCategory::Character) {
@@ -4023,7 +4025,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
} else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
typeSpec->u)) {
exactValue = builder->create<fir::ConvertOp>(
- loc, fir::BoxType::get(addrTy, selectorIsVolatile()), fir::getBase(selector));
+ loc, fir::BoxType::get(addrTy, selectorIsVolatile()),
+ fir::getBase(selector));
addAssocEntitySymbol(selectorBox->clone(exactValue));
}
} else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
@@ -4041,7 +4044,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
addrTy = fir::PointerType::get(addrTy);
if (isAllocatable)
addrTy = fir::HeapType::get(addrTy);
- mlir::Type classTy = fir::ClassType::get(addrTy, selectorIsVolatile());
+ mlir::Type classTy =
+ fir::ClassType::get(addrTy, selectorIsVolatile());
if (classTy == baseTy) {
addAssocEntitySymbol(selector);
} else {
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 9b58578e55474..75185e719393f 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1536,21 +1536,24 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
areRecordsCompatible(inType, outType);
}
-// In general, ptrtoint-like conversions are allowed to lose volatility information
-// because they are either:
+// In general, ptrtoint-like conversions are allowed to lose volatility
+// information because they are either:
//
// 1. passing an entity to an external function and there's nothing we can do
// about volatility after that happens, or
-// 2. for code generation, at which point we represent volatility with attributes
+// 2. for code generation, at which point we represent volatility with
+// attributes
// on the LLVM instructions and intrinsics.
//
// For all other cases, volatility ought to match exactly.
-static mlir::LogicalResult verifyVolatility(mlir::Type inType, mlir::Type outType) {
+static mlir::LogicalResult verifyVolatility(mlir::Type inType,
+ mlir::Type outType) {
const bool toLLVMPointer = mlir::isa<mlir::LLVM::LLVMPointerType>(outType);
const bool toInteger = fir::isa_integer(outType);
- // When converting references to classes or allocatables into boxes for runtime arguments,
- // we cast away all the volatility information and pass a box<none>. This is allowed.
+ // When converting references to classes or allocatables into boxes for
+ // runtime arguments, we cast away all the volatility information and pass a
+ // box<none>. This is allowed.
const bool isBoxNoneLike = [&]() {
if (fir::isBoxNone(outType))
return true;
@@ -1561,14 +1564,15 @@ static mlir::LogicalResult verifyVolatility(mlir::Type inType, mlir::Type outTyp
}
return false;
}();
-
+
const bool isPtrToIntLike = toLLVMPointer || toInteger || isBoxNoneLike;
if (isPtrToIntLike) {
return mlir::success();
}
// In all other cases, we need to check for an exact volatility match.
- return mlir::success(fir::isa_volatile_type(inType) == fir::isa_volatile_type(outType));
+ return mlir::success(fir::isa_volatile_type(inType) ==
+ fir::isa_volatile_type(outType));
}
llvm::LogicalResult fir::ConvertOp::verify() {
diff --git a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
index 309e557e409c0..f9a4c4d0283c7 100644
--- a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
@@ -404,10 +404,11 @@ llvm::LogicalResult SelectTypeConv::genTypeLadderStep(
auto runtimeAttr =
mlir::NamedAttribute(fir::FIROpsDialect::getFirRuntimeAttrName(),
mlir::UnitAttr::get(rewriter.getContext()));
- callee = fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
- rewriter.getFunctionType({descNoneTy, typeDescTy},
- rewriter.getI1Type()),
- {runtimeAttr});
+ callee =
+ fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
+ rewriter.getFunctionType({descNoneTy, typeDescTy},
+ rewriter.getI1Type()),
+ {runtimeAttr});
}
cmp = rewriter
.create<fir::CallOp>(loc, callee,
>From 0345444696c27172179c711d6124b0a315975831 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Fri, 9 May 2025 07:03:19 -0700
Subject: [PATCH 3/3] Refactor the hlfir declare utility for clarity
---
flang/lib/Optimizer/Dialect/FIROps.cpp | 3 +-
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 64 +++++++++++++----------
flang/test/Fir/invalid.fir | 2 +-
3 files changed, 37 insertions(+), 32 deletions(-)
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 75185e719393f..b10b5d998fa70 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1542,8 +1542,7 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
// 1. passing an entity to an external function and there's nothing we can do
// about volatility after that happens, or
// 2. for code generation, at which point we represent volatility with
-// attributes
-// on the LLVM instructions and intrinsics.
+// attributes on the LLVM instructions and intrinsics.
//
// For all other cases, volatility ought to match exactly.
static mlir::LogicalResult verifyVolatility(mlir::Type inType,
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 52517eef2890d..8cfca59ecdada 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -207,34 +207,37 @@ static bool hasExplicitLowerBounds(mlir::Value shape) {
mlir::isa<fir::ShapeShiftType, fir::ShiftType>(shape.getType());
}
-static std::pair<mlir::Type, mlir::Value> updateDeclaredInputTypeWithVolatility(
- mlir::Type inputType, mlir::Value memref, mlir::OpBuilder &builder,
- fir::FortranVariableFlagsAttr fortran_attrs) {
- if (fortran_attrs &&
- bitEnumContainsAny(fortran_attrs.getFlags(),
- fir::FortranVariableFlagsEnum::fortran_volatile)) {
- // A volatile pointer's pointee is volatile.
- const bool isPointer = bitEnumContainsAny(
- fortran_attrs.getFlags(), fir::FortranVariableFlagsEnum::pointer);
- // An allocatable's inner type's volatility matches that of the reference.
- const bool isAllocatable = bitEnumContainsAny(
- fortran_attrs.getFlags(), fir::FortranVariableFlagsEnum::allocatable);
- auto updateType = [&](auto t) {
- using FIRT = decltype(t);
- auto elementType = t.getEleTy();
- const bool elementTypeIsBox = mlir::isa<fir::BoxType>(elementType);
- const bool elementTypeIsVolatile = isPointer || isAllocatable ||
- elementTypeIsBox ||
- fir::isa_volatile_type(elementType);
- auto newEleTy =
- fir::updateTypeWithVolatility(elementType, elementTypeIsVolatile);
- inputType = FIRT::get(newEleTy, true);
- };
- llvm::TypeSwitch<mlir::Type>(inputType)
- .Case<fir::ReferenceType, fir::BoxType, fir::ClassType>(updateType);
- memref =
- builder.create<fir::VolatileCastOp>(memref.getLoc(), inputType, memref);
+static std::pair<mlir::Type, mlir::Value>
+updateDeclaredInputTypeWithVolatility(mlir::Type inputType, mlir::Value memref,
+ mlir::OpBuilder &builder,
+ fir::FortranVariableFlagsEnum flags) {
+ if (!bitEnumContainsAny(flags,
+ fir::FortranVariableFlagsEnum::fortran_volatile)) {
+ return std::make_pair(inputType, memref);
}
+
+ // A volatile pointer's pointee is volatile.
+ const bool isPointer =
+ bitEnumContainsAny(flags, fir::FortranVariableFlagsEnum::pointer);
+ // An allocatable's inner type's volatility matches that of the reference.
+ const bool isAllocatable =
+ bitEnumContainsAny(flags, fir::FortranVariableFlagsEnum::allocatable);
+
+ auto updateType = [&](auto t) {
+ using FIRT = decltype(t);
+ auto elementType = t.getEleTy();
+ const bool elementTypeIsBox = mlir::isa<fir::BaseBoxType>(elementType);
+ const bool elementTypeIsVolatile = isPointer || isAllocatable ||
+ elementTypeIsBox ||
+ fir::isa_volatile_type(elementType);
+ auto newEleTy =
+ fir::updateTypeWithVolatility(elementType, elementTypeIsVolatile);
+ inputType = FIRT::get(newEleTy, true);
+ };
+ llvm::TypeSwitch<mlir::Type>(inputType)
+ .Case<fir::ReferenceType, fir::BoxType, fir::ClassType>(updateType);
+ memref =
+ builder.create<fir::VolatileCastOp>(memref.getLoc(), inputType, memref);
return std::make_pair(inputType, memref);
}
@@ -248,8 +251,11 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
auto nameAttr = builder.getStringAttr(uniq_name);
mlir::Type inputType = memref.getType();
bool hasExplicitLbs = hasExplicitLowerBounds(shape);
- std::tie(inputType, memref) = updateDeclaredInputTypeWithVolatility(
- inputType, memref, builder, fortran_attrs);
+ if (fortran_attrs) {
+ const auto flags = fortran_attrs.getFlags();
+ std::tie(inputType, memref) = updateDeclaredInputTypeWithVolatility(
+ inputType, memref, builder, flags);
+ }
mlir::Type hlfirVariableType =
getHLFIRVariableType(inputType, hasExplicitLbs);
build(builder, result, {hlfirVariableType, inputType}, memref, shape,
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 834eea7df8ebe..fd607fd9066f7 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -1260,7 +1260,7 @@ func.func @dc_invalid_reduction(%arg0: index, %arg1: index) {
// Should fail when volatility changes from a fir.convert
func.func @bad_convert_volatile(%arg0: !fir.ref<i32>) -> !fir.ref<i32, volatile> {
- // expected-error at +1 {{op this conversion does not preserve volatilit}}
+ // expected-error at +1 {{op this conversion does not preserve volatility}}
%0 = fir.convert %arg0 : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
return %0 : !fir.ref<i32, volatile>
}
More information about the flang-commits
mailing list