Skip to content

Commit e42f860

Browse files
[flang][nfc] Support volatility in Fir ops (#134858)
Part two of merging #132486. Support volatility in fir ops. * Introduce a new operation fir.volatile_cast, whose only purpose is to add or take away the volatility of an SSA value's type. The types must be otherwise identical, and any other type conversions must be handled by fir.convert. fir.convert will give an error if the volatility of the inputs does not match, such that all changes to volatility must be handled explicitly through fir.volatile_cast. * Add memory effects to ops that read from or write to memory. The precedent for this comes from the LLVM dialect (feb7bea) where llvm.load/store ops with the volatile attribute report read/write effects to a generic memory resource. This change is similar in spirit but different in two ways: the volatility of an operation is determined by the type of its memref, not an attribute on the op, and the memory effects of a load- or store-like operation on a volatile reference type are reported against a particular memory resource, `VolatileMemoryResource`. This is so MLIR optimizations are able to reorder operations that are not volatile around operations that are, which we believe more precisely models LLVM's volatile memory semantics. @vzakhari suggested this in #132486 citing LangRef. See https://llvm.org/docs/LangRef.html#volatile-memory-accesses Changes needed to generate IR with volatile types are not included in this change, so it should be non-functional, containing only the changes to Fir ops and op utilities that will be needed once we enable lowering to generate volatile types.
1 parent aff1397 commit e42f860

File tree

9 files changed

+250
-25
lines changed

9 files changed

+250
-25
lines changed

Diff for: flang/include/flang/Optimizer/Builder/FIRBuilder.h

+5
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,11 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
397397
mlir::Value createConvert(mlir::Location loc, mlir::Type toTy,
398398
mlir::Value val);
399399

400+
/// Create a fir.convert op with a volatile cast if the source value's type
401+
/// does not match the target type's volatility.
402+
mlir::Value createConvertWithVolatileCast(mlir::Location loc, mlir::Type toTy,
403+
mlir::Value val);
404+
400405
/// Create a fir.store of \p val into \p addr. A lazy conversion
401406
/// of \p val to the element type of \p addr is created if needed.
402407
void createStoreWithConvert(mlir::Location loc, mlir::Value val,

Diff for: flang/include/flang/Optimizer/Dialect/FIROps.h

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ struct DebuggingResource
5050
mlir::StringRef getName() final { return "DebuggingResource"; }
5151
};
5252

53+
/// Model operations which read from/write to volatile memory
54+
struct VolatileMemoryResource
55+
: public mlir::SideEffects::Resource::Base<VolatileMemoryResource> {
56+
mlir::StringRef getName() final { return "VolatileMemoryResource"; }
57+
};
58+
5359
class CoordinateIndicesAdaptor;
5460
using IntOrValue = llvm::PointerUnion<mlir::IntegerAttr, mlir::Value>;
5561

Diff for: flang/include/flang/Optimizer/Dialect/FIROps.td

+31-12
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ def fir_FreeMemOp : fir_Op<"freemem", [MemoryEffects<[MemFree]>]> {
286286
let assemblyFormat = "$heapref attr-dict `:` qualified(type($heapref))";
287287
}
288288

289-
def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
289+
def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface,
290+
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
290291
let summary = "load a value from a memory reference";
291292
let description = [{
292293
Load a value from a memory reference into an ssa-value (virtual register).
@@ -302,7 +303,7 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
302303
or null.
303304
}];
304305

305-
let arguments = (ins Arg<AnyReferenceLike, "", [MemRead]>:$memref,
306+
let arguments = (ins AnyReferenceLike:$memref,
306307
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
307308

308309
let builders = [OpBuilder<(ins "mlir::Value":$refVal)>,
@@ -315,7 +316,8 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
315316
}];
316317
}
317318

318-
def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
319+
def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface,
320+
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
319321
let summary = "store an SSA-value to a memory location";
320322

321323
let description = [{
@@ -335,7 +337,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
335337
}];
336338

337339
let arguments = (ins AnyType:$value,
338-
Arg<AnyReferenceLike, "", [MemWrite]>:$memref,
340+
AnyReferenceLike:$memref,
339341
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
340342

341343
let builders = [OpBuilder<(ins "mlir::Value":$value, "mlir::Value":$memref)>];
@@ -348,7 +350,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
348350
}];
349351
}
350352

351-
def fir_CopyOp : fir_Op<"copy", []> {
353+
def fir_CopyOp : fir_Op<"copy", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
352354
let summary = "copy constant size memory";
353355

354356
let description = [{
@@ -369,8 +371,8 @@ def fir_CopyOp : fir_Op<"copy", []> {
369371
TODO: add FirAliasTagOpInterface to carry TBAA.
370372
}];
371373

372-
let arguments = (ins Arg<AnyRefOfConstantSizeAggregateType, "", [MemRead]>:$source,
373-
Arg<AnyRefOfConstantSizeAggregateType, "", [MemWrite]>:$destination,
374+
let arguments = (ins AnyRefOfConstantSizeAggregateType:$source,
375+
AnyRefOfConstantSizeAggregateType:$destination,
374376
OptionalAttr<UnitAttr>:$no_overlap);
375377

376378
let builders = [OpBuilder<(ins "mlir::Value":$source,
@@ -1373,7 +1375,8 @@ def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoMemoryEffect]> {
13731375
// !- Merge the new and old values into the memory for "A"
13741376
// array_merge_store <updated A> to <A's address>
13751377

1376-
def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
1378+
def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments,
1379+
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
13771380

13781381
let summary = "Load an array as a value.";
13791382

@@ -1412,7 +1415,7 @@ def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
14121415
}];
14131416

14141417
let arguments = (ins
1415-
Arg<AnyRefOrBox, "", [MemRead]>:$memref,
1418+
AnyRefOrBox:$memref,
14161419
Optional<AnyShapeOrShiftType>:$shape,
14171420
Optional<fir_SliceType>:$slice,
14181421
Variadic<AnyIntegerType>:$typeparams
@@ -1624,7 +1627,7 @@ def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
16241627

16251628
It is only possible to use `array_access` on an `array_load` result value or
16261629
a value that can be trace back transitively to an `array_load` as the
1627-
dominating source. Other array operation such as `array_amend` can be in
1630+
dominating source. Other array operations such as `array_amend` can be in
16281631
between.
16291632

16301633
TODO: The above restriction is not enforced. The design of the operation
@@ -1685,7 +1688,7 @@ def fir_ArrayAmendOp : fir_Op<"array_amend", [NoMemoryEffect]> {
16851688
}
16861689

16871690
def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
1688-
[AttrSizedOperandSegments]> {
1691+
[AttrSizedOperandSegments, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
16891692

16901693
let summary = "Store merged array value to memory.";
16911694

@@ -1714,7 +1717,7 @@ def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
17141717
let arguments = (ins
17151718
fir_SequenceType:$original,
17161719
fir_SequenceType:$sequence,
1717-
Arg<AnyRefOrBox, "", [MemWrite]>:$memref,
1720+
AnyRefOrBox:$memref,
17181721
Optional<fir_SliceType>:$slice,
17191722
Variadic<AnyIntegerType>:$typeparams
17201723
);
@@ -2752,6 +2755,22 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoMemoryEffect]> {
27522755
let assemblyFormat = "`(` $symbol `)` attr-dict `:` type($resTy)";
27532756
}
27542757

2758+
def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [NoMemoryEffect]> {
2759+
let summary = "cast between volatile and non-volatile types";
2760+
let description = [{
2761+
Cast between volatile and non-volatile types. The types must be otherwise
2762+
identical. A value's volatility cannot be changed by a fir.convert operation.
2763+
Reinterpreting a value as volatile must be done explicitly using this operation.
2764+
}];
2765+
let arguments = (ins AnyRefOrBox:$value);
2766+
let results = (outs AnyRefOrBox:$res);
2767+
let assemblyFormat = [{
2768+
$value attr-dict `:` functional-type($value, results)
2769+
}];
2770+
let hasVerifier = 1;
2771+
let hasFolder = 1;
2772+
}
2773+
27552774
def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
27562775
let summary = "encapsulates all Fortran entity type conversions";
27572776

Diff for: flang/include/flang/Optimizer/Dialect/FIROpsSupport.h

+20-7
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,26 @@
1515

1616
namespace fir {
1717

18-
/// Return true iff the Operation is a non-volatile LoadOp or ArrayLoadOp.
19-
inline bool nonVolatileLoad(mlir::Operation *op) {
20-
if (auto load = mlir::dyn_cast<fir::LoadOp>(op))
21-
return !load->getAttr("volatile");
22-
if (auto arrLoad = mlir::dyn_cast<fir::ArrayLoadOp>(op))
23-
return !arrLoad->getAttr("volatile");
24-
return false;
18+
/// The LLVM dialect represents volatile memory accesses as read and write
19+
/// effects to an unknown memory location, but this may be overly conservative.
20+
/// LLVM Language Reference only specifies that volatile memory accesses
21+
/// must not be reordered relative to other volatile memory accesses, so it
22+
/// is more precise to use a separate memory resource for volatile memory
23+
/// accesses.
24+
inline void addVolatileMemoryEffects(
25+
mlir::TypeRange type,
26+
llvm::SmallVectorImpl<
27+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
28+
&effects) {
29+
for (mlir::Type t : type) {
30+
if (fir::isa_volatile_type(t)) {
31+
effects.emplace_back(mlir::MemoryEffects::Read::get(),
32+
fir::VolatileMemoryResource::get());
33+
effects.emplace_back(mlir::MemoryEffects::Write::get(),
34+
fir::VolatileMemoryResource::get());
35+
break;
36+
}
37+
}
2538
}
2639

2740
/// Return true iff the Operation is a call.

Diff for: flang/lib/Lower/ConvertExprToHLFIR.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,13 @@ class HlfirDesignatorBuilder {
415415
.Case<fir::SequenceType>([&](fir::SequenceType seqTy) -> mlir::Type {
416416
return fir::SequenceType::get(seqTy.getShape(), newEleTy);
417417
})
418-
.Case<fir::PointerType, fir::HeapType, fir::ReferenceType, fir::BoxType,
419-
fir::ClassType>([&](auto t) -> mlir::Type {
418+
.Case<fir::ReferenceType, fir::BoxType, fir::ClassType>(
419+
[&](auto t) -> mlir::Type {
420+
using FIRT = decltype(t);
421+
return FIRT::get(changeElementType(t.getEleTy(), newEleTy),
422+
t.isVolatile());
423+
})
424+
.Case<fir::PointerType, fir::HeapType>([&](auto t) -> mlir::Type {
420425
using FIRT = decltype(t);
421426
return FIRT::get(changeElementType(t.getEleTy(), newEleTy));
422427
})

Diff for: flang/lib/Optimizer/Builder/FIRBuilder.cpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,17 @@ mlir::Value fir::FirOpBuilder::convertWithSemantics(
577577
return createConvert(loc, toTy, val);
578578
}
579579

580+
mlir::Value fir::FirOpBuilder::createConvertWithVolatileCast(mlir::Location loc,
581+
mlir::Type toTy,
582+
mlir::Value val) {
583+
if (fir::isa_volatile_type(val.getType()) != fir::isa_volatile_type(toTy)) {
584+
mlir::Type volatileAdjustedType = fir::updateTypeWithVolatility(
585+
val.getType(), fir::isa_volatile_type(toTy));
586+
val = create<fir::VolatileCastOp>(loc, volatileAdjustedType, val);
587+
}
588+
return createConvert(loc, toTy, val);
589+
}
590+
580591
mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder,
581592
mlir::Location loc, mlir::Type toTy,
582593
mlir::Value val) {
@@ -739,19 +750,20 @@ mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
739750
<< itemAddr.getType();
740751
llvm_unreachable("not a memory reference type");
741752
}
753+
const bool isVolatile = fir::isa_volatile_type(itemAddr.getType());
742754
mlir::Type boxTy;
743755
mlir::Value tdesc;
744756
// Avoid to wrap a box/class with box/class.
745757
if (mlir::isa<fir::BaseBoxType>(elementType)) {
746758
boxTy = elementType;
747759
} else {
748-
boxTy = fir::BoxType::get(elementType);
760+
boxTy = fir::BoxType::get(elementType, isVolatile);
749761
if (isPolymorphic) {
750762
elementType = fir::updateTypeForUnlimitedPolymorphic(elementType);
751763
if (isAssumedType)
752-
boxTy = fir::BoxType::get(elementType);
764+
boxTy = fir::BoxType::get(elementType, isVolatile);
753765
else
754-
boxTy = fir::ClassType::get(elementType);
766+
boxTy = fir::ClassType::get(elementType, isVolatile);
755767
}
756768
}
757769

0 commit comments

Comments
 (0)