Skip to content

Commit e41ebbe

Browse files
author
Vladislav Vinogradov
committed
[mlir][RFC] Refactor layout representation in MemRefType
The change is based on the proposal from the following discussion: https://llvm.discourse.group/t/rfc-memreftype-affine-maps-list-vs-single-item/3968 * Introduce `MemRefLayoutAttr` interface to get `AffineMap` from an `Attribute` (`AffineMapAttr` implements this interface). * Store layout as a single generic `MemRefLayoutAttr`. This change removes the affine map composition feature and related API. Actually, while the `MemRefType` itself supported it, almost none of the upstream can work with more than 1 affine map in `MemRefType`. The introduced `MemRefLayoutAttr` allows to re-implement this feature in a more stable way - via separate attribute class. Also the interface allows to use different layout representations rather than affine maps. For example, the described "stride + offset" form, which is currently supported in ASM parser only, can now be expressed as separate attribute. Reviewed By: ftynse, bondhugula Differential Revision: https://reviews.llvm.org/D111553
1 parent cf033bb commit e41ebbe

34 files changed

+384
-367
lines changed

mlir/include/mlir-c/BuiltinTypes.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,17 @@ MLIR_CAPI_EXPORTED bool mlirTypeIsAUnrankedMemRef(MlirType type);
229229
/// Creates a MemRef type with the given rank and shape, a potentially empty
230230
/// list of affine layout maps, the given memory space and element type, in the
231231
/// same context as element type. The type is owned by the context.
232-
MLIR_CAPI_EXPORTED MlirType mlirMemRefTypeGet(
233-
MlirType elementType, intptr_t rank, const int64_t *shape, intptr_t numMaps,
234-
MlirAffineMap const *affineMaps, MlirAttribute memorySpace);
232+
MLIR_CAPI_EXPORTED MlirType mlirMemRefTypeGet(MlirType elementType,
233+
intptr_t rank,
234+
const int64_t *shape,
235+
MlirAttribute layout,
236+
MlirAttribute memorySpace);
235237

236238
/// Same as "mlirMemRefTypeGet" but returns a nullptr-wrapping MlirType o
237239
/// illegal arguments, emitting appropriate diagnostics.
238240
MLIR_CAPI_EXPORTED MlirType mlirMemRefTypeGetChecked(
239241
MlirLocation loc, MlirType elementType, intptr_t rank, const int64_t *shape,
240-
intptr_t numMaps, MlirAffineMap const *affineMaps,
241-
MlirAttribute memorySpace);
242+
MlirAttribute layout, MlirAttribute memorySpace);
242243

243244
/// Creates a MemRef type with the given rank, shape, memory space and element
244245
/// type in the same context as the element type. The type has no affine maps,
@@ -264,12 +265,11 @@ mlirUnrankedMemRefTypeGet(MlirType elementType, MlirAttribute memorySpace);
264265
MLIR_CAPI_EXPORTED MlirType mlirUnrankedMemRefTypeGetChecked(
265266
MlirLocation loc, MlirType elementType, MlirAttribute memorySpace);
266267

267-
/// Returns the number of affine layout maps in the given MemRef type.
268-
MLIR_CAPI_EXPORTED intptr_t mlirMemRefTypeGetNumAffineMaps(MlirType type);
268+
/// Returns the layout of the given MemRef type.
269+
MLIR_CAPI_EXPORTED MlirAttribute mlirMemRefTypeGetLayout(MlirType type);
269270

270-
/// Returns the pos-th affine map of the given MemRef type.
271-
MLIR_CAPI_EXPORTED MlirAffineMap mlirMemRefTypeGetAffineMap(MlirType type,
272-
intptr_t pos);
271+
/// Returns the affine map of the given MemRef type.
272+
MLIR_CAPI_EXPORTED MlirAffineMap mlirMemRefTypeGetAffineMap(MlirType type);
273273

274274
/// Returns the memory space of the given MemRef type.
275275
MLIR_CAPI_EXPORTED MlirAttribute mlirMemRefTypeGetMemorySpace(MlirType type);

mlir/include/mlir/IR/BuiltinAttributeInterfaces.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef MLIR_IR_BUILTINATTRIBUTEINTERFACES_H
1010
#define MLIR_IR_BUILTINATTRIBUTEINTERFACES_H
1111

12+
#include "mlir/IR/AffineMap.h"
1213
#include "mlir/IR/Attributes.h"
1314
#include "mlir/IR/Types.h"
1415
#include "mlir/Support/LogicalResult.h"
@@ -227,6 +228,21 @@ class ElementsAttrIterator
227228
ptrdiff_t index;
228229
};
229230
} // namespace detail
231+
232+
//===----------------------------------------------------------------------===//
233+
// MemRefLayoutAttrInterface
234+
//===----------------------------------------------------------------------===//
235+
236+
namespace detail {
237+
238+
// Verify the affine map 'm' can be used as a layout specification
239+
// for memref with 'shape'.
240+
LogicalResult
241+
verifyAffineMapAsLayout(AffineMap m, ArrayRef<int64_t> shape,
242+
function_ref<InFlightDiagnostic()> emitError);
243+
244+
} // namespace detail
245+
230246
} // namespace mlir
231247

232248
//===----------------------------------------------------------------------===//

mlir/include/mlir/IR/BuiltinAttributeInterfaces.td

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,4 +432,52 @@ def ElementsAttrInterface : AttrInterface<"ElementsAttr"> {
432432
}] # ElementsAttrInterfaceAccessors;
433433
}
434434

435+
//===----------------------------------------------------------------------===//
436+
// MemRefLayoutAttrInterface
437+
//===----------------------------------------------------------------------===//
438+
439+
def MemRefLayoutAttrInterface : AttrInterface<"MemRefLayoutAttrInterface"> {
440+
let cppNamespace = "::mlir";
441+
442+
let description = [{
443+
This interface is used for attributes that can represent the MemRef type's
444+
layout semantics, such as dimension order in the memory, strides and offsets.
445+
Such a layout attribute should be representable as a
446+
[semi-affine map](Affine.md/#semi-affine-maps).
447+
448+
Note: the MemRef type's layout is assumed to represent simple strided buffer
449+
layout. For more complicated case, like sparse storage buffers,
450+
it is preferable to use separate type with more specic layout, rather then
451+
introducing extra complexity to the builin MemRef type.
452+
}];
453+
454+
let methods = [
455+
InterfaceMethod<
456+
"Get the MemRef layout as an AffineMap, the method must not return NULL",
457+
"::mlir::AffineMap", "getAffineMap", (ins)
458+
>,
459+
460+
InterfaceMethod<
461+
"Return true if this attribute represents the identity layout",
462+
"bool", "isIdentity", (ins),
463+
[{}],
464+
[{
465+
return $_attr.getAffineMap().isIdentity();
466+
}]
467+
>,
468+
469+
InterfaceMethod<
470+
"Check if the current layout is applicable to the provided shape",
471+
"::mlir::LogicalResult", "verifyLayout",
472+
(ins "::llvm::ArrayRef<int64_t>":$shape,
473+
"::llvm::function_ref<::mlir::InFlightDiagnostic()>":$emitError),
474+
[{}],
475+
[{
476+
return ::mlir::detail::verifyAffineMapAsLayout($_attr.getAffineMap(),
477+
shape, emitError);
478+
}]
479+
>
480+
];
481+
}
482+
435483
#endif // MLIR_IR_BUILTINATTRIBUTEINTERFACES_TD_

mlir/include/mlir/IR/BuiltinAttributes.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class Builtin_Attr<string name, list<Trait> traits = [],
3434
// AffineMapAttr
3535
//===----------------------------------------------------------------------===//
3636

37-
def Builtin_AffineMapAttr : Builtin_Attr<"AffineMap"> {
37+
def Builtin_AffineMapAttr : Builtin_Attr<"AffineMap", [
38+
MemRefLayoutAttrInterface
39+
]> {
3840
let summary = "An Attribute containing an AffineMap object";
3941
let description = [{
4042
Syntax:
@@ -56,7 +58,10 @@ def Builtin_AffineMapAttr : Builtin_Attr<"AffineMap"> {
5658
return $_get(value.getContext(), value);
5759
}]>
5860
];
59-
let extraClassDeclaration = "using ValueType = AffineMap;";
61+
let extraClassDeclaration = [{
62+
using ValueType = AffineMap;
63+
AffineMap getAffineMap() const { return getValue(); }
64+
}];
6065
let skipDefaultBuilders = 1;
6166
let typeBuilder = "IndexType::get($_value.getContext())";
6267
}

mlir/include/mlir/IR/BuiltinTypes.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef MLIR_IR_BUILTINTYPES_H
1010
#define MLIR_IR_BUILTINTYPES_H
1111

12+
#include "BuiltinAttributeInterfaces.h"
1213
#include "SubElementInterfaces.h"
1314

1415
namespace llvm {
@@ -209,12 +210,11 @@ class MemRefType::Builder {
209210
// Build from another MemRefType.
210211
explicit Builder(MemRefType other)
211212
: shape(other.getShape()), elementType(other.getElementType()),
212-
affineMaps(other.getAffineMaps()), memorySpace(other.getMemorySpace()) {
213-
}
213+
layout(other.getLayout()), memorySpace(other.getMemorySpace()) {}
214214

215215
// Build from scratch.
216216
Builder(ArrayRef<int64_t> shape, Type elementType)
217-
: shape(shape), elementType(elementType), affineMaps() {}
217+
: shape(shape), elementType(elementType) {}
218218

219219
Builder &setShape(ArrayRef<int64_t> newShape) {
220220
shape = newShape;
@@ -226,8 +226,8 @@ class MemRefType::Builder {
226226
return *this;
227227
}
228228

229-
Builder &setAffineMaps(ArrayRef<AffineMap> newAffineMaps) {
230-
affineMaps = newAffineMaps;
229+
Builder &setLayout(MemRefLayoutAttrInterface newLayout) {
230+
layout = newLayout;
231231
return *this;
232232
}
233233

@@ -240,13 +240,13 @@ class MemRefType::Builder {
240240
Builder &setMemorySpace(unsigned newMemorySpace);
241241

242242
operator MemRefType() {
243-
return MemRefType::get(shape, elementType, affineMaps, memorySpace);
243+
return MemRefType::get(shape, elementType, layout, memorySpace);
244244
}
245245

246246
private:
247247
ArrayRef<int64_t> shape;
248248
Type elementType;
249-
ArrayRef<AffineMap> affineMaps;
249+
MemRefLayoutAttrInterface layout;
250250
Attribute memorySpace;
251251
};
252252

mlir/include/mlir/IR/BuiltinTypes.td

Lines changed: 10 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,7 @@ def Builtin_MemRef : Builtin_Type<"MemRef", [
278278

279279
stride-list ::= `[` (dimension (`,` dimension)*)? `]`
280280
strided-layout ::= `offset:` dimension `,` `strides: ` stride-list
281-
semi-affine-map-composition ::= (semi-affine-map `,` )* semi-affine-map
282-
layout-specification ::= semi-affine-map-composition | strided-layout
281+
layout-specification ::= semi-affine-map | strided-layout | attribute-value
283282
memory-space ::= attribute-value
284283
```
285284

@@ -486,27 +485,6 @@ def Builtin_MemRef : Builtin_Type<"MemRef", [
486485
#layout_tiled = (i, j) -> (i floordiv 64, j floordiv 64, i mod 64, j mod 64)
487486
```
488487

489-
##### Affine Map Composition
490-
491-
A memref specifies a semi-affine map composition as part of its type. A
492-
semi-affine map composition is a composition of semi-affine maps beginning
493-
with zero or more index maps, and ending with a layout map. The composition
494-
must be conformant: the number of dimensions of the range of one map, must
495-
match the number of dimensions of the domain of the next map in the
496-
composition.
497-
498-
The semi-affine map composition specified in the memref type, maps from
499-
accesses used to index the memref in load/store operations to other index
500-
spaces (i.e. logical to physical index mapping). Each of the
501-
[semi-affine maps](Affine.md/#semi-affine-maps) and thus its composition is required
502-
to be one-to-one.
503-
504-
The semi-affine map composition can be used in dependence analysis, memory
505-
access pattern analysis, and for performance optimizations like
506-
vectorization, copy elision and in-place updates. If an affine map
507-
composition is not specified for the memref, the identity affine map is
508-
assumed.
509-
510488
##### Strided MemRef
511489

512490
A memref may specify a strided layout as part of its type. A stride
@@ -544,36 +522,23 @@ def Builtin_MemRef : Builtin_Type<"MemRef", [
544522
let parameters = (ins
545523
ArrayRefParameter<"int64_t">:$shape,
546524
"Type":$elementType,
547-
ArrayRefParameter<"AffineMap">:$affineMaps,
525+
"MemRefLayoutAttrInterface":$layout,
548526
"Attribute":$memorySpace
549527
);
550528
let builders = [
551529
TypeBuilderWithInferredContext<(ins
552530
"ArrayRef<int64_t>":$shape, "Type":$elementType,
553-
CArg<"ArrayRef<AffineMap>", "{}">:$affineMaps,
554-
CArg<"Attribute", "{}">:$memorySpace
555-
), [{
556-
// Drop identity maps from the composition. This may lead to the
557-
// composition becoming empty, which is interpreted as an implicit
558-
// identity.
559-
auto nonIdentityMaps = llvm::make_filter_range(affineMaps,
560-
[](AffineMap map) { return !map.isIdentity(); });
561-
// Drop default memory space value and replace it with empty attribute.
562-
Attribute nonDefaultMemorySpace = skipDefaultMemorySpace(memorySpace);
563-
return $_get(elementType.getContext(), shape, elementType,
564-
llvm::to_vector<4>(nonIdentityMaps), nonDefaultMemorySpace);
565-
}]>,
531+
CArg<"MemRefLayoutAttrInterface", "{}">:$layout,
532+
CArg<"Attribute", "{}">:$memorySpace)>,
533+
TypeBuilderWithInferredContext<(ins
534+
"ArrayRef<int64_t>":$shape, "Type":$elementType,
535+
CArg<"AffineMap">:$map,
536+
CArg<"Attribute", "{}">:$memorySpace)>,
566537
/// [deprecated] `Attribute`-based form should be used instead.
567538
TypeBuilderWithInferredContext<(ins
568539
"ArrayRef<int64_t>":$shape, "Type":$elementType,
569-
"ArrayRef<AffineMap>":$affineMaps,
570-
"unsigned":$memorySpace
571-
), [{
572-
// Convert deprecated integer-like memory space to Attribute.
573-
Attribute memorySpaceAttr =
574-
wrapIntegerMemorySpace(memorySpace, elementType.getContext());
575-
return MemRefType::get(shape, elementType, affineMaps, memorySpaceAttr);
576-
}]>
540+
"AffineMap":$map,
541+
"unsigned":$memorySpaceInd)>
577542
];
578543
let extraClassDeclaration = [{
579544
/// This is a builder type that keeps local references to arguments.

mlir/lib/Analysis/LoopAnalysis.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,15 +219,8 @@ static bool isContiguousAccess(Value iv, LoadOrStoreOp memoryOp,
219219
assert(memRefDim && "memRefDim == nullptr");
220220
auto memRefType = memoryOp.getMemRefType();
221221

222-
auto layoutMap = memRefType.getAffineMaps();
223-
// TODO: remove dependence on Builder once we support non-identity layout map.
224-
Builder b(memoryOp.getContext());
225-
if (layoutMap.size() >= 2 ||
226-
(layoutMap.size() == 1 &&
227-
!(layoutMap[0] ==
228-
b.getMultiDimIdentityMap(layoutMap[0].getNumDims())))) {
222+
if (!memRefType.getLayout().isIdentity())
229223
return memoryOp.emitError("NYI: non-trivial layoutMap"), false;
230-
}
231224

232225
int uniqueVaryingIndexAlongIv = -1;
233226
auto accessMap = memoryOp.getAffineMap();

mlir/lib/Analysis/Utils.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,7 @@ static unsigned getMemRefEltSizeInBytes(MemRefType memRefType) {
616616
Optional<int64_t> MemRefRegion::getRegionSize() {
617617
auto memRefType = memref.getType().cast<MemRefType>();
618618

619-
auto layoutMaps = memRefType.getAffineMaps();
620-
if (layoutMaps.size() > 1 ||
621-
(layoutMaps.size() == 1 && !layoutMaps[0].isIdentity())) {
619+
if (!memRefType.getLayout().isIdentity()) {
622620
LLVM_DEBUG(llvm::dbgs() << "Non-identity layout map not yet supported\n");
623621
return false;
624622
}

0 commit comments

Comments
 (0)