diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h index 1675c15363868..d7ddb37480ebb 100644 --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -150,7 +150,7 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener { mlir::Block *getAllocaBlock(); /// Safely create a reference type to the type `eleTy`. - mlir::Type getRefType(mlir::Type eleTy); + mlir::Type getRefType(mlir::Type eleTy, bool isVolatile = false); /// Create a sequence of `eleTy` with `rank` dimensions of unknown size. mlir::Type getVarLenSeqTy(mlir::Type eleTy, unsigned rank = 1); diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h index 76e0aa352bcd9..8261c67e4559d 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRType.h +++ b/flang/include/flang/Optimizer/Dialect/FIRType.h @@ -111,6 +111,12 @@ inline bool isa_ref_type(mlir::Type t) { fir::LLVMPointerType>(t); } +inline bool isa_volatile_ref_type(mlir::Type t) { + if (auto refTy = mlir::dyn_cast_or_null(t)) + return refTy.isVolatile(); + return false; +} + /// Is `t` a boxed type? inline bool isa_box_type(mlir::Type t) { return mlir::isa(t); diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td index fd5bbbe44751f..0584c175b36ff 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td +++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td @@ -363,18 +363,22 @@ def fir_ReferenceType : FIR_Type<"Reference", "ref"> { The type of a reference to an entity in memory. }]; - let parameters = (ins "mlir::Type":$eleTy); + let parameters = (ins + "mlir::Type":$eleTy, + DefaultValuedParameter<"bool", "false">:$isVol); let skipDefaultBuilders = 1; let builders = [ - TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType), [{ - return Base::get(elementType.getContext(), elementType); + TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType, CArg<"bool", "false">:$isVol), [{ + return Base::get(elementType.getContext(), elementType, isVol); }]>, ]; let extraClassDeclaration = [{ mlir::Type getElementType() const { return getEleTy(); } + bool isVolatile() const { return (bool)getIsVol(); } + static llvm::StringRef getVolatileKeyword() { return "volatile"; } }]; let genVerifyDecl = 1; diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp index 226ba1e52c968..4ee28fbeb9a0c 100644 --- a/flang/lib/Lower/CallInterface.cpp +++ b/flang/lib/Lower/CallInterface.cpp @@ -1112,7 +1112,6 @@ class Fortran::lower::CallInterfaceImpl { if (obj.attrs.test(Attrs::Value)) isValueAttr = true; // TODO: do we want an mlir::Attribute as well? if (obj.attrs.test(Attrs::Volatile)) { - TODO(loc, "VOLATILE in procedure interface"); addMLIRAttr(fir::getVolatileAttrName()); } // obj.attrs.test(Attrs::Asynchronous) does not impact the way the argument diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp index dc00e0b13f583..3ac10596df5ae 100644 --- a/flang/lib/Lower/ConvertExprToHLFIR.cpp +++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp @@ -223,8 +223,37 @@ class HlfirDesignatorBuilder { designatorNode, getConverter().getFoldingContext(), /*namedConstantSectionsAreAlwaysContiguous=*/false)) return fir::BoxType::get(resultValueType); + + bool isVolatile = false; + + // Check if the base type is volatile + if (partInfo.base.has_value()) { + mlir::Type baseType = partInfo.base.value().getType(); + isVolatile = fir::isa_volatile_ref_type(baseType); + } + + auto isVolatileSymbol = [](const Fortran::semantics::Symbol &symbol) { + return symbol.GetUltimate().attrs().test( + Fortran::semantics::Attr::VOLATILE); + }; + + // Check if this should be a volatile reference + if constexpr (std::is_same_v, + Fortran::evaluate::SymbolRef>) { + if (isVolatileSymbol(designatorNode.get())) + isVolatile = true; + } else if constexpr (std::is_same_v, + Fortran::evaluate::Component>) { + if (isVolatileSymbol(designatorNode.GetLastSymbol())) + isVolatile = true; + } + + // If it's a reference to a ref, account for it + if (auto refTy = mlir::dyn_cast(resultValueType)) + resultValueType = refTy.getEleTy(); + // Other designators can be handled as raw addresses. - return fir::ReferenceType::get(resultValueType); + return fir::ReferenceType::get(resultValueType, isVolatile); } template @@ -414,10 +443,16 @@ class HlfirDesignatorBuilder { .Case([&](fir::SequenceType seqTy) -> mlir::Type { return fir::SequenceType::get(seqTy.getShape(), newEleTy); }) - .Case([&](auto t) -> mlir::Type { - using FIRT = decltype(t); - return FIRT::get(changeElementType(t.getEleTy(), newEleTy)); + // TODO: handle volatility for other types + .Case( + [&](auto t) -> mlir::Type { + using FIRT = decltype(t); + return FIRT::get(changeElementType(t.getEleTy(), newEleTy)); + }) + .Case([&](fir::ReferenceType refTy) -> mlir::Type { + return fir::ReferenceType::get( + changeElementType(refTy.getEleTy(), newEleTy), + refTy.isVolatile()); }) .Default([newEleTy](mlir::Type t) -> mlir::Type { return newEleTy; }); } @@ -1808,6 +1843,7 @@ class HlfirBuilder { auto &expr = std::get(iter); auto &baseOp = std::get(iter); std::string name = converter.getRecordTypeFieldName(sym); + const bool isVolatile = fir::isa_volatile_ref_type(baseOp.getType()); // Generate DesignateOp for the component. // The designator's result type is just a reference to the component type, @@ -1818,7 +1854,7 @@ class HlfirBuilder { assert(compType && "failed to retrieve component type"); mlir::Value compShape = designatorBuilder.genComponentShape(sym, compType); - mlir::Type designatorType = builder.getRefType(compType); + mlir::Type designatorType = builder.getRefType(compType, isVolatile); mlir::Type fieldElemType = hlfir::getFortranElementType(compType); llvm::SmallVector typeParams; diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp index b3d440cedee07..cfae25f8fe4b9 100644 --- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp +++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp @@ -104,9 +104,9 @@ fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, return modOp.lookupSymbol(name); } -mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) { +mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy, bool isVolatile) { assert(!mlir::isa(eleTy) && "cannot be a reference type"); - return fir::ReferenceType::get(eleTy); + return fir::ReferenceType::get(eleTy, isVolatile); } mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) { diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp index 1a31ca33e9465..cf8bb7eaddf70 100644 --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -809,7 +809,8 @@ mlir::Type hlfir::getVariableElementType(hlfir::Entity variable) { } else if (fir::isRecordWithTypeParameters(eleTy)) { return fir::BoxType::get(eleTy); } - return fir::ReferenceType::get(eleTy); + const bool isVolatile = fir::isa_volatile_ref_type(variable.getType()); + return fir::ReferenceType::get(eleTy, isVolatile); } mlir::Type hlfir::getEntityElementType(hlfir::Entity entity) { diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 2cb4cea58c2b0..0065a809e4304 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -3218,6 +3218,8 @@ struct LoadOpConversion : public fir::FIROpConversion { mlir::ConversionPatternRewriter &rewriter) const override { mlir::Type llvmLoadTy = convertObjectType(load.getType()); + const bool isVolatile = + fir::isa_volatile_ref_type(load.getMemref().getType()); if (auto boxTy = mlir::dyn_cast(load.getType())) { // fir.box is a special case because it is considered an ssa value in // fir, but it is lowered as a pointer to a descriptor. So @@ -3247,7 +3249,7 @@ struct LoadOpConversion : public fir::FIROpConversion { mlir::Value boxSize = computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter); auto memcpy = rewriter.create( - loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false); + loc, newBoxStorage, inputBoxStorage, boxSize, isVolatile); if (std::optional optionalTag = load.getTbaa()) memcpy.setTBAATags(*optionalTag); @@ -3255,8 +3257,10 @@ struct LoadOpConversion : public fir::FIROpConversion { attachTBAATag(memcpy, boxTy, boxTy, nullptr); rewriter.replaceOp(load, newBoxStorage); } else { + // TODO: are we losing any attributes from the load op? + auto memref = adaptor.getOperands()[0]; auto loadOp = rewriter.create( - load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs()); + load.getLoc(), llvmLoadTy, memref, /*alignment=*/0, isVolatile); if (std::optional optionalTag = load.getTbaa()) loadOp.setTBAATags(*optionalTag); else @@ -3534,6 +3538,8 @@ struct StoreOpConversion : public fir::FIROpConversion { mlir::Value llvmValue = adaptor.getValue(); mlir::Value llvmMemref = adaptor.getMemref(); mlir::LLVM::AliasAnalysisOpInterface newOp; + const bool isVolatile = + fir::isa_volatile_ref_type(store.getMemref().getType()); if (auto boxTy = mlir::dyn_cast(storeTy)) { mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy); // Always use memcpy because LLVM is not as effective at optimizing @@ -3541,10 +3547,11 @@ struct StoreOpConversion : public fir::FIROpConversion { TypePair boxTypePair{boxTy, llvmBoxTy}; mlir::Value boxSize = computeBoxSize(loc, boxTypePair, llvmValue, rewriter); - newOp = rewriter.create( - loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false); + newOp = rewriter.create(loc, llvmMemref, llvmValue, + boxSize, isVolatile); } else { - newOp = rewriter.create(loc, llvmValue, llvmMemref); + newOp = rewriter.create(loc, llvmValue, llvmMemref, + /*alignment=*/0, isVolatile); } if (std::optional optionalTag = store.getTbaa()) newOp.setTBAATags(*optionalTag); diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp index dc0bee9b060c9..612c407d70c23 100644 --- a/flang/lib/Optimizer/Dialect/FIRType.cpp +++ b/flang/lib/Optimizer/Dialect/FIRType.cpp @@ -649,12 +649,17 @@ mlir::Type changeElementType(mlir::Type type, mlir::Type newElementType, .Case([&](fir::SequenceType seqTy) -> mlir::Type { return fir::SequenceType::get(seqTy.getShape(), newElementType); }) - .Case([&](auto t) -> mlir::Type { - using FIRT = decltype(t); - return FIRT::get( - changeElementType(t.getEleTy(), newElementType, turnBoxIntoClass)); + .Case([&](fir::ReferenceType refTy) -> mlir::Type { + auto newEleTy = changeElementType(refTy.getEleTy(), newElementType, + turnBoxIntoClass); + return fir::ReferenceType::get(newEleTy, refTy.isVolatile()); }) + .Case( + [&](auto t) -> mlir::Type { + using FIRT = decltype(t); + return FIRT::get(changeElementType(t.getEleTy(), newElementType, + turnBoxIntoClass)); + }) .Case([&](fir::BoxType t) -> mlir::Type { mlir::Type newInnerType = changeElementType(t.getEleTy(), newElementType, false); @@ -1057,18 +1062,38 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) { // ReferenceType //===----------------------------------------------------------------------===// -// `ref` `<` type `>` +// `ref` `<` type (`, volatile` $volatile^)? (`, async` $async^)? `>` mlir::Type fir::ReferenceType::parse(mlir::AsmParser &parser) { - return parseTypeSingleton(parser); + if (parser.parseLess()) + return {}; + + mlir::Type eleTy; + if (parser.parseType(eleTy)) + return {}; + + bool isVolatile = false; + if (!parser.parseOptionalComma()) { + if (parser.parseKeyword(getVolatileKeyword())) { + return {}; + } + isVolatile = true; + } + + if (parser.parseGreater()) + return {}; + return get(eleTy, isVolatile); } void fir::ReferenceType::print(mlir::AsmPrinter &printer) const { - printer << "<" << getEleTy() << '>'; + printer << "<" << getEleTy(); + if (isVolatile()) + printer << ", " << getVolatileKeyword(); + printer << '>'; } llvm::LogicalResult fir::ReferenceType::verify( - llvm::function_ref emitError, - mlir::Type eleTy) { + llvm::function_ref emitError, mlir::Type eleTy, + bool isVolatile) { if (mlir::isa(eleTy)) return emitError() << "cannot build a reference to type: " << eleTy << '\n'; @@ -1319,11 +1344,15 @@ changeTypeShape(mlir::Type type, return fir::SequenceType::get(*newShape, seqTy.getEleTy()); return seqTy.getEleTy(); }) - .Case([&](auto t) -> mlir::Type { - using FIRT = decltype(t); - return FIRT::get(changeTypeShape(t.getEleTy(), newShape)); + .Case([&](fir::ReferenceType rt) -> mlir::Type { + return fir::ReferenceType::get(changeTypeShape(rt.getEleTy(), newShape), + rt.isVolatile()); }) + .Case( + [&](auto t) -> mlir::Type { + using FIRT = decltype(t); + return FIRT::get(changeTypeShape(t.getEleTy(), newShape)); + }) .Default([&](mlir::Type t) -> mlir::Type { assert((fir::isa_trivial(t) || llvm::isa(t) || llvm::isa(t)) && diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp index 8851a3a7187b9..4a3308ff4e747 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -214,6 +214,13 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder, auto nameAttr = builder.getStringAttr(uniq_name); mlir::Type inputType = memref.getType(); bool hasExplicitLbs = hasExplicitLowerBounds(shape); + if (fortran_attrs && mlir::isa(inputType) && + bitEnumContainsAny(fortran_attrs.getFlags(), + fir::FortranVariableFlagsEnum::fortran_volatile)) { + auto refType = mlir::cast(inputType); + inputType = fir::ReferenceType::get(refType.getEleTy(), true); + memref = builder.create(memref.getLoc(), inputType, memref); + } mlir::Type hlfirVariableType = getHLFIRVariableType(inputType, hasExplicitLbs); build(builder, result, {hlfirVariableType, inputType}, memref, shape, diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp index 496a5560ac615..aa151f90ed0d1 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp @@ -418,7 +418,9 @@ class DesignateOpConversion firstElementIndices.push_back(indices[i]); i = i + (isTriplet ? 3 : 1); } - mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy); + mlir::Type originalDesignateType = designate.getResult().getType(); + const bool isVolatile = fir::isa_volatile_ref_type(originalDesignateType); + mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy, isVolatile); base = builder.create( loc, arrayCoorType, base, shape, /*slice=*/mlir::Value{}, firstElementIndices, firBaseTypeParameters); @@ -441,6 +443,7 @@ class DesignateOpConversion TODO(loc, "hlfir::designate load of pointer or allocatable"); mlir::Type designateResultType = designate.getResult().getType(); + const bool isVolatile = fir::isa_volatile_ref_type(designateResultType); llvm::SmallVector firBaseTypeParameters; auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams( loc, builder, baseEntity, firBaseTypeParameters); @@ -464,7 +467,7 @@ class DesignateOpConversion mlir::Type componentType = mlir::cast(baseEleTy).getType( designate.getComponent().value()); - mlir::Type coorTy = fir::ReferenceType::get(componentType); + mlir::Type coorTy = fir::ReferenceType::get(componentType, isVolatile); base = builder.create(loc, coorTy, base, fieldIndex); if (mlir::isa(componentType)) { auto variableInterface = mlir::cast( diff --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp index 96a3622f4afee..020915179a670 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp @@ -1126,7 +1126,8 @@ class ReductionMaskConversion : public mlir::OpRewritePattern { builder.create(loc, flagSet, flagRef); mlir::Type resultElemTy = hlfir::getFortranElementType(resultArr.getType()); - mlir::Type returnRefTy = builder.getRefType(resultElemTy); + mlir::Type returnRefTy = builder.getRefType( + resultElemTy, fir::isa_volatile_ref_type(flagRef.getType())); mlir::IndexType idxTy = builder.getIndexType(); for (unsigned int i = 0; i < rank; ++i) { @@ -1153,7 +1154,8 @@ class ReductionMaskConversion : public mlir::OpRewritePattern { auto getAddrFn = [](fir::FirOpBuilder builder, mlir::Location loc, const mlir::Type &resultElemType, mlir::Value resultArr, mlir::Value index) { - mlir::Type resultRefTy = builder.getRefType(resultElemType); + mlir::Type resultRefTy = builder.getRefType( + resultElemType, fir::isa_volatile_ref_type(resultArr.getType())); mlir::Value oneIdx = builder.createIntegerConstant(loc, builder.getIndexType(), 1); index = builder.create(loc, index, oneIdx); @@ -1162,8 +1164,9 @@ class ReductionMaskConversion : public mlir::OpRewritePattern { }; // Initialize the result + const bool isVolatile = fir::isa_volatile_ref_type(resultArr.getType()); mlir::Type resultElemTy = hlfir::getFortranElementType(resultArr.getType()); - mlir::Type resultRefTy = builder.getRefType(resultElemTy); + mlir::Type resultRefTy = builder.getRefType(resultElemTy, isVolatile); mlir::Value returnValue = builder.createIntegerConstant(loc, resultElemTy, 0); for (unsigned int i = 0; i < rank; ++i) { diff --git a/flang/test/Fir/volatile.fir b/flang/test/Fir/volatile.fir new file mode 100644 index 0000000000000..e508d7b88e645 --- /dev/null +++ b/flang/test/Fir/volatile.fir @@ -0,0 +1,18 @@ +// RUN: fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s -o - | FileCheck %s +// CHECK: llvm.store volatile %{{.+}}, %{{.+}} : i32, !llvm.ptr +// CHECK: %{{.+}} = llvm.load volatile %{{.+}} : !llvm.ptr -> i32 +func.func @foo() { + %true = arith.constant true + %false = arith.constant false + %0 = fir.alloca !fir.logical<4> {bindc_name = "a", uniq_name = "_QFEa"} + %1 = fir.convert %0 : (!fir.ref>) -> !fir.ref, volatile> + %2 = fir.alloca !fir.logical<4> {bindc_name = "b", uniq_name = "_QFEb"} + %3 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"} + %4 = fir.convert %false : (i1) -> !fir.logical<4> + fir.store %4 to %1 : !fir.ref, volatile> + %5 = fir.load %1 : !fir.ref, volatile> + fir.store %5 to %2 : !fir.ref> + %6 = fir.convert %true : (i1) -> !fir.logical<4> + fir.store %6 to %1 : !fir.ref, volatile> + return +} diff --git a/flang/test/Integration/volatile.f90 b/flang/test/Integration/volatile.f90 new file mode 100644 index 0000000000000..9d8e93259c60e --- /dev/null +++ b/flang/test/Integration/volatile.f90 @@ -0,0 +1,11 @@ +! RUN: bbc %s -o - | FileCheck %s +logical, volatile :: a +logical :: b +integer :: i +a = .false. +b = a +a = .true. +end + +! CHECK: %{{.+}} = fir.load %{{.+}} : !fir.ref, volatile> +! CHECK: hlfir.assign %{{.+}} to %{{.+}} : !fir.logical<4>, !fir.ref, volatile> diff --git a/flang/test/Lower/volatile.fir b/flang/test/Lower/volatile.fir new file mode 100644 index 0000000000000..3238533269e3b --- /dev/null +++ b/flang/test/Lower/volatile.fir @@ -0,0 +1,21 @@ +// RUN: fir-opt --convert-hlfir-to-fir %s -o - | FileCheck %s +func.func @foo() { + %true = arith.constant true + %false = arith.constant false + %0 = fir.alloca !fir.logical<4> {bindc_name = "a", uniq_name = "_QFEa"} + %1 = fir.convert %0 : (!fir.ref>) -> !fir.ref, volatile> + %2:2 = hlfir.declare %1 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEa"} : (!fir.ref, volatile>) -> (!fir.ref, volatile>, !fir.ref, volatile>) + %3 = fir.alloca !fir.logical<4> {bindc_name = "b", uniq_name = "_QFEb"} + %4:2 = hlfir.declare %3 {uniq_name = "_QFEb"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) + %5 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"} + %6:2 = hlfir.declare %5 {uniq_name = "_QFEi"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %7 = fir.convert %false : (i1) -> !fir.logical<4> + hlfir.assign %7 to %2#0 : !fir.logical<4>, !fir.ref, volatile> + %8 = fir.load %2#0 : !fir.ref, volatile> + hlfir.assign %8 to %4#0 : !fir.logical<4>, !fir.ref> + %9 = fir.convert %true : (i1) -> !fir.logical<4> + hlfir.assign %9 to %2#0 : !fir.logical<4>, !fir.ref, volatile> + return +} +// CHECK: fir.store %{{.+}} to %{{.+}} : !fir.ref, volatile> +// CHECK: %{{.+}} = fir.load %{{.+}} : !fir.ref, volatile>