Skip to content

Commit 7bcd697

Browse files
Add memory effects
Models memory effects on ops that can take volatile references by adding read and write effects to the default resource.
1 parent 7e7b15d commit 7bcd697

File tree

15 files changed

+267
-91
lines changed

15 files changed

+267
-91
lines changed

flang/include/flang/Optimizer/Dialect/FIROps.h

Lines changed: 6 additions & 0 deletions
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

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ 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, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
290290
let summary = "load a value from a memory reference";
291291
let description = [{
292292
Load a value from a memory reference into an ssa-value (virtual register).
@@ -302,7 +302,7 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
302302
or null.
303303
}];
304304

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

308308
let builders = [OpBuilder<(ins "mlir::Value":$refVal)>,
@@ -315,7 +315,8 @@ def fir_LoadOp : fir_OneResultOp<"load", [FirAliasTagOpInterface]> {
315315
}];
316316
}
317317

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

321322
let description = [{
@@ -335,7 +336,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
335336
}];
336337

337338
let arguments = (ins AnyType:$value,
338-
Arg<AnyReferenceLike, "", [MemWrite]>:$memref,
339+
Arg<AnyReferenceLike, "">:$memref,
339340
OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
340341

341342
let builders = [OpBuilder<(ins "mlir::Value":$value, "mlir::Value":$memref)>];
@@ -348,7 +349,7 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
348349
}];
349350
}
350351

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

354355
let description = [{
@@ -369,8 +370,8 @@ def fir_CopyOp : fir_Op<"copy", []> {
369370
TODO: add FirAliasTagOpInterface to carry TBAA.
370371
}];
371372

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

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

1376-
def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
1377+
def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
13771378

13781379
let summary = "Load an array as a value.";
13791380

@@ -1412,7 +1413,7 @@ def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
14121413
}];
14131414

14141415
let arguments = (ins
1415-
Arg<AnyRefOrBox, "", [MemRead]>:$memref,
1416+
AnyRefOrBox:$memref,
14161417
Optional<AnyShapeOrShiftType>:$shape,
14171418
Optional<fir_SliceType>:$slice,
14181419
Variadic<AnyIntegerType>:$typeparams
@@ -1624,7 +1625,7 @@ def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
16241625

16251626
It is only possible to use `array_access` on an `array_load` result value or
16261627
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
1628+
dominating source. Other array operations such as `array_amend` can be in
16281629
between.
16291630

16301631
TODO: The above restriction is not enforced. The design of the operation
@@ -1685,7 +1686,7 @@ def fir_ArrayAmendOp : fir_Op<"array_amend", [NoMemoryEffect]> {
16851686
}
16861687

16871688
def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
1688-
[AttrSizedOperandSegments]> {
1689+
[AttrSizedOperandSegments, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
16891690

16901691
let summary = "Store merged array value to memory.";
16911692

@@ -1714,7 +1715,7 @@ def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
17141715
let arguments = (ins
17151716
fir_SequenceType:$original,
17161717
fir_SequenceType:$sequence,
1717-
Arg<AnyRefOrBox, "", [MemWrite]>:$memref,
1718+
AnyRefOrBox:$memref,
17181719
Optional<fir_SliceType>:$slice,
17191720
Variadic<AnyIntegerType>:$typeparams
17201721
);
@@ -2752,6 +2753,20 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoMemoryEffect]> {
27522753
let assemblyFormat = "`(` $symbol `)` attr-dict `:` type($resTy)";
27532754
}
27542755

2756+
def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [NoMemoryEffect]> {
2757+
let summary = "cast between volatile and non-volatile types";
2758+
let description = [{
2759+
Cast between volatile and non-volatile types. The types must be otherwise
2760+
identical.
2761+
}];
2762+
let arguments = (ins AnyRefOrBox:$value);
2763+
let results = (outs AnyRefOrBox:$res);
2764+
let assemblyFormat = [{
2765+
$value attr-dict `:` functional-type($value, results)
2766+
}];
2767+
let hasVerifier = 1;
2768+
}
2769+
27552770
def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
27562771
let summary = "encapsulates all Fortran entity type conversions";
27572772

flang/include/flang/Optimizer/Dialect/FIRType.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,12 @@ inline bool isa_ref_type(mlir::Type t) {
111111
fir::LLVMPointerType>(t);
112112
}
113113

114-
inline bool isa_volatile_ref_type(mlir::Type t) {
114+
/// Is `t` a FIR dialect type that has been marked volatile?
115+
inline bool isa_volatile_type(mlir::Type t) {
115116
if (auto refTy = mlir::dyn_cast_or_null<fir::ReferenceType>(t))
116117
return refTy.isVolatile();
118+
if (auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(t))
119+
return boxTy.isVolatile();
117120
return false;
118121
}
119122

flang/include/flang/Optimizer/Dialect/FIRTypes.td

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,24 @@ def fir_BoxType : FIR_Type<"Box", "box", [], "BaseBoxType"> {
7777
to) whether the entity is an array, its size, or what type it has.
7878
}];
7979

80-
let parameters = (ins "mlir::Type":$eleTy);
80+
let parameters = (ins "mlir::Type":$eleTy, DefaultValuedParameter<"bool", "false">:$isVolatile);
8181

8282
let skipDefaultBuilders = 1;
8383

8484
let builders = [
8585
TypeBuilderWithInferredContext<(ins
86-
"mlir::Type":$eleTy), [{
87-
return Base::get(eleTy.getContext(), eleTy);
86+
"mlir::Type":$eleTy, CArg<"bool", "false">:$isVolatile), [{
87+
return Base::get(eleTy.getContext(), eleTy, isVolatile);
8888
}]>,
8989
];
9090

9191
let extraClassDeclaration = [{
9292
mlir::Type getElementType() const { return getEleTy(); }
93+
bool isVolatile() const { return (bool)getIsVolatile(); }
9394
}];
9495

9596
let genVerifyDecl = 1;
96-
97-
let assemblyFormat = "`<` $eleTy `>`";
97+
let hasCustomAssemblyFormat = 1;
9898
}
9999

100100
def fir_CharacterType : FIR_Type<"Character", "char"> {
@@ -361,27 +361,33 @@ def fir_ReferenceType : FIR_Type<"Reference", "ref"> {
361361

362362
let description = [{
363363
The type of a reference to an entity in memory.
364+
365+
References can be volatile. Any ops taking an operand of a volatile
366+
reference must set their memory effects appropriately. Accesses of
367+
volatile references are currently modeled as read and write effects
368+
to an unknown memory location.
364369
}];
365370

366371
let parameters = (ins
367372
"mlir::Type":$eleTy,
368-
DefaultValuedParameter<"bool", "false">:$isVol);
373+
DefaultValuedParameter<"bool", "false">:$isVolatile);
369374

370375
let skipDefaultBuilders = 1;
371376

372377
let builders = [
373-
TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType, CArg<"bool", "false">:$isVol), [{
374-
return Base::get(elementType.getContext(), elementType, isVol);
378+
TypeBuilderWithInferredContext<(ins "mlir::Type":$elementType, CArg<"bool", "false">:$isVolatile), [{
379+
return Base::get(elementType.getContext(), elementType, isVolatile);
375380
}]>,
376381
];
377382

378383
let extraClassDeclaration = [{
384+
bool isVolatile() const { return (bool)getIsVolatile(); }
379385
mlir::Type getElementType() const { return getEleTy(); }
380-
bool isVolatile() const { return (bool)getIsVol(); }
381-
static llvm::StringRef getVolatileKeyword() { return "volatile"; }
382386
}];
383387

384388
let genVerifyDecl = 1;
389+
// let assemblyFormat = "`<` $eleTy (`,` `volatile` $isVolatile^)? `>`";
390+
// let assemblyFormat = "`<` $eleTy custom<IsVolatile>($isVolatile)`>`";
385391
let hasCustomAssemblyFormat = 1;
386392
}
387393

flang/lib/Lower/ConvertExprToHLFIR.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ class HlfirDesignatorBuilder {
229229
// Check if the base type is volatile
230230
if (partInfo.base.has_value()) {
231231
mlir::Type baseType = partInfo.base.value().getType();
232-
isVolatile = fir::isa_volatile_ref_type(baseType);
232+
isVolatile = fir::isa_volatile_type(baseType);
233233
}
234234

235235
auto isVolatileSymbol = [](const Fortran::semantics::Symbol &symbol) {
@@ -242,16 +242,8 @@ class HlfirDesignatorBuilder {
242242
Fortran::evaluate::SymbolRef>) {
243243
if (isVolatileSymbol(designatorNode.get()))
244244
isVolatile = true;
245-
} else if constexpr (std::is_same_v<std::decay_t<T>,
246-
Fortran::evaluate::Component>) {
247-
if (isVolatileSymbol(designatorNode.GetLastSymbol()))
248-
isVolatile = true;
249245
}
250246

251-
// If it's a reference to a ref, account for it
252-
if (auto refTy = mlir::dyn_cast<fir::ReferenceType>(resultValueType))
253-
resultValueType = refTy.getEleTy();
254-
255247
// Other designators can be handled as raw addresses.
256248
return fir::ReferenceType::get(resultValueType, isVolatile);
257249
}
@@ -1844,7 +1836,7 @@ class HlfirBuilder {
18441836
auto &expr = std::get<const Fortran::lower::SomeExpr &>(iter);
18451837
auto &baseOp = std::get<hlfir::EntityWithAttributes>(iter);
18461838
std::string name = converter.getRecordTypeFieldName(sym);
1847-
const bool isVolatile = fir::isa_volatile_ref_type(baseOp.getType());
1839+
const bool isVolatile = fir::isa_volatile_type(baseOp.getType());
18481840

18491841
// Generate DesignateOp for the component.
18501842
// The designator's result type is just a reference to the component type,

flang/lib/Optimizer/Builder/HLFIRTools.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ mlir::Type hlfir::getVariableElementType(hlfir::Entity variable) {
819819
} else if (fir::isRecordWithTypeParameters(eleTy)) {
820820
return fir::BoxType::get(eleTy);
821821
}
822-
const bool isVolatile = fir::isa_volatile_ref_type(variable.getType());
822+
const bool isVolatile = fir::isa_volatile_type(variable.getType());
823823
return fir::ReferenceType::get(eleTy, isVolatile);
824824
}
825825

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,20 @@ struct CmpcOpConversion : public fir::FIROpConversion<fir::CmpcOp> {
689689
}
690690
};
691691

692+
/// fir.volatile_cast is only useful at the fir level. Once we lower to LLVM,
693+
/// volatility is described by setting volatile attributes on the LLVM ops.
694+
struct VolatileCastOpConversion
695+
: public fir::FIROpConversion<fir::VolatileCastOp> {
696+
using FIROpConversion::FIROpConversion;
697+
698+
llvm::LogicalResult
699+
matchAndRewrite(fir::VolatileCastOp volatileCast, OpAdaptor adaptor,
700+
mlir::ConversionPatternRewriter &rewriter) const override {
701+
rewriter.replaceOp(volatileCast, adaptor.getOperands()[0]);
702+
return mlir::success();
703+
}
704+
};
705+
692706
/// convert value of from-type to value of to-type
693707
struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
694708
using FIROpConversion::FIROpConversion;
@@ -3224,8 +3238,7 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
32243238
mlir::ConversionPatternRewriter &rewriter) const override {
32253239

32263240
mlir::Type llvmLoadTy = convertObjectType(load.getType());
3227-
const bool isVolatile =
3228-
fir::isa_volatile_ref_type(load.getMemref().getType());
3241+
const bool isVolatile = fir::isa_volatile_type(load.getMemref().getType());
32293242
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(load.getType())) {
32303243
// fir.box is a special case because it is considered an ssa value in
32313244
// fir, but it is lowered as a pointer to a descriptor. So
@@ -3263,10 +3276,10 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
32633276
attachTBAATag(memcpy, boxTy, boxTy, nullptr);
32643277
rewriter.replaceOp(load, newBoxStorage);
32653278
} else {
3266-
// TODO: are we losing any attributes from the load op?
3267-
auto memref = adaptor.getOperands()[0];
3268-
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(
3269-
load.getLoc(), llvmLoadTy, memref, /*alignment=*/0, isVolatile);
3279+
mlir::LLVM::LoadOp loadOp = rewriter.create<mlir::LLVM::LoadOp>(
3280+
load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs());
3281+
if (isVolatile)
3282+
loadOp.setVolatile_Attr(rewriter.getUnitAttr());
32703283
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
32713284
loadOp.setTBAATags(*optionalTag);
32723285
else
@@ -3544,8 +3557,7 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
35443557
mlir::Value llvmValue = adaptor.getValue();
35453558
mlir::Value llvmMemref = adaptor.getMemref();
35463559
mlir::LLVM::AliasAnalysisOpInterface newOp;
3547-
const bool isVolatile =
3548-
fir::isa_volatile_ref_type(store.getMemref().getType());
3560+
const bool isVolatile = fir::isa_volatile_type(store.getMemref().getType());
35493561
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) {
35503562
mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
35513563
// Always use memcpy because LLVM is not as effective at optimizing
@@ -4200,21 +4212,22 @@ void fir::populateFIRToLLVMConversionPatterns(
42004212
BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
42014213
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
42024214
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
4203-
CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
4204-
CopyOpConversion, DTEntryOpConversion, DeclareOpConversion,
4205-
DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
4206-
EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
4207-
FirEndOpConversion, FreeMemOpConversion, GlobalLenOpConversion,
4208-
GlobalOpConversion, InsertOnRangeOpConversion, IsPresentOpConversion,
4209-
LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
4210-
NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
4211-
SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
4212-
ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
4213-
SliceOpConversion, StoreOpConversion, StringLitOpConversion,
4214-
SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
4215-
UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
4216-
UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
4217-
XReboxOpConversion, ZeroOpConversion>(converter, options);
4215+
CmpcOpConversion, VolatileCastOpConversion, ConvertOpConversion,
4216+
CoordinateOpConversion, CopyOpConversion, DTEntryOpConversion,
4217+
DeclareOpConversion, DivcOpConversion, EmboxOpConversion,
4218+
EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion,
4219+
FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion,
4220+
GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
4221+
IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion,
4222+
MulcOpConversion, NegcOpConversion, NoReassocOpConversion,
4223+
SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion,
4224+
SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion,
4225+
ShiftOpConversion, SliceOpConversion, StoreOpConversion,
4226+
StringLitOpConversion, SubcOpConversion, TypeDescOpConversion,
4227+
TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
4228+
UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
4229+
XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter,
4230+
options);
42184231

42194232
// Patterns that are populated without a type converter do not trigger
42204233
// target materializations for the operands of the root op.

0 commit comments

Comments
 (0)