I don’t really have an opinion here, that’s why I haven’t commented so far.
The major reason for using struct
was the impossibility of performing one-to-many type conversions. The common wisdom was that something will do SRoA and DCE at a lower level (LLVM dialect or LLVM IR) anyway, so this is not a performance issue.
AFAIK, descriptor is a concept specific to the Memref->LLVM lowering. Since you mentioned the importance of non-LLVM outfeeds, you probably don’t want to lift this concept to memref
itself.
Do we practically need access to the base pointer? If not, I would consider adding !shape.strided
type into the shape dialect to represent the (sizes + strides + offset) information, which is a more of a memref-level concept.
In any case, I would suggest phrasing this in terms of “strides/offsets” or “strided format” to avoid lifting the implementation detail into the memref dialect/type definition.
We already have the bulit-in tuple type, just no operations to insert/extract. Struct may come with data layout considerations that nobody wants to handle as this level.
This shouldn’t be a runtime performance problem as long as there is DCE at some later stage when the memref is transformed into a descriptor, or some clever lowering/canonicalization that avoids emitting spurious extractvalue
equivalents. There is a some compile-time cost though.
I think the rationale for that was to support aliasing analysis. With all sorts of casts that I lost track of, we are likely past the point where we could still have a happy path by construction, so this may be less of a concern.
I don’t see a difference between “error: memref.get_strided_shape at file.mlir:123:45: op requires operand #0 to satisfy strided memref” and “error: memref.get_stride at file:123:45: op requires operand #0 to satisfy strided memref”, which is the error message we would get by having the AnyStridedMemRef
type constraint in the op definition. There are multiple other such ops in the dialect.