(original) (raw)

On Tue, Oct 16, 2018 at 11:12 AM Chris Lattner via cfe-dev <cfe-dev@lists.llvm.org> wrote:
On Oct 10, 2018, at 11:09 PM, Adam Nemet via cfe-dev <cfe-dev@lists.llvm.org> wrote:
Hi,

We are proposing first-class type support for a new matrix type.

Interesting! Here are some thoughts, I’m sorry but I haven’t read the responses downthread.

This is a natural extension of the current vector type with an extra dimension.
For example, this is what the IR for a matrix multiply would look like for a 4x4 matrix with element type float:

%0 = load <4 x 4 x float>, <4 x 4 x float>\* %a, align 16
%1 = load <4 x 4 x float>, <4 x 4 x float>\* %b, align 16
%2 = call <4 x 4 x float> @llvm.matrix.multiply.m4\_4f32.m4\_4f32.m4\_4f32(<4 x 4 x float> %0, <4 x 4 x float> %1)
store <4 x 4 x float> %2, <4 x 4 x float>\* %c, align 16

LLVM already has a pretty general vector type (arbitrary number of elements). I’m aware of hardware that has rectangular vectors, e.g. nvidia tensor cores, Google has a proprietary in-house design with non-square vector registers, etc.

Currently we support element-wise binary operations, matrix multiply, matrix-scalar multiply, matrix transpose, extract/insert of an element. Besides the regular full-matrix load and store, we also support loading and storing a matrix as a submatrix of a larger matrix in memory. We are also planning to implement vector-extract/insert and matrix-vector multiply.

All of these are currently implemented as intrinsics. Where applicable we also plan to support these operations with native IR instructions (e.g. add/fadd).

Ok. Makes sense, I agree that supporting the existing pointwise vector operations makes sense.

These are exposed in clang via builtins. E.g. the above operations looks like this in C/C++:

typedef float mf4x4\_t \_\_attribute\_\_((matrix\_type(4, 4)));

mf4x4\_t add(mf4x4\_t a, mf4x4\_t b) {
return \_\_builtin\_matrix\_multiply(a, b);
}

I’d recommend splitting the clang discussion from the LLVM discussion, they are completely different tradeoffs involved. I’ll focus on the LLVM IR side of things.


\*\* Benefits \*\*

Having matrices represented as IR values allows for the usual algebraic and redundancy optimizations. But most importantly, by lifting memory aliasing concerns, we can guarantee vectorization to target-specific vectors.

Right, it is basically the same benefit as having a vector type. You also get the ability to have specific alignments etc.

I think there are several options in the design space here:

1\. Do nothing to the type system, but just use the existing vector types (<16 x float> in this case) with a new set of operations.
2\. Introduce a “matrix” concept and associated operations.
3\. Introduce N-dimensional vector registers and associated operations.


Personally, I’d be in favor of going with #1 followed by #3 followed distantly by #2.

FWIW, I strongly prefer #1 to the other options.

The reason I’m opposed to a matrix \*type\* is that this is far too specific of a concept to put into LLVM. We don’t even have signedness of integers in the type system: the instruction set is the major load bearing part of the IR design, and the instruction set is extensible through intrinsics.

Strongly agree.
Arguing in favor of #1: AFAICT, you only need to add the new intrinsics to do matmul etc. You could just define them to take 1D vectors but apply math to them that interprets them as a 2D space. This is completely an IR level modeling issue, and would be a very non-invasive patch. You’re literally just adding a few intrinsics. All the pointwise operations and insert/extract/shuffles will “just work”. The frontend handles mapping 2d indices to 1D indices.

Even better, it makes it easy to support interesting row-major and col-major style operations w/o further diversification of the type system.