[flang-commits] [flang] [flang][rfc] Add represention of volatile references (PR #132486)
Asher Mancinelli via flang-commits
flang-commits at lists.llvm.org
Thu Apr 3 15:29:03 PDT 2025
https://github.com/ashermancinelli updated https://github.com/llvm/llvm-project/pull/132486
>From 2d747fee52ccac1c94dc377399a186ab407a1ed5 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Thu, 20 Mar 2025 08:01:56 -0700
Subject: [PATCH 01/15] [flang] Add represention of volatile references
Flang currently lacks support for volatile variables. For some cases, the
compiler produces TODO error messages and others are ignored. Some of our tests
are like the example from C.4 Clause 8 notes: The VOLATILE attribute (8.5.20)
and require volatile variables.
This change is a minimal draft of support for volatility in Fortran. This
misses several important features that will be required for full support,
such as volatile boxes and other reference-like types. This commit only
supports volatility for !fir.ref<T> types and is the minimum needed to
get end-to-end examples working.
---
.../flang/Optimizer/Builder/FIRBuilder.h | 2 +-
.../include/flang/Optimizer/Dialect/FIRType.h | 6 ++
.../flang/Optimizer/Dialect/FIRTypes.td | 10 +++-
flang/lib/Lower/CallInterface.cpp | 1 -
flang/lib/Lower/ConvertExprToHLFIR.cpp | 48 ++++++++++++++--
flang/lib/Optimizer/Builder/FIRBuilder.cpp | 4 +-
flang/lib/Optimizer/Builder/HLFIRTools.cpp | 3 +-
flang/lib/Optimizer/CodeGen/CodeGen.cpp | 17 ++++--
flang/lib/Optimizer/Dialect/FIRType.cpp | 57 ++++++++++++++-----
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 7 +++
.../HLFIR/Transforms/ConvertToFIR.cpp | 7 ++-
.../Transforms/OptimizedBufferization.cpp | 9 ++-
flang/test/Fir/volatile.fir | 18 ++++++
flang/test/Integration/volatile.f90 | 11 ++++
flang/test/Lower/volatile.fir | 21 +++++++
15 files changed, 183 insertions(+), 38 deletions(-)
create mode 100644 flang/test/Fir/volatile.fir
create mode 100644 flang/test/Integration/volatile.f90
create mode 100644 flang/test/Lower/volatile.fir
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 1583cfb3f5b51..ddd4ef7114a63 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<fir::ReferenceType>(t))
+ return refTy.isVolatile();
+ return false;
+}
+
/// Is `t` a boxed type?
inline bool isa_box_type(mlir::Type t) {
return mlir::isa<fir::BaseBoxType, fir::BoxCharType, fir::BoxProcType>(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 09bc1babf079c..4ee425e1704c9 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<std::decay_t<T>,
+ Fortran::evaluate::SymbolRef>) {
+ if (isVolatileSymbol(designatorNode.get()))
+ isVolatile = true;
+ } else if constexpr (std::is_same_v<std::decay_t<T>,
+ 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<fir::ReferenceType>(resultValueType))
+ resultValueType = refTy.getEleTy();
+
// Other designators can be handled as raw addresses.
- return fir::ReferenceType::get(resultValueType);
+ return fir::ReferenceType::get(resultValueType, isVolatile);
}
template <typename T>
@@ -415,10 +444,16 @@ class HlfirDesignatorBuilder {
.Case<fir::SequenceType>([&](fir::SequenceType seqTy) -> mlir::Type {
return fir::SequenceType::get(seqTy.getShape(), newEleTy);
})
- .Case<fir::PointerType, fir::HeapType, fir::ReferenceType, fir::BoxType,
- fir::ClassType>([&](auto t) -> mlir::Type {
- using FIRT = decltype(t);
- return FIRT::get(changeElementType(t.getEleTy(), newEleTy));
+ // TODO: handle volatility for other types
+ .Case<fir::PointerType, fir::HeapType, fir::BoxType, fir::ClassType>(
+ [&](auto t) -> mlir::Type {
+ using FIRT = decltype(t);
+ return FIRT::get(changeElementType(t.getEleTy(), newEleTy));
+ })
+ .Case<fir::ReferenceType>([&](fir::ReferenceType refTy) -> mlir::Type {
+ return fir::ReferenceType::get(
+ changeElementType(refTy.getEleTy(), newEleTy),
+ refTy.isVolatile());
})
.Default([newEleTy](mlir::Type t) -> mlir::Type { return newEleTy; });
}
@@ -1809,6 +1844,7 @@ class HlfirBuilder {
auto &expr = std::get<const Fortran::lower::SomeExpr &>(iter);
auto &baseOp = std::get<hlfir::EntityWithAttributes>(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,
@@ -1819,7 +1855,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<mlir::Value, 1> typeParams;
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index fdc155ef2ef18..7fc30ca125a87 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -105,9 +105,9 @@ fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp,
return modOp.lookupSymbol<fir::GlobalOp>(name);
}
-mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) {
+mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy, bool isVolatile) {
assert(!mlir::isa<fir::ReferenceType>(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 06a3e177da1d0..38368cf38d0f2 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -801,7 +801,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 b54b497ee4ba1..90f2474dafca3 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3224,6 +3224,8 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
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<fir::BaseBoxType>(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
@@ -3253,7 +3255,7 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter);
auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>(
- loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false);
+ loc, newBoxStorage, inputBoxStorage, boxSize, isVolatile);
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
memcpy.setTBAATags(*optionalTag);
@@ -3261,8 +3263,10 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
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<mlir::LLVM::LoadOp>(
- load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs());
+ load.getLoc(), llvmLoadTy, memref, /*alignment=*/0, isVolatile);
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
loadOp.setTBAATags(*optionalTag);
else
@@ -3540,6 +3544,8 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
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<fir::BaseBoxType>(storeTy)) {
mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
// Always use memcpy because LLVM is not as effective at optimizing
@@ -3547,10 +3553,11 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
TypePair boxTypePair{boxTy, llvmBoxTy};
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, llvmValue, rewriter);
- newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
- loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false);
+ newOp = rewriter.create<mlir::LLVM::MemcpyOp>(loc, llvmMemref, llvmValue,
+ boxSize, isVolatile);
} else {
- newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref);
+ newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref,
+ /*alignment=*/0, isVolatile);
}
if (std::optional<mlir::ArrayAttr> optionalTag = store.getTbaa())
newOp.setTBAATags(*optionalTag);
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index f3f969ba401e5..90942522d9073 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>([&](fir::SequenceType seqTy) -> mlir::Type {
return fir::SequenceType::get(seqTy.getShape(), newElementType);
})
- .Case<fir::PointerType, fir::HeapType, fir::ReferenceType,
- fir::ClassType>([&](auto t) -> mlir::Type {
- using FIRT = decltype(t);
- return FIRT::get(
- changeElementType(t.getEleTy(), newElementType, turnBoxIntoClass));
+ .Case<fir::ReferenceType>([&](fir::ReferenceType refTy) -> mlir::Type {
+ auto newEleTy = changeElementType(refTy.getEleTy(), newElementType,
+ turnBoxIntoClass);
+ return fir::ReferenceType::get(newEleTy, refTy.isVolatile());
})
+ .Case<fir::PointerType, fir::HeapType, fir::ClassType>(
+ [&](auto t) -> mlir::Type {
+ using FIRT = decltype(t);
+ return FIRT::get(changeElementType(t.getEleTy(), newElementType,
+ turnBoxIntoClass));
+ })
.Case<fir::BoxType>([&](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^)? `>`
mlir::Type fir::ReferenceType::parse(mlir::AsmParser &parser) {
- return parseTypeSingleton<fir::ReferenceType>(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<mlir::InFlightDiagnostic()> emitError,
- mlir::Type eleTy) {
+ llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type eleTy,
+ bool isVolatile) {
if (mlir::isa<ShapeType, ShapeShiftType, SliceType, FieldType, LenType,
ReferenceType, TypeDescType>(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<fir::PointerType, fir::HeapType, fir::ReferenceType, fir::BoxType,
- fir::ClassType>([&](auto t) -> mlir::Type {
- using FIRT = decltype(t);
- return FIRT::get(changeTypeShape(t.getEleTy(), newShape));
+ .Case<fir::ReferenceType>([&](fir::ReferenceType rt) -> mlir::Type {
+ return fir::ReferenceType::get(changeTypeShape(rt.getEleTy(), newShape),
+ rt.isVolatile());
})
+ .Case<fir::PointerType, fir::HeapType, fir::BoxType, fir::ClassType>(
+ [&](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<fir::RecordType>(t) ||
llvm::isa<mlir::NoneType>(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<fir::ReferenceType>(inputType) &&
+ bitEnumContainsAny(fortran_attrs.getFlags(),
+ fir::FortranVariableFlagsEnum::fortran_volatile)) {
+ auto refType = mlir::cast<fir::ReferenceType>(inputType);
+ inputType = fir::ReferenceType::get(refType.getEleTy(), true);
+ memref = builder.create<fir::ConvertOp>(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<fir::ArrayCoorOp>(
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<mlir::Value> firBaseTypeParameters;
auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams(
loc, builder, baseEntity, firBaseTypeParameters);
@@ -464,7 +467,7 @@ class DesignateOpConversion
mlir::Type componentType =
mlir::cast<fir::RecordType>(baseEleTy).getType(
designate.getComponent().value());
- mlir::Type coorTy = fir::ReferenceType::get(componentType);
+ mlir::Type coorTy = fir::ReferenceType::get(componentType, isVolatile);
base = builder.create<fir::CoordinateOp>(loc, coorTy, base, fieldIndex);
if (mlir::isa<fir::BaseBoxType>(componentType)) {
auto variableInterface = mlir::cast<fir::FortranVariableOpInterface>(
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<Op> {
builder.create<fir::StoreOp>(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<Op> {
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<mlir::arith::AddIOp>(loc, index, oneIdx);
@@ -1162,8 +1164,9 @@ class ReductionMaskConversion : public mlir::OpRewritePattern<Op> {
};
// 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.logical<4>>) -> !fir.ref<!fir.logical<4>, 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<!fir.logical<4>, volatile>
+ %5 = fir.load %1 : !fir.ref<!fir.logical<4>, volatile>
+ fir.store %5 to %2 : !fir.ref<!fir.logical<4>>
+ %6 = fir.convert %true : (i1) -> !fir.logical<4>
+ fir.store %6 to %1 : !fir.ref<!fir.logical<4>, 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<!fir.logical<4>, volatile>
+! CHECK: hlfir.assign %{{.+}} to %{{.+}} : !fir.logical<4>, !fir.ref<!fir.logical<4>, 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.logical<4>>) -> !fir.ref<!fir.logical<4>, volatile>
+ %2:2 = hlfir.declare %1 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFEa"} : (!fir.ref<!fir.logical<4>, volatile>) -> (!fir.ref<!fir.logical<4>, volatile>, !fir.ref<!fir.logical<4>, volatile>)
+ %3 = fir.alloca !fir.logical<4> {bindc_name = "b", uniq_name = "_QFEb"}
+ %4:2 = hlfir.declare %3 {uniq_name = "_QFEb"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
+ %5 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"}
+ %6:2 = hlfir.declare %5 {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %7 = fir.convert %false : (i1) -> !fir.logical<4>
+ hlfir.assign %7 to %2#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>, volatile>
+ %8 = fir.load %2#0 : !fir.ref<!fir.logical<4>, volatile>
+ hlfir.assign %8 to %4#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>
+ %9 = fir.convert %true : (i1) -> !fir.logical<4>
+ hlfir.assign %9 to %2#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>, volatile>
+ return
+}
+// CHECK: fir.store %{{.+}} to %{{.+}} : !fir.ref<!fir.logical<4>, volatile>
+// CHECK: %{{.+}} = fir.load %{{.+}} : !fir.ref<!fir.logical<4>, volatile>
>From c66bf64b952df0035e4a8968fc9b5af8cc25ae0f Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Sat, 22 Mar 2025 08:29:27 -0700
Subject: [PATCH 02/15] Add memory effects
Models memory effects on ops that can take volatile references by
adding read and write effects to the default resource.
---
.../include/flang/Optimizer/Dialect/FIROps.h | 6 +
.../include/flang/Optimizer/Dialect/FIROps.td | 39 +++++--
.../include/flang/Optimizer/Dialect/FIRType.h | 5 +-
.../flang/Optimizer/Dialect/FIRTypes.td | 26 +++--
flang/lib/Lower/ConvertExprToHLFIR.cpp | 12 +-
flang/lib/Optimizer/Builder/HLFIRTools.cpp | 2 +-
flang/lib/Optimizer/CodeGen/CodeGen.cpp | 59 ++++++----
flang/lib/Optimizer/Dialect/FIROps.cpp | 104 +++++++++++++++++-
flang/lib/Optimizer/Dialect/FIRType.cpp | 58 +++++++---
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 14 ++-
.../HLFIR/Transforms/ConvertToFIR.cpp | 11 +-
.../Transforms/OptimizedBufferization.cpp | 6 +-
flang/test/Fir/invalid-types.fir | 12 +-
flang/test/Fir/volatile.fir | 2 +-
flang/test/Lower/volatile.fir | 2 +-
15 files changed, 267 insertions(+), 91 deletions(-)
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index ed301016ad01c..f3dbf47351ab8 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.h
@@ -50,6 +50,12 @@ struct DebuggingResource
mlir::StringRef getName() final { return "DebuggingResource"; }
};
+/// Model operations which read from/write to volatile memory
+struct VolatileMemoryResource
+ : public mlir::SideEffects::Resource::Base<VolatileMemoryResource> {
+ mlir::StringRef getName() final { return "VolatileMemoryResource"; }
+};
+
class CoordinateIndicesAdaptor;
using IntOrValue = llvm::PointerUnion<mlir::IntegerAttr, mlir::Value>;
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 753e4bd18dc6d..cf45ca171a243 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -286,7 +286,7 @@ def fir_FreeMemOp : fir_Op<"freemem", [MemoryEffects<[MemFree]>]> {
let assemblyFormat = "$heapref attr-dict `:` qualified(type($heapref))";
}
-def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
+def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "load a value from a memory reference";
let description = [{
Load a value from a memory reference into an ssa-value (virtual register).
@@ -302,7 +302,7 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
or null.
}];
- let arguments = (ins Arg<AnyReferenceLike, "", [MemRead]>:$memref,
+ let arguments = (ins Arg<AnyReferenceLike, "">:$memref,
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
let builders = [OpBuilder<(ins "mlir::Value":$refVal)>,
@@ -315,7 +315,8 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
}];
}
-def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
+def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface,
+ DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "store an SSA-value to a memory location";
let description = [{
@@ -335,7 +336,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
}];
let arguments = (ins AnyType:$value,
- Arg<AnyReferenceLike, "", [MemWrite]>:$memref,
+ Arg<AnyReferenceLike, "">:$memref,
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
let builders = [OpBuilder<(ins "mlir::Value":$value, "mlir::Value":$memref)>];
@@ -348,7 +349,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
}];
}
-def fir_CopyOp : fir_Op<"copy", []> {
+def fir_CopyOp : fir_Op<"copy", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "copy constant size memory";
let description = [{
@@ -369,8 +370,8 @@ def fir_CopyOp : fir_Op<"copy", []> {
TODO: add FirAliasTagOpInterface to carry TBAA.
}];
- let arguments = (ins Arg<AnyRefOfConstantSizeAggregateType, "", [MemRead]>:$source,
- Arg<AnyRefOfConstantSizeAggregateType, "", [MemWrite]>:$destination,
+ let arguments = (ins AnyRefOfConstantSizeAggregateType:$source,
+ AnyRefOfConstantSizeAggregateType:$destination,
OptionalAttr<UnitAttr>:$no_overlap);
let builders = [OpBuilder<(ins "mlir::Value":$source,
@@ -1373,7 +1374,7 @@ def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoMemoryEffect]> {
// !- Merge the new and old values into the memory for "A"
// array_merge_store <updated A> to <A's address>
-def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
+def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "Load an array as a value.";
@@ -1412,7 +1413,7 @@ def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
}];
let arguments = (ins
- Arg<AnyRefOrBox, "", [MemRead]>:$memref,
+ AnyRefOrBox:$memref,
Optional<AnyShapeOrShiftType>:$shape,
Optional<fir_SliceType>:$slice,
Variadic<AnyIntegerType>:$typeparams
@@ -1624,7 +1625,7 @@ def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
It is only possible to use `array_access` on an `array_load` result value or
a value that can be trace back transitively to an `array_load` as the
- dominating source. Other array operation such as `array_amend` can be in
+ dominating source. Other array operations such as `array_amend` can be in
between.
TODO: The above restriction is not enforced. The design of the operation
@@ -1685,7 +1686,7 @@ def fir_ArrayAmendOp : fir_Op<"array_amend", [NoMemoryEffect]> {
}
def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
- [AttrSizedOperandSegments]> {
+ [AttrSizedOperandSegments, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "Store merged array value to memory.";
@@ -1714,7 +1715,7 @@ def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
let arguments = (ins
fir_SequenceType:$original,
fir_SequenceType:$sequence,
- Arg<AnyRefOrBox, "", [MemWrite]>:$memref,
+ AnyRefOrBox:$memref,
Optional<fir_SliceType>:$slice,
Variadic<AnyIntegerType>:$typeparams
);
@@ -2752,6 +2753,20 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoMemoryEffect]> {
let assemblyFormat = "`(` $symbol `)` attr-dict `:` type($resTy)";
}
+def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [NoMemoryEffect]> {
+ let summary = "cast between volatile and non-volatile types";
+ let description = [{
+ Cast between volatile and non-volatile types. The types must be otherwise
+ identical.
+ }];
+ let arguments = (ins AnyRefOrBox:$value);
+ let results = (outs AnyRefOrBox:$res);
+ let assemblyFormat = [{
+ $value attr-dict `:` functional-type($value, results)
+ }];
+ let hasVerifier = 1;
+}
+
def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
let summary = "encapsulates all Fortran entity type conversions";
diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h
index 8261c67e4559d..af516191d8f1f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRType.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRType.h
@@ -111,9 +111,12 @@ inline bool isa_ref_type(mlir::Type t) {
fir::LLVMPointerType>(t);
}
-inline bool isa_volatile_ref_type(mlir::Type t) {
+/// Is `t` a FIR dialect type that has been marked volatile?
+inline bool isa_volatile_type(mlir::Type t) {
if (auto refTy = mlir::dyn_cast_or_null<fir::ReferenceType>(t))
return refTy.isVolatile();
+ if (auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(t))
+ return boxTy.isVolatile();
return false;
}
diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
index 0584c175b36ff..d0aeb5b6765a0 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
@@ -77,24 +77,24 @@ def fir_BoxType : FIR_Type<"Box", "box", [], "BaseBoxType"> {
to) whether the entity is an array, its size, or what type it has.
}];
- let parameters = (ins "mlir::Type":$eleTy);
+ let parameters = (ins "mlir::Type":$eleTy, DefaultValuedParameter<"bool", "false">:$isVolatile);
let skipDefaultBuilders = 1;
let builders = [
TypeBuilderWithInferredContext<(ins
- "mlir::Type":$eleTy), [{
- return Base::get(eleTy.getContext(), eleTy);
+ "mlir::Type":$eleTy, CArg<"bool", "false">:$isVolatile), [{
+ return Base::get(eleTy.getContext(), eleTy, isVolatile);
}]>,
];
let extraClassDeclaration = [{
mlir::Type getElementType() const { return getEleTy(); }
+ bool isVolatile() const { return (bool)getIsVolatile(); }
}];
let genVerifyDecl = 1;
-
- let assemblyFormat = "`<` $eleTy `>`";
+ let hasCustomAssemblyFormat = 1;
}
def fir_CharacterType : FIR_Type<"Character", "char"> {
@@ -361,27 +361,33 @@ def fir_ReferenceType : FIR_Type<"Reference", "ref"> {
let description = [{
The type of a reference to an entity in memory.
+
+ References can be volatile. Any ops taking an operand of a volatile
+ reference must set their memory effects appropriately. Accesses of
+ volatile references are currently modeled as read and write effects
+ to an unknown memory location.
}];
let parameters = (ins
"mlir::Type":$eleTy,
- DefaultValuedParameter<"bool", "false">:$isVol);
+ DefaultValuedParameter<"bool", "false">:$isVolatile);
let skipDefaultBuilders = 1;
let builders = [
- TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType, CArg<"bool", "false">:$isVol), [{
- return Base::get(elementType.getContext(), elementType, isVol);
+ TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType, CArg<"bool", "false">:$isVolatile), [{
+ return Base::get(elementType.getContext(), elementType, isVolatile);
}]>,
];
let extraClassDeclaration = [{
+ bool isVolatile() const { return (bool)getIsVolatile(); }
mlir::Type getElementType() const { return getEleTy(); }
- bool isVolatile() const { return (bool)getIsVol(); }
- static llvm::StringRef getVolatileKeyword() { return "volatile"; }
}];
let genVerifyDecl = 1;
+ // let assemblyFormat = "`<` $eleTy (`,` `volatile` $isVolatile^)? `>`";
+ // let assemblyFormat = "`<` $eleTy custom<IsVolatile>($isVolatile)`>`";
let hasCustomAssemblyFormat = 1;
}
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index 4ee425e1704c9..be6e17b3f9fa6 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -229,7 +229,7 @@ class HlfirDesignatorBuilder {
// 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);
+ isVolatile = fir::isa_volatile_type(baseType);
}
auto isVolatileSymbol = [](const Fortran::semantics::Symbol &symbol) {
@@ -242,16 +242,8 @@ class HlfirDesignatorBuilder {
Fortran::evaluate::SymbolRef>) {
if (isVolatileSymbol(designatorNode.get()))
isVolatile = true;
- } else if constexpr (std::is_same_v<std::decay_t<T>,
- 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<fir::ReferenceType>(resultValueType))
- resultValueType = refTy.getEleTy();
-
// Other designators can be handled as raw addresses.
return fir::ReferenceType::get(resultValueType, isVolatile);
}
@@ -1844,7 +1836,7 @@ class HlfirBuilder {
auto &expr = std::get<const Fortran::lower::SomeExpr &>(iter);
auto &baseOp = std::get<hlfir::EntityWithAttributes>(iter);
std::string name = converter.getRecordTypeFieldName(sym);
- const bool isVolatile = fir::isa_volatile_ref_type(baseOp.getType());
+ const bool isVolatile = fir::isa_volatile_type(baseOp.getType());
// Generate DesignateOp for the component.
// The designator's result type is just a reference to the component type,
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index 38368cf38d0f2..d019e3972f5e1 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -801,7 +801,7 @@ mlir::Type hlfir::getVariableElementType(hlfir::Entity variable) {
} else if (fir::isRecordWithTypeParameters(eleTy)) {
return fir::BoxType::get(eleTy);
}
- const bool isVolatile = fir::isa_volatile_ref_type(variable.getType());
+ const bool isVolatile = fir::isa_volatile_type(variable.getType());
return fir::ReferenceType::get(eleTy, isVolatile);
}
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 90f2474dafca3..bd0eb599c3e38 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -689,6 +689,20 @@ struct CmpcOpConversion : public fir::FIROpConversion<fir::CmpcOp> {
}
};
+/// fir.volatile_cast is only useful at the fir level. Once we lower to LLVM,
+/// volatility is described by setting volatile attributes on the LLVM ops.
+struct VolatileCastOpConversion
+ : public fir::FIROpConversion<fir::VolatileCastOp> {
+ using FIROpConversion::FIROpConversion;
+
+ llvm::LogicalResult
+ matchAndRewrite(fir::VolatileCastOp volatileCast, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ rewriter.replaceOp(volatileCast, adaptor.getOperands()[0]);
+ return mlir::success();
+ }
+};
+
/// convert value of from-type to value of to-type
struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
using FIROpConversion::FIROpConversion;
@@ -3224,8 +3238,7 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
mlir::ConversionPatternRewriter &rewriter) const override {
mlir::Type llvmLoadTy = convertObjectType(load.getType());
- const bool isVolatile =
- fir::isa_volatile_ref_type(load.getMemref().getType());
+ const bool isVolatile = fir::isa_volatile_type(load.getMemref().getType());
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(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
@@ -3263,10 +3276,10 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
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<mlir::LLVM::LoadOp>(
- load.getLoc(), llvmLoadTy, memref, /*alignment=*/0, isVolatile);
+ mlir::LLVM::LoadOp loadOp = rewriter.create<mlir::LLVM::LoadOp>(
+ load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs());
+ if (isVolatile)
+ loadOp.setVolatile_Attr(rewriter.getUnitAttr());
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
loadOp.setTBAATags(*optionalTag);
else
@@ -3544,8 +3557,7 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
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());
+ const bool isVolatile = fir::isa_volatile_type(store.getMemref().getType());
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) {
mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
// Always use memcpy because LLVM is not as effective at optimizing
@@ -4200,21 +4212,22 @@ void fir::populateFIRToLLVMConversionPatterns(
BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
- CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
- CopyOpConversion, DTEntryOpConversion, DeclareOpConversion,
- DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
- EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
- FirEndOpConversion, FreeMemOpConversion, GlobalLenOpConversion,
- GlobalOpConversion, InsertOnRangeOpConversion, IsPresentOpConversion,
- LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
- NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
- SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
- ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
- SliceOpConversion, StoreOpConversion, StringLitOpConversion,
- SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
- UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
- UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
- XReboxOpConversion, ZeroOpConversion>(converter, options);
+ CmpcOpConversion, VolatileCastOpConversion, ConvertOpConversion,
+ CoordinateOpConversion, CopyOpConversion, DTEntryOpConversion,
+ DeclareOpConversion, DivcOpConversion, EmboxOpConversion,
+ EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion,
+ FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion,
+ GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
+ IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion,
+ MulcOpConversion, NegcOpConversion, NoReassocOpConversion,
+ SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion,
+ SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion,
+ ShiftOpConversion, SliceOpConversion, StoreOpConversion,
+ StringLitOpConversion, SubcOpConversion, TypeDescOpConversion,
+ TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
+ UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
+ XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter,
+ options);
// Patterns that are populated without a type converter do not trigger
// target materializations for the operands of the root op.
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 2d8017d0318d2..d84f0b5606824 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
@@ -37,6 +38,24 @@ namespace {
#include "flang/Optimizer/Dialect/CanonicalizationPatterns.inc"
} // namespace
+/// Volatile references are currently modeled as read and write effects
+/// to an unknown memory location. This is how the LLVM dialect models
+/// volatile memory accesses, but may be overly conservative. LLVM
+/// Language Reference only specifies that volatile memory accesses
+/// must not be reordered relative to other volatile memory accesses,
+/// so it would be more precise to use a separate memory resource for
+/// volatile memory accesses. Be conservative for now.
+static void addVolatileMemoryEffects(
+ mlir::Type type, llvm::SmallVectorImpl<mlir::SideEffects::EffectInstance<
+ mlir::MemoryEffects::Effect>> &effects) {
+ if (fir::isa_volatile_type(type)) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(),
+ fir::VolatileMemoryResource::get());
+ effects.emplace_back(mlir::MemoryEffects::Write::get(),
+ fir::VolatileMemoryResource::get());
+ }
+}
+
static void propagateAttributes(mlir::Operation *fromOp,
mlir::Operation *toOp) {
if (!fromOp || !toOp)
@@ -853,6 +872,16 @@ std::vector<mlir::Value> fir::ArrayLoadOp::getExtents() {
return {};
}
+void fir::ArrayLoadOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(),
+ &getOperation()->getOpOperand(0),
+ mlir::SideEffects::DefaultResource::get());
+ addVolatileMemoryEffects(getMemref().getType(), effects);
+}
+
llvm::LogicalResult fir::ArrayLoadOp::verify() {
auto eleTy = fir::dyn_cast_ptrOrBoxEleTy(getMemref().getType());
auto arrTy = mlir::dyn_cast<fir::SequenceType>(eleTy);
@@ -935,6 +964,16 @@ llvm::LogicalResult fir::ArrayMergeStoreOp::verify() {
return mlir::success();
}
+void fir::ArrayMergeStoreOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ effects.emplace_back(mlir::MemoryEffects::Write::get(),
+ &getOperation()->getOpOperand(0),
+ mlir::SideEffects::DefaultResource::get());
+ addVolatileMemoryEffects(getMemref().getType(), effects);
+}
+
//===----------------------------------------------------------------------===//
// ArrayFetchOp
//===----------------------------------------------------------------------===//
@@ -1322,6 +1361,29 @@ mlir::ParseResult fir::CmpcOp::parse(mlir::OpAsmParser &parser,
return parseCmpOp<fir::CmpcOp>(parser, result);
}
+//===----------------------------------------------------------------------===//
+// VolatileCastOp
+//===----------------------------------------------------------------------===//
+
+llvm::LogicalResult fir::VolatileCastOp::verify() {
+ mlir::Type fromType = getValue().getType();
+ mlir::Type toType = getType();
+ bool sameBaseType =
+ (mlir::isa<fir::BoxType>(fromType) && mlir::isa<fir::BoxType>(toType)) ||
+ (mlir::isa<fir::ReferenceType>(fromType) &&
+ mlir::isa<fir::ReferenceType>(toType));
+ bool sameElementType = fir::dyn_cast_ptrOrBoxEleTy(fromType) ==
+ fir::dyn_cast_ptrOrBoxEleTy(toType);
+ llvm::dbgs() << fromType << " / " << toType << "\n"
+ << sameBaseType << " " << sameElementType << "\n";
+ if (fromType == toType ||
+ fir::isa_volatile_type(fromType) == fir::isa_volatile_type(toType) ||
+ !sameBaseType || !sameElementType)
+ return emitOpError("types must be identical except for volatility ")
+ << fromType << " / " << toType;
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// ConvertOp
//===----------------------------------------------------------------------===//
@@ -1461,7 +1523,13 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
}
llvm::LogicalResult fir::ConvertOp::verify() {
- if (canBeConverted(getValue().getType(), getType()))
+ mlir::Type inType = getValue().getType();
+ mlir::Type outType = getType();
+ if (fir::isa_volatile_type(inType) != fir::isa_volatile_type(outType))
+ return emitOpError("cannot convert between volatile and non-volatile "
+ "types, use fir.volatile_cast instead ")
+ << inType << " / " << outType;
+ if (canBeConverted(inType, outType))
return mlir::success();
return emitOpError("invalid type conversion")
<< getValue().getType() << " / " << getType();
@@ -2599,6 +2667,16 @@ void fir::LoadOp::print(mlir::OpAsmPrinter &p) {
p << " : " << getMemref().getType();
}
+void fir::LoadOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(),
+ &getOperation()->getOpOperand(0),
+ mlir::SideEffects::DefaultResource::get());
+ addVolatileMemoryEffects(getMemref().getType(), effects);
+}
+
//===----------------------------------------------------------------------===//
// DoLoopOp
//===----------------------------------------------------------------------===//
@@ -3951,6 +4029,16 @@ void fir::StoreOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
build(builder, result, value, memref, {});
}
+void fir::StoreOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ effects.emplace_back(mlir::MemoryEffects::Write::get(),
+ &getOperation()->getOpOperand(1),
+ mlir::SideEffects::DefaultResource::get());
+ addVolatileMemoryEffects(getMemref().getType(), effects);
+}
+
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
@@ -3971,6 +4059,20 @@ llvm::LogicalResult fir::CopyOp::verify() {
return mlir::success();
}
+void fir::CopyOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(),
+ &getOperation()->getOpOperand(0),
+ mlir::SideEffects::DefaultResource::get());
+ effects.emplace_back(mlir::MemoryEffects::Write::get(),
+ &getOperation()->getOpOperand(1),
+ mlir::SideEffects::DefaultResource::get());
+ addVolatileMemoryEffects(getDestination().getType(), effects);
+ addVolatileMemoryEffects(getSource().getType(), effects);
+}
+
//===----------------------------------------------------------------------===//
// StringLitOp
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 90942522d9073..5e1fbe71e54c5 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -20,6 +20,7 @@
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/DialectImplementation.h"
+#include "mlir/Support/LLVM.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/TypeSwitch.h"
@@ -76,6 +77,21 @@ bool verifySameLists(llvm::ArrayRef<RecordType::TypePair> a1,
return a1 == a2;
}
+static llvm::StringRef getVolatileKeyword() { return "volatile"; }
+
+static mlir::ParseResult parseOptionalCommaAndKeyword(mlir::AsmParser &parser,
+ mlir::StringRef keyword,
+ bool &parsedKeyword) {
+ if (!parser.parseOptionalComma()) {
+ if (parser.parseKeyword(keyword))
+ return mlir::failure();
+ parsedKeyword = true;
+ return mlir::success();
+ }
+ parsedKeyword = false;
+ return mlir::success();
+}
+
RecordType verifyDerived(mlir::AsmParser &parser, RecordType derivedTy,
llvm::ArrayRef<RecordType::TypePair> lenPList,
llvm::ArrayRef<RecordType::TypePair> typeList) {
@@ -745,13 +761,30 @@ static bool cannotBePointerOrHeapElementType(mlir::Type eleTy) {
llvm::LogicalResult
fir::BoxType::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
- mlir::Type eleTy) {
+ mlir::Type eleTy, bool isVolatile) {
if (mlir::isa<fir::BaseBoxType>(eleTy))
return emitError() << "invalid element type\n";
// TODO
return mlir::success();
}
+mlir::Type fir::BoxType::parse(mlir::AsmParser &parser) {
+ mlir::Type ty;
+ bool isVolatile;
+ if (parser.parseLess() || parser.parseType(ty) ||
+ parseOptionalCommaAndKeyword(parser, getVolatileKeyword(), isVolatile) ||
+ parser.parseGreater())
+ return {};
+ return get(ty, isVolatile);
+}
+
+void fir::BoxType::print(mlir::AsmPrinter &printer) const {
+ printer << '<' << getEleTy();
+ if (isVolatile())
+ printer << ", " << getVolatileKeyword();
+ printer << '>';
+}
+
//===----------------------------------------------------------------------===//
// BoxCharType
//===----------------------------------------------------------------------===//
@@ -1064,24 +1097,13 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) {
// `ref` `<` type (`, volatile` $volatile^)? `>`
mlir::Type fir::ReferenceType::parse(mlir::AsmParser &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())
+ mlir::Type ty;
+ bool isVolatile;
+ if (parser.parseLess() || parser.parseType(ty) ||
+ parseOptionalCommaAndKeyword(parser, getVolatileKeyword(), isVolatile) ||
+ parser.parseGreater())
return {};
- return get(eleTy, isVolatile);
+ return get(ty, isVolatile);
}
void fir::ReferenceType::print(mlir::AsmPrinter &printer) const {
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 4a3308ff4e747..94772e82485ae 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -61,6 +61,12 @@ getIntrinsicEffects(mlir::Operation *self,
// }
for (mlir::OpOperand &operand : self->getOpOperands()) {
mlir::Type opTy = operand.get().getType();
+ if (fir::isa_volatile_type(opTy)) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(), &operand,
+ mlir::SideEffects::DefaultResource::get());
+ effects.emplace_back(mlir::MemoryEffects::Write::get(), &operand,
+ mlir::SideEffects::DefaultResource::get());
+ }
if (fir::isa_ref_type(opTy) || fir::isa_box_type(opTy))
effects.emplace_back(mlir::MemoryEffects::Read::get(), &operand,
mlir::SideEffects::DefaultResource::get());
@@ -164,6 +170,11 @@ void hlfir::AssignOp::getEffects(
}
}
+ if (fir::isa_volatile_type(lhsType) || fir::isa_volatile_type(rhsType)) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get());
+ effects.emplace_back(mlir::MemoryEffects::Write::get());
+ }
+
if (getRealloc()) {
// Reallocation of the data cannot be precisely described by this API.
effects.emplace_back(mlir::MemoryEffects::Free::get(),
@@ -219,7 +230,8 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
fir::FortranVariableFlagsEnum::fortran_volatile)) {
auto refType = mlir::cast<fir::ReferenceType>(inputType);
inputType = fir::ReferenceType::get(refType.getEleTy(), true);
- memref = builder.create<fir::ConvertOp>(memref.getLoc(), inputType, memref);
+ memref =
+ builder.create<fir::VolatileCastOp>(memref.getLoc(), inputType, memref);
}
mlir::Type hlfirVariableType =
getHLFIRVariableType(inputType, hasExplicitLbs);
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index aa151f90ed0d1..e804306731636 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -89,7 +89,12 @@ class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
return fir::getBase(builder.createBox(loc, rhsExv));
};
- if (assignOp.isAllocatableAssignment()) {
+ if (fir::isa_volatile_type(lhs.getType()) ||
+ fir::isa_volatile_type(rhs.getType())) {
+ fir::factory::genScalarAssignment(builder, loc, lhsExv, rhsExv,
+ /*needFinalization=*/false,
+ assignOp.isTemporaryLHS());
+ } else if (assignOp.isAllocatableAssignment()) {
// Whole allocatable assignment: use the runtime to deal with the
// reallocation.
mlir::Value from = emboxRHS(rhsExv);
@@ -419,7 +424,7 @@ class DesignateOpConversion
i = i + (isTriplet ? 3 : 1);
}
mlir::Type originalDesignateType = designate.getResult().getType();
- const bool isVolatile = fir::isa_volatile_ref_type(originalDesignateType);
+ const bool isVolatile = fir::isa_volatile_type(originalDesignateType);
mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy, isVolatile);
base = builder.create<fir::ArrayCoorOp>(
loc, arrayCoorType, base, shape,
@@ -443,7 +448,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);
+ const bool isVolatile = fir::isa_volatile_type(designateResultType);
llvm::SmallVector<mlir::Value> firBaseTypeParameters;
auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams(
loc, builder, baseEntity, firBaseTypeParameters);
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
index 020915179a670..61be2c958a405 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
@@ -1127,7 +1127,7 @@ class ReductionMaskConversion : public mlir::OpRewritePattern<Op> {
mlir::Type resultElemTy =
hlfir::getFortranElementType(resultArr.getType());
mlir::Type returnRefTy = builder.getRefType(
- resultElemTy, fir::isa_volatile_ref_type(flagRef.getType()));
+ resultElemTy, fir::isa_volatile_type(flagRef.getType()));
mlir::IndexType idxTy = builder.getIndexType();
for (unsigned int i = 0; i < rank; ++i) {
@@ -1155,7 +1155,7 @@ class ReductionMaskConversion : public mlir::OpRewritePattern<Op> {
const mlir::Type &resultElemType, mlir::Value resultArr,
mlir::Value index) {
mlir::Type resultRefTy = builder.getRefType(
- resultElemType, fir::isa_volatile_ref_type(resultArr.getType()));
+ resultElemType, fir::isa_volatile_type(resultArr.getType()));
mlir::Value oneIdx =
builder.createIntegerConstant(loc, builder.getIndexType(), 1);
index = builder.create<mlir::arith::AddIOp>(loc, index, oneIdx);
@@ -1164,7 +1164,7 @@ class ReductionMaskConversion : public mlir::OpRewritePattern<Op> {
};
// Initialize the result
- const bool isVolatile = fir::isa_volatile_ref_type(resultArr.getType());
+ const bool isVolatile = fir::isa_volatile_type(resultArr.getType());
mlir::Type resultElemTy = hlfir::getFortranElementType(resultArr.getType());
mlir::Type resultRefTy = builder.getRefType(resultElemTy, isVolatile);
mlir::Value returnValue =
diff --git a/flang/test/Fir/invalid-types.fir b/flang/test/Fir/invalid-types.fir
index f4505097086ad..5240293741ae3 100644
--- a/flang/test/Fir/invalid-types.fir
+++ b/flang/test/Fir/invalid-types.fir
@@ -6,8 +6,7 @@ func.func private @box3() -> !fir.boxproc<>
// -----
-// expected-error at +2 {{expected non-function type}}
-// expected-error at +1 {{failed to parse fir_BoxType parameter 'eleTy' which is to be a `mlir::Type`}}
+// expected-error at +1 {{expected non-function type}}
func.func private @box1() -> !fir.box<>
// -----
@@ -158,7 +157,8 @@ func.func private @oth3() -> !fir.vector<10:>
// expected-error at +1 {{invalid element type}}
func.func private @upe() -> !fir.class<!fir.box<i32>>
-// -----
-
-// expected-error at +1 {{invalid element type}}
-func.func private @upe() -> !fir.box<!fir.class<none>>
+// TODO: why are source locations lost on the error message?
+// the error message is printed but without a proper source location.
+// -- ---
+// expected-error skipme @+1 {{invalid element type}}
+// func.func private @upe() -> !fir.box<!fir.class<none>>
diff --git a/flang/test/Fir/volatile.fir b/flang/test/Fir/volatile.fir
index e508d7b88e645..6b3d8709abdeb 100644
--- a/flang/test/Fir/volatile.fir
+++ b/flang/test/Fir/volatile.fir
@@ -5,7 +5,7 @@ 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.logical<4>>) -> !fir.ref<!fir.logical<4>, volatile>
+ %1 = fir.volatile_cast %0 : (!fir.ref<!fir.logical<4>>) -> !fir.ref<!fir.logical<4>, 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>
diff --git a/flang/test/Lower/volatile.fir b/flang/test/Lower/volatile.fir
index 3238533269e3b..3404ab2e1f9ea 100644
--- a/flang/test/Lower/volatile.fir
+++ b/flang/test/Lower/volatile.fir
@@ -3,7 +3,7 @@ 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.logical<4>>) -> !fir.ref<!fir.logical<4>, volatile>
+ %1 = fir.volatile_cast %0 : (!fir.ref<!fir.logical<4>>) -> !fir.ref<!fir.logical<4>, volatile>
%2:2 = hlfir.declare %1 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFEa"} : (!fir.ref<!fir.logical<4>, volatile>) -> (!fir.ref<!fir.logical<4>, volatile>, !fir.ref<!fir.logical<4>, volatile>)
%3 = fir.alloca !fir.logical<4> {bindc_name = "b", uniq_name = "_QFEb"}
%4:2 = hlfir.declare %3 {uniq_name = "_QFEb"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
>From ee35583f893a4d34763b0b37b861edc35490d2cc Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 25 Mar 2025 15:17:57 -0700
Subject: [PATCH 03/15] Move tests and support routines
---
.../flang/Optimizer/Dialect/FIROpsSupport.h | 26 ++++++++++----
flang/lib/Optimizer/Dialect/FIROps.cpp | 34 ++++++-------------
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 5 +--
flang/test/{Lower => HLFIR}/volatile.fir | 0
.../test/{Integration => Lower}/volatile.f90 | 0
5 files changed, 30 insertions(+), 35 deletions(-)
rename flang/test/{Lower => HLFIR}/volatile.fir (100%)
rename flang/test/{Integration => Lower}/volatile.f90 (100%)
diff --git a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
index f7f0a3067b318..8f7ca63ca8d1f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
@@ -15,13 +15,25 @@
namespace fir {
-/// Return true iff the Operation is a non-volatile LoadOp or ArrayLoadOp.
-inline bool nonVolatileLoad(mlir::Operation *op) {
- if (auto load = mlir::dyn_cast<fir::LoadOp>(op))
- return !load->getAttr("volatile");
- if (auto arrLoad = mlir::dyn_cast<fir::ArrayLoadOp>(op))
- return !arrLoad->getAttr("volatile");
- return false;
+/// The LLVM dialect represents volatile memory accesses as read and write
+/// effects to an unknown memory location, but this may be overly conservative.
+/// LLVM Language Reference only specifies that volatile memory accesses
+/// must not be reordered relative to other volatile memory accesses, so it
+/// is more precise to use a separate memory resource for volatile memory
+/// accesses.
+inline void addVolatileMemoryEffects(
+ mlir::TypeRange type,
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ for (mlir::Type t : type) {
+ if (fir::isa_volatile_type(t)) {
+ effects.emplace_back(mlir::MemoryEffects::Read::get(),
+ fir::VolatileMemoryResource::get());
+ effects.emplace_back(mlir::MemoryEffects::Write::get(),
+ fir::VolatileMemoryResource::get());
+ }
+ }
}
/// Return true iff the Operation is a call.
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index d84f0b5606824..a2b42566053f7 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -30,6 +30,7 @@
#include "mlir/IR/Matchers.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/PatternMatch.h"
+#include "mlir/IR/TypeRange.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h"
@@ -38,24 +39,6 @@ namespace {
#include "flang/Optimizer/Dialect/CanonicalizationPatterns.inc"
} // namespace
-/// Volatile references are currently modeled as read and write effects
-/// to an unknown memory location. This is how the LLVM dialect models
-/// volatile memory accesses, but may be overly conservative. LLVM
-/// Language Reference only specifies that volatile memory accesses
-/// must not be reordered relative to other volatile memory accesses,
-/// so it would be more precise to use a separate memory resource for
-/// volatile memory accesses. Be conservative for now.
-static void addVolatileMemoryEffects(
- mlir::Type type, llvm::SmallVectorImpl<mlir::SideEffects::EffectInstance<
- mlir::MemoryEffects::Effect>> &effects) {
- if (fir::isa_volatile_type(type)) {
- effects.emplace_back(mlir::MemoryEffects::Read::get(),
- fir::VolatileMemoryResource::get());
- effects.emplace_back(mlir::MemoryEffects::Write::get(),
- fir::VolatileMemoryResource::get());
- }
-}
-
static void propagateAttributes(mlir::Operation *fromOp,
mlir::Operation *toOp) {
if (!fromOp || !toOp)
@@ -879,7 +862,7 @@ void fir::ArrayLoadOp::getEffects(
effects.emplace_back(mlir::MemoryEffects::Read::get(),
&getOperation()->getOpOperand(0),
mlir::SideEffects::DefaultResource::get());
- addVolatileMemoryEffects(getMemref().getType(), effects);
+ addVolatileMemoryEffects({getMemref().getType()}, effects);
}
llvm::LogicalResult fir::ArrayLoadOp::verify() {
@@ -971,7 +954,7 @@ void fir::ArrayMergeStoreOp::getEffects(
effects.emplace_back(mlir::MemoryEffects::Write::get(),
&getOperation()->getOpOperand(0),
mlir::SideEffects::DefaultResource::get());
- addVolatileMemoryEffects(getMemref().getType(), effects);
+ addVolatileMemoryEffects({getMemref().getType()}, effects);
}
//===----------------------------------------------------------------------===//
@@ -1855,6 +1838,9 @@ llvm::LogicalResult fir::EmboxOp::verify() {
return emitOpError("slice must not be provided for a scalar");
if (getSourceBox() && !mlir::isa<fir::ClassType>(getResult().getType()))
return emitOpError("source_box must be used with fir.class result type");
+ if (fir::isa_volatile_type(getMemref().getType()) !=
+ fir::isa_volatile_type(getResult().getType()))
+ return emitOpError("input and output types must have the same volatility");
return mlir::success();
}
@@ -2674,7 +2660,7 @@ void fir::LoadOp::getEffects(
effects.emplace_back(mlir::MemoryEffects::Read::get(),
&getOperation()->getOpOperand(0),
mlir::SideEffects::DefaultResource::get());
- addVolatileMemoryEffects(getMemref().getType(), effects);
+ addVolatileMemoryEffects({getMemref().getType()}, effects);
}
//===----------------------------------------------------------------------===//
@@ -4036,7 +4022,7 @@ void fir::StoreOp::getEffects(
effects.emplace_back(mlir::MemoryEffects::Write::get(),
&getOperation()->getOpOperand(1),
mlir::SideEffects::DefaultResource::get());
- addVolatileMemoryEffects(getMemref().getType(), effects);
+ addVolatileMemoryEffects({getMemref().getType()}, effects);
}
//===----------------------------------------------------------------------===//
@@ -4069,8 +4055,8 @@ void fir::CopyOp::getEffects(
effects.emplace_back(mlir::MemoryEffects::Write::get(),
&getOperation()->getOpOperand(1),
mlir::SideEffects::DefaultResource::get());
- addVolatileMemoryEffects(getDestination().getType(), effects);
- addVolatileMemoryEffects(getSource().getType(), effects);
+ addVolatileMemoryEffects({getDestination().getType(), getSource().getType()},
+ effects);
}
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 94772e82485ae..d6f3996f668f7 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -170,10 +170,7 @@ void hlfir::AssignOp::getEffects(
}
}
- if (fir::isa_volatile_type(lhsType) || fir::isa_volatile_type(rhsType)) {
- effects.emplace_back(mlir::MemoryEffects::Read::get());
- effects.emplace_back(mlir::MemoryEffects::Write::get());
- }
+ fir::addVolatileMemoryEffects({lhsType, rhsType}, effects);
if (getRealloc()) {
// Reallocation of the data cannot be precisely described by this API.
diff --git a/flang/test/Lower/volatile.fir b/flang/test/HLFIR/volatile.fir
similarity index 100%
rename from flang/test/Lower/volatile.fir
rename to flang/test/HLFIR/volatile.fir
diff --git a/flang/test/Integration/volatile.f90 b/flang/test/Lower/volatile.f90
similarity index 100%
rename from flang/test/Integration/volatile.f90
rename to flang/test/Lower/volatile.f90
>From 03c9d3555e85fdf9b8f534eae1c8aadd178c4277 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 25 Mar 2025 18:29:02 -0700
Subject: [PATCH 04/15] explicitly convert when dummy arg doesnt match callers
type
---
.../flang/Optimizer/Builder/FIRBuilder.h | 3 +
.../include/flang/Optimizer/Dialect/FIROps.td | 5 +-
.../include/flang/Optimizer/Dialect/FIRType.h | 4 ++
.../flang/Optimizer/Dialect/FIRTypes.td | 4 +-
flang/lib/Lower/CallInterface.cpp | 3 +-
flang/lib/Lower/ConvertCall.cpp | 20 ++++++-
flang/lib/Lower/ConvertExprToHLFIR.cpp | 19 +++---
flang/lib/Lower/IO.cpp | 17 +++++-
flang/lib/Optimizer/Builder/FIRBuilder.cpp | 15 ++++-
flang/lib/Optimizer/Dialect/FIROps.cpp | 14 +++--
flang/lib/Optimizer/Dialect/FIRType.cpp | 13 ++++
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 9 +--
.../HLFIR/Transforms/ConvertToFIR.cpp | 7 +--
flang/test/Fir/invalid-types.fir | 10 +++-
flang/test/Lower/volatile.f90 | 59 ++++++++++++++++---
15 files changed, 149 insertions(+), 53 deletions(-)
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index ddd4ef7114a63..20f12d8c00a3b 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -397,6 +397,9 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::Value createConvert(mlir::Location loc, mlir::Type toTy,
mlir::Value val);
+ mlir::Value createConvertWithVolatileCast(mlir::Location loc, mlir::Type toTy,
+ mlir::Value val);
+
/// Create a fir.store of \p val into \p addr. A lazy conversion
/// of \p val to the element type of \p addr is created if needed.
void createStoreWithConvert(mlir::Location loc, mlir::Value val,
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index cf45ca171a243..d8d2b0b8389de 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -302,7 +302,7 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface, DeclareOpInter
or null.
}];
- let arguments = (ins Arg<AnyReferenceLike, "">:$memref,
+ let arguments = (ins AnyReferenceLike:$memref,
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
let builders = [OpBuilder<(ins "mlir::Value":$refVal)>,
@@ -336,7 +336,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface,
}];
let arguments = (ins AnyType:$value,
- Arg<AnyReferenceLike, "">:$memref,
+ AnyReferenceLike:$memref,
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
let builders = [OpBuilder<(ins "mlir::Value":$value, "mlir::Value":$memref)>];
@@ -2765,6 +2765,7 @@ def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [NoMemoryEffect]
$value attr-dict `:` functional-type($value, results)
}];
let hasVerifier = 1;
+ let hasFolder = 1;
}
def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h
index af516191d8f1f..3082e2d9bee14 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRType.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRType.h
@@ -462,6 +462,10 @@ inline mlir::Type wrapInClassOrBoxType(mlir::Type eleTy,
return fir::BoxType::get(eleTy);
}
+/// Re-create the given type with the given volatility, if this is a type
+/// that can represent volatility.
+mlir::Type updateTypeWithVolatility(mlir::Type type, bool isVolatile);
+
/// Return the elementType where intrinsic types are replaced with none for
/// unlimited polymorphic entities.
///
diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
index d0aeb5b6765a0..35fd00f6d3935 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
@@ -365,7 +365,7 @@ def fir_ReferenceType : FIR_Type<"Reference", "ref"> {
References can be volatile. Any ops taking an operand of a volatile
reference must set their memory effects appropriately. Accesses of
volatile references are currently modeled as read and write effects
- to an unknown memory location.
+ to a specific memory resource.
}];
let parameters = (ins
@@ -386,8 +386,6 @@ def fir_ReferenceType : FIR_Type<"Reference", "ref"> {
}];
let genVerifyDecl = 1;
- // let assemblyFormat = "`<` $eleTy (`,` `volatile` $isVolatile^)? `>`";
- // let assemblyFormat = "`<` $eleTy custom<IsVolatile>($isVolatile)`>`";
let hasCustomAssemblyFormat = 1;
}
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 4ee28fbeb9a0c..ddc3f745ef3b3 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1150,7 +1150,8 @@ class Fortran::lower::CallInterfaceImpl {
if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) {
// Pass as fir.ref<fir.box> or fir.ref<fir.class>
- mlir::Type boxRefType = fir::ReferenceType::get(boxType);
+ const bool isVolatile = obj.attrs.test(Attrs::Volatile);
+ mlir::Type boxRefType = fir::ReferenceType::get(boxType, isVolatile);
addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
attrs);
addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 6a0f4d1090adc..d1fa4bd3aa674 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -327,7 +327,6 @@ Fortran::lower::genCallOpAndResult(
charFuncPointerLength = charBox->getLen();
}
}
-
const bool isExprCall =
converter.getLoweringOptions().getLowerToHighLevelFIR() &&
callSiteType.getNumResults() == 1 &&
@@ -1417,6 +1416,25 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
addr = hlfir::genVariableRawAddress(loc, builder, entity);
}
+ // If the volatility of the input type does not match the dummy type,
+ // we need to cast the argument.
+ if (fir::isa_volatile_type(dummyTypeWithActualRank) !=
+ fir::isa_volatile_type(addr.getType())) {
+ const bool isToTypeVolatile =
+ fir::isa_volatile_type(dummyTypeWithActualRank);
+ mlir::Type volatileAdjustedType =
+ llvm::TypeSwitch<mlir::Type, mlir::Type>(addr.getType())
+ .Case<fir::ReferenceType, fir::BoxType>([&](auto ty) {
+ using TYPE = decltype(ty);
+ return TYPE::get(ty.getElementType(), isToTypeVolatile);
+ })
+ .Default([](auto t) {
+ assert(false && "unexpected type");
+ return t;
+ });
+ addr = builder.create<fir::VolatileCastOp>(loc, volatileAdjustedType, addr);
+ }
+
// For ranked actual passed to assumed-rank dummy, the cast to assumed-rank
// box is inserted when building the fir.call op. Inserting it here would
// cause the fir.if results to be assumed-rank in case of OPTIONAL dummy,
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index be6e17b3f9fa6..a4f43aa2d85e3 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -232,15 +232,11 @@ class HlfirDesignatorBuilder {
isVolatile = fir::isa_volatile_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<std::decay_t<T>,
Fortran::evaluate::SymbolRef>) {
- if (isVolatileSymbol(designatorNode.get()))
+ if (designatorNode.get().GetUltimate().attrs().test(
+ Fortran::semantics::Attr::VOLATILE))
isVolatile = true;
}
@@ -436,16 +432,15 @@ class HlfirDesignatorBuilder {
.Case<fir::SequenceType>([&](fir::SequenceType seqTy) -> mlir::Type {
return fir::SequenceType::get(seqTy.getShape(), newEleTy);
})
- // TODO: handle volatility for other types
- .Case<fir::PointerType, fir::HeapType, fir::BoxType, fir::ClassType>(
+ .Case<fir::PointerType, fir::HeapType, fir::ClassType>(
[&](auto t) -> mlir::Type {
using FIRT = decltype(t);
return FIRT::get(changeElementType(t.getEleTy(), newEleTy));
})
- .Case<fir::ReferenceType>([&](fir::ReferenceType refTy) -> mlir::Type {
- return fir::ReferenceType::get(
- changeElementType(refTy.getEleTy(), newEleTy),
- refTy.isVolatile());
+ .Case<fir::ReferenceType, fir::BoxType>([&](auto t) -> mlir::Type {
+ using FIRT = decltype(t);
+ return FIRT::get(changeElementType(t.getEleTy(), newEleTy),
+ t.isVolatile());
})
.Default([newEleTy](mlir::Type t) -> mlir::Type { return newEleTy; });
}
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 07c9e6a1726bf..02839a220c44e 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -709,7 +709,7 @@ static void genOutputItemList(
fir::factory::CharacterExprHelper helper{builder, loc};
if (mlir::isa<fir::BoxType>(argType)) {
mlir::Value box = fir::getBase(converter.genExprBox(loc, *expr, stmtCtx));
- outputFuncArgs.push_back(builder.createConvert(loc, argType, box));
+ outputFuncArgs.push_back(builder.createConvertWithVolatileCast(loc, argType, box));
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(itemTy)))
outputFuncArgs.push_back(getNonTbpDefinedIoTableAddr(converter));
} else if (helper.isCharacterScalar(itemTy)) {
@@ -719,9 +719,9 @@ static void genOutputItemList(
if (!exv.getCharBox())
llvm::report_fatal_error(
"internal error: scalar character not in CharBox");
- outputFuncArgs.push_back(builder.createConvert(
+ outputFuncArgs.push_back(builder.createConvertWithVolatileCast(
loc, outputFunc.getFunctionType().getInput(1), fir::getBase(exv)));
- outputFuncArgs.push_back(builder.createConvert(
+ outputFuncArgs.push_back(builder.createConvertWithVolatileCast(
loc, outputFunc.getFunctionType().getInput(2), fir::getLen(exv)));
} else {
fir::ExtendedValue itemBox = converter.genExprValue(loc, expr, stmtCtx);
@@ -822,6 +822,17 @@ createIoRuntimeCallForItem(Fortran::lower::AbstractConverter &converter,
} else {
mlir::Value itemAddr = fir::getBase(item);
mlir::Type itemTy = fir::unwrapPassByRefType(itemAddr.getType());
+
+ // Handle conversion between volatile and non-volatile reference types
+ // Need to explicitly cast when volatility qualification differs
+ bool srcIsVolatile = fir::isa_volatile_type(itemAddr.getType());
+ bool dstIsVolatile = fir::isa_volatile_type(argType);
+
+ if (srcIsVolatile != dstIsVolatile) {
+ // Create an explicit conversion to handle the volatility difference
+ itemAddr = builder.create<fir::VolatileCastOp>(loc, argType, itemAddr);
+ }
+
inputFuncArgs.push_back(builder.createConvert(loc, argType, itemAddr));
fir::factory::CharacterExprHelper charHelper{builder, loc};
if (charHelper.isCharacterScalar(itemTy)) {
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index 7fc30ca125a87..a180bb35a8475 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -577,6 +577,16 @@ mlir::Value fir::FirOpBuilder::convertWithSemantics(
return createConvert(loc, toTy, val);
}
+mlir::Value fir::FirOpBuilder::createConvertWithVolatileCast(mlir::Location loc,
+ mlir::Type toTy,
+ mlir::Value val) {
+ if (fir::isa_volatile_type(val.getType()) != fir::isa_volatile_type(toTy)) {
+ mlir::Type volatileAdjustedType = fir::updateTypeWithVolatility(val.getType(), fir::isa_volatile_type(toTy));
+ val = create<fir::VolatileCastOp>(loc, volatileAdjustedType, val);
+ }
+ return createConvert(loc, toTy, val);
+}
+
mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder,
mlir::Location loc, mlir::Type toTy,
mlir::Value val) {
@@ -739,17 +749,18 @@ mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
<< itemAddr.getType();
llvm_unreachable("not a memory reference type");
}
+ const bool isVolatile = fir::isa_volatile_type(itemAddr.getType());
mlir::Type boxTy;
mlir::Value tdesc;
// Avoid to wrap a box/class with box/class.
if (mlir::isa<fir::BaseBoxType>(elementType)) {
boxTy = elementType;
} else {
- boxTy = fir::BoxType::get(elementType);
+ boxTy = fir::BoxType::get(elementType, isVolatile);
if (isPolymorphic) {
elementType = fir::updateTypeForUnlimitedPolymorphic(elementType);
if (isAssumedType)
- boxTy = fir::BoxType::get(elementType);
+ boxTy = fir::BoxType::get(elementType, isVolatile);
else
boxTy = fir::ClassType::get(elementType);
}
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index a2b42566053f7..41316e7baa0b5 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1357,8 +1357,6 @@ llvm::LogicalResult fir::VolatileCastOp::verify() {
mlir::isa<fir::ReferenceType>(toType));
bool sameElementType = fir::dyn_cast_ptrOrBoxEleTy(fromType) ==
fir::dyn_cast_ptrOrBoxEleTy(toType);
- llvm::dbgs() << fromType << " / " << toType << "\n"
- << sameBaseType << " " << sameElementType << "\n";
if (fromType == toType ||
fir::isa_volatile_type(fromType) == fir::isa_volatile_type(toType) ||
!sameBaseType || !sameElementType)
@@ -1367,6 +1365,12 @@ llvm::LogicalResult fir::VolatileCastOp::verify() {
return mlir::success();
}
+mlir::OpFoldResult fir::VolatileCastOp::fold(FoldAdaptor adaptor) {
+ if (getValue().getType() == getType())
+ return getValue();
+ return {};
+}
+
//===----------------------------------------------------------------------===//
// ConvertOp
//===----------------------------------------------------------------------===//
@@ -1838,9 +1842,9 @@ llvm::LogicalResult fir::EmboxOp::verify() {
return emitOpError("slice must not be provided for a scalar");
if (getSourceBox() && !mlir::isa<fir::ClassType>(getResult().getType()))
return emitOpError("source_box must be used with fir.class result type");
- if (fir::isa_volatile_type(getMemref().getType()) !=
- fir::isa_volatile_type(getResult().getType()))
- return emitOpError("input and output types must have the same volatility");
+ // if (fir::isa_volatile_type(getMemref().getType()) !=
+ // fir::isa_volatile_type(getResult().getType()))
+ // return emitOpError("input and output types must have the same volatility");
return mlir::success();
}
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 5e1fbe71e54c5..84236c15039b6 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1319,6 +1319,19 @@ bool fir::hasAbstractResult(mlir::FunctionType ty) {
resultType);
}
+mlir::Type fir::updateTypeWithVolatility(mlir::Type type, bool isVolatile) {
+ if (fir::isa_volatile_type(type) == isVolatile)
+ return type;
+ return mlir::TypeSwitch<mlir::Type, mlir::Type>(type)
+ .Case<fir::BoxType, fir::ReferenceType>([&](auto ty) -> mlir::Type {
+ using TYPE = decltype(ty);
+ return TYPE::get(ty.getEleTy(), isVolatile);
+ })
+ .Default([&](mlir::Type t) -> mlir::Type {
+ return t;
+ });
+}
+
/// Convert llvm::Type::TypeID to mlir::Type. \p kind is provided for error
/// messages only.
mlir::Type fir::fromRealTypeID(mlir::MLIRContext *context,
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index d6f3996f668f7..61757b7aca873 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -61,12 +61,7 @@ getIntrinsicEffects(mlir::Operation *self,
// }
for (mlir::OpOperand &operand : self->getOpOperands()) {
mlir::Type opTy = operand.get().getType();
- if (fir::isa_volatile_type(opTy)) {
- effects.emplace_back(mlir::MemoryEffects::Read::get(), &operand,
- mlir::SideEffects::DefaultResource::get());
- effects.emplace_back(mlir::MemoryEffects::Write::get(), &operand,
- mlir::SideEffects::DefaultResource::get());
- }
+ fir::addVolatileMemoryEffects({opTy}, effects);
if (fir::isa_ref_type(opTy) || fir::isa_box_type(opTy))
effects.emplace_back(mlir::MemoryEffects::Read::get(), &operand,
mlir::SideEffects::DefaultResource::get());
@@ -203,7 +198,7 @@ mlir::Type hlfir::DeclareOp::getHLFIRVariableType(mlir::Type inputType,
bool hasDynamicLengthParams = fir::characterWithDynamicLen(eleType) ||
fir::isRecordWithTypeParameters(eleType);
if (hasExplicitLowerBounds || hasDynamicExtents || hasDynamicLengthParams)
- return fir::BoxType::get(type);
+ return fir::BoxType::get(type, fir::isa_volatile_type(inputType));
return inputType;
}
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index e804306731636..c5bd8b30d5214 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -89,12 +89,7 @@ class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
return fir::getBase(builder.createBox(loc, rhsExv));
};
- if (fir::isa_volatile_type(lhs.getType()) ||
- fir::isa_volatile_type(rhs.getType())) {
- fir::factory::genScalarAssignment(builder, loc, lhsExv, rhsExv,
- /*needFinalization=*/false,
- assignOp.isTemporaryLHS());
- } else if (assignOp.isAllocatableAssignment()) {
+ if (assignOp.isAllocatableAssignment()) {
// Whole allocatable assignment: use the runtime to deal with the
// reallocation.
mlir::Value from = emboxRHS(rhsExv);
diff --git a/flang/test/Fir/invalid-types.fir b/flang/test/Fir/invalid-types.fir
index 5240293741ae3..73babb16cb62a 100644
--- a/flang/test/Fir/invalid-types.fir
+++ b/flang/test/Fir/invalid-types.fir
@@ -157,8 +157,14 @@ func.func private @oth3() -> !fir.vector<10:>
// expected-error at +1 {{invalid element type}}
func.func private @upe() -> !fir.class<!fir.box<i32>>
+// -----
+
// TODO: why are source locations lost on the error message?
// the error message is printed but without a proper source location.
-// -- ---
-// expected-error skipme @+1 {{invalid element type}}
+// expected-error <skipme> @+1 {{invalid element type}}
// func.func private @upe() -> !fir.box<!fir.class<none>>
+
+// -----
+
+// expected-error at +1 {{invalid element type}}
+func.func private @upe() -> !fir.class<!fir.box<i32>>
diff --git a/flang/test/Lower/volatile.f90 b/flang/test/Lower/volatile.f90
index 9d8e93259c60e..f041c6c0525d2 100644
--- a/flang/test/Lower/volatile.f90
+++ b/flang/test/Lower/volatile.f90
@@ -1,11 +1,52 @@
! 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<!fir.logical<4>, volatile>
-! CHECK: hlfir.assign %{{.+}} to %{{.+}} : !fir.logical<4>, !fir.ref<!fir.logical<4>, volatile>
+program p
+ integer,volatile::i,arr(10)
+ i=0
+ arr=1
+ ! casting from volatile ref to non-volatile ref should be okay here
+ call not_declared_volatile_in_this_scope(i)
+ call not_declared_volatile_in_this_scope(arr)
+ call declared_volatile_in_this_scope(arr,10)
+ print*,arr,i
+contains
+ elemental subroutine not_declared_volatile_in_this_scope(v)
+ integer,intent(inout)::v
+ v=1
+ end subroutine
+ subroutine declared_volatile_in_this_scope(v,n)
+ integer,intent(in)::n
+ integer,volatile,intent(inout)::v(n)
+ v=1
+ end subroutine
+end program
+
+! CHECK-LABEL: _QQmain
+! CHECK: %[[VAL_10:.*]] = fir.volatile_cast %{{.+}} : (!fir.ref<!fir.array<10xi32>>) -> !fir.ref<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_11:.*]]:2 = hlfir.declare {{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xi32>, volatile>, !fir.ref<!fir.array<10xi32>, volatile>)
+! CHECK: %[[VAL_12:.*]] = fir.alloca i32
+! CHECK: %[[VAL_13:.*]] = fir.volatile_cast %[[VAL_12]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare {{.+}} : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: hlfir.assign %{{.+}} to %[[VAL_14]]#0 : i32, !fir.ref<i32, volatile>
+! CHECK: hlfir.assign %{{.+}} to %[[VAL_11]]#0 : i32, !fir.ref<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_15:.*]] = fir.volatile_cast %[[VAL_14]]#0 : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+! CHECK: fir.call @_QFPnot_declared_volatile_in_this_scope(%[[VAL_15]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<i32>) -> ()
+! CHECK: %[[VAL_19:.*]] = hlfir.designate %{{.+}} (%{{.+}}) : (!fir.ref<!fir.array<10xi32>, volatile>, index) -> !fir.ref<i32, volatile>
+! CHECK: %[[VAL_20:.*]] = fir.volatile_cast %[[VAL_19]] : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+! CHECK: fir.call @_QFPnot_declared_volatile_in_this_scope(%[[VAL_20]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<i32>) -> ()
+! CHECK: %[[VAL_23:.*]] = fir.volatile_cast %[[VAL_11]]#0 : (!fir.ref<!fir.array<10xi32>, volatile>) -> !fir.ref<!fir.array<10xi32>>
+! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_23]] : (!fir.ref<!fir.array<10xi32>>) -> !fir.ref<!fir.array<?xi32>>
+! CHECK: %[[VAL_25:.*]]:3 = hlfir.associate %{{.+}} {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
+! CHECK: fir.call @_QFPdeclared_volatile_in_this_scope(%[[VAL_24]], %[[VAL_25]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>, !fir.ref<i32>) -> ()
+! CHECK: hlfir.end_associate %[[VAL_25]]#1, %[[VAL_25]]#2 : !fir.ref<i32>, i1
+! CHECK: %[[VAL_26:.*]] = fir.address_of(@_QQclX6951b66b308fd310127f64e03dcd1051) : !fir.ref<!fir.char<1,78>>
+! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (!fir.ref<!fir.char<1,78>>) -> !fir.ref<i8>
+! CHECK: %[[VAL_28:.*]] = fir.call @_FortranAioBeginExternalListOutput(
+! CHECK: %[[VAL_29:.*]] = fir.embox %{{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_30:.*]] = fir.volatile_cast %[[VAL_29]] : (!fir.box<!fir.array<10xi32>, volatile>) -> !fir.box<!fir.array<10xi32>>
+! CHECK: %[[VAL_31:.*]] = fir.convert %[[VAL_30]] : (!fir.box<!fir.array<10xi32>>) -> !fir.box<none>
+! CHECK: %[[VAL_32:.*]] = fir.call @_FortranAioOutputDescriptor(
+! CHECK: %[[VAL_33:.*]] = fir.load %[[VAL_14]]#0 : !fir.ref<i32, volatile>
+! CHECK: %[[VAL_34:.*]] = fir.call @_FortranAioOutputInteger32(
+! CHECK: %[[VAL_35:.*]] = fir.call @_FortranAioEndIoStatement(
+! CHECK: return
>From 3ba99950321af4ba231e5fe727f4d163ef3c7b34 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Mon, 31 Mar 2025 13:53:58 -0700
Subject: [PATCH 05/15] format
---
flang/lib/Lower/IO.cpp | 9 +++++----
flang/lib/Optimizer/Builder/FIRBuilder.cpp | 3 ++-
flang/lib/Optimizer/Dialect/FIROps.cpp | 3 ++-
flang/lib/Optimizer/Dialect/FIRType.cpp | 4 +---
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 02839a220c44e..46abf18c42d5d 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -709,7 +709,8 @@ static void genOutputItemList(
fir::factory::CharacterExprHelper helper{builder, loc};
if (mlir::isa<fir::BoxType>(argType)) {
mlir::Value box = fir::getBase(converter.genExprBox(loc, *expr, stmtCtx));
- outputFuncArgs.push_back(builder.createConvertWithVolatileCast(loc, argType, box));
+ outputFuncArgs.push_back(
+ builder.createConvertWithVolatileCast(loc, argType, box));
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(itemTy)))
outputFuncArgs.push_back(getNonTbpDefinedIoTableAddr(converter));
} else if (helper.isCharacterScalar(itemTy)) {
@@ -822,17 +823,17 @@ createIoRuntimeCallForItem(Fortran::lower::AbstractConverter &converter,
} else {
mlir::Value itemAddr = fir::getBase(item);
mlir::Type itemTy = fir::unwrapPassByRefType(itemAddr.getType());
-
+
// Handle conversion between volatile and non-volatile reference types
// Need to explicitly cast when volatility qualification differs
bool srcIsVolatile = fir::isa_volatile_type(itemAddr.getType());
bool dstIsVolatile = fir::isa_volatile_type(argType);
-
+
if (srcIsVolatile != dstIsVolatile) {
// Create an explicit conversion to handle the volatility difference
itemAddr = builder.create<fir::VolatileCastOp>(loc, argType, itemAddr);
}
-
+
inputFuncArgs.push_back(builder.createConvert(loc, argType, itemAddr));
fir::factory::CharacterExprHelper charHelper{builder, loc};
if (charHelper.isCharacterScalar(itemTy)) {
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index a180bb35a8475..1bb1dbbad7b13 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -581,7 +581,8 @@ mlir::Value fir::FirOpBuilder::createConvertWithVolatileCast(mlir::Location loc,
mlir::Type toTy,
mlir::Value val) {
if (fir::isa_volatile_type(val.getType()) != fir::isa_volatile_type(toTy)) {
- mlir::Type volatileAdjustedType = fir::updateTypeWithVolatility(val.getType(), fir::isa_volatile_type(toTy));
+ mlir::Type volatileAdjustedType = fir::updateTypeWithVolatility(
+ val.getType(), fir::isa_volatile_type(toTy));
val = create<fir::VolatileCastOp>(loc, volatileAdjustedType, val);
}
return createConvert(loc, toTy, val);
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 41316e7baa0b5..2dfeebce9df0e 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1844,7 +1844,8 @@ llvm::LogicalResult fir::EmboxOp::verify() {
return emitOpError("source_box must be used with fir.class result type");
// if (fir::isa_volatile_type(getMemref().getType()) !=
// fir::isa_volatile_type(getResult().getType()))
- // return emitOpError("input and output types must have the same volatility");
+ // return emitOpError("input and output types must have the same
+ // volatility");
return mlir::success();
}
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 84236c15039b6..2c9daca68fe64 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1327,9 +1327,7 @@ mlir::Type fir::updateTypeWithVolatility(mlir::Type type, bool isVolatile) {
using TYPE = decltype(ty);
return TYPE::get(ty.getEleTy(), isVolatile);
})
- .Default([&](mlir::Type t) -> mlir::Type {
- return t;
- });
+ .Default([&](mlir::Type t) -> mlir::Type { return t; });
}
/// Convert llvm::Type::TypeID to mlir::Type. \p kind is provided for error
>From 8da80ff3de59bf0a36adffde02ea44295b8daaea Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Mon, 31 Mar 2025 14:36:08 -0700
Subject: [PATCH 06/15] generalize test
---
flang/test/Lower/volatile.f90 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/test/Lower/volatile.f90 b/flang/test/Lower/volatile.f90
index f041c6c0525d2..07f2b4bd03fd0 100644
--- a/flang/test/Lower/volatile.f90
+++ b/flang/test/Lower/volatile.f90
@@ -39,7 +39,7 @@ subroutine declared_volatile_in_this_scope(v,n)
! CHECK: %[[VAL_25:.*]]:3 = hlfir.associate %{{.+}} {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
! CHECK: fir.call @_QFPdeclared_volatile_in_this_scope(%[[VAL_24]], %[[VAL_25]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>, !fir.ref<i32>) -> ()
! CHECK: hlfir.end_associate %[[VAL_25]]#1, %[[VAL_25]]#2 : !fir.ref<i32>, i1
-! CHECK: %[[VAL_26:.*]] = fir.address_of(@_QQclX6951b66b308fd310127f64e03dcd1051) : !fir.ref<!fir.char<1,78>>
+! CHECK: %[[VAL_26:.*]] = fir.address_of(
! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (!fir.ref<!fir.char<1,78>>) -> !fir.ref<i8>
! CHECK: %[[VAL_28:.*]] = fir.call @_FortranAioBeginExternalListOutput(
! CHECK: %[[VAL_29:.*]] = fir.embox %{{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>, volatile>
>From a9afcb0c59c8329556742c970a46cef042c5df5f Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Mon, 31 Mar 2025 15:01:53 -0700
Subject: [PATCH 07/15] fix test on windows
---
flang/test/Lower/volatile.f90 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/test/Lower/volatile.f90 b/flang/test/Lower/volatile.f90
index 07f2b4bd03fd0..72d2c387c7b91 100644
--- a/flang/test/Lower/volatile.f90
+++ b/flang/test/Lower/volatile.f90
@@ -40,7 +40,7 @@ subroutine declared_volatile_in_this_scope(v,n)
! CHECK: fir.call @_QFPdeclared_volatile_in_this_scope(%[[VAL_24]], %[[VAL_25]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>, !fir.ref<i32>) -> ()
! CHECK: hlfir.end_associate %[[VAL_25]]#1, %[[VAL_25]]#2 : !fir.ref<i32>, i1
! CHECK: %[[VAL_26:.*]] = fir.address_of(
-! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (!fir.ref<!fir.char<1,78>>) -> !fir.ref<i8>
+! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]]
! CHECK: %[[VAL_28:.*]] = fir.call @_FortranAioBeginExternalListOutput(
! CHECK: %[[VAL_29:.*]] = fir.embox %{{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>, volatile>
! CHECK: %[[VAL_30:.*]] = fir.volatile_cast %[[VAL_29]] : (!fir.box<!fir.array<10xi32>, volatile>) -> !fir.box<!fir.array<10xi32>>
>From c9c0f14974c5d06ea61de1eea7fbe006129b314f Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Mon, 31 Mar 2025 19:13:59 -0700
Subject: [PATCH 08/15] Propagate locations in box/ref type parsers
---
flang/lib/Optimizer/Dialect/FIROps.cpp | 4 +---
flang/lib/Optimizer/Dialect/FIRType.cpp | 12 +++++++-----
flang/test/Fir/invalid-types.fir | 6 ++----
3 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 2dfeebce9df0e..0dd6c698c117c 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1357,9 +1357,7 @@ llvm::LogicalResult fir::VolatileCastOp::verify() {
mlir::isa<fir::ReferenceType>(toType));
bool sameElementType = fir::dyn_cast_ptrOrBoxEleTy(fromType) ==
fir::dyn_cast_ptrOrBoxEleTy(toType);
- if (fromType == toType ||
- fir::isa_volatile_type(fromType) == fir::isa_volatile_type(toType) ||
- !sameBaseType || !sameElementType)
+ if (!sameBaseType || !sameElementType)
return emitOpError("types must be identical except for volatility ")
<< fromType << " / " << toType;
return mlir::success();
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 2c9daca68fe64..591e85cc23820 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -136,7 +136,7 @@ mlir::Type fir::parseFirType(FIROpsDialect *dialect,
auto parseResult = generatedTypeParser(parser, &typeTag, genType);
if (parseResult.has_value())
return genType;
- parser.emitError(parser.getNameLoc(), "unknown fir type: ") << typeTag;
+ parser.emitError(parser.getCurrentLocation(), "unknown fir type: ") << typeTag;
return {};
}
@@ -770,12 +770,13 @@ fir::BoxType::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
mlir::Type fir::BoxType::parse(mlir::AsmParser &parser) {
mlir::Type ty;
- bool isVolatile;
+ bool isVolatile = false;
+ auto loc = parser.getCurrentLocation();
if (parser.parseLess() || parser.parseType(ty) ||
parseOptionalCommaAndKeyword(parser, getVolatileKeyword(), isVolatile) ||
parser.parseGreater())
return {};
- return get(ty, isVolatile);
+ return parser.getChecked<BoxType>(loc, parser.getContext(), ty, isVolatile);
}
void fir::BoxType::print(mlir::AsmPrinter &printer) const {
@@ -1098,12 +1099,13 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) {
// `ref` `<` type (`, volatile` $volatile^)? `>`
mlir::Type fir::ReferenceType::parse(mlir::AsmParser &parser) {
mlir::Type ty;
- bool isVolatile;
+ bool isVolatile = false;
+ auto loc = parser.getCurrentLocation();
if (parser.parseLess() || parser.parseType(ty) ||
parseOptionalCommaAndKeyword(parser, getVolatileKeyword(), isVolatile) ||
parser.parseGreater())
return {};
- return get(ty, isVolatile);
+ return parser.getChecked<ReferenceType>(loc, parser.getContext(), ty, isVolatile);
}
void fir::ReferenceType::print(mlir::AsmPrinter &printer) const {
diff --git a/flang/test/Fir/invalid-types.fir b/flang/test/Fir/invalid-types.fir
index 73babb16cb62a..0ba085f770c0e 100644
--- a/flang/test/Fir/invalid-types.fir
+++ b/flang/test/Fir/invalid-types.fir
@@ -159,10 +159,8 @@ func.func private @upe() -> !fir.class<!fir.box<i32>>
// -----
-// TODO: why are source locations lost on the error message?
-// the error message is printed but without a proper source location.
-// expected-error <skipme> @+1 {{invalid element type}}
-// func.func private @upe() -> !fir.box<!fir.class<none>>
+// expected-error at +1 {{invalid element type}}
+func.func private @upe() -> !fir.box<!fir.class<none>>
// -----
>From b1f87d0d3522576c67ad87261b27e98754cb8c61 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Mon, 31 Mar 2025 19:21:59 -0700
Subject: [PATCH 09/15] format
---
flang/lib/Optimizer/Dialect/FIRType.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 591e85cc23820..32eacf1352b98 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -136,7 +136,8 @@ mlir::Type fir::parseFirType(FIROpsDialect *dialect,
auto parseResult = generatedTypeParser(parser, &typeTag, genType);
if (parseResult.has_value())
return genType;
- parser.emitError(parser.getCurrentLocation(), "unknown fir type: ") << typeTag;
+ parser.emitError(parser.getCurrentLocation(), "unknown fir type: ")
+ << typeTag;
return {};
}
@@ -1105,7 +1106,8 @@ mlir::Type fir::ReferenceType::parse(mlir::AsmParser &parser) {
parseOptionalCommaAndKeyword(parser, getVolatileKeyword(), isVolatile) ||
parser.parseGreater())
return {};
- return parser.getChecked<ReferenceType>(loc, parser.getContext(), ty, isVolatile);
+ return parser.getChecked<ReferenceType>(loc, parser.getContext(), ty,
+ isVolatile);
}
void fir::ReferenceType::print(mlir::AsmPrinter &printer) const {
>From 886781fa6ff28b505a0f332340a9108ed07d3b20 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 1 Apr 2025 06:28:39 -0700
Subject: [PATCH 10/15] Handle volatile box types
---
flang/include/flang/Optimizer/Dialect/FIRTypes.td | 2 +-
flang/lib/Optimizer/Dialect/FIROps.cpp | 8 ++++----
flang/lib/Optimizer/Dialect/FIRType.cpp | 14 ++++++++------
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
index 35fd00f6d3935..00dc66724a9c5 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
@@ -90,7 +90,7 @@ def fir_BoxType : FIR_Type<"Box", "box", [], "BaseBoxType"> {
let extraClassDeclaration = [{
mlir::Type getElementType() const { return getEleTy(); }
- bool isVolatile() const { return (bool)getIsVolatile(); }
+ bool isVolatile() const { return getIsVolatile(); }
}];
let genVerifyDecl = 1;
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 0dd6c698c117c..bdc7ebe0c1e39 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1840,10 +1840,10 @@ llvm::LogicalResult fir::EmboxOp::verify() {
return emitOpError("slice must not be provided for a scalar");
if (getSourceBox() && !mlir::isa<fir::ClassType>(getResult().getType()))
return emitOpError("source_box must be used with fir.class result type");
- // if (fir::isa_volatile_type(getMemref().getType()) !=
- // fir::isa_volatile_type(getResult().getType()))
- // return emitOpError("input and output types must have the same
- // volatility");
+ if (fir::isa_volatile_type(getMemref().getType()) !=
+ fir::isa_volatile_type(getResult().getType()))
+ return emitOpError("cannot convert between volatile and non-volatile "
+ "types, use fir.volatile_cast instead");
return mlir::success();
}
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 32eacf1352b98..b06a6826d3341 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -681,8 +681,9 @@ mlir::Type changeElementType(mlir::Type type, mlir::Type newElementType,
mlir::Type newInnerType =
changeElementType(t.getEleTy(), newElementType, false);
if (turnBoxIntoClass)
- return fir::ClassType::get(newInnerType);
- return fir::BoxType::get(newInnerType);
+ return fir::ClassType::get(
+ newInnerType); // TODO: volatility on class types
+ return fir::BoxType::get(newInnerType, t.isVolatile());
})
.Default([&](mlir::Type t) -> mlir::Type {
assert((fir::isa_trivial(t) || llvm::isa<fir::RecordType>(t) ||
@@ -1381,11 +1382,12 @@ changeTypeShape(mlir::Type type,
return fir::SequenceType::get(*newShape, seqTy.getEleTy());
return seqTy.getEleTy();
})
- .Case<fir::ReferenceType>([&](fir::ReferenceType rt) -> mlir::Type {
- return fir::ReferenceType::get(changeTypeShape(rt.getEleTy(), newShape),
- rt.isVolatile());
+ .Case<fir::ReferenceType, fir::BoxType>([&](auto t) -> mlir::Type {
+ using FIRT = decltype(t);
+ return FIRT::get(changeTypeShape(t.getEleTy(), newShape),
+ t.isVolatile());
})
- .Case<fir::PointerType, fir::HeapType, fir::BoxType, fir::ClassType>(
+ .Case<fir::PointerType, fir::HeapType, fir::ClassType>(
[&](auto t) -> mlir::Type {
using FIRT = decltype(t);
return FIRT::get(changeTypeShape(t.getEleTy(), newShape));
>From 5cfb9e4949ff1b922816781a6f34d1eb4c9a42cd Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 1 Apr 2025 06:54:46 -0700
Subject: [PATCH 11/15] Add more invalid type checks
---
flang/test/Fir/invalid-types.fir | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/flang/test/Fir/invalid-types.fir b/flang/test/Fir/invalid-types.fir
index 0ba085f770c0e..6428df0feb3e6 100644
--- a/flang/test/Fir/invalid-types.fir
+++ b/flang/test/Fir/invalid-types.fir
@@ -104,6 +104,11 @@ func.func private @mem3() -> !fir.ref<>
// -----
+// expected-error at +1 {{expected non-function type}}
+func.func private @mem3() -> !fir.ref<, volatile>
+
+// -----
+
// expected-error at +1 {{expected ':'}}
func.func private @arr1() -> !fir.array<*>
@@ -164,5 +169,10 @@ func.func private @upe() -> !fir.box<!fir.class<none>>
// -----
+// expected-error at +1 {{invalid element type}}
+func.func private @upe() -> !fir.box<!fir.class<none>, volatile>
+
+// -----
+
// expected-error at +1 {{invalid element type}}
func.func private @upe() -> !fir.class<!fir.box<i32>>
>From 43778968e53a36ffc8dadd0fca1e4c1591dacafb Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 1 Apr 2025 07:37:53 -0700
Subject: [PATCH 12/15] Add missing box type volatile handling
---
flang/lib/Optimizer/Dialect/FIRType.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index b06a6826d3341..8ec852dc5afdc 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -136,7 +136,7 @@ mlir::Type fir::parseFirType(FIROpsDialect *dialect,
auto parseResult = generatedTypeParser(parser, &typeTag, genType);
if (parseResult.has_value())
return genType;
- parser.emitError(parser.getCurrentLocation(), "unknown fir type: ")
+ parser.emitError(parser.getNameLoc(), "unknown fir type: ")
<< typeTag;
return {};
}
@@ -680,9 +680,9 @@ mlir::Type changeElementType(mlir::Type type, mlir::Type newElementType,
.Case<fir::BoxType>([&](fir::BoxType t) -> mlir::Type {
mlir::Type newInnerType =
changeElementType(t.getEleTy(), newElementType, false);
+ // TODO: volatility on class types
if (turnBoxIntoClass)
- return fir::ClassType::get(
- newInnerType); // TODO: volatility on class types
+ return fir::ClassType::get(newInnerType);
return fir::BoxType::get(newInnerType, t.isVolatile());
})
.Default([&](mlir::Type t) -> mlir::Type {
>From b3c5d26c0b2f0e60c4179cd849864bafd4de9b90 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Tue, 1 Apr 2025 07:42:59 -0700
Subject: [PATCH 13/15] format
---
flang/lib/Optimizer/Dialect/FIRType.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 8ec852dc5afdc..4b93028eea5d2 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -136,8 +136,7 @@ mlir::Type fir::parseFirType(FIROpsDialect *dialect,
auto parseResult = generatedTypeParser(parser, &typeTag, genType);
if (parseResult.has_value())
return genType;
- parser.emitError(parser.getNameLoc(), "unknown fir type: ")
- << typeTag;
+ parser.emitError(parser.getNameLoc(), "unknown fir type: ") << typeTag;
return {};
}
>From fcf27ed28c379e31699e662bce9aa6348e2f5082 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Wed, 2 Apr 2025 13:45:14 -0700
Subject: [PATCH 14/15] Add missing casts in runtime argument preparation
---
flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index a93c98f223839..5440b36c0c628 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -829,14 +829,16 @@ template <int N, typename A>
void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
fir::FirOpBuilder &builder, mlir::Location loc,
mlir::FunctionType fTy, A arg) {
- result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
+ result.emplace_back(
+ builder.createConvertWithVolatileCast(loc, fTy.getInput(N), arg));
}
template <int N, typename A, typename... As>
void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
fir::FirOpBuilder &builder, mlir::Location loc,
mlir::FunctionType fTy, A arg, As... args) {
- result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
+ result.emplace_back(
+ builder.createConvertWithVolatileCast(loc, fTy.getInput(N), arg));
createArguments<N + 1>(result, builder, loc, fTy, args...);
}
} // namespace helper
>From dd0bfd1130d19339df864f3bbec9f029ba4e6e44 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Thu, 3 Apr 2025 15:24:54 -0700
Subject: [PATCH 15/15] Handle conversions in funciton returns
---
flang/lib/Lower/Bridge.cpp | 3 +-
flang/test/Lower/volatile.f90 | 132 +++++++++++++++++++++++++++++-----
2 files changed, 117 insertions(+), 18 deletions(-)
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 65edf1cea8761..c779620ad1159 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1755,7 +1755,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// tags all result variables with one of the largest types to allow
// them to share the same storage. Convert this to the actual type.
if (resultRef.getType() != resultRefType)
- resultRef = builder->createConvert(loc, resultRefType, resultRef);
+ resultRef = builder->createConvertWithVolatileCast(
+ loc, resultRefType, resultRef);
return builder->create<fir::LoadOp>(loc, resultRef);
});
genExitRoutine(false, resultVal);
diff --git a/flang/test/Lower/volatile.f90 b/flang/test/Lower/volatile.f90
index 72d2c387c7b91..9b0b341c72ab7 100644
--- a/flang/test/Lower/volatile.f90
+++ b/flang/test/Lower/volatile.f90
@@ -8,7 +8,7 @@ program p
call not_declared_volatile_in_this_scope(i)
call not_declared_volatile_in_this_scope(arr)
call declared_volatile_in_this_scope(arr,10)
- print*,arr,i
+ print*,arr,i,a(),b(),c()
contains
elemental subroutine not_declared_volatile_in_this_scope(v)
integer,intent(inout)::v
@@ -19,34 +19,132 @@ subroutine declared_volatile_in_this_scope(v,n)
integer,volatile,intent(inout)::v(n)
v=1
end subroutine
+ function a()
+ integer,volatile::a
+ a=1
+ end function
+ function b() result(r)
+ integer,volatile::r
+ r=2
+ end function
+ function c() result(r)
+ volatile::r
+ r=3
+ end function
end program
-! CHECK-LABEL: _QQmain
-! CHECK: %[[VAL_10:.*]] = fir.volatile_cast %{{.+}} : (!fir.ref<!fir.array<10xi32>>) -> !fir.ref<!fir.array<10xi32>, volatile>
-! CHECK: %[[VAL_11:.*]]:2 = hlfir.declare {{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xi32>, volatile>, !fir.ref<!fir.array<10xi32>, volatile>)
-! CHECK: %[[VAL_12:.*]] = fir.alloca i32
+! CHECK-LABEL: func.func @_QQmain
+! CHECK: %[[VAL_0:.*]] = arith.constant 0 : index
+! CHECK: %[[VAL_1:.*]] = arith.constant 11 : i32
+! CHECK: %[[VAL_2:.*]] = arith.constant 6 : i32
+! CHECK: %[[VAL_3:.*]] = arith.constant 10 : i32
+! CHECK: %[[VAL_4:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_5:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_6:.*]] = arith.constant 0 : i32
+! CHECK: %[[VAL_7:.*]] = arith.constant 10 : index
+! CHECK: %[[VAL_8:.*]] = fir.address_of(@_QFEarr) : !fir.ref<!fir.array<10xi32>>
+! CHECK: %[[VAL_9:.*]] = fir.shape %[[VAL_7]] : (index) -> !fir.shape<1>
+! CHECK: %[[VAL_10:.*]] = fir.volatile_cast %[[VAL_8]] : (!fir.ref<!fir.array<10xi32>>) -> !fir.ref<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_10]](%[[VAL_9]]) {{.*}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xi32>, volatile>, !fir.ref<!fir.array<10xi32>, volatile>)
+! CHECK: %[[VAL_12:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"}
! CHECK: %[[VAL_13:.*]] = fir.volatile_cast %[[VAL_12]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
-! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare {{.+}} : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
-! CHECK: hlfir.assign %{{.+}} to %[[VAL_14]]#0 : i32, !fir.ref<i32, volatile>
-! CHECK: hlfir.assign %{{.+}} to %[[VAL_11]]#0 : i32, !fir.ref<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare %[[VAL_13]] {{.*}} : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: hlfir.assign %[[VAL_6]] to %[[VAL_14]]#0 : i32, !fir.ref<i32, volatile>
+! CHECK: hlfir.assign %[[VAL_5]] to %[[VAL_11]]#0 : i32, !fir.ref<!fir.array<10xi32>, volatile>
! CHECK: %[[VAL_15:.*]] = fir.volatile_cast %[[VAL_14]]#0 : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
! CHECK: fir.call @_QFPnot_declared_volatile_in_this_scope(%[[VAL_15]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<i32>) -> ()
-! CHECK: %[[VAL_19:.*]] = hlfir.designate %{{.+}} (%{{.+}}) : (!fir.ref<!fir.array<10xi32>, volatile>, index) -> !fir.ref<i32, volatile>
+! CHECK: cf.br ^bb1(%[[VAL_4]], %[[VAL_7]] : index, index)
+! CHECK: ^bb1(%[[VAL_16:.*]]: index, %[[VAL_17:.*]]: index):
+! CHECK: %[[VAL_18:.*]] = arith.cmpi sgt, %[[VAL_17]], %[[VAL_0]] : index
+! CHECK: cf.cond_br %[[VAL_18]], ^bb2, ^bb3
+! CHECK: ^bb2:
+! CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_11]]#0 (%[[VAL_16]]) : (!fir.ref<!fir.array<10xi32>, volatile>, index) -> !fir.ref<i32, volatile>
! CHECK: %[[VAL_20:.*]] = fir.volatile_cast %[[VAL_19]] : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
! CHECK: fir.call @_QFPnot_declared_volatile_in_this_scope(%[[VAL_20]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<i32>) -> ()
+! CHECK: %[[VAL_21:.*]] = arith.addi %[[VAL_16]], %[[VAL_4]] overflow<nsw> : index
+! CHECK: %[[VAL_22:.*]] = arith.subi %[[VAL_17]], %[[VAL_4]] : index
+! CHECK: cf.br ^bb1(%[[VAL_21]], %[[VAL_22]] : index, index)
+! CHECK: ^bb3:
! CHECK: %[[VAL_23:.*]] = fir.volatile_cast %[[VAL_11]]#0 : (!fir.ref<!fir.array<10xi32>, volatile>) -> !fir.ref<!fir.array<10xi32>>
! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_23]] : (!fir.ref<!fir.array<10xi32>>) -> !fir.ref<!fir.array<?xi32>>
-! CHECK: %[[VAL_25:.*]]:3 = hlfir.associate %{{.+}} {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
+! CHECK: %[[VAL_25:.*]]:3 = hlfir.associate %[[VAL_3]] {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
! CHECK: fir.call @_QFPdeclared_volatile_in_this_scope(%[[VAL_24]], %[[VAL_25]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>, !fir.ref<i32>) -> ()
! CHECK: hlfir.end_associate %[[VAL_25]]#1, %[[VAL_25]]#2 : !fir.ref<i32>, i1
-! CHECK: %[[VAL_26:.*]] = fir.address_of(
-! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]]
-! CHECK: %[[VAL_28:.*]] = fir.call @_FortranAioBeginExternalListOutput(
-! CHECK: %[[VAL_29:.*]] = fir.embox %{{.+}} : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>, volatile>
+! CHECK: %[[VAL_26:.*]] = fir.address_of
+! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (!fir.ref<!fir.char<1,{{.+}}>>) -> !fir.ref<i8>
+! CHECK: %[[VAL_28:.*]] = fir.call @_FortranAioBeginExternalListOutput(%[[VAL_2]], %[[VAL_27]], %[[VAL_1]]) fastmath<contract>
+! CHECK: %[[VAL_29:.*]] = fir.embox %[[VAL_11]]#0(%[[VAL_9]]) : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>, volatile>
! CHECK: %[[VAL_30:.*]] = fir.volatile_cast %[[VAL_29]] : (!fir.box<!fir.array<10xi32>, volatile>) -> !fir.box<!fir.array<10xi32>>
! CHECK: %[[VAL_31:.*]] = fir.convert %[[VAL_30]] : (!fir.box<!fir.array<10xi32>>) -> !fir.box<none>
-! CHECK: %[[VAL_32:.*]] = fir.call @_FortranAioOutputDescriptor(
+! CHECK: %[[VAL_32:.*]] = fir.call @_FortranAioOutputDescriptor(%[[VAL_28]], %[[VAL_31]])
! CHECK: %[[VAL_33:.*]] = fir.load %[[VAL_14]]#0 : !fir.ref<i32, volatile>
-! CHECK: %[[VAL_34:.*]] = fir.call @_FortranAioOutputInteger32(
-! CHECK: %[[VAL_35:.*]] = fir.call @_FortranAioEndIoStatement(
+! CHECK: %[[VAL_34:.*]] = fir.call @_FortranAioOutputInteger32(%[[VAL_28]], %[[VAL_33]])
+! CHECK: %[[VAL_35:.*]] = fir.call @_QFPa() fastmath<contract> : () -> i32
+! CHECK: %[[VAL_36:.*]] = fir.call @_FortranAioOutputInteger32(%[[VAL_28]], %[[VAL_35]])
+! CHECK: %[[VAL_37:.*]] = fir.call @_QFPb() fastmath<contract> : () -> i32
+! CHECK: %[[VAL_38:.*]] = fir.call @_FortranAioOutputInteger32(%[[VAL_28]], %[[VAL_37]])
+! CHECK: %[[VAL_39:.*]] = fir.call @_QFPc() fastmath<contract> : () -> f32
+! CHECK: %[[VAL_40:.*]] = fir.call @_FortranAioOutputReal32(%[[VAL_28]], %[[VAL_39]])
+! CHECK: %[[VAL_41:.*]] = fir.call @_FortranAioEndIoStatement(%[[VAL_28]])
! CHECK: return
+! CHECK: }
+
+! CHECK-LABEL: func.func private @_QFPnot_declared_volatile_in_this_scope(
+! CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "v"}) attributes {{.+}} {
+! CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_2:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[VAL_2]] {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: hlfir.assign %[[VAL_1]] to %[[VAL_3]]#0 : i32, !fir.ref<i32>
+! CHECK: return
+! CHECK: }
+
+! CHECK-LABEL: func.func private @_QFPdeclared_volatile_in_this_scope(
+! CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "v", fir.volatile},
+! CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n"}) attributes {{.+}} {
+! CHECK: %[[VAL_2:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_3:.*]] = arith.constant 0 : index
+! CHECK: %[[VAL_4:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %[[VAL_4]] {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i32>
+! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (i32) -> index
+! CHECK: %[[VAL_8:.*]] = arith.cmpi sgt, %[[VAL_7]], %[[VAL_3]] : index
+! CHECK: %[[VAL_9:.*]] = arith.select %[[VAL_8]], %[[VAL_7]], %[[VAL_3]] : index
+! CHECK: %[[VAL_10:.*]] = fir.shape %[[VAL_9]] : (index) -> !fir.shape<1>
+! CHECK: %[[VAL_11:.*]] = fir.volatile_cast %[[VAL_0]] : (!fir.ref<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>, volatile>
+! CHECK: %[[VAL_12:.*]]:2 = hlfir.declare %[[VAL_11]](%[[VAL_10]]) dummy_scope %[[VAL_4]] {{.*}} : (!fir.ref<!fir.array<?xi32>, volatile>, !fir.shape<1>, !fir.dscope) -> (!fir.box<!fir.array<?xi32>, volatile>, !fir.ref<!fir.array<?xi32>, volatile>)
+! CHECK: hlfir.assign %[[VAL_2]] to %[[VAL_12]]#0 : i32, !fir.box<!fir.array<?xi32>, volatile>
+! CHECK: return
+! CHECK: }
+
+! CHECK-LABEL: func.func private @_QFPa() -> i32 attributes {{.+}} {
+! CHECK: %[[VAL_0:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_1:.*]] = fir.alloca i32 {bindc_name = "a", uniq_name = "_QFFaEa"}
+! CHECK: %[[VAL_2:.*]] = fir.volatile_cast %[[VAL_1]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {{.*}} : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: hlfir.assign %[[VAL_0]] to %[[VAL_3]]#0 : i32, !fir.ref<i32, volatile>
+! CHECK: %[[VAL_4:.*]] = fir.volatile_cast %[[VAL_3]]#0 : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_4]] : !fir.ref<i32>
+! CHECK: return %[[VAL_5]] : i32
+! CHECK: }
+
+! CHECK-LABEL: func.func private @_QFPb() -> i32 attributes {{.+}} {
+! CHECK: %[[VAL_0:.*]] = arith.constant 2 : i32
+! CHECK: %[[VAL_1:.*]] = fir.alloca i32 {bindc_name = "r", uniq_name = "_QFFbEr"}
+! CHECK: %[[VAL_2:.*]] = fir.volatile_cast %[[VAL_1]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {{.*}} : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: hlfir.assign %[[VAL_0]] to %[[VAL_3]]#0 : i32, !fir.ref<i32, volatile>
+! CHECK: %[[VAL_4:.*]] = fir.volatile_cast %[[VAL_3]]#0 : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_4]] : !fir.ref<i32>
+! CHECK: return %[[VAL_5]] : i32
+! CHECK: }
+
+! CHECK-LABEL: func.func private @_QFPc() -> f32 attributes {{.+}} {
+! CHECK: %[[VAL_0:.*]] = arith.constant 3.000000e+00 : f32
+! CHECK: %[[VAL_1:.*]] = fir.alloca f32 {bindc_name = "r", uniq_name = "_QFFcEr"}
+! CHECK: %[[VAL_2:.*]] = fir.volatile_cast %[[VAL_1]] : (!fir.ref<f32>) -> !fir.ref<f32, volatile>
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {{.*}} : (!fir.ref<f32, volatile>) -> (!fir.ref<f32, volatile>, !fir.ref<f32, volatile>)
+! CHECK: hlfir.assign %[[VAL_0]] to %[[VAL_3]]#0 : f32, !fir.ref<f32, volatile>
+! CHECK: %[[VAL_4:.*]] = fir.volatile_cast %[[VAL_3]]#0 : (!fir.ref<f32, volatile>) -> !fir.ref<f32>
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_4]] : !fir.ref<f32>
+! CHECK: return %[[VAL_5]] : f32
+! CHECK: }
More information about the flang-commits
mailing list