brintos

brintos / llvm-project-archived public Read only

0
0
Text · 111.3 KiB · 7e70ba2 Raw
2909 lines · cpp
1//===- LowerMatrixIntrinsics.cpp -  Lower matrix intrinsics -----*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Lower matrix intrinsics to vector operations.10//11// TODO:12//  * Improve fusion:13//   * Support more cases, e.g. multiply-add, multiply-sub, operands/results14//     transposed.15//   * Improve cost-modeling, e.g. choose different number of rows/columns16//     columns for tiles, consider cost of copies on alias.17//18//===----------------------------------------------------------------------===//19 20#include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h"21#include "llvm/ADT/PostOrderIterator.h"22#include "llvm/ADT/STLExtras.h"23#include "llvm/ADT/ScopeExit.h"24#include "llvm/ADT/SmallVector.h"25#include "llvm/ADT/Statistic.h"26#include "llvm/Analysis/AliasAnalysis.h"27#include "llvm/Analysis/DomTreeUpdater.h"28#include "llvm/Analysis/LoopInfo.h"29#include "llvm/Analysis/OptimizationRemarkEmitter.h"30#include "llvm/Analysis/TargetTransformInfo.h"31#include "llvm/Analysis/ValueTracking.h"32#include "llvm/Analysis/VectorUtils.h"33#include "llvm/IR/CFG.h"34#include "llvm/IR/DataLayout.h"35#include "llvm/IR/DebugInfoMetadata.h"36#include "llvm/IR/DerivedTypes.h"37#include "llvm/IR/Function.h"38#include "llvm/IR/IRBuilder.h"39#include "llvm/IR/InstrTypes.h"40#include "llvm/IR/Instructions.h"41#include "llvm/IR/IntrinsicInst.h"42#include "llvm/IR/MatrixBuilder.h"43#include "llvm/IR/PatternMatch.h"44#include "llvm/Support/Alignment.h"45#include "llvm/Support/CommandLine.h"46#include "llvm/Support/Compiler.h"47#include "llvm/Support/Debug.h"48#include "llvm/Transforms/Utils/BasicBlockUtils.h"49#include "llvm/Transforms/Utils/LoopUtils.h"50#include "llvm/Transforms/Utils/MatrixUtils.h"51 52#include <cmath>53 54using namespace llvm;55using namespace PatternMatch;56 57#define DEBUG_TYPE "lower-matrix-intrinsics"58 59STATISTIC(FlattenedMatrices, "Number of matrix flattenings");60STATISTIC(ReshapedMatrices, "Number of matrix reshapes");61STATISTIC(SplitMatrices, "Number of matrix splits");62 63static cl::opt<bool>64    FuseMatrix("fuse-matrix", cl::init(true), cl::Hidden,65               cl::desc("Enable/disable fusing matrix instructions."));66// TODO: Allow and use non-square tiles.67static cl::opt<unsigned> TileSize(68    "fuse-matrix-tile-size", cl::init(4), cl::Hidden,69    cl::desc(70        "Tile size for matrix instruction fusion using square-shaped tiles."));71static cl::opt<bool> TileUseLoops("fuse-matrix-use-loops", cl::init(false),72                                  cl::Hidden,73                                  cl::desc("Generate loop nest for tiling."));74static cl::opt<bool> ForceFusion(75    "force-fuse-matrix", cl::init(false), cl::Hidden,76    cl::desc("Force matrix instruction fusion even if not profitable."));77static cl::opt<bool> AllowContractEnabled(78    "matrix-allow-contract", cl::init(false), cl::Hidden,79    cl::desc("Allow the use of FMAs if available and profitable. This may "80             "result in different results, due to less rounding error."));81 82static cl::opt<bool>83    VerifyShapeInfo("verify-matrix-shapes", cl::Hidden,84                    cl::desc("Enable/disable matrix shape verification."),85                    cl::init(false));86 87enum class MatrixLayoutTy { ColumnMajor, RowMajor };88 89static cl::opt<MatrixLayoutTy> MatrixLayout(90    "matrix-default-layout", cl::init(MatrixLayoutTy::ColumnMajor),91    cl::desc("Sets the default matrix layout"),92    cl::values(clEnumValN(MatrixLayoutTy::ColumnMajor, "column-major",93                          "Use column-major layout"),94               clEnumValN(MatrixLayoutTy::RowMajor, "row-major",95                          "Use row-major layout")));96 97static cl::opt<bool> PrintAfterTransposeOpt("matrix-print-after-transpose-opt",98                                            cl::init(false));99 100static cl::opt<unsigned> SplitMatmulRemainderOverThreshold(101    "matrix-split-matmul-remainder-over-threshold", cl::Hidden,102    cl::desc("Illegal remainder vectors over this size in bits should be split "103             "in the inner loop of matmul"),104    cl::init(0));105 106/// Helper function to either return Scope, if it is a subprogram or the107/// attached subprogram for a local scope.108static DISubprogram *getSubprogram(DIScope *Scope) {109  if (auto *Subprogram = dyn_cast<DISubprogram>(Scope))110    return Subprogram;111  return cast<DILocalScope>(Scope)->getSubprogram();112}113 114/// Return true if V is a splat of a value (which is used when multiplying a115/// matrix with a scalar).116static bool isSplat(Value *V) {117  if (auto *SV = dyn_cast<ShuffleVectorInst>(V))118    return SV->isZeroEltSplat();119  return false;120}121 122/// Match any mul operation (fp or integer).123template <typename LTy, typename RTy>124static auto m_AnyMul(const LTy &L, const RTy &R) {125  return m_CombineOr(m_Mul(L, R), m_FMul(L, R));126}127 128/// Match any add operation (fp or integer).129template <typename LTy, typename RTy>130static auto m_AnyAdd(const LTy &L, const RTy &R) {131  return m_CombineOr(m_Add(L, R), m_FAdd(L, R));132}133 134// Given an element pointer \p BasePtr to the start of a (sub) matrix, compute135// the start address of vector \p VecIdx with type (\p EltType x \p NumElements)136// assuming \p Stride elements between start two consecutive vectors.137// \p Stride must be >= \p NumElements.138// For column-major matrixes, the function computes the address of a column139// vectors and \p NumElements must be set to the number of elements in a column140// (= number of rows of the matrix). For row-major matrixes, the function141// computes the address of a row vector and \p NumElements must be set to the142// number of elements in a column (= number of columns of the matrix).143//144// Consider a 4x4 matrix in column-mjaor layout like below145//146//      0       1      2      3147// 0   v_0_0  v_0_1  v_0_2  v_0_3148// 1   v_1_0  v_1_1  v_1_2  v_1_3149// 2   v_2_0  v_2_1  v_2_2  v_2_3150// 3   v_3_0  v_3_1  v_3_2  v_3_3151 152// To compute the column addresses for a 2x3 sub-matrix at row 1 and column 1,153// we need a pointer to the first element of the submatrix as base pointer.154// Then we can use computeVectorAddr to compute the addresses for the columns155// of the sub-matrix.156//157// Column 0: computeVectorAddr(Base, 0 (column), 4 (stride), 2 (num rows), ..)158//           -> just returns Base159// Column 1: computeVectorAddr(Base, 1 (column), 4 (stride), 2 (num rows), ..)160//           -> returns Base + (1 * 4)161// Column 2: computeVectorAddr(Base, 2 (column), 4 (stride), 2 (num rows), ..)162//           -> returns Base + (2 * 4)163//164// The graphic below illustrates the number of elements in a column (marked165// with |) and the number of skipped elements (marked with }).166//167//         v_0_0  v_0_1 {v_0_2 {v_0_3168//                Base   Col 1  Col 2169//                  |     |      |170//         v_1_0 |v_1_1 |v_1_2 |v_1_3171//         v_2_0 |v_2_1 |v_2_2 |v_2_3172//         v_3_0 {v_3_1 {v_3_2  v_3_3173//174static Value *computeVectorAddr(Value *BasePtr, Value *VecIdx, Value *Stride,175                                unsigned NumElements, Type *EltType,176                                IRBuilder<> &Builder) {177 178  assert((!isa<ConstantInt>(Stride) ||179          cast<ConstantInt>(Stride)->getZExtValue() >= NumElements) &&180         "Stride must be >= the number of elements in the result vector.");181 182  // Compute the start of the vector with index VecIdx as VecIdx * Stride.183  Value *VecStart = Builder.CreateMul(VecIdx, Stride, "vec.start");184 185  // Get pointer to the start of the selected vector. Skip GEP creation,186  // if we select vector 0.187  if (isa<ConstantInt>(VecStart) && cast<ConstantInt>(VecStart)->isZero())188    VecStart = BasePtr;189  else190    VecStart = Builder.CreateGEP(EltType, BasePtr, VecStart, "vec.gep");191 192  return VecStart;193}194 195namespace {196struct ShapeInfo {197  unsigned NumRows;198  unsigned NumColumns;199 200  bool IsColumnMajor;201 202  ShapeInfo(unsigned NumRows = 0, unsigned NumColumns = 0)203      : NumRows(NumRows), NumColumns(NumColumns),204        IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {}205 206  ShapeInfo(Value *NumRows, Value *NumColumns)207      : ShapeInfo(cast<ConstantInt>(NumRows)->getZExtValue(),208                  cast<ConstantInt>(NumColumns)->getZExtValue()) {}209 210  bool operator==(const ShapeInfo &other) {211    return NumRows == other.NumRows && NumColumns == other.NumColumns;212  }213  bool operator!=(const ShapeInfo &other) { return !(*this == other); }214 215  /// Returns true if shape-information is defined, meaning both dimensions216  /// are != 0.217  operator bool() const {218    assert(NumRows == 0 || NumColumns != 0);219    return NumRows != 0;220  }221 222  unsigned getStride() const {223    if (IsColumnMajor)224      return NumRows;225    return NumColumns;226  }227 228  unsigned getNumVectors() const {229    if (IsColumnMajor)230      return NumColumns;231    return NumRows;232  }233 234  /// Returns the transposed shape.235  ShapeInfo t() const { return ShapeInfo(NumColumns, NumRows); }236 237  friend raw_ostream &operator<<(raw_ostream &OS, ShapeInfo SI);238 239  LLVM_DUMP_METHOD void dump() const { dbgs() << *this << '\n'; }240};241 242raw_ostream &operator<<(raw_ostream &OS, ShapeInfo SI) {243  return OS << SI.NumRows << 'x' << SI.NumColumns;244}245 246} // namespace247 248static bool isShapePreserving(Value *V) {249  Instruction *I = dyn_cast<Instruction>(V);250  if (!I)251    return true;252 253  if (isa<SelectInst>(I))254    return true;255 256  if (I->isBinaryOp())257    return true;258 259  if (auto *Cast = dyn_cast<CastInst>(V)) {260    switch (Cast->getOpcode()) {261    case llvm::Instruction::Trunc:262    case llvm::Instruction::ZExt:263    case llvm::Instruction::SExt:264    case llvm::Instruction::FPToUI:265    case llvm::Instruction::FPToSI:266    case llvm::Instruction::UIToFP:267    case llvm::Instruction::SIToFP:268    case llvm::Instruction::FPTrunc:269    case llvm::Instruction::FPExt:270      return true;271    case llvm::Instruction::AddrSpaceCast:272    case CastInst::PtrToAddr:273    case CastInst::PtrToInt:274    case CastInst::IntToPtr:275      return false;276    case CastInst::BitCast: {277      if (auto *SrcVTy = dyn_cast<FixedVectorType>(Cast->getSrcTy()))278        if (auto *DestVTy = dyn_cast<FixedVectorType>(Cast->getDestTy()))279          return SrcVTy->getNumElements() == DestVTy->getNumElements();280      return false;281    }282    case llvm::Instruction::CastOpsEnd:283      llvm_unreachable("not an actual cast op");284    }285    llvm_unreachable("unhandled cast opcode");286  }287 288  if (auto *II = dyn_cast<IntrinsicInst>(V))289    switch (II->getIntrinsicID()) {290    case Intrinsic::abs:291    case Intrinsic::fabs:292      return true;293    default:294      return false;295    }296 297  switch (I->getOpcode()) {298  case Instruction::PHI:299  case Instruction::FNeg:300    return true;301  default:302    return false;303  }304}305 306/// Return an iterator over the operands of \p I that should share shape307/// information with \p I.308static iterator_range<Use *> getShapedOperandsForInst(Instruction *I) {309  assert(isShapePreserving(I) &&310         "Can't retrieve shaped operands for an instruction that does not "311         "preserve shape information");312  auto Ops = I->operands();313  return isa<SelectInst>(I) ? drop_begin(Ops) : Ops;314}315 316/// Return the ShapeInfo for the result of \p I, it it can be determined.317static std::optional<ShapeInfo>318computeShapeInfoForInst(Instruction *I,319                        const DenseMap<Value *, ShapeInfo> &ShapeMap) {320  Value *M;321  Value *N;322  Value *K;323  if (match(I, m_Intrinsic<Intrinsic::matrix_multiply>(324                   m_Value(), m_Value(), m_Value(M), m_Value(N), m_Value(K))))325    return ShapeInfo(M, K);326  if (match(I, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(), m_Value(M),327                                                        m_Value(N)))) {328    // Flip dimensions.329    return ShapeInfo(N, M);330  }331  if (match(I, m_Intrinsic<Intrinsic::matrix_column_major_store>(332                   m_Value(), m_Value(), m_Value(), m_Value(), m_Value(M),333                   m_Value(N))))334    return ShapeInfo(N, M);335  if (match(I, m_Intrinsic<Intrinsic::matrix_column_major_load>(336                   m_Value(), m_Value(), m_Value(), m_Value(M), m_Value(N))))337    return ShapeInfo(M, N);338  Value *MatrixA;339  if (match(I, m_Store(m_Value(MatrixA), m_Value()))) {340    auto OpShape = ShapeMap.find(MatrixA);341    if (OpShape != ShapeMap.end())342      return OpShape->second;343  }344 345  if (isShapePreserving(I)) {346    auto ShapedOps = getShapedOperandsForInst(I);347    // Find the first operand that has a known shape and use that.348    for (auto &Op : ShapedOps) {349      auto OpShape = ShapeMap.find(Op.get());350      if (OpShape != ShapeMap.end())351        return OpShape->second;352    }353  }354  return std::nullopt;355}356 357namespace {358 359/// LowerMatrixIntrinsics contains the methods used to lower matrix intrinsics.360///361/// Currently, the lowering for each matrix intrinsic is done as follows:362/// 1. Propagate the shape information from intrinsics to connected363/// instructions.364/// 2. Lower instructions with shape information (assuming column-major layout).365///  The lowering works similarly using row-major layout.366///  2.1. Get column vectors for each argument. If we already lowered the367///       definition of an argument, use the produced column vectors directly.368///       If not, split the operand vector containing an embedded matrix into369///       a set of column vectors,370///  2.2. Lower the instruction in terms of column major operations, which371///       yields a set of column vectors containing result matrix. Note that we372///       lower all instructions that have shape information. Besides the373///       intrinsics, this includes stores for example.374///  2.3. Update uses of the lowered instruction. If we have shape information375///       for a user, there is nothing to do, as we will look up the result376///       column matrix when lowering the user. For other uses, we embed the377///       result matrix in a flat vector and update the use.378///  2.4. Cache the result column matrix for the instruction we lowered379/// 3. After we lowered all instructions in a function, remove the now380///    obsolete instructions.381///382class LowerMatrixIntrinsics {383  Function &Func;384  const DataLayout &DL;385  const TargetTransformInfo &TTI;386  FunctionAnalysisManager *AM;387  AliasAnalysis *AA = nullptr;388  DominatorTree *DT = nullptr;389  LoopInfo *LI = nullptr;390  OptimizationRemarkEmitter *ORE = nullptr;391 392  /// Contains estimates of the number of operations (loads, stores, compute)393  /// required to lower a matrix operation.394  struct OpInfoTy {395    /// Number of stores emitted to generate this matrix.396    unsigned NumStores = 0;397    /// Number of loads emitted to generate this matrix.398    unsigned NumLoads = 0;399    /// Number of compute operations emitted to generate this matrix.400    unsigned NumComputeOps = 0;401    /// Most of the time transposes can be fused with matrix multiplies or can402    /// be folded away via algebraic simplifications.  This is the number of403    /// transposes that we failed to make "free" via such optimizations.404    unsigned NumExposedTransposes = 0;405 406    OpInfoTy &operator+=(const OpInfoTy &RHS) {407      NumStores += RHS.NumStores;408      NumLoads += RHS.NumLoads;409      NumComputeOps += RHS.NumComputeOps;410      NumExposedTransposes += RHS.NumExposedTransposes;411      return *this;412    }413  };414 415  /// Wrapper class representing a matrix as a set of vectors, either in row or416  /// column major layout. All vectors must have the same vector type.417  class MatrixTy {418    SmallVector<Value *, 16> Vectors;419 420    OpInfoTy OpInfo;421 422    bool IsColumnMajor = true;423 424  public:425    MatrixTy() : IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {}426    MatrixTy(ArrayRef<Value *> Vectors)427        : Vectors(Vectors),428          IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {}429    MatrixTy(unsigned NumRows, unsigned NumColumns, Type *EltTy)430        : IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {431 432      unsigned D = isColumnMajor() ? NumColumns : NumRows;433      for (unsigned J = 0; J < D; ++J)434        addVector(PoisonValue::get(FixedVectorType::get(435            EltTy, isColumnMajor() ? NumRows : NumColumns)));436    }437 438    Value *getVector(unsigned i) const { return Vectors[i]; }439    Value *getColumn(unsigned i) const {440      assert(isColumnMajor() && "only supported for column-major matrixes");441      return Vectors[i];442    }443    Value *getRow(unsigned i) const {444      assert(!isColumnMajor() && "only supported for row-major matrixes");445      return Vectors[i];446    }447 448    void setVector(unsigned i, Value *V) { Vectors[i] = V; }449 450    Type *getElementType() const { return getVectorTy()->getElementType(); }451 452    unsigned getNumVectors() const {453      if (isColumnMajor())454        return getNumColumns();455      return getNumRows();456    }457 458    unsigned getNumColumns() const {459      if (isColumnMajor())460        return Vectors.size();461      else {462        assert(Vectors.size() > 0 && "Cannot call getNumRows without columns");463        return getVectorTy()->getNumElements();464      }465    }466    unsigned getNumRows() const {467      if (isColumnMajor()) {468        assert(Vectors.size() > 0 && "Cannot call getNumRows without columns");469        return getVectorTy()->getNumElements();470      } else471        return Vectors.size();472    }473 474    void addVector(Value *V) { Vectors.push_back(V); }475    FixedVectorType *getColumnTy() {476      assert(isColumnMajor() && "only supported for column-major matrixes");477      return getVectorTy();478    }479 480    FixedVectorType *getVectorTy() const {481      return cast<FixedVectorType>(Vectors[0]->getType());482    }483 484    iterator_range<SmallVector<Value *, 8>::iterator> columns() {485      assert(isColumnMajor() &&486             "columns() only supported for column-major matrixes");487      return make_range(Vectors.begin(), Vectors.end());488    }489 490    iterator_range<SmallVector<Value *, 8>::iterator> vectors() {491      return make_range(Vectors.begin(), Vectors.end());492    }493 494    /// Embed the vectors of the matrix into a flat vector by concatenating495    /// them.496    Value *embedInVector(IRBuilder<> &Builder) const {497      return Vectors.size() == 1 ? Vectors[0]498                                 : concatenateVectors(Builder, Vectors);499    }500 501    MatrixTy &addNumLoads(unsigned N) {502      OpInfo.NumLoads += N;503      return *this;504    }505 506    void setNumLoads(unsigned N) { OpInfo.NumLoads = N; }507 508    MatrixTy &addNumStores(unsigned N) {509      OpInfo.NumStores += N;510      return *this;511    }512 513    MatrixTy &addNumExposedTransposes(unsigned N) {514      OpInfo.NumExposedTransposes += N;515      return *this;516    }517 518    MatrixTy &addNumComputeOps(unsigned N) {519      OpInfo.NumComputeOps += N;520      return *this;521    }522 523    unsigned getNumStores() const { return OpInfo.NumStores; }524    unsigned getNumLoads() const { return OpInfo.NumLoads; }525    unsigned getNumComputeOps() const { return OpInfo.NumComputeOps; }526 527    const OpInfoTy &getOpInfo() const { return OpInfo; }528 529    bool isColumnMajor() const { return IsColumnMajor; }530 531    unsigned getStride() const {532      if (isColumnMajor())533        return getNumRows();534      return getNumColumns();535    }536 537    ShapeInfo shape() const { return {getNumRows(), getNumColumns()}; }538 539    /// Extract a vector of \p NumElts starting at index (\p I, \p J). If the540    /// matrix is column-major, the result vector is extracted from a column541    /// vector, otherwise from a row vector.542    Value *extractVector(unsigned I, unsigned J, unsigned NumElts,543                         IRBuilder<> &Builder) const {544      Value *Vec = isColumnMajor() ? getColumn(J) : getRow(I);545      assert(cast<FixedVectorType>(Vec->getType())->getNumElements() >=546                 NumElts &&547             "Extracted vector will contain poison values");548      return Builder.CreateShuffleVector(549          Vec, createSequentialMask(isColumnMajor() ? I : J, NumElts, 0),550          "block");551    }552  };553 554  /// Maps instructions to their shape information. The shape information555  /// describes the shape to be used while lowering. This matches the shape of556  /// the result value of the instruction, with the only exceptions being store557  /// instructions and the matrix_column_major_store intrinsics. For those, the558  /// shape information indicates that those instructions should be lowered559  /// using shape information as well. Note that extra care is needed when560  /// erasing or RAUW'ing a value that is present in ShapeMap. If the561  /// replacement is also a matrix operation, use562  /// updateShapeAndReplaceAllUsesWith to make sure the replacement is added to563  /// ShapeMap.  We don't use ValueMap, as there are also cases where we do not564  /// want to add shape information for a replacement instruction. When directly565  /// erasing a value with an entry in ShapeMap, use566  /// eraseFromParentAndRemoveFromShapeMap to make sure ShapeMap is also updated567  /// accordingly.568  DenseMap<Value *, ShapeInfo> ShapeMap;569 570  /// List of instructions to remove. While lowering, we are not replacing all571  /// users of a lowered instruction, if shape information is available and572  /// those need to be removed after we finished lowering.573  SmallVector<Instruction *, 16> ToRemove;574 575  /// Map from instructions to their produced column matrix.576  MapVector<Value *, MatrixTy> Inst2ColumnMatrix;577 578private:579  static FastMathFlags getFastMathFlags(Instruction *Inst) {580    FastMathFlags FMF;581 582    if (isa<FPMathOperator>(*Inst))583      FMF = Inst->getFastMathFlags();584 585    FMF.setAllowContract(AllowContractEnabled || FMF.allowContract());586 587    return FMF;588  }589 590public:591  LowerMatrixIntrinsics(Function &F, TargetTransformInfo &TTI,592                        FunctionAnalysisManager *AM)593      : Func(F), DL(F.getDataLayout()), TTI(TTI), AM(AM) {}594 595  unsigned getNumOps(Type *VT) {596    assert(isa<FixedVectorType>(VT) && "Expected vector type");597    return getNumOps(VT->getScalarType(),598                     cast<FixedVectorType>(VT)->getNumElements());599  }600 601  /// Is this the minimal version executed in the backend pipelines.602  bool isMinimal() const {603    return !DT;604  }605 606  /// Return the estimated number of vector ops required for an operation on607  /// \p VT * N.608  unsigned getNumOps(Type *ST, unsigned N) {609    return std::ceil((ST->getPrimitiveSizeInBits() * N).getFixedValue() /610                     double(TTI.getRegisterBitWidth(611                                   TargetTransformInfo::RGK_FixedWidthVector)612                                .getFixedValue()));613  }614 615  /// Return the set of vectors that a matrix value is lowered to.616  ///617  /// If we lowered \p MatrixVal, just return the cache result matrix. Otherwise618  /// split the flat vector \p MatrixVal containing a matrix with shape \p SI619  /// into vectors.620  MatrixTy getMatrix(Value *MatrixVal, const ShapeInfo &SI,621                     IRBuilder<> &Builder) {622    FixedVectorType *VType = cast<FixedVectorType>(MatrixVal->getType());623    assert(VType->getNumElements() == SI.NumRows * SI.NumColumns &&624           "The vector size must match the number of matrix elements");625 626    // Check if we lowered MatrixVal using shape information. In that case,627    // return the existing matrix, if it matches the requested shape628    // information. If there is a mis-match, embed the result in a flat629    // vector and split it later.630    auto Found = Inst2ColumnMatrix.find(MatrixVal);631    if (Found != Inst2ColumnMatrix.end()) {632      MatrixTy &M = Found->second;633      // Return the found matrix, if its shape matches the requested shape634      // information635      if (SI.NumRows == M.getNumRows() && SI.NumColumns == M.getNumColumns())636        return M;637 638      MatrixVal = M.embedInVector(Builder);639    }640 641    // Otherwise split MatrixVal.642    SmallVector<Value *, 16> SplitVecs;643    for (unsigned MaskStart = 0; MaskStart < VType->getNumElements();644         MaskStart += SI.getStride()) {645      Value *V = Builder.CreateShuffleVector(646          MatrixVal, createSequentialMask(MaskStart, SI.getStride(), 0),647          "split");648      SplitVecs.push_back(V);649    }650 651    if (Instruction *Inst = dyn_cast<Instruction>(MatrixVal)) {652      if (Found != Inst2ColumnMatrix.end()) {653        // FIXME: re: "at least": SplitVecs.size() doesn't count the shuffles654        // that embedInVector created.655        LLVM_DEBUG(dbgs() << "matrix reshape from " << Found->second.shape()656                          << " to " << SI << " using at least "657                          << SplitVecs.size() << " shuffles on behalf of:\n"658                          << *Inst << '\n');659        ReshapedMatrices++;660      } else if (!ShapeMap.contains(MatrixVal)) {661        LLVM_DEBUG(662            dbgs()663            << "splitting a " << SI << " matrix with " << SplitVecs.size()664            << " shuffles beacuse we do not have a shape-aware lowering for "665               "its def:\n"666            << *Inst << '\n');667        (void)Inst;668        SplitMatrices++;669      } else {670        // The ShapeMap has it, so it's a case where we're being lowered671        // before the def, and we expect that InstCombine will clean things up672        // afterward.673      }674    }675 676    return {SplitVecs};677  }678 679  /// If \p V already has a known shape return false.  Otherwise set the shape680  /// for instructions that support it.681  bool setShapeInfo(Value *V, ShapeInfo Shape) {682    assert(Shape && "Shape not set");683    if (isa<UndefValue>(V) || !supportsShapeInfo(V))684      return false;685 686    auto SIter = ShapeMap.find(V);687    if (SIter != ShapeMap.end()) {688      if (VerifyShapeInfo && (SIter->second.NumRows != Shape.NumRows ||689                              SIter->second.NumColumns != Shape.NumColumns)) {690        errs() << "Conflicting shapes (" << SIter->second.NumRows << "x"691               << SIter->second.NumColumns << " vs " << Shape.NumRows << "x"692               << Shape.NumColumns << ") for " << *V << "\n";693        report_fatal_error(694            "Matrix shape verification failed, compilation aborted!");695      }696 697      LLVM_DEBUG(dbgs() << "  not overriding existing shape: "698                        << SIter->second.NumRows << " "699                        << SIter->second.NumColumns << " for " << *V << "\n");700      return false;701    }702 703    ShapeMap.insert({V, Shape});704    LLVM_DEBUG(dbgs() << "  " << Shape.NumRows << " x " << Shape.NumColumns705                      << " for " << *V << "\n");706    return true;707  }708 709  /// Returns true if shape information can be used for \p V. The supported710  /// instructions must match the instructions that can be lowered by this pass.711  bool supportsShapeInfo(Value *V) {712    Instruction *Inst = dyn_cast<Instruction>(V);713    if (!Inst)714      return false;715 716    IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst);717    if (II)718      switch (II->getIntrinsicID()) {719      case Intrinsic::matrix_multiply:720      case Intrinsic::matrix_transpose:721      case Intrinsic::matrix_column_major_load:722      case Intrinsic::matrix_column_major_store:723        return true;724      default:725        break;726      }727    return isShapePreserving(V) || isa<StoreInst>(V) || isa<LoadInst>(V);728  }729 730  /// Propagate the shape information of instructions to their users.731  /// The work list contains instructions for which we can compute the shape,732  /// either based on the information provided by matrix intrinsics or known733  /// shapes of operands.734  SmallVector<Instruction *, 32>735  propagateShapeForward(SmallVectorImpl<Instruction *> &WorkList) {736    SmallVector<Instruction *, 32> NewWorkList;737    // Pop an element for which we guaranteed to have at least one of the738    // operand shapes.  Add the shape for this and then add users to the work739    // list.740    LLVM_DEBUG(dbgs() << "Forward-propagate shapes:\n");741    while (!WorkList.empty()) {742      Instruction *Inst = WorkList.pop_back_val();743 744      // New entry, set the value and insert operands745      bool Propagate = false;746      if (auto SI = computeShapeInfoForInst(Inst, ShapeMap))747        Propagate = setShapeInfo(Inst, *SI);748 749      if (Propagate) {750        NewWorkList.push_back(Inst);751        for (auto *User : Inst->users())752          if (ShapeMap.count(User) == 0)753            WorkList.push_back(cast<Instruction>(User));754      }755    }756 757    return NewWorkList;758  }759 760  /// Propagate the shape to operands of instructions with shape information.761  /// \p Worklist contains the instruction for which we already know the shape.762  SmallVector<Instruction *, 32>763  propagateShapeBackward(SmallVectorImpl<Instruction *> &WorkList) {764    SmallVector<Instruction *, 32> NewWorkList;765 766    auto pushInstruction = [](Value *V,767                              SmallVectorImpl<Instruction *> &WorkList) {768      Instruction *I = dyn_cast<Instruction>(V);769      if (I)770        WorkList.push_back(I);771    };772    // Pop an element with known shape.  Traverse the operands, if their shape773    // derives from the result shape and is unknown, add it and add them to the774    // worklist.775    LLVM_DEBUG(dbgs() << "Backward-propagate shapes:\n");776    while (!WorkList.empty()) {777      Value *V = WorkList.pop_back_val();778 779      size_t BeforeProcessingV = WorkList.size();780      if (!isa<Instruction>(V))781        continue;782 783      Value *MatrixA;784      Value *MatrixB;785      Value *M;786      Value *N;787      Value *K;788      if (match(V, m_Intrinsic<Intrinsic::matrix_multiply>(789                       m_Value(MatrixA), m_Value(MatrixB), m_Value(M),790                       m_Value(N), m_Value(K)))) {791        if (setShapeInfo(MatrixA, {M, N}))792          pushInstruction(MatrixA, WorkList);793 794        if (setShapeInfo(MatrixB, {N, K}))795          pushInstruction(MatrixB, WorkList);796 797      } else if (match(V, m_Intrinsic<Intrinsic::matrix_transpose>(798                              m_Value(MatrixA), m_Value(M), m_Value(N)))) {799        // Flip dimensions.800        if (setShapeInfo(MatrixA, {M, N}))801          pushInstruction(MatrixA, WorkList);802      } else if (match(V, m_Intrinsic<Intrinsic::matrix_column_major_store>(803                              m_Value(MatrixA), m_Value(), m_Value(), m_Value(),804                              m_Value(M), m_Value(N)))) {805        if (setShapeInfo(MatrixA, {M, N})) {806          pushInstruction(MatrixA, WorkList);807        }808      } else if (isa<LoadInst>(V) ||809                 match(V, m_Intrinsic<Intrinsic::matrix_column_major_load>())) {810        // Nothing to do, no matrix input.811      } else if (isa<StoreInst>(V)) {812        // Nothing to do.  We forward-propagated to this so we would just813        // backward propagate to an instruction with an already known shape.814      } else if (isShapePreserving(V)) {815        auto ShapedOps = getShapedOperandsForInst(cast<Instruction>(V));816        // Propagate to all operands.817        ShapeInfo Shape = ShapeMap[V];818        for (Use &U : ShapedOps) {819          if (setShapeInfo(U.get(), Shape))820            pushInstruction(U.get(), WorkList);821        }822      }823      // After we discovered new shape info for new instructions in the824      // worklist, we use their users as seeds for the next round of forward825      // propagation.826      for (size_t I = BeforeProcessingV; I != WorkList.size(); I++)827        for (User *U : WorkList[I]->users())828          if (isa<Instruction>(U) && V != U)829            NewWorkList.push_back(cast<Instruction>(U));830    }831    return NewWorkList;832  }833 834  /// (Op0 op Op1)^T -> Op0^T op Op1^T835  /// Transpose \p Op0 and \p Op1 of shape \p Shape0 and \p Shape1, then use836  /// them on both sides of \p Operation.837  Instruction *distributeTransposes(838      Value *Op0, ShapeInfo Shape0, Value *Op1, ShapeInfo Shape1,839      MatrixBuilder &Builder,840      function_ref<Instruction *(Value *, ShapeInfo, Value *, ShapeInfo)>841          Operation) {842    Value *T0 = Builder.CreateMatrixTranspose(843        Op0, Shape0.NumRows, Shape0.NumColumns, Op0->getName() + "_t");844    // We are being run after shape prop, add shape for newly created845    // instructions so that we lower them later.846    setShapeInfo(T0, Shape0.t());847    Value *T1 = Builder.CreateMatrixTranspose(848        Op1, Shape1.NumRows, Shape1.NumColumns, Op1->getName() + "_t");849    setShapeInfo(T1, Shape1.t());850    return Operation(T0, Shape0.t(), T1, Shape1.t());851  }852 853  /// Erase \p Inst from both ShapeMap (if an entry exists) and erase \p Inst854  /// itself.855  void eraseFromParentAndRemoveFromShapeMap(Instruction *Inst) {856    ShapeMap.erase(Inst);857    Inst->eraseFromParent();858  }859 860  /// Erase \p V from \p BB and move \II forward to avoid invalidating861  /// iterators.862  void eraseFromParentAndMove(Value *V, BasicBlock::reverse_iterator &II,863                              BasicBlock &BB) {864    auto *Inst = cast<Instruction>(V);865    // Still used, don't erase.866    if (!Inst->use_empty())867      return;868    if (II != BB.rend() && Inst == &*II)869      ++II;870    eraseFromParentAndRemoveFromShapeMap(Inst);871  }872 873  /// Add a new entry to ShapeMap for \p New with \p Old's shape info, erase the874  /// entry for \p Old and replace all uses of \p Old with \p New.875  void updateShapeAndReplaceAllUsesWith(Instruction &Old, Value *New) {876    // We need to remove Old from the ShapeMap otherwise RAUW will replace it877    // with New. We should only add New it it supportsShapeInfo so we insert878    // it conditionally instead.879    auto S = ShapeMap.find(&Old);880    if (S != ShapeMap.end()) {881      ShapeMap.erase(S);882      if (supportsShapeInfo(New))883        ShapeMap.insert({New, S->second});884    }885    Old.replaceAllUsesWith(New);886  }887 888  /// Sink a top-level transpose inside matmuls and adds.889  /// This creates and erases instructions as needed, and returns the newly890  /// created instruction while updating the iterator to avoid invalidation. If891  /// this returns nullptr, no new instruction was created.892  Instruction *sinkTranspose(Instruction &I, BasicBlock::reverse_iterator &II,893                             bool &Changed) {894    BasicBlock &BB = *I.getParent();895    IRBuilder<> IB(&I);896    MatrixBuilder Builder(IB);897 898    Value *TA, *TAMA, *TAMB;899    ConstantInt *R, *K, *C;900    if (!match(&I, m_Intrinsic<Intrinsic::matrix_transpose>(901                       m_Value(TA), m_ConstantInt(R), m_ConstantInt(C))))902      return nullptr;903 904    // Transpose of a transpose is a nop when the shapes match.905    Value *TATA;906    if (match(TA, m_Intrinsic<Intrinsic::matrix_transpose>(907                      m_Value(TATA), m_Specific(C), m_Specific(R)))) {908      updateShapeAndReplaceAllUsesWith(I, TATA);909      eraseFromParentAndMove(&I, II, BB);910      eraseFromParentAndMove(TA, II, BB);911      Changed = true;912      return nullptr;913    }914 915    // k^T -> k916    if (isSplat(TA)) {917      updateShapeAndReplaceAllUsesWith(I, TA);918      eraseFromParentAndMove(&I, II, BB);919      Changed = true;920      return nullptr;921    }922 923    // (A * B)^t -> B^t * A^t924    // RxK KxC      CxK   KxR925    if (match(TA, m_Intrinsic<Intrinsic::matrix_multiply>(926                      m_Value(TAMA), m_Value(TAMB), m_ConstantInt(R),927                      m_ConstantInt(K), m_ConstantInt(C)))) {928      auto NewInst = distributeTransposes(929          TAMB, {K, C}, TAMA, {R, K}, Builder,930          [&](Value *T0, ShapeInfo Shape0, Value *T1, ShapeInfo Shape1) {931            return Builder.CreateMatrixMultiply(T0, T1, Shape0.NumRows,932                                                Shape0.NumColumns,933                                                Shape1.NumColumns, "mmul");934          });935      updateShapeAndReplaceAllUsesWith(I, NewInst);936      eraseFromParentAndMove(&I, II, BB);937      eraseFromParentAndMove(TA, II, BB);938      Changed = true;939      return NewInst;940    }941 942    // Same as above, but with a mul, which occurs when multiplied943    // with a scalar.944    // (A * k)^t -> A^t * k945    //  R  x  C     RxC946    if (match(TA, m_AnyMul(m_Value(TAMA), m_Value(TAMB))) &&947        (isSplat(TAMA) || isSplat(TAMB))) {948      IRBuilder<> LocalBuilder(&I);949      // We know that the transposed operand is of shape RxC.950      // An when multiplied with a scalar, the shape is preserved.951      auto NewInst = distributeTransposes(952          TAMA, {R, C}, TAMB, {R, C}, Builder,953          [&](Value *T0, ShapeInfo Shape0, Value *T1, ShapeInfo Shape1) {954            bool IsFP = I.getType()->isFPOrFPVectorTy();955            auto *Mul = IsFP ? LocalBuilder.CreateFMul(T0, T1, "mmul")956                             : LocalBuilder.CreateMul(T0, T1, "mmul");957            auto *Result = cast<Instruction>(Mul);958            setShapeInfo(Result, Shape0);959            return Result;960          });961      updateShapeAndReplaceAllUsesWith(I, NewInst);962      eraseFromParentAndMove(&I, II, BB);963      eraseFromParentAndMove(TA, II, BB);964      Changed = true;965      return NewInst;966    }967 968    // (A + B)^t -> A^t + B^t969    // RxC RxC      CxR   CxR970    if (match(TA, m_AnyAdd(m_Value(TAMA), m_Value(TAMB)))) {971      IRBuilder<> LocalBuilder(&I);972      auto NewInst = distributeTransposes(973          TAMA, {R, C}, TAMB, {R, C}, Builder,974          [&](Value *T0, ShapeInfo Shape0, Value *T1, ShapeInfo Shape1) {975            bool IsFP = I.getType()->isFPOrFPVectorTy();976            auto *Add = IsFP ? LocalBuilder.CreateFAdd(T0, T1, "madd")977                             : LocalBuilder.CreateAdd(T0, T1, "madd");978 979            auto *Result = cast<Instruction>(Add);980            setShapeInfo(Result, Shape0);981            return Result;982          });983      updateShapeAndReplaceAllUsesWith(I, NewInst);984      eraseFromParentAndMove(&I, II, BB);985      eraseFromParentAndMove(TA, II, BB);986      Changed = true;987      return NewInst;988    }989 990    return nullptr;991  }992 993  bool liftTranspose(Instruction &I) {994    // Erase dead Instructions after lifting transposes from binops.995    auto CleanupBinOp = [this](Instruction &T, Value *A, Value *B) {996      if (T.use_empty())997        eraseFromParentAndRemoveFromShapeMap(&T);998      if (A->use_empty())999        eraseFromParentAndRemoveFromShapeMap(cast<Instruction>(A));1000      if (A != B && B->use_empty())1001        eraseFromParentAndRemoveFromShapeMap(cast<Instruction>(B));1002    };1003 1004    Value *A, *B, *AT, *BT;1005    ConstantInt *R, *K, *C;1006    // A^t * B ^t -> (B * A)^t1007    if (match(&I, m_Intrinsic<Intrinsic::matrix_multiply>(1008                      m_Value(A), m_Value(B), m_ConstantInt(R),1009                      m_ConstantInt(K), m_ConstantInt(C))) &&1010        match(A, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(AT))) &&1011        match(B, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value((BT))))) {1012      IRBuilder<> IB(&I);1013      MatrixBuilder Builder(IB);1014      Value *M = Builder.CreateMatrixMultiply(1015          BT, AT, C->getZExtValue(), K->getZExtValue(), R->getZExtValue());1016      setShapeInfo(M, {C, R});1017      Instruction *NewInst = Builder.CreateMatrixTranspose(M, C->getZExtValue(),1018                                                           R->getZExtValue());1019      updateShapeAndReplaceAllUsesWith(I, NewInst);1020      CleanupBinOp(I, A, B);1021      return true;1022    }1023    // A^t + B ^t -> (A + B)^t. Pick rows and columns from first transpose. If1024    // the shape of the second transpose is different, there's a shape conflict1025    // which gets resolved by picking the shape of the first operand.1026    else if (match(&I, m_FAdd(m_Value(A), m_Value(B))) &&1027             match(A, m_Intrinsic<Intrinsic::matrix_transpose>(1028                          m_Value(AT), m_ConstantInt(R), m_ConstantInt(C))) &&1029             match(B, m_Intrinsic<Intrinsic::matrix_transpose>(1030                          m_Value(BT), m_ConstantInt(), m_ConstantInt()))) {1031      IRBuilder<> Builder(&I);1032      auto *Add = Builder.CreateFAdd(AT, BT, "mfadd");1033      MatrixBuilder MBuilder(Builder);1034      Instruction *NewInst = MBuilder.CreateMatrixTranspose(1035          Add, R->getZExtValue(), C->getZExtValue(), "mfadd_t");1036      updateShapeAndReplaceAllUsesWith(I, NewInst);1037      assert(computeShapeInfoForInst(NewInst, ShapeMap) ==1038                 computeShapeInfoForInst(&I, ShapeMap) &&1039             "Shape of new instruction doesn't match original shape.");1040      CleanupBinOp(I, A, B);1041      if (auto *AddI = dyn_cast<Instruction>(Add)) {1042        setShapeInfo(AddI, {R, C});1043        assert(1044            computeShapeInfoForInst(AddI, ShapeMap).value_or(ShapeMap[AddI]) ==1045                ShapeMap[AddI] &&1046            "Shape of updated addition doesn't match cached shape.");1047      }1048      return true;1049    }1050    return false;1051  }1052 1053  /// Try moving transposes in order to fold them away or into multiplies.1054  bool optimizeTransposes() {1055    bool Changed = false;1056    // First sink all transposes inside matmuls and adds, hoping that we end up1057    // with NN, NT or TN variants.1058    for (BasicBlock &BB : reverse(Func)) {1059      for (auto II = BB.rbegin(); II != BB.rend();) {1060        Instruction &I = *II;1061        // We may remove II.  By default continue on the next/prev instruction.1062        ++II;1063        if (Instruction *NewInst = sinkTranspose(I, II, Changed))1064          II = std::next(BasicBlock::reverse_iterator(NewInst));1065      }1066    }1067 1068    // If we have a TT matmul or a TT add, lift the transpose. We may be able1069    // to fold into consuming multiply or add.1070    for (BasicBlock &BB : Func) {1071      for (Instruction &I : llvm::make_early_inc_range(BB)) {1072        Changed |= liftTranspose(I);1073      }1074    }1075    return Changed;1076  }1077 1078  bool Visit() {1079    SmallVector<Instruction *, 32> WorkList;1080 1081    // Initially only the shape of matrix intrinsics is known.1082    // Initialize the work list with ops carrying shape information.1083    for (BasicBlock &BB : Func)1084      for (Instruction &Inst : BB) {1085        IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst);1086        if (!II)1087          continue;1088 1089        switch (II->getIntrinsicID()) {1090        case Intrinsic::matrix_multiply:1091        case Intrinsic::matrix_transpose:1092        case Intrinsic::matrix_column_major_load:1093        case Intrinsic::matrix_column_major_store:1094          WorkList.push_back(&Inst);1095          break;1096        default:1097          break;1098        }1099      }1100 1101    // Avoid unnecessary work if there are no matrix intrinsics in the function.1102    if (WorkList.empty())1103      return false;1104 1105    if (AM) {1106      ORE = &AM->getResult<OptimizationRemarkEmitterAnalysis>(Func);1107      AA = &AM->getResult<AAManager>(Func);1108      DT = &AM->getResult<DominatorTreeAnalysis>(Func);1109      LI = &AM->getResult<LoopAnalysis>(Func);1110    }1111 1112    // Propagate shapes until nothing changes any longer.1113    while (!WorkList.empty()) {1114      WorkList = propagateShapeForward(WorkList);1115      WorkList = propagateShapeBackward(WorkList);1116    }1117 1118    bool Changed = false;1119    if (!isMinimal()) {1120      Changed |= optimizeTransposes();1121      if (PrintAfterTransposeOpt) {1122        dbgs() << "Dump after matrix transpose optimization:\n";1123        Func.print(dbgs());1124      }1125    }1126 1127    SmallVector<CallInst *, 16> MaybeFusableInsts;1128    SmallVector<Instruction *, 16> MatrixInsts;1129    SmallVector<IntrinsicInst *, 16> LifetimeEnds;1130 1131    // First, collect all instructions with shape information and candidates for1132    // fusion (currently only matrix multiplies).1133    ReversePostOrderTraversal<Function *> RPOT(&Func);1134    for (auto *BB : RPOT)1135      for (Instruction &I : *BB) {1136        if (match(&I, m_Intrinsic<Intrinsic::lifetime_end>()))1137          LifetimeEnds.push_back(cast<IntrinsicInst>(&I));1138        if (!ShapeMap.contains(&I))1139          continue;1140        if (match(&I, m_Intrinsic<Intrinsic::matrix_multiply>()))1141          MaybeFusableInsts.push_back(cast<CallInst>(&I));1142        MatrixInsts.push_back(&I);1143      }1144 1145    // Second, try to lower any dot products1146    SmallPtrSet<Instruction *, 16> FusedInsts;1147    for (CallInst *CI : MaybeFusableInsts)1148      lowerDotProduct(CI, FusedInsts, getFastMathFlags(CI));1149 1150    // Third, try to fuse candidates.1151    for (CallInst *CI : MaybeFusableInsts)1152      if (!FusedInsts.contains(CI))1153        LowerMatrixMultiplyFused(CI, FusedInsts, LifetimeEnds);1154 1155    Changed |= !FusedInsts.empty();1156 1157    // Fourth, pre-process all the PHINode's. The incoming values will be1158    // assigned later in VisitPHI.1159    for (Instruction *Inst : MatrixInsts) {1160      if (FusedInsts.count(Inst))1161        continue;1162 1163      auto *PHI = dyn_cast<PHINode>(Inst);1164      if (!PHI)1165        continue;1166 1167      const ShapeInfo &SI = ShapeMap.at(Inst);1168      auto *EltTy = cast<FixedVectorType>(PHI->getType())->getElementType();1169      MatrixTy PhiM(SI.NumRows, SI.NumColumns, EltTy);1170 1171      IRBuilder<> Builder(Inst);1172      for (unsigned VI = 0, VE = PhiM.getNumVectors(); VI != VE; ++VI)1173        PhiM.setVector(VI, Builder.CreatePHI(PhiM.getVectorTy(),1174                                             PHI->getNumIncomingValues(),1175                                             PHI->getName()));1176      assert(!Inst2ColumnMatrix.contains(PHI) && "map already contains phi?");1177      Inst2ColumnMatrix[PHI] = PhiM;1178    }1179 1180    // Fifth, lower remaining instructions with shape information.1181    for (Instruction *Inst : MatrixInsts) {1182      if (FusedInsts.count(Inst))1183        continue;1184 1185      const ShapeInfo &SI = ShapeMap.at(Inst);1186 1187      Value *Op1;1188      Value *Op2;1189      MatrixTy Result;1190      IRBuilder<> Builder(Inst);1191      if (auto *BinOp = dyn_cast<BinaryOperator>(Inst))1192        Result = VisitBinaryOperator(BinOp, SI, Builder);1193      else if (auto *Cast = dyn_cast<CastInst>(Inst))1194        Result = VisitCastInstruction(Cast, SI, Builder);1195      else if (auto *UnOp = dyn_cast<UnaryOperator>(Inst))1196        Result = VisitUnaryOperator(UnOp, SI, Builder);1197      else if (auto *Intr = dyn_cast<IntrinsicInst>(Inst))1198        Result = VisitIntrinsicInst(Intr, SI, Builder);1199      else if (auto *Select = dyn_cast<SelectInst>(Inst))1200        Result = VisitSelectInst(Select, SI, Builder);1201      else if (match(Inst, m_Load(m_Value(Op1))))1202        Result = VisitLoad(cast<LoadInst>(Inst), SI, Op1, Builder);1203      else if (match(Inst, m_Store(m_Value(Op1), m_Value(Op2))))1204        Result = VisitStore(cast<StoreInst>(Inst), SI, Op1, Op2, Builder);1205      else if (auto *PHI = dyn_cast<PHINode>(Inst))1206        Result = VisitPHI(PHI, SI, Builder);1207      else1208        continue;1209 1210      finalizeLowering(Inst, Result, Builder);1211      Changed = true;1212    }1213 1214    if (ORE) {1215      RemarkGenerator RemarkGen(Inst2ColumnMatrix, *ORE, Func);1216      RemarkGen.emitRemarks();1217    }1218 1219    // Delete the instructions backwards, as it has a reduced likelihood of1220    // having to update as many def-use and use-def chains.1221    //1222    // Because we add to ToRemove during fusion we can't guarantee that defs1223    // are before uses.  Change uses to poison temporarily as these should get1224    // removed as well.1225    //1226    // For verification, we keep track of where we changed uses to poison in1227    // PoisonedInsts and then check that we in fact remove them.1228    SmallPtrSet<Instruction *, 16> PoisonedInsts;1229    for (auto *Inst : reverse(ToRemove)) {1230      for (Use &U : llvm::make_early_inc_range(Inst->uses())) {1231        if (auto *Poisoned = dyn_cast<Instruction>(U.getUser()))1232          PoisonedInsts.insert(Poisoned);1233        U.set(PoisonValue::get(Inst->getType()));1234      }1235      Inst->eraseFromParent();1236      PoisonedInsts.erase(Inst);1237    }1238    if (!PoisonedInsts.empty()) {1239      // If we didn't remove all poisoned instructions, it's a hard error.1240      dbgs() << "Poisoned but present instructions:\n";1241      for (auto *I : PoisonedInsts)1242        dbgs() << *I << "\n";1243      llvm_unreachable("Poisoned but instruction not removed");1244    }1245 1246    return Changed;1247  }1248 1249  /// Replace intrinsic calls.1250  MatrixTy VisitIntrinsicInst(IntrinsicInst *Inst, const ShapeInfo &SI,1251                              IRBuilder<> &Builder) {1252    assert(Inst->getCalledFunction() &&1253           Inst->getCalledFunction()->isIntrinsic());1254 1255    switch (Inst->getCalledFunction()->getIntrinsicID()) {1256    case Intrinsic::matrix_multiply:1257      return LowerMultiply(Inst, Builder);1258    case Intrinsic::matrix_transpose:1259      return LowerTranspose(Inst, Builder);1260    case Intrinsic::matrix_column_major_load:1261      return LowerColumnMajorLoad(Inst, Builder);1262    case Intrinsic::matrix_column_major_store:1263      return LowerColumnMajorStore(Inst, Builder);1264    case Intrinsic::abs:1265    case Intrinsic::fabs: {1266      MatrixTy Result;1267      MatrixTy M = getMatrix(Inst->getOperand(0), SI, Builder);1268      Builder.setFastMathFlags(getFastMathFlags(Inst));1269 1270      for (auto *Vector : M.vectors()) {1271        switch (Inst->getIntrinsicID()) {1272        case Intrinsic::abs:1273          Result.addVector(Builder.CreateBinaryIntrinsic(Intrinsic::abs, Vector,1274                                                         Inst->getOperand(1)));1275          continue;1276        case Intrinsic::fabs:1277          Result.addVector(1278              Builder.CreateUnaryIntrinsic(Inst->getIntrinsicID(), Vector));1279          continue;1280        default:1281          llvm_unreachable("unexpected intrinsic");1282        }1283      }1284 1285      return Result.addNumComputeOps(getNumOps(Result.getVectorTy()) *1286                                     Result.getNumVectors());1287    }1288    default:1289      break;1290    }1291    llvm_unreachable(1292        "only intrinsics supporting shape info should be seen here");1293  }1294 1295  /// Compute the alignment for a column/row \p Idx with \p Stride between them.1296  /// The address at \p Idx == 0 has alignment \p A. If \p Stride is a1297  /// ConstantInt, reduce the initial alignment based on the byte offset. For1298  /// non-ConstantInt strides, return the common alignment of the initial1299  /// alignment and the element size in bytes.1300  Align getAlignForIndex(unsigned Idx, Value *Stride, Type *ElementTy,1301                         MaybeAlign A) const {1302    Align InitialAlign = DL.getValueOrABITypeAlignment(A, ElementTy);1303    if (Idx == 0)1304      return InitialAlign;1305 1306    TypeSize ElementSizeInBits = DL.getTypeSizeInBits(ElementTy);1307    if (auto *ConstStride = dyn_cast<ConstantInt>(Stride)) {1308      uint64_t StrideInBytes =1309          ConstStride->getZExtValue() * ElementSizeInBits / 8;1310      return commonAlignment(InitialAlign, Idx * StrideInBytes);1311    }1312    return commonAlignment(InitialAlign, ElementSizeInBits / 8);1313  }1314 1315  IntegerType *getIndexType(Value *Ptr) const {1316    return cast<IntegerType>(DL.getIndexType(Ptr->getType()));1317  }1318 1319  Value *getIndex(Value *Ptr, uint64_t V) const {1320    return ConstantInt::get(getIndexType(Ptr), V);1321  }1322 1323  Value *castToIndexType(Value *Ptr, Value *V, IRBuilder<> &Builder) const {1324    assert(isa<IntegerType>(V->getType()) &&1325           "Attempted to cast non-integral type to integer index");1326    // In case the data layout's index type differs in width from the type of1327    // the value we're given, truncate or zero extend to the appropriate width.1328    // We zero extend here as indices are unsigned.1329    return Builder.CreateZExtOrTrunc(V, getIndexType(Ptr),1330                                     V->getName() + ".cast");1331  }1332 1333  /// Load a matrix with \p Shape starting at \p Ptr and using \p Stride between1334  /// vectors.1335  MatrixTy loadMatrix(Type *Ty, Value *Ptr, MaybeAlign MAlign, Value *Stride,1336                      bool IsVolatile, ShapeInfo Shape, IRBuilder<> &Builder) {1337    auto *VType = cast<FixedVectorType>(Ty);1338    Type *EltTy = VType->getElementType();1339    Type *VecTy = FixedVectorType::get(EltTy, Shape.getStride());1340    Value *EltPtr = Ptr;1341    MatrixTy Result;1342    Stride = castToIndexType(Ptr, Stride, Builder);1343    for (unsigned I = 0, E = Shape.getNumVectors(); I < E; ++I) {1344      Value *GEP = computeVectorAddr(1345          EltPtr, Builder.getIntN(Stride->getType()->getScalarSizeInBits(), I),1346          Stride, Shape.getStride(), EltTy, Builder);1347      Value *Vector = Builder.CreateAlignedLoad(1348          VecTy, GEP, getAlignForIndex(I, Stride, EltTy, MAlign),1349          IsVolatile, "col.load");1350 1351      Result.addVector(Vector);1352    }1353    return Result.addNumLoads(getNumOps(Result.getVectorTy()) *1354                              Result.getNumVectors());1355  }1356 1357  /// Loads a sub-matrix with shape \p ResultShape from a \p R x \p C matrix,1358  /// starting at \p MatrixPtr[I][J].1359  MatrixTy loadMatrix(Value *MatrixPtr, MaybeAlign Align, bool IsVolatile,1360                      ShapeInfo MatrixShape, Value *I, Value *J,1361                      ShapeInfo ResultShape, Type *EltTy,1362                      IRBuilder<> &Builder) {1363    Value *Offset = Builder.CreateAdd(1364        Builder.CreateMul(J, getIndex(MatrixPtr, MatrixShape.getStride())), I);1365 1366    Value *TileStart = Builder.CreateGEP(EltTy, MatrixPtr, Offset);1367    auto *TileTy = FixedVectorType::get(EltTy, ResultShape.NumRows *1368                                                   ResultShape.NumColumns);1369 1370    return loadMatrix(TileTy, TileStart, Align,1371                      getIndex(MatrixPtr, MatrixShape.getStride()), IsVolatile,1372                      ResultShape, Builder);1373  }1374 1375  /// Lower a load instruction with shape information.1376  MatrixTy LowerLoad(Instruction *Inst, Value *Ptr, MaybeAlign Align,1377                     Value *Stride, bool IsVolatile, ShapeInfo Shape,1378                     IRBuilder<> &Builder) {1379    return loadMatrix(Inst->getType(), Ptr, Align, Stride, IsVolatile, Shape,1380                      Builder);1381  }1382 1383  /// Lowers llvm.matrix.column.major.load.1384  ///1385  /// The intrinsic loads a matrix from memory using a stride between columns.1386  MatrixTy LowerColumnMajorLoad(CallInst *Inst, IRBuilder<> &Builder) {1387    assert(MatrixLayout == MatrixLayoutTy::ColumnMajor &&1388           "Intrinsic only supports column-major layout!");1389    Value *Ptr = Inst->getArgOperand(0);1390    Value *Stride = Inst->getArgOperand(1);1391    return LowerLoad(Inst, Ptr, Inst->getParamAlign(0), Stride,1392                     cast<ConstantInt>(Inst->getArgOperand(2))->isOne(),1393                     {Inst->getArgOperand(3), Inst->getArgOperand(4)}, Builder);1394  }1395 1396  /// Stores a sub-matrix \p StoreVal into the \p R x \p C matrix starting at \p1397  /// MatrixPtr[I][J].1398  void storeMatrix(const MatrixTy &StoreVal, Value *MatrixPtr,1399                   MaybeAlign MAlign, bool IsVolatile, ShapeInfo MatrixShape,1400                   Value *I, Value *J, Type *EltTy, IRBuilder<> &Builder) {1401    Value *Offset = Builder.CreateAdd(1402        Builder.CreateMul(J, getIndex(MatrixPtr, MatrixShape.getStride())), I);1403 1404    Value *TileStart = Builder.CreateGEP(EltTy, MatrixPtr, Offset);1405    auto *TileTy = FixedVectorType::get(EltTy, StoreVal.getNumRows() *1406                                                   StoreVal.getNumColumns());1407 1408    storeMatrix(TileTy, StoreVal, TileStart, MAlign,1409                getIndex(MatrixPtr, MatrixShape.getStride()), IsVolatile,1410                Builder);1411  }1412 1413  /// Store matrix \p StoreVal starting at \p Ptr and using \p Stride between1414  /// vectors.1415  MatrixTy storeMatrix(Type *Ty, MatrixTy StoreVal, Value *Ptr,1416                       MaybeAlign MAlign, Value *Stride, bool IsVolatile,1417                       IRBuilder<> &Builder) {1418    auto *VType = cast<FixedVectorType>(Ty);1419    Value *EltPtr = Ptr;1420    Stride = castToIndexType(Ptr, Stride, Builder);1421    for (auto Vec : enumerate(StoreVal.vectors())) {1422      Value *GEP = computeVectorAddr(1423          EltPtr,1424          Builder.getIntN(Stride->getType()->getScalarSizeInBits(),1425                          Vec.index()),1426          Stride, StoreVal.getStride(), VType->getElementType(), Builder);1427      Builder.CreateAlignedStore(Vec.value(), GEP,1428                                 getAlignForIndex(Vec.index(), Stride,1429                                                  VType->getElementType(),1430                                                  MAlign),1431                                 IsVolatile);1432    }1433    return MatrixTy().addNumStores(getNumOps(StoreVal.getVectorTy()) *1434                                   StoreVal.getNumVectors());1435  }1436 1437  /// Lower a store instruction with shape information.1438  MatrixTy LowerStore(Instruction *Inst, Value *Matrix, Value *Ptr,1439                      MaybeAlign A, Value *Stride, bool IsVolatile,1440                      ShapeInfo Shape, IRBuilder<> &Builder) {1441    auto StoreVal = getMatrix(Matrix, Shape, Builder);1442    return storeMatrix(Matrix->getType(), StoreVal, Ptr, A, Stride, IsVolatile,1443                       Builder);1444  }1445 1446  /// Lowers llvm.matrix.column.major.store.1447  ///1448  /// The intrinsic store a matrix back memory using a stride between columns.1449  MatrixTy LowerColumnMajorStore(CallInst *Inst, IRBuilder<> &Builder) {1450    assert(MatrixLayout == MatrixLayoutTy::ColumnMajor &&1451           "Intrinsic only supports column-major layout!");1452    Value *Matrix = Inst->getArgOperand(0);1453    Value *Ptr = Inst->getArgOperand(1);1454    Value *Stride = Inst->getArgOperand(2);1455    return LowerStore(Inst, Matrix, Ptr, Inst->getParamAlign(1), Stride,1456                      cast<ConstantInt>(Inst->getArgOperand(3))->isOne(),1457                      {Inst->getArgOperand(4), Inst->getArgOperand(5)},1458                      Builder);1459  }1460 1461  // Set elements I..I+NumElts-1 to Block1462  Value *insertVector(Value *Col, unsigned I, Value *Block,1463                      IRBuilder<> &Builder) {1464 1465    // First, bring Block to the same size as Col1466    unsigned BlockNumElts =1467        cast<FixedVectorType>(Block->getType())->getNumElements();1468    unsigned NumElts = cast<FixedVectorType>(Col->getType())->getNumElements();1469    assert(NumElts >= BlockNumElts && "Too few elements for current block");1470 1471    Block = Builder.CreateShuffleVector(1472        Block, createSequentialMask(0, BlockNumElts, NumElts - BlockNumElts));1473 1474    // If Col is 7 long and I is 2 and BlockNumElts is 2 the mask is: 0, 1, 7,1475    // 8, 4, 5, 61476    SmallVector<int, 16> Mask;1477    unsigned i;1478    for (i = 0; i < I; i++)1479      Mask.push_back(i);1480 1481    unsigned VecNumElts =1482        cast<FixedVectorType>(Col->getType())->getNumElements();1483    for (; i < I + BlockNumElts; i++)1484      Mask.push_back(i - I + VecNumElts);1485 1486    for (; i < VecNumElts; i++)1487      Mask.push_back(i);1488 1489    return Builder.CreateShuffleVector(Col, Block, Mask);1490  }1491 1492  Value *createMulAdd(Value *Sum, Value *A, Value *B, bool UseFPOp,1493                      IRBuilder<> &Builder, bool AllowContraction,1494                      unsigned &NumComputeOps) {1495    NumComputeOps += getNumOps(A->getType());1496    if (!Sum)1497      return UseFPOp ? Builder.CreateFMul(A, B) : Builder.CreateMul(A, B);1498 1499    if (UseFPOp) {1500      if (AllowContraction) {1501        // Use fmuladd for floating point operations and let the backend decide1502        // if that's profitable.1503        return Builder.CreateIntrinsic(Intrinsic::fmuladd, A->getType(),1504                                       {A, B, Sum});1505      }1506      NumComputeOps += getNumOps(A->getType());1507      Value *Mul = Builder.CreateFMul(A, B);1508      return Builder.CreateFAdd(Sum, Mul);1509    }1510 1511    NumComputeOps += getNumOps(A->getType());1512    Value *Mul = Builder.CreateMul(A, B);1513    return Builder.CreateAdd(Sum, Mul);1514  }1515 1516  /// Cache \p Matrix as result of \p Inst and update the uses of \p Inst. For1517  /// users with shape information, there's nothing to do: they will use the1518  /// cached value when they are lowered. For other users, \p Matrix is1519  /// flattened and the uses are updated to use it. Also marks \p Inst for1520  /// deletion.1521  void finalizeLowering(Instruction *Inst, MatrixTy Matrix,1522                        IRBuilder<> &Builder) {1523    auto inserted = Inst2ColumnMatrix.insert(std::make_pair(Inst, Matrix));1524    (void)inserted;1525    assert((inserted.second || isa<PHINode>(Inst)) &&1526           "multiple matrix lowering mapping");1527 1528    ToRemove.push_back(Inst);1529    Value *Flattened = nullptr;1530    for (Use &U : llvm::make_early_inc_range(Inst->uses())) {1531      if (ShapeMap.contains(U.getUser()))1532        continue;1533 1534      if (!Flattened) {1535        Flattened = Matrix.embedInVector(Builder);1536        LLVM_DEBUG(1537            if (Instruction *User = dyn_cast<Instruction>(U.getUser())) dbgs()1538                << "flattening a " << Matrix.shape() << " matrix:\n"1539                << *Inst1540                << "\nbecause we do not have a shape-aware lowering for its "1541                   "user:\n"1542                << *User << '\n';);1543        FlattenedMatrices++;1544      }1545      U.set(Flattened);1546    }1547  }1548 1549  /// Special case for MatMul lowering. Prevents scalar loads of row-major1550  /// vectors Lowers to vector reduction add instead of sequential add if1551  /// reassocation is enabled.1552  void lowerDotProduct(CallInst *MatMul,1553                       SmallPtrSet<Instruction *, 16> &FusedInsts,1554                       FastMathFlags FMF) {1555    if (FusedInsts.contains(MatMul) ||1556        MatrixLayout != MatrixLayoutTy::ColumnMajor)1557      return;1558    ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));1559    ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));1560 1561    if (LShape.NumRows != 1 || RShape.NumColumns != 1) // not a dot product1562      return;1563 1564    Value *LHS = MatMul->getArgOperand(0);1565    Value *RHS = MatMul->getArgOperand(1);1566 1567    Type *ElementType = cast<FixedVectorType>(LHS->getType())->getElementType();1568    bool IsIntVec = ElementType->isIntegerTy();1569 1570    // Floating point reductions require reassocation.1571    if (!IsIntVec && !FMF.allowReassoc())1572      return;1573 1574    auto CanBeFlattened = [](Value *Op) {1575      if (match(Op, m_BinOp()))1576        return true;1577      return match(1578          Op, m_OneUse(m_CombineOr(1579                  m_Load(m_Value()),1580                  m_CombineOr(m_Intrinsic<Intrinsic::matrix_transpose>(),1581                              m_Intrinsic<Intrinsic::matrix_column_major_load>(1582                                  m_Value(), m_SpecificInt(1))))));1583    };1584    // Returns the cost benefit of using \p Op with the dot product lowering. If1585    // the returned cost is < 0, the argument is cheaper to use in the1586    // dot-product lowering.1587    auto GetCostForArg = [this, &CanBeFlattened](Value *Op, unsigned N) {1588      if (!ShapeMap.contains(Op))1589        return InstructionCost::getInvalid();1590 1591      if (!isa<Instruction>(Op))1592        return InstructionCost(0);1593 1594      FixedVectorType *VecTy = cast<FixedVectorType>(Op->getType());1595      Type *EltTy = VecTy->getElementType();1596 1597      if (!CanBeFlattened(Op)) {1598        InstructionCost EmbedCost(0);1599        // Roughly estimate the cost for embedding the columns into a vector.1600        for (unsigned I = 1; I < N; ++I)1601          EmbedCost += TTI.getShuffleCost(1602              TTI::SK_Splice, FixedVectorType::get(EltTy, 1),1603              FixedVectorType::get(EltTy, 1), {}, TTI::TCK_RecipThroughput);1604        return EmbedCost;1605      }1606 1607      if (match(Op, m_BinOp()) && ShapeMap.contains(Op)) {1608        InstructionCost OriginalCost =1609            TTI.getArithmeticInstrCost(cast<Instruction>(Op)->getOpcode(),1610                                       EltTy) *1611            N;1612        InstructionCost NewCost = TTI.getArithmeticInstrCost(1613            cast<Instruction>(Op)->getOpcode(), VecTy);1614        return NewCost - OriginalCost;1615      }1616 1617      if (match(Op, m_Intrinsic<Intrinsic::matrix_transpose>())) {1618        // The transpose can be skipped for the dot product lowering, roughly1619        // estimate the savings as the cost of embedding the columns in a1620        // vector.1621        InstructionCost EmbedCost(0);1622        for (unsigned I = 1; I < N; ++I)1623          EmbedCost -= TTI.getShuffleCost(1624              TTI::SK_Splice, FixedVectorType::get(EltTy, 1),1625              FixedVectorType::get(EltTy, 1), {}, TTI::TCK_RecipThroughput);1626        return EmbedCost;1627      }1628 1629      // Costs for loads.1630      if (N == 1)1631        return InstructionCost(0);1632 1633      return TTI.getMemoryOpCost(Instruction::Load, VecTy, Align(1), 0) -1634             N * TTI.getMemoryOpCost(Instruction::Load, EltTy, Align(1), 0);1635    };1636 1637    // Iterate over LHS and operations feeding LHS and check if it is profitable1638    // to flatten the visited ops.  For each op, we compute the difference1639    // between the flattened and matrix versions.1640    SmallPtrSet<Value *, 4> Seen;1641    SmallVector<Value *> WorkList;1642    SmallVector<Value *> ToFlatten;1643    WorkList.push_back(LHS);1644    InstructionCost LHSCost(0);1645    while (!WorkList.empty()) {1646      Value *Op = WorkList.pop_back_val();1647      if (!Seen.insert(Op).second)1648        continue;1649 1650      InstructionCost OpCost = GetCostForArg(Op, LShape.NumColumns);1651      if (OpCost + LHSCost >= LHSCost)1652        continue;1653 1654      LHSCost += OpCost;1655      ToFlatten.push_back(Op);1656      if (auto *I = dyn_cast<Instruction>(Op))1657        WorkList.append(I->op_begin(), I->op_end());1658    }1659 1660    // We compare the costs of a vector.reduce.add to sequential add.1661    int AddOpCode = IsIntVec ? Instruction::Add : Instruction::FAdd;1662    int MulOpCode = IsIntVec ? Instruction::Mul : Instruction::FMul;1663    InstructionCost ReductionCost =1664        TTI.getArithmeticReductionCost(1665            AddOpCode, cast<FixedVectorType>(LHS->getType()),1666            IsIntVec ? std::nullopt : std::optional(FMF)) +1667        TTI.getArithmeticInstrCost(MulOpCode, LHS->getType());1668    InstructionCost SequentialAddCost =1669        TTI.getArithmeticInstrCost(AddOpCode, ElementType) *1670            (LShape.NumColumns - 1) +1671        TTI.getArithmeticInstrCost(MulOpCode, ElementType) *1672            (LShape.NumColumns);1673    if ((LHSCost + ReductionCost - SequentialAddCost) > InstructionCost(0))1674      return;1675 1676    FusedInsts.insert(MatMul);1677    IRBuilder<> Builder(MatMul);1678    auto FlattenArg = [&Builder, &FusedInsts, &CanBeFlattened,1679                       this](Value *Op) {1680      // Matmul must be the only user of loads because we don't use LowerLoad1681      // for row vectors (LowerLoad results in scalar loads and shufflevectors1682      // instead of single vector load).1683      if (!CanBeFlattened(Op))1684        return;1685 1686      if (match(Op, m_BinOp())) {1687        auto It = ShapeMap.find(Op);1688        if (It != ShapeMap.end()) {1689          It->second = It->second.t();1690          return;1691        }1692      }1693 1694      FusedInsts.insert(cast<Instruction>(Op));1695      // If vector uses the builtin load, lower to a LoadInst1696      Value *Arg;1697      if (match(Op, m_Intrinsic<Intrinsic::matrix_column_major_load>(1698                        m_Value(Arg)))) {1699        auto *NewLoad = Builder.CreateLoad(Op->getType(), Arg);1700        Op->replaceAllUsesWith(NewLoad);1701        eraseFromParentAndRemoveFromShapeMap(cast<Instruction>(Op));1702        return;1703      } else if (match(Op, m_Intrinsic<Intrinsic::matrix_transpose>(1704                               m_Value(Arg)))) {1705        ToRemove.push_back(cast<Instruction>(Op));1706        Op->replaceAllUsesWith(Arg);1707        return;1708      }1709    };1710 1711    for (auto *V : ToFlatten)1712      FlattenArg(V);1713 1714    LHS = MatMul->getArgOperand(0);1715 1716    // Insert mul/fmul and llvm.vector.reduce.fadd1717    Value *Mul =1718        IsIntVec ? Builder.CreateMul(LHS, RHS) : Builder.CreateFMul(LHS, RHS);1719 1720    Value *Result;1721    if (IsIntVec)1722      Result = Builder.CreateAddReduce(Mul);1723    else {1724      Result = Builder.CreateFAddReduce(1725          ConstantFP::get(1726              cast<FixedVectorType>(LHS->getType())->getElementType(), 0.0),1727          Mul);1728      cast<Instruction>(Result)->setFastMathFlags(FMF);1729    }1730 1731    // pack scalar back into a matrix and then replace matmul inst1732    Result = Builder.CreateInsertElement(PoisonValue::get(MatMul->getType()),1733                                         Result, uint64_t(0));1734    MatMul->replaceAllUsesWith(Result);1735    FusedInsts.insert(MatMul);1736    ToRemove.push_back(MatMul);1737  }1738 1739  /// Given \p Remainder iterations of the the matmul inner loop,1740  /// potentially lower \p Blocksize that is used for the underlying1741  /// vector.1742  unsigned capBlockSize(unsigned BlockSize, unsigned Remainder, Type *EltType) {1743    if (BlockSize <= Remainder)1744      return BlockSize;1745 1746    // If the remainder is also a legal type just use it.1747    auto *VecTy = FixedVectorType::get(EltType, Remainder);1748    if (TTI.isTypeLegal(VecTy))1749      return Remainder;1750 1751    // Similarly, if the vector is small enough that we don't want1752    // to split further.1753    if (VecTy->getPrimitiveSizeInBits() <= SplitMatmulRemainderOverThreshold)1754      return Remainder;1755 1756    // Gradually lower the vectorization factor to cover the1757    // remainder.1758    do {1759      BlockSize /= 2;1760    } while (BlockSize > Remainder);1761    return BlockSize;1762  }1763 1764  /// Compute \p Result += \p A * \p B for input matrices with left-associating1765  /// addition.1766  ///1767  /// We can fold a transpose into the operand that is used to extract scalars.1768  /// This is the first operands with row-major and the second with1769  /// column-major.  If \p IsScalarMatrixTransposed we assume the appropriate1770  /// operand is transposed.1771  void emitMatrixMultiply(MatrixTy &Result, const MatrixTy &A,1772                          const MatrixTy &B, IRBuilder<> &Builder, bool IsTiled,1773                          bool IsScalarMatrixTransposed, FastMathFlags FMF) {1774    const unsigned VF = std::max<unsigned>(1775        TTI.getRegisterBitWidth(TargetTransformInfo::RGK_FixedWidthVector)1776                .getFixedValue() /1777            Result.getElementType()->getPrimitiveSizeInBits().getFixedValue(),1778        1U);1779    unsigned R = Result.getNumRows();1780    unsigned C = Result.getNumColumns();1781    unsigned M = A.getNumColumns();1782 1783    bool IsFP = Result.getElementType()->isFloatingPointTy();1784    assert(A.isColumnMajor() == B.isColumnMajor() &&1785           Result.isColumnMajor() == A.isColumnMajor() &&1786           "operands must agree on matrix layout");1787    unsigned NumComputeOps = 0;1788 1789    Builder.setFastMathFlags(FMF);1790 1791    if (A.isColumnMajor()) {1792      // Multiply columns from the first operand with scalars from the second1793      // operand. Then move along the K axes and accumulate the columns.  With1794      // this the adds can be vectorized without reassociation.1795      for (unsigned J = 0; J < C; ++J) {1796        unsigned BlockSize = VF;1797        // If Result is zero, we don't need to accumulate in the K==0 iteration.1798        bool isSumZero = isa<ConstantAggregateZero>(Result.getColumn(J));1799 1800        for (unsigned I = 0; I < R; I += BlockSize) {1801          // Lower block size to make sure we stay within bounds.1802          BlockSize = capBlockSize(BlockSize, R - I, Result.getElementType());1803          Value *Sum = IsTiled ? Result.extractVector(I, J, BlockSize, Builder)1804                               : nullptr;1805          for (unsigned K = 0; K < M; ++K) {1806            Value *L = A.extractVector(I, K, BlockSize, Builder);1807            Value *RH = Builder.CreateExtractElement(1808                B.getColumn(IsScalarMatrixTransposed ? K : J),1809                IsScalarMatrixTransposed ? J : K);1810            Value *Splat = Builder.CreateVectorSplat(BlockSize, RH, "splat");1811            Sum =1812                createMulAdd(isSumZero && K == 0 ? nullptr : Sum, L, Splat,1813                             IsFP, Builder, FMF.allowContract(), NumComputeOps);1814          }1815          Result.setVector(J,1816                           insertVector(Result.getVector(J), I, Sum, Builder));1817        }1818      }1819    } else {1820      // Multiply rows from the second operand with scalars from the first1821      // operand. Then move along the K axes and accumulate the rows.  With this1822      // the adds can be vectorized without reassociation.1823      for (unsigned I = 0; I < R; ++I) {1824        unsigned BlockSize = VF;1825        bool isSumZero = isa<ConstantAggregateZero>(Result.getRow(I));1826        for (unsigned J = 0; J < C; J += BlockSize) {1827          // Lower the vectorization factor to cover the remainder.1828          BlockSize = capBlockSize(BlockSize, C - J, Result.getElementType());1829 1830          Value *Sum = nullptr;1831          for (unsigned K = 0; K < M; ++K) {1832            Value *R = B.extractVector(K, J, BlockSize, Builder);1833            Value *LH = Builder.CreateExtractElement(1834                A.getVector(IsScalarMatrixTransposed ? K : I),1835                IsScalarMatrixTransposed ? I : K);1836            Value *Splat = Builder.CreateVectorSplat(BlockSize, LH, "splat");1837            Sum =1838                createMulAdd(isSumZero && K == 0 ? nullptr : Sum, Splat, R,1839                             IsFP, Builder, FMF.allowContract(), NumComputeOps);1840          }1841          Result.setVector(I,1842                           insertVector(Result.getVector(I), J, Sum, Builder));1843        }1844      }1845    }1846    Result.addNumComputeOps(NumComputeOps);1847  }1848 1849  /// Ensure that the memory in \p Load does not alias \p Store by potentially1850  /// copying it to a new location.  This new or otherwise the original location1851  /// is returned.1852  Value *getNonAliasingPointer(LoadInst *Load, StoreInst *Store,1853                               CallInst *MatMul) {1854    MemoryLocation StoreLoc = MemoryLocation::get(Store);1855    MemoryLocation LoadLoc = MemoryLocation::get(Load);1856 1857    // If we can statically determine noalias we're good.1858    if (AA->isNoAlias(LoadLoc, StoreLoc))1859      return Load->getPointerOperand();1860 1861    // Create code to check if the memory locations of the Load and Store1862    // overlap and if they do, copy Load's operand to a new buffer.1863 1864    // First, create  new blocks for 2n part of the check and the copy.1865    BasicBlock *Check0 = MatMul->getParent();1866    // FIXME: Use lazy DTU and update SplitBlock to accept a DTU instead of a1867    // DT. Manually collect dominator tree updates, to avoid unnecessary work,1868    // as we adjust Check0 and Check1's branches.1869    SmallVector<DominatorTree::UpdateType, 4> DTUpdates;1870    for (BasicBlock *Succ : successors(Check0))1871      DTUpdates.push_back({DT->Delete, Check0, Succ});1872 1873    BasicBlock *Check1 =1874        SplitBlock(MatMul->getParent(), MatMul, (DomTreeUpdater *)nullptr, LI,1875                   nullptr, "alias_cont");1876    BasicBlock *Copy =1877        SplitBlock(MatMul->getParent(), MatMul, (DomTreeUpdater *)nullptr, LI,1878                   nullptr, "copy");1879    BasicBlock *Fusion =1880        SplitBlock(MatMul->getParent(), MatMul, (DomTreeUpdater *)nullptr, LI,1881                   nullptr, "no_alias");1882 1883    // Check if the loaded memory location begins before the end of the store1884    // location. If the condition holds, they might overlap, otherwise they are1885    // guaranteed to not overlap.1886    IRBuilder<> Builder(MatMul);1887    Check0->getTerminator()->eraseFromParent();1888    Builder.SetInsertPoint(Check0);1889    Type *IntPtrTy = Builder.getIntPtrTy(Load->getDataLayout());1890    Value *StoreBegin = Builder.CreatePtrToInt(1891        const_cast<Value *>(StoreLoc.Ptr), IntPtrTy, "store.begin");1892    Value *StoreEnd = Builder.CreateAdd(1893        StoreBegin, ConstantInt::get(IntPtrTy, StoreLoc.Size.getValue()),1894        "store.end", true, true);1895    Value *LoadBegin = Builder.CreatePtrToInt(const_cast<Value *>(LoadLoc.Ptr),1896                                              IntPtrTy, "load.begin");1897    Builder.CreateCondBr(Builder.CreateICmpULT(LoadBegin, StoreEnd), Check1,1898                         Fusion);1899 1900    // Check if the store begins before the end of the load location. If the1901    // condition holds, they alias, otherwise they are guaranteed to not1902    // overlap.1903    Check1->getTerminator()->eraseFromParent();1904    Builder.SetInsertPoint(Check1, Check1->begin());1905    Value *LoadEnd = Builder.CreateAdd(1906        LoadBegin, ConstantInt::get(IntPtrTy, LoadLoc.Size.getValue()),1907        "load.end", true, true);1908    Builder.CreateCondBr(Builder.CreateICmpULT(StoreBegin, LoadEnd), Copy,1909                         Fusion);1910 1911    // Copy load operand to new alloca.1912    Builder.SetInsertPoint(Copy, Copy->begin());1913    auto *VT = cast<FixedVectorType>(Load->getType());1914    // Use an array type for the alloca, to avoid potentially huge alignment1915    // requirements for large vector types.1916    auto *ArrayTy = ArrayType::get(VT->getElementType(), VT->getNumElements());1917    AllocaInst *Alloca =1918        Builder.CreateAlloca(ArrayTy, Load->getPointerAddressSpace());1919 1920    Builder.CreateMemCpy(Alloca, Alloca->getAlign(), Load->getPointerOperand(),1921                         Load->getAlign(), LoadLoc.Size.getValue());1922    Builder.SetInsertPoint(Fusion, Fusion->begin());1923    PHINode *PHI = Builder.CreatePHI(Load->getPointerOperandType(), 3);1924    PHI->addIncoming(Load->getPointerOperand(), Check0);1925    PHI->addIncoming(Load->getPointerOperand(), Check1);1926    PHI->addIncoming(Alloca, Copy);1927 1928    // Adjust DT.1929    DTUpdates.push_back({DT->Insert, Check0, Check1});1930    DTUpdates.push_back({DT->Insert, Check0, Fusion});1931    DTUpdates.push_back({DT->Insert, Check1, Copy});1932    DTUpdates.push_back({DT->Insert, Check1, Fusion});1933    DT->applyUpdates(DTUpdates);1934    return PHI;1935  }1936 1937  bool isFusionProfitable(CallInst *MatMul) {1938    if (ForceFusion)1939      return true;1940 1941    ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));1942    ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));1943 1944    const unsigned R = LShape.NumRows;1945    const unsigned C = RShape.NumColumns;1946    const unsigned M = LShape.NumColumns;1947    auto *EltType = cast<FixedVectorType>(MatMul->getType())->getElementType();1948 1949    const unsigned VF = std::max<unsigned>(1950        TTI.getRegisterBitWidth(TargetTransformInfo::RGK_FixedWidthVector)1951                .getFixedValue() /1952            EltType->getPrimitiveSizeInBits().getFixedValue(),1953        1U);1954 1955    // Cost model for tiling1956    //1957    // For tiling to be beneficial, we need reuse either along the R or1958    // the C axis.  We vectorize along the R axis so that means at least1959    // 3 elements.1960    // TODO: Also consider cost of copying if operands alias.1961    if (R <= VF && C == 1)1962      return false;1963    // Then we need enough elements to exceed the number of vector1964    // registers we have.  Note that this is an oversimplification since1965    // fusing also takes some extra loads which may exceed the number of1966    // reloads necessary.1967    unsigned Op0Regs = (R + VF - 1) / VF * M;1968    unsigned Op1Regs = (M + VF - 1) / VF * C;1969    return Op0Regs + Op1Regs >1970           TTI.getNumberOfRegisters(TTI.getRegisterClassForType(true));1971  }1972 1973  MatrixTy getZeroMatrix(Type *EltType, unsigned R, unsigned C) {1974    MatrixTy Res;1975    auto *ColumType = FixedVectorType::get(EltType, R);1976    for (unsigned I = 0; I < C; ++I)1977      Res.addVector(ConstantAggregateZero::get(ColumType));1978    return Res;1979  }1980 1981  void createTiledLoops(CallInst *MatMul, Value *LPtr, ShapeInfo LShape,1982                        Value *RPtr, ShapeInfo RShape, StoreInst *Store) {1983    auto *EltType = cast<FixedVectorType>(MatMul->getType())->getElementType();1984 1985    // Create the main tiling loop nest.1986    TileInfo TI(LShape.NumRows, RShape.NumColumns, LShape.NumColumns, TileSize);1987    DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);1988    Instruction *InsertI = cast<Instruction>(MatMul);1989    BasicBlock *Start = InsertI->getParent();1990    BasicBlock *End =1991        SplitBlock(InsertI->getParent(), InsertI, DT, LI, nullptr, "continue");1992    IRBuilder<> Builder(MatMul);1993    BasicBlock *InnerBody = TI.CreateTiledLoops(Start, End, Builder, DTU, *LI);1994 1995    Type *TileVecTy =1996        FixedVectorType::get(MatMul->getType()->getScalarType(), TileSize);1997    MatrixTy TileResult;1998    // Insert in the inner loop header.1999    Builder.SetInsertPoint(TI.KLoop.Header->getTerminator());2000    // Create PHI nodes for the result columns to accumulate across iterations.2001    SmallVector<PHINode *, 4> ColumnPhis;2002    for (unsigned I = 0; I < TileSize; I++) {2003      auto *Phi = Builder.CreatePHI(TileVecTy, 2, "result.vec." + Twine(I));2004      Phi->addIncoming(ConstantAggregateZero::get(TileVecTy),2005                       TI.RowLoop.Header->getSingleSuccessor());2006      TileResult.addVector(Phi);2007      ColumnPhis.push_back(Phi);2008    }2009 2010    // Insert in the inner loop body, which computes2011    //   Res += Load(CurrentRow, K) * Load(K, CurrentColumn)2012    Builder.SetInsertPoint(InnerBody->getTerminator());2013    // Load tiles of the operands.2014    MatrixTy A =2015        loadMatrix(LPtr, {}, false, LShape, TI.RowLoop.Index, TI.KLoop.Index,2016                   {TileSize, TileSize}, EltType, Builder);2017    MatrixTy B =2018        loadMatrix(RPtr, {}, false, RShape, TI.KLoop.Index, TI.ColumnLoop.Index,2019                   {TileSize, TileSize}, EltType, Builder);2020    emitMatrixMultiply(TileResult, A, B, Builder, true, false,2021                       getFastMathFlags(MatMul));2022    // Store result after the inner loop is done.2023    Builder.SetInsertPoint(TI.RowLoop.Latch->getTerminator());2024    storeMatrix(TileResult, Store->getPointerOperand(), Store->getAlign(),2025                Store->isVolatile(), {LShape.NumRows, RShape.NumColumns},2026                TI.RowLoop.Index, TI.ColumnLoop.Index, EltType, Builder);2027 2028    for (unsigned I = 0; I < TileResult.getNumVectors(); I++)2029      ColumnPhis[I]->addIncoming(TileResult.getVector(I), TI.KLoop.Latch);2030 2031    // Force unrolling of a few iterations of the inner loop, to make sure there2032    // is enough work per iteration.2033    // FIXME: The unroller should make this decision directly instead, but2034    // currently the cost-model is not up to the task.2035    unsigned InnerLoopUnrollCount = std::min(10u, LShape.NumColumns / TileSize);2036    addStringMetadataToLoop(LI->getLoopFor(TI.KLoop.Header),2037                            "llvm.loop.unroll.count", InnerLoopUnrollCount);2038  }2039 2040  void emitSIMDTiling(CallInst *MatMul, LoadInst *LoadOp0, LoadInst *LoadOp1,2041                      StoreInst *Store,2042                      SmallPtrSetImpl<Instruction *> &FusedInsts) {2043    assert(MatrixLayout == MatrixLayoutTy::ColumnMajor &&2044           "Tiling only supported for column-major matrixes at the moment!");2045    if (!isFusionProfitable(MatMul))2046      return;2047 2048    ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));2049    ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));2050 2051    const unsigned R = LShape.NumRows;2052    const unsigned C = RShape.NumColumns;2053    const unsigned M = LShape.NumColumns;2054    auto *EltType = cast<FixedVectorType>(MatMul->getType())->getElementType();2055 2056    Value *APtr = getNonAliasingPointer(LoadOp0, Store, MatMul);2057    Value *BPtr = getNonAliasingPointer(LoadOp1, Store, MatMul);2058    Value *CPtr = Store->getPointerOperand();2059 2060    if (TileUseLoops && (R % TileSize == 0 && C % TileSize == 0))2061      createTiledLoops(MatMul, APtr, LShape, BPtr, RShape, Store);2062    else {2063      IRBuilder<> Builder(Store);2064      for (unsigned J = 0; J < C; J += TileSize)2065        for (unsigned I = 0; I < R; I += TileSize) {2066          const unsigned TileR = std::min(R - I, unsigned(TileSize));2067          const unsigned TileC = std::min(C - J, unsigned(TileSize));2068          MatrixTy Res = getZeroMatrix(EltType, TileR, TileC);2069 2070          for (unsigned K = 0; K < M; K += TileSize) {2071            const unsigned TileM = std::min(M - K, unsigned(TileSize));2072            MatrixTy A =2073                loadMatrix(APtr, LoadOp0->getAlign(), LoadOp0->isVolatile(),2074                           LShape, getIndex(APtr, I), getIndex(APtr, K),2075                           {TileR, TileM}, EltType, Builder);2076            MatrixTy B =2077                loadMatrix(BPtr, LoadOp1->getAlign(), LoadOp1->isVolatile(),2078                           RShape, getIndex(BPtr, K), getIndex(BPtr, J),2079                           {TileM, TileC}, EltType, Builder);2080            emitMatrixMultiply(Res, A, B, Builder, true, false,2081                               getFastMathFlags(MatMul));2082          }2083          storeMatrix(Res, CPtr, Store->getAlign(), Store->isVolatile(), {R, M},2084                      getIndex(CPtr, I), getIndex(CPtr, J), EltType, Builder);2085        }2086    }2087 2088    // Mark eliminated instructions as fused and remove them.2089    FusedInsts.insert(Store);2090    FusedInsts.insert(MatMul);2091    eraseFromParentAndRemoveFromShapeMap(Store);2092    eraseFromParentAndRemoveFromShapeMap(MatMul);2093    if (LoadOp0->use_empty()) {2094      FusedInsts.insert(LoadOp0);2095      eraseFromParentAndRemoveFromShapeMap(LoadOp0);2096    }2097    if (LoadOp1 != LoadOp0 && LoadOp1->use_empty()) {2098      FusedInsts.insert(LoadOp1);2099      eraseFromParentAndRemoveFromShapeMap(LoadOp1);2100    }2101  }2102 2103  /// Try to lower matrix multiply chains by fusing operations.2104  ///2105  /// Call finalizeLowering on lowered instructions.  Instructions that are2106  /// completely eliminated by fusion are added to \p FusedInsts.2107  void2108  LowerMatrixMultiplyFused(CallInst *MatMul,2109                           SmallPtrSetImpl<Instruction *> &FusedInsts,2110                           SmallVector<IntrinsicInst *, 16> &LifetimeEnds) {2111    if (!FuseMatrix || !DT)2112      return;2113 2114    assert(AA && LI && "Analyses should be available");2115 2116    Value *A = MatMul->getArgOperand(0);2117    Value *B = MatMul->getArgOperand(1);2118 2119    // We can fold the transpose into the operand that is used to fetch scalars.2120    Value *T;2121    if (MatrixLayout == MatrixLayoutTy::ColumnMajor2122            ? match(B, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(T)))2123            : match(A, m_Intrinsic<Intrinsic::matrix_transpose>(m_Value(T)))) {2124      IRBuilder<> Builder(MatMul);2125      auto *EltType =2126          cast<FixedVectorType>(MatMul->getType())->getElementType();2127      ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));2128      ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));2129      const unsigned R = LShape.NumRows;2130      const unsigned M = LShape.NumColumns;2131      const unsigned C = RShape.NumColumns;2132 2133      MatrixTy MA;2134      MatrixTy MB;2135 2136      Value *Transpose;2137      if (MatrixLayout == MatrixLayoutTy::ColumnMajor) {2138        MA = getMatrix(A, ShapeInfo(R, M), Builder);2139        MB = getMatrix(T, ShapeInfo(C, M), Builder);2140        Transpose = B;2141      } else {2142        MA = getMatrix(T, ShapeInfo(R, M), Builder);2143        MB = getMatrix(B, ShapeInfo(C, M), Builder);2144        Transpose = A;2145      }2146 2147      // Initialize the output2148      MatrixTy Result(R, C, EltType);2149 2150      emitMatrixMultiply(Result, MA, MB, Builder, false, true,2151                         getFastMathFlags(MatMul));2152 2153      FusedInsts.insert(MatMul);2154      if (Transpose->hasOneUse()) {2155        FusedInsts.insert(cast<Instruction>(Transpose));2156        ToRemove.push_back(cast<Instruction>(Transpose));2157        // TODO: add a fake entry for the folded instruction so that this is2158        // included in the expression in the remark.2159        Inst2ColumnMatrix[Transpose] = MatrixTy(M, C, EltType);2160      }2161      finalizeLowering(MatMul, Result, Builder);2162      return;2163    }2164 2165    if (!MatMul->hasOneUse() || MatrixLayout != MatrixLayoutTy::ColumnMajor)2166      return;2167 2168    // Lower {ld, ld} -> matmul -> st chains.  No need to call finalizeLowering2169    // since the single store user will be lowered as part of this.2170    auto *LoadOp0 = dyn_cast<LoadInst>(A);2171    auto *LoadOp1 = dyn_cast<LoadInst>(B);2172    auto *Store = dyn_cast<StoreInst>(*MatMul->user_begin());2173    if (LoadOp0 && LoadOp1 && Store) {2174      // The store address must dominate the MatMul instruction, otherwise2175      // we create invalid IR.2176      SetVector<Value *> WorkList;2177      WorkList.insert(Store->getOperand(1));2178      SmallVector<Instruction *> ToHoist;2179      for (unsigned I = 0; I != WorkList.size(); ++I) {2180        Value *Current = WorkList[I];2181        auto *CurrI = dyn_cast<Instruction>(Current);2182        if (!CurrI)2183          continue;2184        if (isa<PHINode>(CurrI))2185          return;2186        if (DT->dominates(CurrI, MatMul))2187          continue;2188        if (CurrI->mayHaveSideEffects() || CurrI->mayReadFromMemory())2189          return;2190        ToHoist.push_back(CurrI);2191        WorkList.insert_range(CurrI->operands());2192      }2193 2194      sort(ToHoist, [this](Instruction *A, Instruction *B) {2195        return DT->dominates(A, B);2196      });2197      for (Instruction *I : ToHoist)2198        I->moveBefore(MatMul->getIterator());2199 2200      // Deal with lifetime.end calls that might be between Load0/Load1 and the2201      // store. To avoid introducing loads to dead objects (i.e. after the2202      // lifetime has been termined by @llvm.lifetime.end), either sink them2203      // after the store if in the same block, or remove the lifetime.end marker2204      // otherwise. This might pessimize further optimizations, by extending the2205      // lifetime of the object until the function returns, but should be2206      // conservatively correct.2207      MemoryLocation Load0Loc = MemoryLocation::get(LoadOp0);2208      MemoryLocation Load1Loc = MemoryLocation::get(LoadOp1);2209      BasicBlock *StoreParent = Store->getParent();2210      bool FusableOpsInSameBlock = LoadOp0->getParent() == StoreParent &&2211                                   LoadOp1->getParent() == StoreParent;2212      for (unsigned Idx = 0; Idx != LifetimeEnds.size();) {2213        IntrinsicInst *End = LifetimeEnds[Idx];2214        auto Inc = make_scope_exit([&Idx]() { Idx++; });2215        // If the lifetime.end is guaranteed to be before the loads or after the2216        // store, it won't interfere with fusion.2217        if (DT->dominates(End, LoadOp0) && DT->dominates(End, LoadOp1))2218          continue;2219        if (DT->dominates(Store, End))2220          continue;2221        // If all fusable ops are in the same block and the lifetime.end is in a2222        // different block, it won't interfere with fusion.2223        if (FusableOpsInSameBlock && End->getParent() != StoreParent)2224          continue;2225 2226        // If the loads don't alias the lifetime.end, it won't interfere with2227        // fusion.2228        MemoryLocation EndLoc = MemoryLocation::getForArgument(End, 0, nullptr);2229        if (!EndLoc.Ptr)2230          continue;2231        if (AA->isNoAlias(Load0Loc, EndLoc) && AA->isNoAlias(Load1Loc, EndLoc))2232          continue;2233 2234        // If both lifetime.end and the store are in the same block, extend the2235        // lifetime until after the store, so the new lifetime covers the loads2236        // we introduce later.2237        if (End->getParent() == StoreParent) {2238          End->moveAfter(Store);2239          continue;2240        }2241 2242        // Otherwise remove the conflicting lifetime.end marker.2243        ToRemove.push_back(End);2244        std::swap(LifetimeEnds[Idx], LifetimeEnds.back());2245        LifetimeEnds.pop_back();2246        Inc.release();2247      }2248 2249      emitSIMDTiling(MatMul, LoadOp0, LoadOp1, Store, FusedInsts);2250      return;2251    }2252  }2253 2254  /// Lowers llvm.matrix.multiply.2255  MatrixTy LowerMultiply(CallInst *MatMul, IRBuilder<> &Builder) {2256    auto *EltType = cast<FixedVectorType>(MatMul->getType())->getElementType();2257    ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3));2258    ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4));2259 2260    const MatrixTy &Lhs = getMatrix(MatMul->getArgOperand(0), LShape, Builder);2261    const MatrixTy &Rhs = getMatrix(MatMul->getArgOperand(1), RShape, Builder);2262    assert(Lhs.getElementType() == Rhs.getElementType() &&2263           "Matrix multiply argument element types do not match.");2264 2265    const unsigned R = LShape.NumRows;2266    const unsigned C = RShape.NumColumns;2267    assert(LShape.NumColumns == RShape.NumRows);2268 2269    // Initialize the output2270    MatrixTy Result(R, C, EltType);2271    assert(Lhs.getElementType() == Result.getElementType() &&2272           "Matrix multiply result element type does not match arguments.");2273 2274    emitMatrixMultiply(Result, Lhs, Rhs, Builder, false, false,2275                       getFastMathFlags(MatMul));2276    return Result;2277  }2278 2279  /// Lowers llvm.matrix.transpose.2280  MatrixTy LowerTranspose(CallInst *Inst, IRBuilder<> &Builder) {2281    MatrixTy Result;2282    Value *InputVal = Inst->getArgOperand(0);2283    FixedVectorType *VectorTy = cast<FixedVectorType>(InputVal->getType());2284    ShapeInfo ArgShape(Inst->getArgOperand(1), Inst->getArgOperand(2));2285    MatrixTy InputMatrix = getMatrix(InputVal, ArgShape, Builder);2286 2287    const unsigned NewNumVecs =2288        InputMatrix.isColumnMajor() ? ArgShape.NumRows : ArgShape.NumColumns;2289    const unsigned NewNumElts =2290        InputMatrix.isColumnMajor() ? ArgShape.NumColumns : ArgShape.NumRows;2291 2292    for (unsigned I = 0; I < NewNumVecs; ++I) {2293      // Build a single result vector. First initialize it.2294      Value *ResultVector = PoisonValue::get(2295          FixedVectorType::get(VectorTy->getElementType(), NewNumElts));2296      // Go through the old elements and insert it into the resulting vector.2297      for (auto J : enumerate(InputMatrix.vectors())) {2298        Value *Elt = Builder.CreateExtractElement(J.value(), I);2299        // Row and column indices are transposed.2300        ResultVector =2301            Builder.CreateInsertElement(ResultVector, Elt, J.index());2302      }2303      Result.addVector(ResultVector);2304    }2305 2306    // TODO: Improve estimate of operations needed for transposes. Currently we2307    // just count the insertelement/extractelement instructions, but do not2308    // account for later simplifications/combines.2309    return Result.addNumComputeOps(2 * ArgShape.NumRows * ArgShape.NumColumns)2310        .addNumExposedTransposes(1);2311  }2312 2313  /// Lower load instructions.2314  MatrixTy VisitLoad(LoadInst *Inst, const ShapeInfo &SI, Value *Ptr,2315                     IRBuilder<> &Builder) {2316    return LowerLoad(Inst, Ptr, Inst->getAlign(), getIndex(Ptr, SI.getStride()),2317                     Inst->isVolatile(), SI, Builder);2318  }2319 2320  MatrixTy VisitStore(StoreInst *Inst, const ShapeInfo &SI, Value *StoredVal,2321                      Value *Ptr, IRBuilder<> &Builder) {2322    return LowerStore(Inst, StoredVal, Ptr, Inst->getAlign(),2323                      getIndex(Ptr, SI.getStride()), Inst->isVolatile(), SI,2324                      Builder);2325  }2326 2327  MatrixTy VisitPHI(PHINode *Inst, const ShapeInfo &SI, IRBuilder<> &Builder) {2328    auto BlockIP = Inst->getParent()->getFirstInsertionPt();2329    Builder.SetInsertPoint(BlockIP);2330    MatrixTy PhiM = getMatrix(Inst, SI, Builder);2331 2332    for (auto [IncomingV, IncomingB] :2333         llvm::zip_equal(Inst->incoming_values(), Inst->blocks())) {2334      // getMatrix() may insert some instructions to help with reshaping. The2335      // safest place for those is at the top of the block after the rest of the2336      // PHI's. Even better, if we can put it in the incoming block.2337      Builder.SetInsertPoint(BlockIP);2338      if (auto *IncomingInst = dyn_cast<Instruction>(IncomingV))2339        if (auto MaybeIP = IncomingInst->getInsertionPointAfterDef())2340          Builder.SetInsertPoint(*MaybeIP);2341 2342      MatrixTy OpM = getMatrix(IncomingV, SI, Builder);2343 2344      for (unsigned VI = 0, VE = PhiM.getNumVectors(); VI != VE; ++VI) {2345        PHINode *NewPHI = cast<PHINode>(PhiM.getVector(VI));2346        NewPHI->addIncoming(OpM.getVector(VI), IncomingB);2347      }2348    }2349 2350    // finalizeLowering() may also insert instructions in some cases. The safe2351    // place for those is at the end of the initial block of PHIs.2352    Builder.SetInsertPoint(BlockIP);2353    return PhiM;2354  }2355 2356  /// Lower binary operators.2357  MatrixTy VisitBinaryOperator(BinaryOperator *Inst, const ShapeInfo &SI,2358                               IRBuilder<> &Builder) {2359    Value *Lhs = Inst->getOperand(0);2360    Value *Rhs = Inst->getOperand(1);2361 2362    MatrixTy Result;2363    MatrixTy A = getMatrix(Lhs, SI, Builder);2364    MatrixTy B = getMatrix(Rhs, SI, Builder);2365    assert(A.isColumnMajor() == B.isColumnMajor() &&2366           Result.isColumnMajor() == A.isColumnMajor() &&2367           "operands must agree on matrix layout");2368 2369    Builder.setFastMathFlags(getFastMathFlags(Inst));2370 2371    for (auto [AV, BV] : llvm::zip_equal(A.vectors(), B.vectors()))2372      Result.addVector(Builder.CreateBinOp(Inst->getOpcode(), AV, BV));2373 2374    return Result.addNumComputeOps(getNumOps(Result.getVectorTy()) *2375                                   Result.getNumVectors());2376  }2377 2378  /// Lower unary operators.2379  MatrixTy VisitUnaryOperator(UnaryOperator *Inst, const ShapeInfo &SI,2380                              IRBuilder<> &Builder) {2381    Value *Op = Inst->getOperand(0);2382 2383    MatrixTy Result;2384    MatrixTy M = getMatrix(Op, SI, Builder);2385 2386    Builder.setFastMathFlags(getFastMathFlags(Inst));2387 2388    // Helper to perform unary op on vectors.2389    auto BuildVectorOp = [&Builder, Inst](Value *Op) {2390      switch (Inst->getOpcode()) {2391      case Instruction::FNeg:2392        return Builder.CreateFNeg(Op);2393      default:2394        llvm_unreachable("Unsupported unary operator for matrix");2395      }2396    };2397 2398    for (auto *Vector : M.vectors())2399      Result.addVector(BuildVectorOp(Vector));2400 2401    return Result.addNumComputeOps(getNumOps(Result.getVectorTy()) *2402                                   Result.getNumVectors());2403  }2404 2405  /// Lower cast instructions.2406  MatrixTy VisitCastInstruction(CastInst *Inst, const ShapeInfo &Shape,2407                                IRBuilder<> &Builder) {2408    Value *Op = Inst->getOperand(0);2409 2410    MatrixTy Result;2411    MatrixTy M = getMatrix(Op, Shape, Builder);2412 2413    Builder.setFastMathFlags(getFastMathFlags(Inst));2414 2415    auto *OrigVTy = cast<VectorType>(Inst->getType());2416    auto *NewVTy = VectorType::get(OrigVTy->getElementType(),2417                                   ElementCount::getFixed(M.getStride()));2418 2419    for (auto *Vector : M.vectors())2420      Result.addVector(Builder.CreateCast(Inst->getOpcode(), Vector, NewVTy));2421 2422    return Result.addNumComputeOps(getNumOps(Result.getVectorTy()) *2423                                   Result.getNumVectors());2424  }2425 2426  /// Lower selects.2427  MatrixTy VisitSelectInst(SelectInst *Inst, const ShapeInfo &Shape,2428                           IRBuilder<> &Builder) {2429    Value *Cond = Inst->getOperand(0);2430    Value *OpA = Inst->getOperand(1);2431    Value *OpB = Inst->getOperand(2);2432 2433    MatrixTy Result;2434    MatrixTy A = getMatrix(OpA, Shape, Builder);2435    MatrixTy B = getMatrix(OpB, Shape, Builder);2436 2437    SmallVector<Value*> CondV;2438    if (isa<FixedVectorType>(Cond->getType())) {2439      MatrixTy C = getMatrix(Cond, Shape, Builder);2440      llvm::copy(C.vectors(), std::back_inserter(CondV));2441    } else {2442      CondV.resize(A.getNumVectors());2443      llvm::fill(CondV, Cond);2444    }2445 2446    for (auto [CV, AV, BV] : llvm::zip_equal(CondV, A.vectors(), B.vectors()))2447      Result.addVector(Builder.CreateSelect(CV, AV, BV));2448 2449    return Result.addNumComputeOps(getNumOps(Result.getVectorTy()) *2450                                   Result.getNumVectors());2451  }2452 2453  /// Helper to linearize a matrix expression tree into a string. Currently2454  /// matrix expressions are linarized by starting at an expression leaf and2455  /// linearizing bottom up.2456  struct ExprLinearizer {2457    unsigned LengthToBreak = 100;2458    std::string Str;2459    raw_string_ostream Stream;2460    unsigned LineLength = 0;2461    const DataLayout &DL;2462 2463    /// Mapping from instructions to matrixes. It is used to identify2464    /// matrix instructions.2465    const MapVector<Value *, MatrixTy> &Inst2Matrix;2466 2467    /// Mapping from values to the leaves of all expressions that the value is2468    /// part of.2469    const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared;2470 2471    /// Set of matrix expressions in the scope of a given DISubprogram.2472    const SmallSetVector<Value *, 32> &ExprsInSubprogram;2473 2474    /// Leaf node of the expression to linearize.2475    Value *Leaf;2476 2477    /// Used to keep track of sub-expressions that get reused while linearizing2478    /// the expression. Re-used sub-expressions are marked as (reused).2479    SmallPtrSet<Value *, 8> ReusedExprs;2480 2481    ExprLinearizer(const DataLayout &DL,2482                   const MapVector<Value *, MatrixTy> &Inst2Matrix,2483                   const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared,2484                   const SmallSetVector<Value *, 32> &ExprsInSubprogram,2485                   Value *Leaf)2486        : Stream(Str), DL(DL), Inst2Matrix(Inst2Matrix), Shared(Shared),2487          ExprsInSubprogram(ExprsInSubprogram), Leaf(Leaf) {}2488 2489    void indent(unsigned N) {2490      LineLength += N;2491      for (unsigned i = 0; i < N; i++)2492        Stream << " ";2493    }2494 2495    void lineBreak() {2496      Stream << "\n";2497      LineLength = 0;2498    }2499 2500    void maybeIndent(unsigned Indent) {2501      if (LineLength >= LengthToBreak)2502        lineBreak();2503 2504      if (LineLength == 0)2505        indent(Indent);2506    }2507 2508    void write(StringRef S) {2509      LineLength += S.size();2510      Stream << S;2511    }2512 2513    Value *getUnderlyingObjectThroughLoads(Value *V) {2514      if (Value *Ptr = getPointerOperand(V))2515        return getUnderlyingObjectThroughLoads(Ptr);2516      else if (V->getType()->isPointerTy())2517        return getUnderlyingObject(V);2518      return V;2519    }2520 2521    /// Returns true if \p V is a matrix value in the given subprogram.2522    bool isMatrix(Value *V) const { return ExprsInSubprogram.count(V); }2523 2524    /// If \p V is a matrix value, print its shape as NumRows x NumColumns to2525    /// \p SS.2526    void prettyPrintMatrixType(Value *V, raw_string_ostream &SS) {2527      auto M = Inst2Matrix.find(V);2528      if (M == Inst2Matrix.end())2529        SS << "unknown";2530      else {2531        SS << M->second.getNumRows();2532        SS << "x";2533        SS << M->second.getNumColumns();2534      }2535    }2536 2537    /// Write the called function name. Handles calls to llvm.matrix.*2538    /// specially: we write the name, followed by the dimensions of the input2539    /// matrixes, followed by the scalar type name.2540    void writeFnName(CallInst *CI) {2541      if (!CI->getCalledFunction())2542        write("<no called fn>");2543      else {2544        StringRef Name = CI->getCalledFunction()->getName();2545        if (!Name.starts_with("llvm.matrix")) {2546          write(Name);2547          return;2548        }2549        auto *II = cast<IntrinsicInst>(CI);2550        write(Intrinsic::getBaseName(II->getIntrinsicID())2551                  .drop_front(StringRef("llvm.matrix.").size()));2552        write(".");2553        std::string Tmp;2554        raw_string_ostream SS(Tmp);2555 2556        switch (II->getIntrinsicID()) {2557        case Intrinsic::matrix_multiply:2558          prettyPrintMatrixType(II->getOperand(0), SS);2559          SS << ".";2560          prettyPrintMatrixType(II->getOperand(1), SS);2561          SS << "." << *II->getType()->getScalarType();2562          break;2563        case Intrinsic::matrix_transpose:2564          prettyPrintMatrixType(II->getOperand(0), SS);2565          SS << "." << *II->getType()->getScalarType();2566          break;2567        case Intrinsic::matrix_column_major_load:2568          prettyPrintMatrixType(II, SS);2569          SS << "." << *II->getType()->getScalarType();2570          break;2571        case Intrinsic::matrix_column_major_store:2572          prettyPrintMatrixType(II->getOperand(0), SS);2573          SS << "." << *II->getOperand(0)->getType()->getScalarType();2574          break;2575        default:2576          llvm_unreachable("Unhandled case");2577        }2578        write(Tmp);2579      }2580    }2581 2582    unsigned getNumShapeArgs(CallInst *CI) const {2583      if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(CI)) {2584        switch (II->getIntrinsicID()) {2585        case Intrinsic::matrix_multiply:2586          return 3;2587        case Intrinsic::matrix_transpose:2588          return 2;2589        case Intrinsic::matrix_column_major_load:2590        case Intrinsic::matrix_column_major_store:2591          return 3;2592        default:2593          return 0;2594        }2595      }2596      return 0;2597    }2598 2599    /// Special printing for values: for pointers, we print if they refer to an2600    /// (function) external address or a stack address, for other values we2601    /// either print the constant or "scalar"/"matrix" for other values.2602    void write(Value *V) {2603      V = getUnderlyingObjectThroughLoads(V);2604      if (V->getType()->isPointerTy()) {2605        if (isa<AllocaInst>(V)) {2606          Stream << "stack addr";2607          LineLength += StringRef("stack addr").size();2608        } else {2609          Stream << "addr";2610          LineLength += StringRef("addr").size();2611        }2612        if (!V->getName().empty()) {2613          Stream << " %" << V->getName() << "";2614          LineLength += V->getName().size() + 2;2615        }2616        return;2617      }2618 2619      std::string Tmp;2620      raw_string_ostream TmpStream(Tmp);2621 2622      if (auto *CI = dyn_cast<ConstantInt>(V))2623        TmpStream << CI->getValue();2624      else if (isa<Constant>(V))2625        TmpStream << "constant";2626      else {2627        if (isMatrix(V))2628          TmpStream << "matrix";2629        else2630          TmpStream << "scalar";2631      }2632      Tmp = std::string(StringRef(Tmp).trim());2633      LineLength += Tmp.size();2634      Stream << Tmp;2635    }2636 2637    /// Linearize expression \p Expr starting at an indentation of \p Indent.2638    /// Expressions that are re-used multiple times are prefixed with (reused)2639    /// at the re-used root instruction.2640    void linearizeExpr(Value *Expr, unsigned Indent, bool ParentReused,2641                       bool ParentShared) {2642      auto *I = cast<Instruction>(Expr);2643      maybeIndent(Indent);2644      SmallVector<Value *, 8> Ops;2645 2646      // Is Expr shared with other expression leaves?2647      bool ExprShared = false;2648 2649      // Deal with shared subtrees. Mark them as shared, if required.2650      if (!ParentShared) {2651        auto SI = Shared.find(Expr);2652        assert(SI != Shared.end() && SI->second.count(Leaf));2653 2654        for (Value *S : SI->second) {2655          if (S == Leaf)2656            continue;2657          DebugLoc DL = cast<Instruction>(S)->getDebugLoc();2658          write("shared with remark at line " + std::to_string(DL.getLine()) +2659                " column " + std::to_string(DL.getCol()) + " (");2660        }2661        ExprShared = SI->second.size() > 1;2662      }2663 2664      bool Reused = !ReusedExprs.insert(Expr).second;2665      if (Reused && !ParentReused)2666        write("(reused) ");2667 2668      if (auto *CI = dyn_cast<CallInst>(I)) {2669        writeFnName(CI);2670 2671        Ops.append(CI->arg_begin(), CI->arg_end() - getNumShapeArgs(CI));2672      } else if (isa<BitCastInst>(Expr)) {2673        // Special case bitcasts, which are used to materialize matrixes from2674        // non-matrix ops.2675        write("matrix");2676        return;2677      } else {2678        Ops.append(I->value_op_begin(), I->value_op_end());2679        write(I->getOpcodeName());2680      }2681 2682      write("(");2683 2684      unsigned NumOpsToBreak = 1;2685      if (match(Expr, m_Intrinsic<Intrinsic::matrix_column_major_load>()))2686        NumOpsToBreak = 2;2687 2688      for (Value *Op : Ops) {2689        if (Ops.size() > NumOpsToBreak)2690          lineBreak();2691 2692        maybeIndent(Indent + 1);2693        if (isMatrix(Op))2694          linearizeExpr(Op, Indent + 1, Reused, ExprShared);2695        else2696          write(Op);2697        if (Op != Ops.back())2698          write(", ");2699      }2700 2701      write(")");2702    }2703 2704    const std::string &getResult() {2705      return Str;2706    }2707  };2708 2709  /// Generate remarks for matrix operations in a function. To generate remarks2710  /// for matrix expressions, the following approach is used:2711  /// 1. Use the inlined-at debug information to group matrix operations to the2712  ///    DISubprograms they are contained in.2713  /// 2. Collect leaves of matrix expressions (done in2714  ///    RemarkGenerator::getExpressionLeaves) for each subprogram - expression2715  //     mapping.  Leaves are lowered matrix instructions without other matrix2716  //     users (like stores) in the current subprogram.2717  /// 3. For each leaf, create a remark containing a linearizied version of the2718  ///    matrix expression. The expression is linearized by a recursive2719  ///    bottom-up traversal of the matrix operands, starting at a leaf. Note2720  ///    that multiple leaves can share sub-expressions. Shared subexpressions2721  ///    are explicitly marked as shared().2722  struct RemarkGenerator {2723    const MapVector<Value *, MatrixTy> &Inst2Matrix;2724    OptimizationRemarkEmitter &ORE;2725    Function &Func;2726    const DataLayout &DL;2727 2728    RemarkGenerator(const MapVector<Value *, MatrixTy> &Inst2Matrix,2729                    OptimizationRemarkEmitter &ORE, Function &Func)2730        : Inst2Matrix(Inst2Matrix), ORE(ORE), Func(Func),2731          DL(Func.getDataLayout()) {}2732 2733    /// Return all leaves of the expressions in \p ExprsInSubprogram. Those are2734    /// instructions in Inst2Matrix returning void or without any users in2735    /// \p ExprsInSubprogram. Currently that should only include stores.2736    SmallVector<Value *, 4>2737    getExpressionLeaves(const SmallSetVector<Value *, 32> &ExprsInSubprogram) {2738      SmallVector<Value *, 4> Leaves;2739      for (auto *Expr : ExprsInSubprogram)2740        if (Expr->getType()->isVoidTy() ||2741            !any_of(Expr->users(), [&ExprsInSubprogram](User *U) {2742              return ExprsInSubprogram.count(U);2743            }))2744          Leaves.push_back(Expr);2745      return Leaves;2746    }2747 2748    /// Recursively traverse expression \p V starting at \p Leaf and add \p Leaf2749    /// to all visited expressions in \p Shared. Limit the matrix operations to2750    /// the ones in \p ExprsInSubprogram.2751    void collectSharedInfo(Value *Leaf, Value *V,2752                           const SmallSetVector<Value *, 32> &ExprsInSubprogram,2753                           DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared) {2754 2755      if (!ExprsInSubprogram.count(V))2756        return;2757 2758      Shared[V].insert(Leaf);2759 2760      for (Value *Op : cast<Instruction>(V)->operand_values())2761        collectSharedInfo(Leaf, Op, ExprsInSubprogram, Shared);2762    }2763 2764    /// Calculate the number of exclusive and shared op counts for expression2765    /// starting at \p V. Expressions used multiple times are counted once.2766    /// Limit the matrix operations to the ones in \p ExprsInSubprogram.2767    std::pair<OpInfoTy, OpInfoTy>2768    sumOpInfos(Value *Root, SmallPtrSetImpl<Value *> &ReusedExprs,2769               const SmallSetVector<Value *, 32> &ExprsInSubprogram,2770               DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared) const {2771      if (!ExprsInSubprogram.count(Root))2772        return {};2773 2774      // Already counted this expression. Stop.2775      if (!ReusedExprs.insert(Root).second)2776        return {};2777 2778      OpInfoTy SharedCount;2779      OpInfoTy Count;2780 2781      auto I = Shared.find(Root);2782      auto CM = Inst2Matrix.find(Root);2783      if (I->second.size() == 1)2784        Count = CM->second.getOpInfo();2785      else2786        SharedCount = CM->second.getOpInfo();2787 2788      for (Value *Op : cast<Instruction>(Root)->operand_values()) {2789        auto C = sumOpInfos(Op, ReusedExprs, ExprsInSubprogram, Shared);2790        Count += C.first;2791        SharedCount += C.second;2792      }2793      return {Count, SharedCount};2794    }2795 2796    void emitRemarks() {2797      if (!ORE.allowExtraAnalysis(DEBUG_TYPE))2798        return;2799 2800      // Map matrix operations to their containting subprograms, by traversing2801      // the inlinedAt chain. If the function does not have a DISubprogram, we2802      // only map them to the containing function.2803      MapVector<DISubprogram *, SmallVector<Value *, 8>> Subprog2Exprs;2804      for (const auto &KV : Inst2Matrix) {2805        if (Func.getSubprogram()) {2806          auto *I = cast<Instruction>(KV.first);2807          DILocation *Context = I->getDebugLoc();2808          while (Context) {2809            Subprog2Exprs[getSubprogram(Context->getScope())].push_back(2810                KV.first);2811            Context = DebugLoc(Context).getInlinedAt();2812          }2813        } else {2814          Subprog2Exprs[nullptr].push_back(KV.first);2815        }2816      }2817      for (auto &KV : Subprog2Exprs) {2818        SmallSetVector<Value *, 32> ExprsInSubprogram(KV.second.begin(),2819                                                      KV.second.end());2820        auto Leaves = getExpressionLeaves(ExprsInSubprogram);2821 2822        DenseMap<Value *, SmallPtrSet<Value *, 2>> Shared;2823        for (Value *Leaf : Leaves)2824          collectSharedInfo(Leaf, Leaf, ExprsInSubprogram, Shared);2825 2826        // Generate remarks for each leaf.2827        for (auto *L : Leaves) {2828 2829          DebugLoc Loc = cast<Instruction>(L)->getDebugLoc();2830          DILocation *Context = cast<Instruction>(L)->getDebugLoc();2831          while (Context) {2832            if (getSubprogram(Context->getScope()) == KV.first) {2833              Loc = Context;2834              break;2835            }2836            Context = DebugLoc(Context).getInlinedAt();2837          }2838 2839          SmallPtrSet<Value *, 8> ReusedExprs;2840          OpInfoTy Counts, SharedCounts;2841          std::tie(Counts, SharedCounts) =2842              sumOpInfos(L, ReusedExprs, ExprsInSubprogram, Shared);2843 2844          OptimizationRemark Rem(DEBUG_TYPE, "matrix-lowered", Loc,2845                                 cast<Instruction>(L)->getParent());2846 2847          Rem << "Lowered with ";2848          Rem << ore::NV("NumStores", Counts.NumStores) << " stores, "2849              << ore::NV("NumLoads", Counts.NumLoads) << " loads, "2850              << ore::NV("NumComputeOps", Counts.NumComputeOps)2851              << " compute ops, "2852              << ore::NV("NumExposedTransposes", Counts.NumExposedTransposes)2853              << " exposed transposes";2854 2855          if (SharedCounts.NumStores > 0 || SharedCounts.NumLoads > 0 ||2856              SharedCounts.NumComputeOps > 0) {2857            Rem << ",\nadditionally "2858                << ore::NV("NumStores", SharedCounts.NumStores) << " stores, "2859                << ore::NV("NumLoads", SharedCounts.NumLoads) << " loads, "2860                << ore::NV("NumFPOps", SharedCounts.NumComputeOps)2861                << " compute ops"2862                << " are shared with other expressions";2863          }2864 2865          Rem << ("\n" + linearize(L, Shared, ExprsInSubprogram, DL));2866          ORE.emit(Rem);2867        }2868      }2869    }2870 2871    std::string2872    linearize(Value *L,2873              const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared,2874              const SmallSetVector<Value *, 32> &ExprsInSubprogram,2875              const DataLayout &DL) {2876      ExprLinearizer Lin(DL, Inst2Matrix, Shared, ExprsInSubprogram, L);2877      Lin.linearizeExpr(L, 0, false, false);2878      return Lin.getResult();2879    }2880  };2881};2882} // namespace2883 2884PreservedAnalyses LowerMatrixIntrinsicsPass::run(Function &F,2885                                                 FunctionAnalysisManager &AM) {2886  auto &TTI = AM.getResult<TargetIRAnalysis>(F);2887 2888  LowerMatrixIntrinsics LMT(F, TTI, Minimal ? nullptr : &AM);2889  if (LMT.Visit()) {2890    PreservedAnalyses PA;2891    if (!Minimal) {2892      PA.preserve<LoopAnalysis>();2893      PA.preserve<DominatorTreeAnalysis>();2894    }2895    return PA;2896  }2897  return PreservedAnalyses::all();2898}2899 2900void LowerMatrixIntrinsicsPass::printPipeline(2901    raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {2902  static_cast<PassInfoMixin<LowerMatrixIntrinsicsPass> *>(this)->printPipeline(2903      OS, MapClassName2PassName);2904  OS << '<';2905  if (Minimal)2906    OS << "minimal";2907  OS << '>';2908}2909