summaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp')
-rw-r--r--llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp441
1 files changed, 353 insertions, 88 deletions
diff --git a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp
index 8e251ca940a3..42c183a6408e 100644
--- a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp
+++ b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp
@@ -34,6 +34,7 @@
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/MatrixBuilder.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
@@ -50,11 +51,6 @@ using namespace PatternMatch;
#define DEBUG_TYPE "lower-matrix-intrinsics"
-static cl::opt<bool> EnableShapePropagation(
- "matrix-propagate-shape", cl::init(true), cl::Hidden,
- cl::desc("Enable/disable shape propagation from matrix intrinsics to other "
- "instructions."));
-
static cl::opt<bool>
FuseMatrix("fuse-matrix", cl::init(true), cl::Hidden,
cl::desc("Enable/disable fusing matrix instructions."));
@@ -200,11 +196,16 @@ class LowerMatrixIntrinsics {
unsigned NumLoads = 0;
/// Number of compute operations emitted to generate this matrix.
unsigned NumComputeOps = 0;
+ /// Most of the time transposes can be fused with matrix multiplies or can
+ /// be folded away via algebraic simplifications. This is the number of
+ /// transposes that we failed to make "free" via such optimizations.
+ unsigned NumExposedTransposes = 0;
OpInfoTy &operator+=(const OpInfoTy &RHS) {
NumStores += RHS.NumStores;
NumLoads += RHS.NumLoads;
NumComputeOps += RHS.NumComputeOps;
+ NumExposedTransposes += RHS.NumExposedTransposes;
return *this;
}
};
@@ -309,6 +310,11 @@ class LowerMatrixIntrinsics {
return *this;
}
+ MatrixTy &addNumExposedTransposes(unsigned N) {
+ OpInfo.NumExposedTransposes += N;
+ return *this;
+ }
+
MatrixTy &addNumComputeOps(unsigned N) {
OpInfo.NumComputeOps += N;
return *this;
@@ -384,8 +390,10 @@ class LowerMatrixIntrinsics {
/// the result value of the instruction, with the only exceptions being store
/// instructions and the matrix_column_major_store intrinsics. For those, the
/// shape information indicates that those instructions should be lowered
- /// using shape information as well.
- DenseMap<Value *, ShapeInfo> ShapeMap;
+ /// using shape information as well. A ValueMap is used so that when
+ /// sub-passes like optimizeTransposes performs RAUW the map stays
+ /// up-to-date.
+ ValueMap<Value *, ShapeInfo> ShapeMap;
/// List of instructions to remove. While lowering, we are not replacing all
/// users of a lowered instruction, if shape information is available and
@@ -395,6 +403,18 @@ class LowerMatrixIntrinsics {
/// Map from instructions to their produced column matrix.
MapVector<Value *, MatrixTy> Inst2ColumnMatrix;
+private:
+ static FastMathFlags getFastMathFlags(Instruction *Inst) {
+ FastMathFlags FMF;
+
+ if (isa<FPMathOperator>(*Inst))
+ FMF = Inst->getFastMathFlags();
+
+ FMF.setAllowContract(AllowContractEnabled || FMF.allowContract());
+
+ return FMF;
+ }
+
public:
LowerMatrixIntrinsics(Function &F, TargetTransformInfo &TTI,
AliasAnalysis *AA, DominatorTree *DT, LoopInfo *LI,
@@ -408,12 +428,18 @@ public:
cast<FixedVectorType>(VT)->getNumElements());
}
- //
+ /// Is this the minimal version executed in the backend pipelines.
+ bool isMinimal() const {
+ return !DT;
+ }
+
/// Return the estimated number of vector ops required for an operation on
/// \p VT * N.
unsigned getNumOps(Type *ST, unsigned N) {
return std::ceil((ST->getPrimitiveSizeInBits() * N).getFixedSize() /
- double(TTI.getRegisterBitWidth(true)));
+ double(TTI.getRegisterBitWidth(
+ TargetTransformInfo::RGK_FixedWidthVector)
+ .getFixedSize()));
}
/// Return the set of vectors that a matrix value is lowered to.
@@ -657,34 +683,161 @@ public:
return NewWorkList;
}
- bool Visit() {
- if (EnableShapePropagation) {
- SmallVector<Instruction *, 32> WorkList;
+ /// Try moving transposes in order to fold them away or into multiplies.
+ void optimizeTransposes() {
+ auto ReplaceAllUsesWith = [this](Instruction &Old, Value *New) {
+ // We need to remove Old from the ShapeMap otherwise RAUW will replace it
+ // with New. We should only add New it it supportsShapeInfo so we insert
+ // it conditionally instead.
+ auto S = ShapeMap.find(&Old);
+ if (S != ShapeMap.end()) {
+ ShapeMap.erase(S);
+ if (supportsShapeInfo(New))
+ ShapeMap.insert({New, S->second});
+ }
+ Old.replaceAllUsesWith(New);
+ };
- // Initially only the shape of matrix intrinsics is known.
- // Initialize the work list with ops carrying shape information.
- for (BasicBlock &BB : Func)
- for (Instruction &Inst : BB) {
- IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst);
- if (!II)
- continue;
+ // First sink all transposes inside matmuls, hoping that we end up with NN,
+ // NT or TN variants.
+ for (BasicBlock &BB : reverse(Func)) {
+ for (auto II = BB.rbegin(); II != BB.rend();) {
+ Instruction &I = *II;
+ // We may remove II. By default continue on the next/prev instruction.
+ ++II;
+ // If we were to erase II, move again.
+ auto EraseFromParent = [&II](Value *V) {
+ auto *Inst = cast<Instruction>(V);
+ if (Inst->use_empty()) {
+ if (Inst == &*II) {
+ ++II;
+ }
+ Inst->eraseFromParent();
+ }
+ };
- switch (II->getIntrinsicID()) {
- case Intrinsic::matrix_multiply:
- case Intrinsic::matrix_transpose:
- case Intrinsic::matrix_column_major_load:
- case Intrinsic::matrix_column_major_store:
- WorkList.push_back(&Inst);
- break;
- default:
- break;
+ // If we're creating a new instruction, continue from there.
+ Instruction *NewInst = nullptr;
+
+ IRBuilder<> IB(&I);
+ MatrixBuilder<IRBuilder<>> Builder(IB);
+
+ Value *TA, *TAMA, *TAMB;
+ ConstantInt *R, *K, *C;
+ if (match(&I, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(TA)))) {
+
+ // Transpose of a transpose is a nop
+ Value *TATA;
+ if (match(TA,
+ m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(TATA)))) {
+ ReplaceAllUsesWith(I, TATA);
+ EraseFromParent(&I);
+ EraseFromParent(TA);
}
+
+ // (A * B)^t -> B^t * A^t
+ // RxK KxC CxK KxR
+ else if (match(TA, m_Intrinsic<Intrinsic::matrix_multiply>(
+ m_Value(TAMA), m_Value(TAMB), m_ConstantInt(R),
+ m_ConstantInt(K), m_ConstantInt(C)))) {
+ Value *T0 = Builder.CreateMatrixTranspose(TAMB, K->getZExtValue(),
+ C->getZExtValue(),
+ TAMB->getName() + "_t");
+ // We are being run after shape prop, add shape for newly created
+ // instructions so that we lower them later.
+ setShapeInfo(T0, {C, K});
+ Value *T1 = Builder.CreateMatrixTranspose(TAMA, R->getZExtValue(),
+ K->getZExtValue(),
+ TAMA->getName() + "_t");
+ setShapeInfo(T1, {K, R});
+ NewInst = Builder.CreateMatrixMultiply(T0, T1, C->getZExtValue(),
+ K->getZExtValue(),
+ R->getZExtValue(), "mmul");
+ ReplaceAllUsesWith(I, NewInst);
+ EraseFromParent(&I);
+ EraseFromParent(TA);
+ }
+ }
+
+ // If we replaced I with a new instruction, continue from there.
+ if (NewInst)
+ II = std::next(BasicBlock::reverse_iterator(NewInst));
+ }
+ }
+
+ // If we have a TT matmul, lift the transpose. We may be able to fold into
+ // consuming multiply.
+ for (BasicBlock &BB : Func) {
+ for (BasicBlock::iterator II = BB.begin(); II != BB.end();) {
+ Instruction *I = &*II;
+ // We may remove I.
+ ++II;
+ Value *A, *B, *AT, *BT;
+ ConstantInt *R, *K, *C;
+ // A^t * B ^t -> (B * A)^t
+ if (match(&*I, m_Intrinsic<Intrinsic::matrix_multiply>(
+ m_Value(A), m_Value(B), m_ConstantInt(R),
+ m_ConstantInt(K), m_ConstantInt(C))) &&
+ match(A, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(AT))) &&
+ match(B, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value((BT))))) {
+ IRBuilder<> IB(&*I);
+ MatrixBuilder<IRBuilder<>> Builder(IB);
+ Value *M = Builder.CreateMatrixMultiply(
+ BT, AT, C->getZExtValue(), K->getZExtValue(), R->getZExtValue());
+ setShapeInfo(M, {C, R});
+ Instruction *NewInst = Builder.CreateMatrixTranspose(
+ M, C->getZExtValue(), R->getZExtValue());
+ ReplaceAllUsesWith(*I, NewInst);
+ if (I->use_empty())
+ I->eraseFromParent();
+ if (A->use_empty())
+ cast<Instruction>(A)->eraseFromParent();
+ if (A != B && B->use_empty())
+ cast<Instruction>(B)->eraseFromParent();
+ }
+ }
+ }
+ }
+
+ bool Visit() {
+ SmallVector<Instruction *, 32> WorkList;
+
+ // Initially only the shape of matrix intrinsics is known.
+ // Initialize the work list with ops carrying shape information.
+ for (BasicBlock &BB : Func)
+ for (Instruction &Inst : BB) {
+ IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst);
+ if (!II)
+ continue;
+
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::matrix_multiply:
+ case Intrinsic::matrix_transpose:
+ case Intrinsic::matrix_column_major_load:
+ case Intrinsic::matrix_column_major_store:
+ WorkList.push_back(&Inst);
+ break;
+ default:
+ break;
}
- // Propagate shapes until nothing changes any longer.
- while (!WorkList.empty()) {
- WorkList = propagateShapeForward(WorkList);
- WorkList = propagateShapeBackward(WorkList);
}
+
+ // Avoid unnecessary work if there are no matrix intrinsics in the function.
+ if (WorkList.empty())
+ return false;
+
+ // Propagate shapes until nothing changes any longer.
+ while (!WorkList.empty()) {
+ WorkList = propagateShapeForward(WorkList);
+ WorkList = propagateShapeBackward(WorkList);
+ }
+
+ if (!isMinimal()) {
+ optimizeTransposes();
+ LLVM_DEBUG({
+ dbgs() << "Dump after matrix transpose optimization:\n";
+ Func.dump();
+ });
}
bool Changed = false;
@@ -736,8 +889,33 @@ public:
RemarkGen.emitRemarks();
}
- for (Instruction *Inst : reverse(ToRemove))
+ // Delete the instructions backwards, as it has a reduced likelihood of
+ // having to update as many def-use and use-def chains.
+ //
+ // Because we add to ToRemove during fusion we can't guarantee that defs
+ // are before uses. Change uses to undef temporarily as these should get
+ // removed as well.
+ //
+ // For verification, we keep track of where we changed uses to undefs in
+ // UndefedInsts and then check that we in fact remove them.
+ SmallSet<Instruction *, 16> UndefedInsts;
+ for (auto *Inst : reverse(ToRemove)) {
+ for (auto I = Inst->use_begin(), E = Inst->use_end(); I != E;) {
+ Use &U = *I++;
+ if (auto *Undefed = dyn_cast<Instruction>(U.getUser()))
+ UndefedInsts.insert(Undefed);
+ U.set(UndefValue::get(Inst->getType()));
+ }
Inst->eraseFromParent();
+ UndefedInsts.erase(Inst);
+ }
+ if (!UndefedInsts.empty()) {
+ // If we didn't remove all undefed instructions, it's a hard error.
+ dbgs() << "Undefed but present instructions:\n";
+ for (auto *I : UndefedInsts)
+ dbgs() << *I << "\n";
+ llvm_unreachable("Undefed but instruction not removed");
+ }
return Changed;
}
@@ -797,15 +975,16 @@ public:
/// vectors.
MatrixTy loadMatrix(Type *Ty, Value *Ptr, MaybeAlign MAlign, Value *Stride,
bool IsVolatile, ShapeInfo Shape, IRBuilder<> &Builder) {
- auto VType = cast<VectorType>(Ty);
- Value *EltPtr = createElementPtr(Ptr, VType->getElementType(), Builder);
+ auto *VType = cast<VectorType>(Ty);
+ Type *EltTy = VType->getElementType();
+ Type *VecTy = FixedVectorType::get(EltTy, Shape.getStride());
+ Value *EltPtr = createElementPtr(Ptr, EltTy, Builder);
MatrixTy Result;
for (unsigned I = 0, E = Shape.getNumVectors(); I < E; ++I) {
Value *GEP = computeVectorAddr(EltPtr, Builder.getInt64(I), Stride,
- Shape.getStride(), VType->getElementType(),
- Builder);
+ Shape.getStride(), EltTy, Builder);
Value *Vector = Builder.CreateAlignedLoad(
- GEP, getAlignForIndex(I, Stride, VType->getElementType(), MAlign),
+ VecTy, GEP, getAlignForIndex(I, Stride, EltTy, MAlign),
IsVolatile, "col.load");
Result.addVector(Vector);
@@ -987,18 +1166,19 @@ public:
}
/// Cache \p Matrix as result of \p Inst and update the uses of \p Inst. For
- /// users with shape information, there's nothing to do: the will use the
+ /// users with shape information, there's nothing to do: they will use the
/// cached value when they are lowered. For other users, \p Matrix is
/// flattened and the uses are updated to use it. Also marks \p Inst for
/// deletion.
void finalizeLowering(Instruction *Inst, MatrixTy Matrix,
IRBuilder<> &Builder) {
- Inst2ColumnMatrix.insert(std::make_pair(Inst, Matrix));
+ auto inserted = Inst2ColumnMatrix.insert(std::make_pair(Inst, Matrix));
+ (void)inserted;
+ assert(inserted.second && "multiple matrix lowering mapping");
ToRemove.push_back(Inst);
Value *Flattened = nullptr;
- for (auto I = Inst->use_begin(), E = Inst->use_end(); I != E;) {
- Use &U = *I++;
+ for (Use &U : llvm::make_early_inc_range(Inst->uses())) {
if (ShapeMap.find(U.getUser()) == ShapeMap.end()) {
if (!Flattened)
Flattened = Matrix.embedInVector(Builder);
@@ -1009,11 +1189,17 @@ public:
/// Compute \p Result += \p A * \p B for input matrices with left-associating
/// addition.
+ ///
+ /// We can fold a transpose into the operand that is used to extract scalars.
+ /// This is the first operands with row-major and the second with
+ /// column-major. If \p IsScalarMatrixTransposed we assume the appropriate
+ /// operand is transposed.
void emitMatrixMultiply(MatrixTy &Result, const MatrixTy &A,
- const MatrixTy &B, bool AllowContraction,
- IRBuilder<> &Builder, bool isTiled) {
+ const MatrixTy &B, IRBuilder<> &Builder, bool IsTiled,
+ bool IsScalarMatrixTransposed, FastMathFlags FMF) {
const unsigned VF = std::max<unsigned>(
- TTI.getRegisterBitWidth(true) /
+ TTI.getRegisterBitWidth(TargetTransformInfo::RGK_FixedWidthVector)
+ .getFixedSize() /
Result.getElementType()->getPrimitiveSizeInBits().getFixedSize(),
1U);
unsigned R = Result.getNumRows();
@@ -1025,6 +1211,9 @@ public:
Result.isColumnMajor() == A.isColumnMajor() &&
"operands must agree on matrix layout");
unsigned NumComputeOps = 0;
+
+ Builder.setFastMathFlags(FMF);
+
if (A.isColumnMajor()) {
// Multiply columns from the first operand with scalars from the second
// operand. Then move along the K axes and accumulate the columns. With
@@ -1039,15 +1228,17 @@ public:
while (I + BlockSize > R)
BlockSize /= 2;
- Value *Sum = isTiled ? Result.extractVector(I, J, BlockSize, Builder)
+ Value *Sum = IsTiled ? Result.extractVector(I, J, BlockSize, Builder)
: nullptr;
for (unsigned K = 0; K < M; ++K) {
Value *L = A.extractVector(I, K, BlockSize, Builder);
- Value *RH = Builder.CreateExtractElement(B.getColumn(J), K);
+ Value *RH = Builder.CreateExtractElement(
+ B.getColumn(IsScalarMatrixTransposed ? K : J),
+ IsScalarMatrixTransposed ? J : K);
Value *Splat = Builder.CreateVectorSplat(BlockSize, RH, "splat");
- Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, L, Splat,
- Result.getElementType()->isFloatingPointTy(),
- Builder, AllowContraction, NumComputeOps);
+ Sum =
+ createMulAdd(isSumZero && K == 0 ? nullptr : Sum, L, Splat,
+ IsFP, Builder, FMF.allowContract(), NumComputeOps);
}
Result.setVector(J,
insertVector(Result.getVector(J), I, Sum, Builder));
@@ -1068,10 +1259,13 @@ public:
Value *Sum = nullptr;
for (unsigned K = 0; K < M; ++K) {
Value *R = B.extractVector(K, J, BlockSize, Builder);
- Value *LH = Builder.CreateExtractElement(A.getVector(I), K);
+ Value *LH = Builder.CreateExtractElement(
+ A.getVector(IsScalarMatrixTransposed ? K : I),
+ IsScalarMatrixTransposed ? I : K);
Value *Splat = Builder.CreateVectorSplat(BlockSize, LH, "splat");
- Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, Splat, R,
- IsFP, Builder, AllowContraction, NumComputeOps);
+ Sum =
+ createMulAdd(isSumZero && K == 0 ? nullptr : Sum, Splat, R,
+ IsFP, Builder, FMF.allowContract(), NumComputeOps);
}
Result.setVector(I,
insertVector(Result.getVector(I), J, Sum, Builder));
@@ -1089,10 +1283,8 @@ public:
MemoryLocation StoreLoc = MemoryLocation::get(Store);
MemoryLocation LoadLoc = MemoryLocation::get(Load);
- AliasResult LdAliased = AA->alias(LoadLoc, StoreLoc);
-
// If we can statically determine noalias we're good.
- if (!LdAliased)
+ if (AA->isNoAlias(LoadLoc, StoreLoc))
return Load->getPointerOperand();
// Create code to check if the memory locations of the Load and Store
@@ -1179,10 +1371,11 @@ public:
const unsigned M = LShape.NumColumns;
auto *EltType = cast<VectorType>(MatMul->getType())->getElementType();
- const unsigned VF =
- std::max<unsigned>(TTI.getRegisterBitWidth(true) /
- EltType->getPrimitiveSizeInBits().getFixedSize(),
- 1U);
+ const unsigned VF = std::max<unsigned>(
+ TTI.getRegisterBitWidth(TargetTransformInfo::RGK_FixedWidthVector)
+ .getFixedSize() /
+ EltType->getPrimitiveSizeInBits().getFixedSize(),
+ 1U);
// Cost model for tiling
//
@@ -1210,8 +1403,7 @@ public:
}
void createTiledLoops(CallInst *MatMul, Value *LPtr, ShapeInfo LShape,
- Value *RPtr, ShapeInfo RShape, StoreInst *Store,
- bool AllowContract) {
+ Value *RPtr, ShapeInfo RShape, StoreInst *Store) {
auto *EltType = cast<VectorType>(MatMul->getType())->getElementType();
// Create the main tiling loop nest.
@@ -1247,7 +1439,8 @@ public:
{TileSize, TileSize}, EltType, Builder);
MatrixTy B = loadMatrix(RPtr, {}, false, RShape, TI.CurrentK, TI.CurrentCol,
{TileSize, TileSize}, EltType, Builder);
- emitMatrixMultiply(TileResult, A, B, AllowContract, Builder, true);
+ emitMatrixMultiply(TileResult, A, B, Builder, true, false,
+ getFastMathFlags(MatMul));
// Store result after the inner loop is done.
Builder.SetInsertPoint(TI.RowLoopLatch->getTerminator());
storeMatrix(TileResult, Store->getPointerOperand(), Store->getAlign(),
@@ -1286,11 +1479,8 @@ public:
Value *BPtr = getNonAliasingPointer(LoadOp1, Store, MatMul);
Value *CPtr = Store->getPointerOperand();
- bool AllowContract = AllowContractEnabled || (isa<FPMathOperator>(MatMul) &&
- MatMul->hasAllowContract());
if (TileUseLoops && (R % TileSize == 0 && C % TileSize == 0))
- createTiledLoops(MatMul, APtr, LShape, BPtr, RShape, Store,
- AllowContract);
+ createTiledLoops(MatMul, APtr, LShape, BPtr, RShape, Store);
else {
IRBuilder<> Builder(Store);
for (unsigned J = 0; J < C; J += TileSize)
@@ -1309,7 +1499,8 @@ public:
loadMatrix(BPtr, LoadOp1->getAlign(), LoadOp1->isVolatile(),
RShape, Builder.getInt64(K), Builder.getInt64(J),
{TileM, TileC}, EltType, Builder);
- emitMatrixMultiply(Res, A, B, AllowContract, Builder, true);
+ emitMatrixMultiply(Res, A, B, Builder, true, false,
+ getFastMathFlags(MatMul));
}
storeMatrix(Res, CPtr, Store->getAlign(), Store->isVolatile(), {R, M},
Builder.getInt64(I), Builder.getInt64(J), EltType,
@@ -1326,7 +1517,7 @@ public:
FusedInsts.insert(LoadOp0);
LoadOp0->eraseFromParent();
}
- if (LoadOp1->hasNUses(0)) {
+ if (LoadOp1 != LoadOp0 && LoadOp1->hasNUses(0)) {
FusedInsts.insert(LoadOp1);
LoadOp1->eraseFromParent();
}
@@ -1334,29 +1525,97 @@ public:
/// Try to lower matrix multiply chains by fusing operations.
///
- /// Currently we only lower {ld, ld} -> matmul -> st chains.
- //
- /// No need to return a MatrixTy object for the result of the operation, since
- /// the single store user will be lowered as part of this. Instructions that
- /// are completely eliminated by fusion are added to \p FusedInsts.
+ /// Call finalizeLowering on lowered instructions. Instructions that are
+ /// completely eliminated by fusion are added to \p FusedInsts.
void LowerMatrixMultiplyFused(CallInst *MatMul,
SmallPtrSetImpl<Instruction *> &FusedInsts) {
- if (!FuseMatrix || !MatMul->hasOneUse() ||
- MatrixLayout != MatrixLayoutTy::ColumnMajor || !DT)
+ if (!FuseMatrix || !DT)
return;
assert(AA && LI && "Analyses should be available");
- auto *LoadOp0 = dyn_cast<LoadInst>(MatMul->getOperand(0));
- auto *LoadOp1 = dyn_cast<LoadInst>(MatMul->getOperand(1));
+ Value *A = MatMul->getArgOperand(0);
+ Value *B = MatMul->getArgOperand(1);
+
+ // We can fold the transpose into the operand that is used to fetch scalars.
+ Value *T;
+ if (MatrixLayout == MatrixLayoutTy::ColumnMajor
+ ? match(B, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(T)))
+ : match(A, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(T)))) {
+ IRBuilder<> Builder(MatMul);
+ auto *EltType = cast<VectorType>(MatMul->getType())->getElementType();
+ ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));
+ ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));
+ const unsigned R = LShape.NumRows;
+ const unsigned M = LShape.NumColumns;
+ const unsigned C = RShape.NumColumns;
+
+ MatrixTy MA;
+ MatrixTy MB;
+
+ Value *Transpose;
+ if (MatrixLayout == MatrixLayoutTy::ColumnMajor) {
+ MA = getMatrix(A, ShapeInfo(R, M), Builder);
+ MB = getMatrix(T, ShapeInfo(C, M), Builder);
+ Transpose = B;
+ } else {
+ MA = getMatrix(T, ShapeInfo(R, M), Builder);
+ MB = getMatrix(B, ShapeInfo(C, M), Builder);
+ Transpose = A;
+ }
+
+ // Initialize the output
+ MatrixTy Result(R, C, EltType);
+
+ emitMatrixMultiply(Result, MA, MB, Builder, false, true,
+ getFastMathFlags(MatMul));
+
+ FusedInsts.insert(MatMul);
+ if (Transpose->hasOneUse()) {
+ FusedInsts.insert(cast<Instruction>(Transpose));
+ ToRemove.push_back(cast<Instruction>(Transpose));
+ // TODO: add a fake entry for the folded instruction so that this is
+ // included in the expression in the remark.
+ Inst2ColumnMatrix[Transpose] = MatrixTy(M, C, EltType);
+ }
+ finalizeLowering(MatMul, Result, Builder);
+ return;
+ }
+
+ if (!MatMul->hasOneUse() || MatrixLayout != MatrixLayoutTy::ColumnMajor)
+ return;
+
+ // Lower {ld, ld} -> matmul -> st chains. No need to call finalizeLowering
+ // since the single store user will be lowered as part of this.
+ auto *LoadOp0 = dyn_cast<LoadInst>(A);
+ auto *LoadOp1 = dyn_cast<LoadInst>(B);
auto *Store = dyn_cast<StoreInst>(*MatMul->user_begin());
if (LoadOp0 && LoadOp1 && Store) {
// The store address must dominate the MatMul instruction, otherwise
// we create invalid IR.
- // FIXME: See if we can hoist the store address computation.
- auto *AddrI = dyn_cast<Instruction>(Store->getOperand(1));
- if (AddrI && (!DT->dominates(AddrI, MatMul)))
- return;
+ SetVector<Value *> WorkList;
+ WorkList.insert(Store->getOperand(1));
+ SmallVector<Instruction *> ToHoist;
+ for (unsigned I = 0; I != WorkList.size(); ++I) {
+ Value *Current = WorkList[I];
+ auto *CurrI = dyn_cast<Instruction>(Current);
+ if (!CurrI)
+ continue;
+ if (isa<PHINode>(CurrI))
+ return;
+ if (DT->dominates(CurrI, MatMul))
+ continue;
+ if (CurrI->mayHaveSideEffects() || CurrI->mayReadFromMemory())
+ return;
+ ToHoist.push_back(CurrI);
+ WorkList.insert(CurrI->op_begin(), CurrI->op_end());
+ }
+
+ sort(ToHoist, [this](Instruction *A, Instruction *B) {
+ return DT->dominates(A, B);
+ });
+ for (Instruction *I : ToHoist)
+ I->moveBefore(MatMul);
emitSIMDTiling(MatMul, LoadOp0, LoadOp1, Store, FusedInsts);
return;
@@ -1384,9 +1643,8 @@ public:
assert(Lhs.getElementType() == Result.getElementType() &&
"Matrix multiply result element type does not match arguments.");
- bool AllowContract = AllowContractEnabled || (isa<FPMathOperator>(MatMul) &&
- MatMul->hasAllowContract());
- emitMatrixMultiply(Result, Lhs, Rhs, AllowContract, Builder, false);
+ emitMatrixMultiply(Result, Lhs, Rhs, Builder, false, false,
+ getFastMathFlags(MatMul));
finalizeLowering(MatMul, Result, Builder);
}
@@ -1423,7 +1681,8 @@ public:
// account for later simplifications/combines.
finalizeLowering(
Inst,
- Result.addNumComputeOps(2 * ArgShape.NumRows * ArgShape.NumColumns),
+ Result.addNumComputeOps(2 * ArgShape.NumRows * ArgShape.NumColumns)
+ .addNumExposedTransposes(1),
Builder);
}
@@ -1470,6 +1729,8 @@ public:
Result.isColumnMajor() == A.isColumnMajor() &&
"operands must agree on matrix layout");
+ Builder.setFastMathFlags(getFastMathFlags(Inst));
+
// Helper to perform binary op on vectors.
auto BuildVectorOp = [&Builder, Inst](Value *LHS, Value *RHS) {
switch (Inst->getOpcode()) {
@@ -1514,6 +1775,8 @@ public:
MatrixTy Result;
MatrixTy M = getMatrix(Op, Shape, Builder);
+ Builder.setFastMathFlags(getFastMathFlags(Inst));
+
// Helper to perform unary op on vectors.
auto BuildVectorOp = [&Builder, Inst](Value *Op) {
switch (Inst->getOpcode()) {
@@ -1631,7 +1894,7 @@ public:
return;
}
IntrinsicInst *II = dyn_cast<IntrinsicInst>(CI);
- write(StringRef(Intrinsic::getName(II->getIntrinsicID(), {}))
+ write(Intrinsic::getBaseName(II->getIntrinsicID())
.drop_front(StringRef("llvm.matrix.").size()));
write(".");
std::string Tmp;
@@ -1938,7 +2201,9 @@ public:
Rem << ore::NV("NumStores", Counts.NumStores) << " stores, "
<< ore::NV("NumLoads", Counts.NumLoads) << " loads, "
<< ore::NV("NumComputeOps", Counts.NumComputeOps)
- << " compute ops";
+ << " compute ops, "
+ << ore::NV("NumExposedTransposes", Counts.NumExposedTransposes)
+ << " exposed transposes";
if (SharedCounts.NumStores > 0 || SharedCounts.NumLoads > 0 ||
SharedCounts.NumComputeOps > 0) {