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-exception 6 // 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/results 14 // transposed. 15 // * Improve cost-modeling, e.g. choose different number of rows/columns 16 // columns for tiles, consider cost of copies on alias. 17 // 18 //===----------------------------------------------------------------------===// 19 20 #include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h" 21 #include "llvm/ADT/GraphTraits.h" 22 #include "llvm/ADT/PostOrderIterator.h" 23 #include "llvm/ADT/SmallVector.h" 24 #include "llvm/Analysis/AliasAnalysis.h" 25 #include "llvm/Analysis/DomTreeUpdater.h" 26 #include "llvm/Analysis/OptimizationRemarkEmitter.h" 27 #include "llvm/Analysis/TargetTransformInfo.h" 28 #include "llvm/Analysis/ValueTracking.h" 29 #include "llvm/Analysis/VectorUtils.h" 30 #include "llvm/IR/CFG.h" 31 #include "llvm/IR/DataLayout.h" 32 #include "llvm/IR/DebugInfoMetadata.h" 33 #include "llvm/IR/Function.h" 34 #include "llvm/IR/IRBuilder.h" 35 #include "llvm/IR/Instructions.h" 36 #include "llvm/IR/IntrinsicInst.h" 37 #include "llvm/IR/PatternMatch.h" 38 #include "llvm/InitializePasses.h" 39 #include "llvm/Pass.h" 40 #include "llvm/Support/Debug.h" 41 #include "llvm/Transforms/Scalar.h" 42 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 43 44 using namespace llvm; 45 using namespace PatternMatch; 46 47 #define DEBUG_TYPE "lower-matrix-intrinsics" 48 49 static cl::opt<bool> EnableShapePropagation( 50 "matrix-propagate-shape", cl::init(true), cl::Hidden, 51 cl::desc("Enable/disable shape propagation from matrix intrinsics to other " 52 "instructions.")); 53 54 static cl::opt<bool> 55 FuseMatrix("fuse-matrix", cl::init(true), cl::Hidden, 56 cl::desc("Enable/disable fusing matrix instructions.")); 57 // TODO: Allow and use non-square tiles. 58 static cl::opt<unsigned> TileSize( 59 "fuse-matrix-tile-size", cl::init(4), cl::Hidden, 60 cl::desc( 61 "Tile size for matrix instruction fusion using square-shaped tiles.")); 62 static cl::opt<bool> ForceFusion( 63 "force-fuse-matrix", cl::init(false), cl::Hidden, 64 cl::desc("Force matrix instruction fusion even if not profitable.")); 65 static cl::opt<bool> AllowContractEnabled( 66 "matrix-allow-contract", cl::init(false), cl::Hidden, 67 cl::desc("Allow the use of FMAs if available and profitable. This may " 68 "result in different results, due to less rounding error.")); 69 70 enum class MatrixLayoutTy { ColumnMajor, RowMajor }; 71 72 static cl::opt<MatrixLayoutTy> MatrixLayout( 73 "matrix-default-layout", cl::init(MatrixLayoutTy::ColumnMajor), 74 cl::desc("Sets the default matrix layout"), 75 cl::values(clEnumValN(MatrixLayoutTy::ColumnMajor, "column-major", 76 "Use column-major layout"), 77 clEnumValN(MatrixLayoutTy::RowMajor, "row-major", 78 "Use row-major layout"))); 79 80 /// Helper function to either return Scope, if it is a subprogram or the 81 /// attached subprogram for a local scope. 82 static DISubprogram *getSubprogram(DIScope *Scope) { 83 if (auto *Subprogram = dyn_cast<DISubprogram>(Scope)) 84 return Subprogram; 85 return cast<DILocalScope>(Scope)->getSubprogram(); 86 } 87 88 namespace { 89 90 // Given an element pointer \p BasePtr to the start of a (sub) matrix, compute 91 // the start address of vector \p VecIdx with type (\p EltType x \p NumElements) 92 // assuming \p Stride elements between start two consecutive vectors. 93 // \p Stride must be >= \p NumElements. 94 // For column-major matrixes, the function computes the address of a column 95 // vectors and \p NumElements must be set to the number of elements in a column 96 // (= number of rows of the matrix). For row-major matrixes, the function 97 // computes the address of a row vector and \p NumElements must be set to the 98 // number of elements in a column (= number of columns of the matrix). 99 // 100 // Consider a 4x4 matrix in column-mjaor layout like below 101 // 102 // 0 1 2 3 103 // 0 v_0_0 v_0_1 v_0_2 v_0_3 104 // 1 v_1_0 v_1_1 v_1_2 v_1_3 105 // 2 v_2_0 v_2_1 v_2_2 v_2_3 106 // 3 v_3_0 v_3_1 v_3_2 v_3_3 107 108 // To compute the column addresses for a 2x3 sub-matrix at row 1 and column 1, 109 // we need a pointer to the first element of the submatrix as base pointer. 110 // Then we can use computeVectorAddr to compute the addresses for the columns 111 // of the sub-matrix. 112 // 113 // Column 0: computeVectorAddr(Base, 0 (column), 4 (stride), 2 (num rows), ..) 114 // -> just returns Base 115 // Column 1: computeVectorAddr(Base, 1 (column), 4 (stride), 2 (num rows), ..) 116 // -> returns Base + (1 * 4) 117 // Column 2: computeVectorAddr(Base, 2 (column), 4 (stride), 2 (num rows), ..) 118 // -> returns Base + (2 * 4) 119 // 120 // The graphic below illustrates the number of elements in a column (marked 121 // with |) and the number of skipped elements (marked with }). 122 // 123 // v_0_0 v_0_1 {v_0_2 {v_0_3 124 // Base Col 1 Col 2 125 // | | | 126 // v_1_0 |v_1_1 |v_1_2 |v_1_3 127 // v_2_0 |v_2_1 |v_2_2 |v_2_3 128 // v_3_0 {v_3_1 {v_3_2 v_3_3 129 // 130 Value *computeVectorAddr(Value *BasePtr, Value *VecIdx, Value *Stride, 131 unsigned NumElements, Type *EltType, 132 IRBuilder<> &Builder) { 133 134 assert((!isa<ConstantInt>(Stride) || 135 cast<ConstantInt>(Stride)->getZExtValue() >= NumElements) && 136 "Stride must be >= the number of elements in the result vector."); 137 unsigned AS = cast<PointerType>(BasePtr->getType())->getAddressSpace(); 138 139 // Compute the start of the vector with index VecIdx as VecIdx * Stride. 140 Value *VecStart = Builder.CreateMul(VecIdx, Stride, "vec.start"); 141 142 // Get pointer to the start of the selected vector. Skip GEP creation, 143 // if we select vector 0. 144 if (isa<ConstantInt>(VecStart) && cast<ConstantInt>(VecStart)->isZero()) 145 VecStart = BasePtr; 146 else 147 VecStart = Builder.CreateGEP(EltType, BasePtr, VecStart, "vec.gep"); 148 149 // Cast elementwise vector start pointer to a pointer to a vector 150 // (EltType x NumElements)*. 151 auto *VecType = FixedVectorType::get(EltType, NumElements); 152 Type *VecPtrType = PointerType::get(VecType, AS); 153 return Builder.CreatePointerCast(VecStart, VecPtrType, "vec.cast"); 154 } 155 156 /// LowerMatrixIntrinsics contains the methods used to lower matrix intrinsics. 157 /// 158 /// Currently, the lowering for each matrix intrinsic is done as follows: 159 /// 1. Propagate the shape information from intrinsics to connected 160 /// instructions. 161 /// 2. Lower instructions with shape information (assuming column-major layout). 162 /// The lowering works similarly using row-major layout. 163 /// 2.1. Get column vectors for each argument. If we already lowered the 164 /// definition of an argument, use the produced column vectors directly. 165 /// If not, split the operand vector containing an embedded matrix into 166 /// a set of column vectors, 167 /// 2.2. Lower the instruction in terms of column major operations, which 168 /// yields a set of column vectors containing result matrix. Note that we 169 /// lower all instructions that have shape information. Besides the 170 /// intrinsics, this includes stores for example. 171 /// 2.3. Update uses of the lowered instruction. If we have shape information 172 /// for a user, there is nothing to do, as we will look up the result 173 /// column matrix when lowering the user. For other uses, we embed the 174 /// result matrix in a flat vector and update the use. 175 /// 2.4. Cache the result column matrix for the instruction we lowered 176 /// 3. After we lowered all instructions in a function, remove the now 177 /// obsolete instructions. 178 /// 179 class LowerMatrixIntrinsics { 180 Function &Func; 181 const DataLayout &DL; 182 const TargetTransformInfo &TTI; 183 AliasAnalysis &AA; 184 DominatorTree &DT; 185 LoopInfo &LI; 186 OptimizationRemarkEmitter &ORE; 187 188 /// Contains estimates of the number of operations (loads, stores, compute) required to lower a matrix operation. 189 struct OpInfoTy { 190 /// Number of stores emitted to generate this matrix. 191 unsigned NumStores = 0; 192 /// Number of loads emitted to generate this matrix. 193 unsigned NumLoads = 0; 194 /// Number of compute operations emitted to generate this matrix. 195 unsigned NumComputeOps = 0; 196 197 OpInfoTy &operator+=(const OpInfoTy &RHS) { 198 NumStores += RHS.NumStores; 199 NumLoads += RHS.NumLoads; 200 NumComputeOps += RHS.NumComputeOps; 201 return *this; 202 } 203 }; 204 205 /// Wrapper class representing a matrix as a set of vectors, either in row or 206 /// column major layout. All vectors must have the same vector type. 207 class MatrixTy { 208 SmallVector<Value *, 16> Vectors; 209 210 OpInfoTy OpInfo; 211 212 bool IsColumnMajor = true; 213 214 public: 215 MatrixTy() 216 : Vectors(), 217 IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} 218 MatrixTy(ArrayRef<Value *> Vectors) 219 : Vectors(Vectors.begin(), Vectors.end()), 220 IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} 221 MatrixTy(unsigned NumRows, unsigned NumColumns, Type *EltTy) 222 : IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) { 223 224 unsigned D = isColumnMajor() ? NumColumns : NumRows; 225 for (unsigned J = 0; J < D; ++J) 226 addVector(UndefValue::get(FixedVectorType::get( 227 EltTy, isColumnMajor() ? NumRows : NumColumns))); 228 } 229 230 Value *getVector(unsigned i) const { return Vectors[i]; } 231 Value *getColumn(unsigned i) const { 232 assert(isColumnMajor() && "only supported for column-major matrixes"); 233 return Vectors[i]; 234 } 235 Value *getRow(unsigned i) const { 236 assert(!isColumnMajor() && "only supported for row-major matrixes"); 237 return Vectors[i]; 238 } 239 240 void setVector(unsigned i, Value *V) { Vectors[i] = V; } 241 242 Type *getElementType() { return getVectorTy()->getElementType(); } 243 244 unsigned getNumVectors() const { 245 if (isColumnMajor()) 246 return getNumColumns(); 247 return getNumRows(); 248 } 249 250 unsigned getNumColumns() const { 251 if (isColumnMajor()) 252 return Vectors.size(); 253 else { 254 assert(Vectors.size() > 0 && "Cannot call getNumRows without columns"); 255 return cast<VectorType>(Vectors[0]->getType())->getNumElements(); 256 } 257 } 258 unsigned getNumRows() const { 259 if (isColumnMajor()) { 260 assert(Vectors.size() > 0 && "Cannot call getNumRows without columns"); 261 return cast<VectorType>(Vectors[0]->getType())->getNumElements(); 262 } else 263 return Vectors.size(); 264 } 265 266 void addVector(Value *V) { Vectors.push_back(V); } 267 VectorType *getColumnTy() { 268 assert(isColumnMajor() && "only supported for column-major matrixes"); 269 return getVectorTy(); 270 } 271 272 VectorType *getVectorTy() { 273 return cast<VectorType>(Vectors[0]->getType()); 274 } 275 276 iterator_range<SmallVector<Value *, 8>::iterator> columns() { 277 assert(isColumnMajor() && 278 "columns() only supported for column-major matrixes"); 279 return make_range(Vectors.begin(), Vectors.end()); 280 } 281 282 iterator_range<SmallVector<Value *, 8>::iterator> vectors() { 283 return make_range(Vectors.begin(), Vectors.end()); 284 } 285 286 /// Embed the vectors of the matrix into a flat vector by concatenating 287 /// them. 288 Value *embedInVector(IRBuilder<> &Builder) const { 289 return Vectors.size() == 1 ? Vectors[0] 290 : concatenateVectors(Builder, Vectors); 291 } 292 293 MatrixTy &addNumLoads(unsigned N) { 294 OpInfo.NumLoads += N; 295 return *this; 296 } 297 298 void setNumLoads(unsigned N) { OpInfo.NumLoads = N; } 299 300 MatrixTy &addNumStores(unsigned N) { 301 OpInfo.NumStores += N; 302 return *this; 303 } 304 305 MatrixTy &addNumComputeOps(unsigned N) { 306 OpInfo.NumComputeOps += N; 307 return *this; 308 } 309 310 unsigned getNumStores() const { return OpInfo.NumStores; } 311 unsigned getNumLoads() const { return OpInfo.NumLoads; } 312 unsigned getNumComputeOps() const { return OpInfo.NumComputeOps; } 313 314 const OpInfoTy &getOpInfo() const { return OpInfo; } 315 316 bool isColumnMajor() const { return IsColumnMajor; } 317 318 unsigned getStride() const { 319 if (isColumnMajor()) 320 return getNumRows(); 321 return getNumColumns(); 322 } 323 324 /// Extract a vector of \p NumElts starting at index (\p I, \p J). If the 325 /// matrix is column-major, the result vector is extracted from a column 326 /// vector, otherwise from a row vector. 327 Value *extractVector(unsigned I, unsigned J, unsigned NumElts, 328 IRBuilder<> &Builder) const { 329 Value *Vec = isColumnMajor() ? getColumn(J) : getRow(I); 330 Value *Undef = UndefValue::get(Vec->getType()); 331 return Builder.CreateShuffleVector( 332 Vec, Undef, createSequentialMask(isColumnMajor() ? I : J, NumElts, 0), 333 "block"); 334 } 335 }; 336 337 struct ShapeInfo { 338 unsigned NumRows; 339 unsigned NumColumns; 340 341 bool IsColumnMajor; 342 343 ShapeInfo(unsigned NumRows = 0, unsigned NumColumns = 0) 344 : NumRows(NumRows), NumColumns(NumColumns), 345 IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} 346 347 ShapeInfo(Value *NumRows, Value *NumColumns) 348 : ShapeInfo(cast<ConstantInt>(NumRows)->getZExtValue(), 349 cast<ConstantInt>(NumColumns)->getZExtValue()) {} 350 351 bool operator==(const ShapeInfo &other) { 352 return NumRows == other.NumRows && NumColumns == other.NumColumns; 353 } 354 bool operator!=(const ShapeInfo &other) { return !(*this == other); } 355 356 /// Returns true if shape-information is defined, meaning both dimensions 357 /// are != 0. 358 operator bool() const { 359 assert(NumRows == 0 || NumColumns != 0); 360 return NumRows != 0; 361 } 362 363 unsigned getStride() const { 364 if (IsColumnMajor) 365 return NumRows; 366 return NumColumns; 367 } 368 369 unsigned getNumVectors() const { 370 if (IsColumnMajor) 371 return NumColumns; 372 return NumRows; 373 } 374 }; 375 376 /// Maps instructions to their shape information. The shape information 377 /// describes the shape to be used while lowering. This matches the shape of 378 /// the result value of the instruction, with the only exceptions being store 379 /// instructions and the matrix_column_major_store intrinsics. For those, the 380 /// shape information indicates that those instructions should be lowered 381 /// using shape information as well. 382 DenseMap<Value *, ShapeInfo> ShapeMap; 383 384 /// List of instructions to remove. While lowering, we are not replacing all 385 /// users of a lowered instruction, if shape information is available and 386 /// those need to be removed after we finished lowering. 387 SmallVector<Instruction *, 16> ToRemove; 388 389 /// Map from instructions to their produced column matrix. 390 MapVector<Value *, MatrixTy> Inst2ColumnMatrix; 391 392 public: 393 LowerMatrixIntrinsics(Function &F, TargetTransformInfo &TTI, 394 AliasAnalysis &AA, DominatorTree &DT, LoopInfo &LI, 395 OptimizationRemarkEmitter &ORE) 396 : Func(F), DL(F.getParent()->getDataLayout()), TTI(TTI), AA(AA), DT(DT), 397 LI(LI), ORE(ORE) {} 398 399 unsigned getNumOps(Type *VT) { 400 assert(isa<VectorType>(VT) && "Expected vector type"); 401 return getNumOps(VT->getScalarType(), 402 cast<VectorType>(VT)->getNumElements()); 403 } 404 405 // 406 /// Return the estimated number of vector ops required for an operation on 407 /// \p VT * N. 408 unsigned getNumOps(Type *ST, unsigned N) { 409 return std::ceil((ST->getPrimitiveSizeInBits() * N).getFixedSize() / 410 double(TTI.getRegisterBitWidth(true))); 411 } 412 413 /// Return the set of vectors that a matrix value is lowered to. 414 /// 415 /// If we lowered \p MatrixVal, just return the cache result matrix. Otherwise 416 /// split the flat vector \p MatrixVal containing a matrix with shape \p SI 417 /// into vectors. 418 MatrixTy getMatrix(Value *MatrixVal, const ShapeInfo &SI, 419 IRBuilder<> &Builder) { 420 VectorType *VType = dyn_cast<VectorType>(MatrixVal->getType()); 421 assert(VType && "MatrixVal must be a vector type"); 422 assert(VType->getNumElements() == SI.NumRows * SI.NumColumns && 423 "The vector size must match the number of matrix elements"); 424 425 // Check if we lowered MatrixVal using shape information. In that case, 426 // return the existing matrix, if it matches the requested shape 427 // information. If there is a mis-match, embed the result in a flat 428 // vector and split it later. 429 auto Found = Inst2ColumnMatrix.find(MatrixVal); 430 if (Found != Inst2ColumnMatrix.end()) { 431 MatrixTy &M = Found->second; 432 // Return the found matrix, if its shape matches the requested shape 433 // information 434 if (SI.NumRows == M.getNumRows() && SI.NumColumns == M.getNumColumns()) 435 return M; 436 437 MatrixVal = M.embedInVector(Builder); 438 } 439 440 // Otherwise split MatrixVal. 441 SmallVector<Value *, 16> SplitVecs; 442 Value *Undef = UndefValue::get(VType); 443 for (unsigned MaskStart = 0; MaskStart < VType->getNumElements(); 444 MaskStart += SI.getStride()) { 445 Value *V = Builder.CreateShuffleVector( 446 MatrixVal, Undef, createSequentialMask(MaskStart, SI.getStride(), 0), 447 "split"); 448 SplitVecs.push_back(V); 449 } 450 451 return {SplitVecs}; 452 } 453 454 /// If \p V already has a known shape return false. Otherwise set the shape 455 /// for instructions that support it. 456 bool setShapeInfo(Value *V, ShapeInfo Shape) { 457 assert(Shape && "Shape not set"); 458 if (isa<UndefValue>(V) || !supportsShapeInfo(V)) 459 return false; 460 461 auto SIter = ShapeMap.find(V); 462 if (SIter != ShapeMap.end()) { 463 LLVM_DEBUG(dbgs() << " not overriding existing shape: " 464 << SIter->second.NumRows << " " 465 << SIter->second.NumColumns << " for " << *V << "\n"); 466 return false; 467 } 468 469 ShapeMap.insert({V, Shape}); 470 LLVM_DEBUG(dbgs() << " " << Shape.NumRows << " x " << Shape.NumColumns 471 << " for " << *V << "\n"); 472 return true; 473 } 474 475 bool isUniformShape(Value *V) { 476 Instruction *I = dyn_cast<Instruction>(V); 477 if (!I) 478 return true; 479 480 switch (I->getOpcode()) { 481 case Instruction::FAdd: 482 case Instruction::FSub: 483 case Instruction::FMul: // Scalar multiply. 484 case Instruction::Add: 485 case Instruction::Mul: 486 case Instruction::Sub: 487 return true; 488 default: 489 return false; 490 } 491 } 492 493 /// Returns true if shape information can be used for \p V. The supported 494 /// instructions must match the instructions that can be lowered by this pass. 495 bool supportsShapeInfo(Value *V) { 496 Instruction *Inst = dyn_cast<Instruction>(V); 497 if (!Inst) 498 return false; 499 500 IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst); 501 if (II) 502 switch (II->getIntrinsicID()) { 503 case Intrinsic::matrix_multiply: 504 case Intrinsic::matrix_transpose: 505 case Intrinsic::matrix_column_major_load: 506 case Intrinsic::matrix_column_major_store: 507 return true; 508 default: 509 return false; 510 } 511 return isUniformShape(V) || isa<StoreInst>(V) || isa<LoadInst>(V); 512 } 513 514 /// Propagate the shape information of instructions to their users. 515 /// The work list contains instructions for which we can compute the shape, 516 /// either based on the information provided by matrix intrinsics or known 517 /// shapes of operands. 518 SmallVector<Instruction *, 32> 519 propagateShapeForward(SmallVectorImpl<Instruction *> &WorkList) { 520 SmallVector<Instruction *, 32> NewWorkList; 521 // Pop an element for which we guaranteed to have at least one of the 522 // operand shapes. Add the shape for this and then add users to the work 523 // list. 524 LLVM_DEBUG(dbgs() << "Forward-propagate shapes:\n"); 525 while (!WorkList.empty()) { 526 Instruction *Inst = WorkList.back(); 527 WorkList.pop_back(); 528 529 // New entry, set the value and insert operands 530 bool Propagate = false; 531 532 Value *MatrixA; 533 Value *MatrixB; 534 Value *M; 535 Value *N; 536 Value *K; 537 if (match(Inst, m_Intrinsic<Intrinsic::matrix_multiply>( 538 m_Value(MatrixA), m_Value(MatrixB), m_Value(M), 539 m_Value(N), m_Value(K)))) { 540 Propagate = setShapeInfo(Inst, {M, K}); 541 } else if (match(Inst, m_Intrinsic<Intrinsic::matrix_transpose>( 542 m_Value(MatrixA), m_Value(M), m_Value(N)))) { 543 // Flip dimensions. 544 Propagate = setShapeInfo(Inst, {N, M}); 545 } else if (match(Inst, m_Intrinsic<Intrinsic::matrix_column_major_store>( 546 m_Value(MatrixA), m_Value(), m_Value(), 547 m_Value(), m_Value(M), m_Value(N)))) { 548 Propagate = setShapeInfo(Inst, {N, M}); 549 } else if (match(Inst, m_Intrinsic<Intrinsic::matrix_column_major_load>( 550 m_Value(), m_Value(), m_Value(), m_Value(M), 551 m_Value(N)))) { 552 Propagate = setShapeInfo(Inst, {M, N}); 553 } else if (match(Inst, m_Store(m_Value(MatrixA), m_Value()))) { 554 auto OpShape = ShapeMap.find(MatrixA); 555 if (OpShape != ShapeMap.end()) 556 setShapeInfo(Inst, OpShape->second); 557 continue; 558 } else if (isUniformShape(Inst)) { 559 // Find the first operand that has a known shape and use that. 560 for (auto &Op : Inst->operands()) { 561 auto OpShape = ShapeMap.find(Op.get()); 562 if (OpShape != ShapeMap.end()) { 563 Propagate |= setShapeInfo(Inst, OpShape->second); 564 break; 565 } 566 } 567 } 568 569 if (Propagate) { 570 NewWorkList.push_back(Inst); 571 for (auto *User : Inst->users()) 572 if (ShapeMap.count(User) == 0) 573 WorkList.push_back(cast<Instruction>(User)); 574 } 575 } 576 577 return NewWorkList; 578 } 579 580 /// Propagate the shape to operands of instructions with shape information. 581 /// \p Worklist contains the instruction for which we already know the shape. 582 SmallVector<Instruction *, 32> 583 propagateShapeBackward(SmallVectorImpl<Instruction *> &WorkList) { 584 SmallVector<Instruction *, 32> NewWorkList; 585 586 auto pushInstruction = [](Value *V, 587 SmallVectorImpl<Instruction *> &WorkList) { 588 Instruction *I = dyn_cast<Instruction>(V); 589 if (I) 590 WorkList.push_back(I); 591 }; 592 // Pop an element with known shape. Traverse the operands, if their shape 593 // derives from the result shape and is unknown, add it and add them to the 594 // worklist. 595 LLVM_DEBUG(dbgs() << "Backward-propagate shapes:\n"); 596 while (!WorkList.empty()) { 597 Value *V = WorkList.back(); 598 WorkList.pop_back(); 599 600 size_t BeforeProcessingV = WorkList.size(); 601 if (!isa<Instruction>(V)) 602 continue; 603 604 Value *MatrixA; 605 Value *MatrixB; 606 Value *M; 607 Value *N; 608 Value *K; 609 if (match(V, m_Intrinsic<Intrinsic::matrix_multiply>( 610 m_Value(MatrixA), m_Value(MatrixB), m_Value(M), 611 m_Value(N), m_Value(K)))) { 612 if (setShapeInfo(MatrixA, {M, N})) 613 pushInstruction(MatrixA, WorkList); 614 615 if (setShapeInfo(MatrixB, {N, K})) 616 pushInstruction(MatrixB, WorkList); 617 618 } else if (match(V, m_Intrinsic<Intrinsic::matrix_transpose>( 619 m_Value(MatrixA), m_Value(M), m_Value(N)))) { 620 // Flip dimensions. 621 if (setShapeInfo(MatrixA, {M, N})) 622 pushInstruction(MatrixA, WorkList); 623 } else if (match(V, m_Intrinsic<Intrinsic::matrix_column_major_store>( 624 m_Value(MatrixA), m_Value(), m_Value(), m_Value(), 625 m_Value(M), m_Value(N)))) { 626 if (setShapeInfo(MatrixA, {M, N})) { 627 pushInstruction(MatrixA, WorkList); 628 } 629 } else if (isa<LoadInst>(V) || 630 match(V, m_Intrinsic<Intrinsic::matrix_column_major_load>())) { 631 // Nothing to do, no matrix input. 632 } else if (isa<StoreInst>(V)) { 633 // Nothing to do. We forward-propagated to this so we would just 634 // backward propagate to an instruction with an already known shape. 635 } else if (isUniformShape(V)) { 636 // Propagate to all operands. 637 ShapeInfo Shape = ShapeMap[V]; 638 for (Use &U : cast<Instruction>(V)->operands()) { 639 if (setShapeInfo(U.get(), Shape)) 640 pushInstruction(U.get(), WorkList); 641 } 642 } 643 // After we discovered new shape info for new instructions in the 644 // worklist, we use their users as seeds for the next round of forward 645 // propagation. 646 for (size_t I = BeforeProcessingV; I != WorkList.size(); I++) 647 for (User *U : WorkList[I]->users()) 648 if (isa<Instruction>(U) && V != U) 649 NewWorkList.push_back(cast<Instruction>(U)); 650 } 651 return NewWorkList; 652 } 653 654 bool Visit() { 655 if (EnableShapePropagation) { 656 SmallVector<Instruction *, 32> WorkList; 657 658 // Initially only the shape of matrix intrinsics is known. 659 // Initialize the work list with ops carrying shape information. 660 for (BasicBlock &BB : Func) 661 for (Instruction &Inst : BB) { 662 IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst); 663 if (!II) 664 continue; 665 666 switch (II->getIntrinsicID()) { 667 case Intrinsic::matrix_multiply: 668 case Intrinsic::matrix_transpose: 669 case Intrinsic::matrix_column_major_load: 670 case Intrinsic::matrix_column_major_store: 671 WorkList.push_back(&Inst); 672 break; 673 default: 674 break; 675 } 676 } 677 // Propagate shapes until nothing changes any longer. 678 while (!WorkList.empty()) { 679 WorkList = propagateShapeForward(WorkList); 680 WorkList = propagateShapeBackward(WorkList); 681 } 682 } 683 684 bool Changed = false; 685 SmallVector<CallInst *, 16> MaybeFusableInsts; 686 SmallVector<Instruction *, 16> MatrixInsts; 687 688 // First, collect all instructions with shape information and candidates for 689 // fusion (currently only matrix multiplies). 690 ReversePostOrderTraversal<Function *> RPOT(&Func); 691 for (auto *BB : RPOT) 692 for (Instruction &I : *BB) { 693 if (ShapeMap.find(&I) == ShapeMap.end()) 694 continue; 695 if (match(&I, m_Intrinsic<Intrinsic::matrix_multiply>())) 696 MaybeFusableInsts.push_back(cast<CallInst>(&I)); 697 MatrixInsts.push_back(&I); 698 } 699 700 // Second, try to fuse candidates. 701 SmallPtrSet<Instruction *, 16> FusedInsts; 702 for (CallInst *CI : MaybeFusableInsts) 703 LowerMatrixMultiplyFused(CI, FusedInsts); 704 Changed = !FusedInsts.empty(); 705 706 // Third, lower remaining instructions with shape information. 707 for (Instruction *Inst : MatrixInsts) { 708 if (FusedInsts.count(Inst)) 709 continue; 710 711 IRBuilder<> Builder(Inst); 712 713 if (CallInst *CInst = dyn_cast<CallInst>(Inst)) 714 Changed |= VisitCallInst(CInst); 715 716 Value *Op1; 717 Value *Op2; 718 if (auto *BinOp = dyn_cast<BinaryOperator>(Inst)) 719 Changed |= VisitBinaryOperator(BinOp); 720 if (match(Inst, m_Load(m_Value(Op1)))) 721 Changed |= VisitLoad(cast<LoadInst>(Inst), Op1, Builder); 722 else if (match(Inst, m_Store(m_Value(Op1), m_Value(Op2)))) 723 Changed |= VisitStore(cast<StoreInst>(Inst), Op1, Op2, Builder); 724 } 725 726 RemarkGenerator RemarkGen(Inst2ColumnMatrix, ORE, Func); 727 RemarkGen.emitRemarks(); 728 729 for (Instruction *Inst : reverse(ToRemove)) 730 Inst->eraseFromParent(); 731 732 return Changed; 733 } 734 735 LoadInst *createVectorLoad(Value *ColumnPtr, Type *EltType, bool IsVolatile, 736 IRBuilder<> &Builder) { 737 return Builder.CreateAlignedLoad(ColumnPtr, 738 Align(DL.getABITypeAlignment(EltType)), 739 IsVolatile, "col.load"); 740 } 741 742 StoreInst *createVectorStore(Value *ColumnValue, Value *ColumnPtr, 743 Type *EltType, bool IsVolatile, 744 IRBuilder<> &Builder) { 745 return Builder.CreateAlignedStore(ColumnValue, ColumnPtr, 746 DL.getABITypeAlign(EltType), IsVolatile); 747 } 748 749 /// Turns \p BasePtr into an elementwise pointer to \p EltType. 750 Value *createElementPtr(Value *BasePtr, Type *EltType, IRBuilder<> &Builder) { 751 unsigned AS = cast<PointerType>(BasePtr->getType())->getAddressSpace(); 752 Type *EltPtrType = PointerType::get(EltType, AS); 753 return Builder.CreatePointerCast(BasePtr, EltPtrType); 754 } 755 756 /// Replace intrinsic calls 757 bool VisitCallInst(CallInst *Inst) { 758 if (!Inst->getCalledFunction() || !Inst->getCalledFunction()->isIntrinsic()) 759 return false; 760 761 switch (Inst->getCalledFunction()->getIntrinsicID()) { 762 case Intrinsic::matrix_multiply: 763 LowerMultiply(Inst); 764 break; 765 case Intrinsic::matrix_transpose: 766 LowerTranspose(Inst); 767 break; 768 case Intrinsic::matrix_column_major_load: 769 LowerColumnMajorLoad(Inst); 770 break; 771 case Intrinsic::matrix_column_major_store: 772 LowerColumnMajorStore(Inst); 773 break; 774 default: 775 return false; 776 } 777 return true; 778 } 779 780 /// Load a matrix with \p Shape starting at \p Ptr and using \p Stride between 781 /// vectors. 782 MatrixTy loadMatrix(Type *Ty, Value *Ptr, Value *Stride, bool IsVolatile, 783 ShapeInfo Shape, IRBuilder<> &Builder) { 784 auto VType = cast<VectorType>(Ty); 785 Value *EltPtr = createElementPtr(Ptr, VType->getElementType(), Builder); 786 MatrixTy Result; 787 for (unsigned I = 0, E = Shape.getNumVectors(); I < E; ++I) { 788 Value *GEP = computeVectorAddr(EltPtr, Builder.getInt64(I), Stride, 789 Shape.getStride(), VType->getElementType(), 790 Builder); 791 Value *Vector = 792 createVectorLoad(GEP, VType->getElementType(), IsVolatile, Builder); 793 Result.addVector(Vector); 794 } 795 return Result.addNumLoads(getNumOps(Result.getVectorTy()) * 796 Result.getNumVectors()); 797 } 798 799 /// Loads a sub-matrix with shape \p ResultShape from a \p R x \p C matrix, 800 /// starting at \p MatrixPtr[I][J]. 801 MatrixTy loadMatrix(Value *MatrixPtr, bool IsVolatile, ShapeInfo MatrixShape, 802 Value *I, Value *J, ShapeInfo ResultShape, Type *EltTy, 803 IRBuilder<> &Builder) { 804 805 Value *Offset = Builder.CreateAdd( 806 Builder.CreateMul(J, Builder.getInt64(MatrixShape.getStride())), I); 807 808 unsigned AS = cast<PointerType>(MatrixPtr->getType())->getAddressSpace(); 809 Value *EltPtr = 810 Builder.CreatePointerCast(MatrixPtr, PointerType::get(EltTy, AS)); 811 Value *TileStart = Builder.CreateGEP(EltTy, EltPtr, Offset); 812 auto *TileTy = FixedVectorType::get(EltTy, ResultShape.NumRows * 813 ResultShape.NumColumns); 814 Type *TilePtrTy = PointerType::get(TileTy, AS); 815 Value *TilePtr = 816 Builder.CreatePointerCast(TileStart, TilePtrTy, "col.cast"); 817 818 return loadMatrix(TileTy, TilePtr, 819 Builder.getInt64(MatrixShape.getStride()), IsVolatile, 820 ResultShape, Builder); 821 } 822 823 /// Lower a load instruction with shape information. 824 void LowerLoad(Instruction *Inst, Value *Ptr, Value *Stride, bool IsVolatile, 825 ShapeInfo Shape) { 826 IRBuilder<> Builder(Inst); 827 finalizeLowering( 828 Inst, 829 loadMatrix(Inst->getType(), Ptr, Stride, IsVolatile, Shape, Builder), 830 Builder); 831 } 832 833 /// Lowers llvm.matrix.column.major.load. 834 /// 835 /// The intrinsic loads a matrix from memory using a stride between columns. 836 void LowerColumnMajorLoad(CallInst *Inst) { 837 assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && 838 "Intrinsic only supports column-major layout!"); 839 Value *Ptr = Inst->getArgOperand(0); 840 Value *Stride = Inst->getArgOperand(1); 841 LowerLoad(Inst, Ptr, Stride, 842 cast<ConstantInt>(Inst->getArgOperand(2))->isOne(), 843 {Inst->getArgOperand(3), Inst->getArgOperand(4)}); 844 } 845 846 /// Stores a sub-matrix \p StoreVal into the \p R x \p C matrix starting at \p 847 /// MatrixPtr[I][J]. 848 void storeMatrix(const MatrixTy &StoreVal, Value *MatrixPtr, bool IsVolatile, 849 ShapeInfo MatrixShape, Value *I, Value *J, Type *EltTy, 850 IRBuilder<> &Builder) { 851 Value *Offset = Builder.CreateAdd( 852 Builder.CreateMul(J, Builder.getInt64(MatrixShape.getStride())), I); 853 854 unsigned AS = cast<PointerType>(MatrixPtr->getType())->getAddressSpace(); 855 Value *EltPtr = 856 Builder.CreatePointerCast(MatrixPtr, PointerType::get(EltTy, AS)); 857 Value *TileStart = Builder.CreateGEP(EltTy, EltPtr, Offset); 858 auto *TileTy = FixedVectorType::get(EltTy, StoreVal.getNumRows() * 859 StoreVal.getNumColumns()); 860 Type *TilePtrTy = PointerType::get(TileTy, AS); 861 Value *TilePtr = 862 Builder.CreatePointerCast(TileStart, TilePtrTy, "col.cast"); 863 864 storeMatrix(TileTy, StoreVal, TilePtr, 865 Builder.getInt64(MatrixShape.getStride()), IsVolatile, Builder); 866 } 867 868 /// Store matrix \p StoreVal starting at \p Ptr and using \p Stride between 869 /// vectors. 870 MatrixTy storeMatrix(Type *Ty, MatrixTy StoreVal, Value *Ptr, Value *Stride, 871 bool IsVolatile, IRBuilder<> &Builder) { 872 auto VType = cast<VectorType>(Ty); 873 Value *EltPtr = createElementPtr(Ptr, VType->getElementType(), Builder); 874 for (auto Vec : enumerate(StoreVal.vectors())) { 875 Value *GEP = computeVectorAddr(EltPtr, Builder.getInt64(Vec.index()), 876 Stride, StoreVal.getStride(), 877 VType->getElementType(), Builder); 878 createVectorStore(Vec.value(), GEP, VType->getElementType(), IsVolatile, 879 Builder); 880 } 881 return MatrixTy().addNumStores(getNumOps(StoreVal.getVectorTy()) * 882 StoreVal.getNumVectors()); 883 } 884 885 /// Lower a store instruction with shape information. 886 void LowerStore(Instruction *Inst, Value *Matrix, Value *Ptr, Value *Stride, 887 bool IsVolatile, ShapeInfo Shape) { 888 IRBuilder<> Builder(Inst); 889 auto StoreVal = getMatrix(Matrix, Shape, Builder); 890 finalizeLowering(Inst, 891 storeMatrix(Matrix->getType(), StoreVal, Ptr, Stride, 892 IsVolatile, Builder), 893 Builder); 894 } 895 896 /// Lowers llvm.matrix.column.major.store. 897 /// 898 /// The intrinsic store a matrix back memory using a stride between columns. 899 void LowerColumnMajorStore(CallInst *Inst) { 900 assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && 901 "Intrinsic only supports column-major layout!"); 902 Value *Matrix = Inst->getArgOperand(0); 903 Value *Ptr = Inst->getArgOperand(1); 904 Value *Stride = Inst->getArgOperand(2); 905 LowerStore(Inst, Matrix, Ptr, Stride, 906 cast<ConstantInt>(Inst->getArgOperand(3))->isOne(), 907 {Inst->getArgOperand(4), Inst->getArgOperand(5)}); 908 } 909 910 // Set elements I..I+NumElts-1 to Block 911 Value *insertVector(Value *Col, unsigned I, Value *Block, 912 IRBuilder<> &Builder) { 913 914 // First, bring Block to the same size as Col 915 unsigned BlockNumElts = 916 cast<VectorType>(Block->getType())->getNumElements(); 917 unsigned NumElts = cast<VectorType>(Col->getType())->getNumElements(); 918 assert(NumElts >= BlockNumElts && "Too few elements for current block"); 919 920 Value *Undef = UndefValue::get(Block->getType()); 921 Block = Builder.CreateShuffleVector( 922 Block, Undef, 923 createSequentialMask(0, BlockNumElts, NumElts - BlockNumElts)); 924 925 // If Col is 7 long and I is 2 and BlockNumElts is 2 the mask is: 0, 1, 7, 926 // 8, 4, 5, 6 927 SmallVector<int, 16> Mask; 928 unsigned i; 929 for (i = 0; i < I; i++) 930 Mask.push_back(i); 931 932 unsigned VecNumElts = cast<VectorType>(Col->getType())->getNumElements(); 933 for (; i < I + BlockNumElts; i++) 934 Mask.push_back(i - I + VecNumElts); 935 936 for (; i < VecNumElts; i++) 937 Mask.push_back(i); 938 939 return Builder.CreateShuffleVector(Col, Block, Mask); 940 } 941 942 Value *createMulAdd(Value *Sum, Value *A, Value *B, bool UseFPOp, 943 IRBuilder<> &Builder, bool AllowContraction, 944 unsigned &NumComputeOps) { 945 NumComputeOps += getNumOps(A->getType()); 946 if (!Sum) 947 return UseFPOp ? Builder.CreateFMul(A, B) : Builder.CreateMul(A, B); 948 949 if (UseFPOp) { 950 if (AllowContraction) { 951 // Use fmuladd for floating point operations and let the backend decide 952 // if that's profitable. 953 Function *FMulAdd = Intrinsic::getDeclaration( 954 Func.getParent(), Intrinsic::fmuladd, A->getType()); 955 return Builder.CreateCall(FMulAdd, {A, B, Sum}); 956 } 957 NumComputeOps += getNumOps(A->getType()); 958 Value *Mul = Builder.CreateFMul(A, B); 959 return Builder.CreateFAdd(Sum, Mul); 960 } 961 962 NumComputeOps += getNumOps(A->getType()); 963 Value *Mul = Builder.CreateMul(A, B); 964 return Builder.CreateAdd(Sum, Mul); 965 } 966 967 /// Cache \p Matrix as result of \p Inst and update the uses of \p Inst. For 968 /// users with shape information, there's nothing to do: the will use the 969 /// cached value when they are lowered. For other users, \p Matrix is 970 /// flattened and the uses are updated to use it. Also marks \p Inst for 971 /// deletion. 972 void finalizeLowering(Instruction *Inst, MatrixTy Matrix, 973 IRBuilder<> &Builder) { 974 Inst2ColumnMatrix.insert(std::make_pair(Inst, Matrix)); 975 976 ToRemove.push_back(Inst); 977 Value *Flattened = nullptr; 978 for (auto I = Inst->use_begin(), E = Inst->use_end(); I != E;) { 979 Use &U = *I++; 980 if (ShapeMap.find(U.getUser()) == ShapeMap.end()) { 981 if (!Flattened) 982 Flattened = Matrix.embedInVector(Builder); 983 U.set(Flattened); 984 } 985 } 986 } 987 988 /// Compute \p Result += \p A * \p B for input matrices with left-associating 989 /// addition. 990 void emitMatrixMultiply(MatrixTy &Result, const MatrixTy &A, 991 const MatrixTy &B, bool AllowContraction, 992 IRBuilder<> &Builder, bool isTiled) { 993 const unsigned VF = std::max<unsigned>( 994 TTI.getRegisterBitWidth(true) / 995 Result.getElementType()->getPrimitiveSizeInBits().getFixedSize(), 996 1U); 997 unsigned R = Result.getNumRows(); 998 unsigned C = Result.getNumColumns(); 999 unsigned M = A.getNumColumns(); 1000 1001 bool IsFP = Result.getElementType()->isFloatingPointTy(); 1002 assert(A.isColumnMajor() == B.isColumnMajor() && 1003 Result.isColumnMajor() == A.isColumnMajor() && 1004 "operands must agree on matrix layout"); 1005 unsigned NumComputeOps = 0; 1006 if (A.isColumnMajor()) { 1007 // Multiply columns from the first operand with scalars from the second 1008 // operand. Then move along the K axes and accumulate the columns. With 1009 // this the adds can be vectorized without reassociation. 1010 for (unsigned J = 0; J < C; ++J) { 1011 unsigned BlockSize = VF; 1012 // If Result is zero, we don't need to accumulate in the K==0 iteration. 1013 bool isSumZero = isa<ConstantAggregateZero>(Result.getColumn(J)); 1014 1015 for (unsigned I = 0; I < R; I += BlockSize) { 1016 // Gradually lower the vectorization factor to cover the remainder. 1017 while (I + BlockSize > R) 1018 BlockSize /= 2; 1019 1020 Value *Sum = isTiled ? Result.extractVector(I, J, BlockSize, Builder) 1021 : nullptr; 1022 for (unsigned K = 0; K < M; ++K) { 1023 Value *L = A.extractVector(I, K, BlockSize, Builder); 1024 Value *RH = Builder.CreateExtractElement(B.getColumn(J), K); 1025 Value *Splat = Builder.CreateVectorSplat(BlockSize, RH, "splat"); 1026 Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, L, Splat, 1027 Result.getElementType()->isFloatingPointTy(), 1028 Builder, AllowContraction, NumComputeOps); 1029 } 1030 Result.setVector(J, 1031 insertVector(Result.getVector(J), I, Sum, Builder)); 1032 } 1033 } 1034 } else { 1035 // Multiply rows from the second operand with scalars from the first 1036 // operand. Then move along the K axes and accumulate the rows. With this 1037 // the adds can be vectorized without reassociation. 1038 for (unsigned I = 0; I < R; ++I) { 1039 unsigned BlockSize = VF; 1040 bool isSumZero = isa<ConstantAggregateZero>(Result.getRow(I)); 1041 for (unsigned J = 0; J < C; J += BlockSize) { 1042 // Gradually lower the vectorization factor to cover the remainder. 1043 while (J + BlockSize > C) 1044 BlockSize /= 2; 1045 1046 Value *Sum = nullptr; 1047 for (unsigned K = 0; K < M; ++K) { 1048 Value *R = B.extractVector(K, J, BlockSize, Builder); 1049 Value *LH = Builder.CreateExtractElement(A.getVector(I), K); 1050 Value *Splat = Builder.CreateVectorSplat(BlockSize, LH, "splat"); 1051 Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, Splat, R, 1052 IsFP, Builder, AllowContraction, NumComputeOps); 1053 } 1054 Result.setVector(I, 1055 insertVector(Result.getVector(I), J, Sum, Builder)); 1056 } 1057 } 1058 } 1059 Result.addNumComputeOps(NumComputeOps); 1060 } 1061 1062 /// Ensure that the memory in \p Load does not alias \p Store by potentially 1063 /// copying it to a new location. This new or otherwise the original location 1064 /// is returned. 1065 Value *getNonAliasingPointer(LoadInst *Load, StoreInst *Store, 1066 CallInst *MatMul) { 1067 MemoryLocation StoreLoc = MemoryLocation::get(Store); 1068 MemoryLocation LoadLoc = MemoryLocation::get(Load); 1069 1070 AliasResult LdAliased = AA.alias(LoadLoc, StoreLoc); 1071 1072 // If we can statically determine noalias we're good. 1073 if (!LdAliased) 1074 return Load->getPointerOperand(); 1075 1076 // Create code to check if the memory locations of the Load and Store 1077 // overlap and if they do, copy Load's operand to a new buffer. 1078 1079 // First, create new blocks for 2n part of the check and the copy. 1080 BasicBlock *Check0 = MatMul->getParent(); 1081 // FIXME: Use lazy DTU and update SplitBlock to accept a DTU instead of a 1082 // DT. Manually collect dominator tree updates, to avoid unnecessary work, 1083 // as we adjust Check0 and Check1's branches. 1084 SmallVector<DominatorTree::UpdateType, 4> DTUpdates; 1085 for (BasicBlock *Succ : successors(Check0)) 1086 DTUpdates.push_back({DT.Delete, Check0, Succ}); 1087 1088 BasicBlock *Check1 = SplitBlock(MatMul->getParent(), MatMul, nullptr, &LI, 1089 nullptr, "alias_cont"); 1090 BasicBlock *Copy = 1091 SplitBlock(MatMul->getParent(), MatMul, nullptr, &LI, nullptr, "copy"); 1092 BasicBlock *Fusion = SplitBlock(MatMul->getParent(), MatMul, nullptr, &LI, 1093 nullptr, "no_alias"); 1094 1095 // Check if the loaded memory location begins before the end of the store 1096 // location. If the condition holds, they might overlap, otherwise they are 1097 // guaranteed to not overlap. 1098 IRBuilder<> Builder(MatMul); 1099 Check0->getTerminator()->eraseFromParent(); 1100 Builder.SetInsertPoint(Check0); 1101 Type *IntPtrTy = Builder.getIntPtrTy(Load->getModule()->getDataLayout()); 1102 Value *StoreBegin = Builder.CreatePtrToInt( 1103 const_cast<Value *>(StoreLoc.Ptr), IntPtrTy, "store.begin"); 1104 Value *StoreEnd = Builder.CreateAdd( 1105 StoreBegin, ConstantInt::get(IntPtrTy, StoreLoc.Size.getValue()), 1106 "store.end", true, true); 1107 Value *LoadBegin = Builder.CreatePtrToInt(const_cast<Value *>(LoadLoc.Ptr), 1108 IntPtrTy, "load.begin"); 1109 Builder.CreateCondBr(Builder.CreateICmpULT(LoadBegin, StoreEnd), Check1, 1110 Fusion); 1111 1112 // Check if the store begins before the end of the load location. If the 1113 // condition holds, they alias, otherwise they are guaranteed to not 1114 // overlap. 1115 Check1->getTerminator()->eraseFromParent(); 1116 Builder.SetInsertPoint(Check1, Check1->begin()); 1117 Value *LoadEnd = Builder.CreateAdd( 1118 LoadBegin, ConstantInt::get(IntPtrTy, LoadLoc.Size.getValue()), 1119 "load.end", true, true); 1120 Builder.CreateCondBr(Builder.CreateICmpULT(StoreBegin, LoadEnd), Copy, 1121 Fusion); 1122 1123 // Copy load operand to new alloca. 1124 Builder.SetInsertPoint(Copy, Copy->begin()); 1125 AllocaInst *NewLd = 1126 Builder.CreateAlloca(Load->getType(), Load->getPointerAddressSpace()); 1127 Builder.CreateMemCpy(NewLd, NewLd->getAlign(), 1128 Load->getPointerOperand(), Load->getAlign(), 1129 LoadLoc.Size.getValue()); 1130 Builder.SetInsertPoint(Fusion, Fusion->begin()); 1131 PHINode *PHI = Builder.CreatePHI(Load->getPointerOperandType(), 3); 1132 PHI->addIncoming(Load->getPointerOperand(), Check0); 1133 PHI->addIncoming(Load->getPointerOperand(), Check1); 1134 PHI->addIncoming(NewLd, Copy); 1135 1136 // Adjust DT. 1137 DTUpdates.push_back({DT.Insert, Check0, Check1}); 1138 DTUpdates.push_back({DT.Insert, Check0, Fusion}); 1139 DTUpdates.push_back({DT.Insert, Check1, Copy}); 1140 DTUpdates.push_back({DT.Insert, Check1, Fusion}); 1141 DT.applyUpdates(DTUpdates); 1142 return PHI; 1143 } 1144 1145 bool isFusionProfitable(CallInst *MatMul) { 1146 if (ForceFusion) 1147 return true; 1148 1149 ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); 1150 ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); 1151 1152 const unsigned R = LShape.NumRows; 1153 const unsigned C = RShape.NumColumns; 1154 const unsigned M = LShape.NumColumns; 1155 auto *EltType = cast<VectorType>(MatMul->getType())->getElementType(); 1156 1157 const unsigned VF = 1158 std::max<unsigned>(TTI.getRegisterBitWidth(true) / 1159 EltType->getPrimitiveSizeInBits().getFixedSize(), 1160 1U); 1161 1162 // Cost model for tiling 1163 // 1164 // For tiling to be beneficial, we need reuse either along the R or 1165 // the C axis. We vectorize along the R axis so that means at least 1166 // 3 elements. 1167 // TODO: Also consider cost of copying if operands alias. 1168 if (R <= VF && C == 1) 1169 return false; 1170 // Then we need enough elements to exceed the number of vector 1171 // registers we have. Note that this is an oversimplification since 1172 // fusing also takes some extra loads which may exceed the number of 1173 // reloads necessary. 1174 unsigned Op0Regs = (R + VF - 1) / VF * M; 1175 unsigned Op1Regs = (M + VF - 1) / VF * C; 1176 return Op0Regs + Op1Regs > TTI.getNumberOfRegisters(true); 1177 } 1178 1179 MatrixTy getZeroMatrix(Type *EltType, unsigned R, unsigned C) { 1180 MatrixTy Res; 1181 auto *ColumType = FixedVectorType::get(EltType, R); 1182 for (unsigned I = 0; I < C; ++I) 1183 Res.addVector(ConstantAggregateZero::get(ColumType)); 1184 return Res; 1185 } 1186 1187 void emitSIMDTiling(CallInst *MatMul, LoadInst *LoadOp0, LoadInst *LoadOp1, 1188 StoreInst *Store, 1189 SmallPtrSetImpl<Instruction *> &FusedInsts) { 1190 assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && 1191 "Tiling only supported for column-major matrixes at the moment!"); 1192 if (!isFusionProfitable(MatMul)) 1193 return; 1194 1195 ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); 1196 ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); 1197 1198 const unsigned R = LShape.NumRows; 1199 const unsigned C = RShape.NumColumns; 1200 const unsigned M = LShape.NumColumns; 1201 auto *EltType = cast<VectorType>(MatMul->getType())->getElementType(); 1202 1203 Value *APtr = getNonAliasingPointer(LoadOp0, Store, MatMul); 1204 Value *BPtr = getNonAliasingPointer(LoadOp1, Store, MatMul); 1205 Value *CPtr = Store->getPointerOperand(); 1206 1207 bool AllowContract = AllowContractEnabled || (isa<FPMathOperator>(MatMul) && 1208 MatMul->hasAllowContract()); 1209 IRBuilder<> Builder(Store); 1210 for (unsigned J = 0; J < C; J += TileSize) 1211 for (unsigned I = 0; I < R; I += TileSize) { 1212 const unsigned TileR = std::min(R - I, unsigned(TileSize)); 1213 const unsigned TileC = std::min(C - J, unsigned(TileSize)); 1214 MatrixTy Res = getZeroMatrix(EltType, TileR, TileC); 1215 1216 for (unsigned K = 0; K < M; K += TileSize) { 1217 const unsigned TileM = std::min(M - K, unsigned(TileSize)); 1218 MatrixTy A = loadMatrix(APtr, LoadOp0->isVolatile(), LShape, 1219 Builder.getInt64(I), Builder.getInt64(K), 1220 {TileR, TileM}, EltType, Builder); 1221 MatrixTy B = loadMatrix(BPtr, LoadOp1->isVolatile(), RShape, 1222 Builder.getInt64(K), Builder.getInt64(J), 1223 {TileM, TileC}, EltType, Builder); 1224 emitMatrixMultiply(Res, A, B, AllowContract, Builder, true); 1225 } 1226 storeMatrix(Res, CPtr, Store->isVolatile(), {R, M}, Builder.getInt64(I), 1227 Builder.getInt64(J), EltType, Builder); 1228 } 1229 1230 // Mark eliminated instructions as fused and remove them. 1231 FusedInsts.insert(Store); 1232 FusedInsts.insert(MatMul); 1233 Store->eraseFromParent(); 1234 MatMul->eraseFromParent(); 1235 if (LoadOp0->hasNUses(0)) { 1236 FusedInsts.insert(LoadOp0); 1237 LoadOp0->eraseFromParent(); 1238 } 1239 if (LoadOp1->hasNUses(0)) { 1240 FusedInsts.insert(LoadOp1); 1241 LoadOp1->eraseFromParent(); 1242 } 1243 } 1244 1245 /// Try to lower matrix multiply chains by fusing operations. 1246 /// 1247 /// Currently we only lower {ld, ld} -> matmul -> st chains. 1248 // 1249 /// No need to return a MatrixTy object for the result of the operation, since 1250 /// the single store user will be lowered as part of this. Instructions that 1251 /// are completely eliminated by fusion are added to \p FusedInsts. 1252 void LowerMatrixMultiplyFused(CallInst *MatMul, 1253 SmallPtrSetImpl<Instruction *> &FusedInsts) { 1254 if (!FuseMatrix || !MatMul->hasOneUse() || 1255 MatrixLayout != MatrixLayoutTy::ColumnMajor) 1256 return; 1257 1258 auto *LoadOp0 = dyn_cast<LoadInst>(MatMul->getOperand(0)); 1259 auto *LoadOp1 = dyn_cast<LoadInst>(MatMul->getOperand(1)); 1260 auto *Store = dyn_cast<StoreInst>(*MatMul->user_begin()); 1261 if (LoadOp0 && LoadOp1 && Store) { 1262 // The store address must dominate the MatMul instruction, otherwise 1263 // we create invalid IR. 1264 // FIXME: See if we can hoist the store address computation. 1265 auto *AddrI = dyn_cast<Instruction>(Store->getOperand(1)); 1266 if (AddrI && (!DT.dominates(AddrI, MatMul))) 1267 return; 1268 1269 emitSIMDTiling(MatMul, LoadOp0, LoadOp1, Store, FusedInsts); 1270 return; 1271 } 1272 } 1273 1274 /// Lowers llvm.matrix.multiply. 1275 void LowerMultiply(CallInst *MatMul) { 1276 IRBuilder<> Builder(MatMul); 1277 auto *EltType = cast<VectorType>(MatMul->getType())->getElementType(); 1278 ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); 1279 ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); 1280 1281 const MatrixTy &Lhs = getMatrix(MatMul->getArgOperand(0), LShape, Builder); 1282 const MatrixTy &Rhs = getMatrix(MatMul->getArgOperand(1), RShape, Builder); 1283 1284 const unsigned R = LShape.NumRows; 1285 const unsigned C = RShape.NumColumns; 1286 assert(LShape.NumColumns == RShape.NumRows); 1287 1288 // Initialize the output 1289 MatrixTy Result(R, C, EltType); 1290 1291 bool AllowContract = AllowContractEnabled || (isa<FPMathOperator>(MatMul) && 1292 MatMul->hasAllowContract()); 1293 emitMatrixMultiply(Result, Lhs, Rhs, AllowContract, Builder, false); 1294 finalizeLowering(MatMul, Result, Builder); 1295 } 1296 1297 /// Lowers llvm.matrix.transpose. 1298 void LowerTranspose(CallInst *Inst) { 1299 MatrixTy Result; 1300 IRBuilder<> Builder(Inst); 1301 Value *InputVal = Inst->getArgOperand(0); 1302 VectorType *VectorTy = cast<VectorType>(InputVal->getType()); 1303 ShapeInfo ArgShape(Inst->getArgOperand(1), Inst->getArgOperand(2)); 1304 MatrixTy InputMatrix = getMatrix(InputVal, ArgShape, Builder); 1305 1306 const unsigned NewNumVecs = 1307 InputMatrix.isColumnMajor() ? ArgShape.NumRows : ArgShape.NumColumns; 1308 const unsigned NewNumElts = 1309 InputMatrix.isColumnMajor() ? ArgShape.NumColumns : ArgShape.NumRows; 1310 1311 for (unsigned I = 0; I < NewNumVecs; ++I) { 1312 // Build a single result vector. First initialize it. 1313 Value *ResultVector = UndefValue::get( 1314 FixedVectorType::get(VectorTy->getElementType(), NewNumElts)); 1315 // Go through the old elements and insert it into the resulting vector. 1316 for (auto J : enumerate(InputMatrix.vectors())) { 1317 Value *Elt = Builder.CreateExtractElement(J.value(), I); 1318 // Row and column indices are transposed. 1319 ResultVector = 1320 Builder.CreateInsertElement(ResultVector, Elt, J.index()); 1321 } 1322 Result.addVector(ResultVector); 1323 } 1324 1325 // TODO: Improve estimate of operations needed for transposes. Currently we 1326 // just count the insertelement/extractelement instructions, but do not 1327 // account for later simplifications/combines. 1328 finalizeLowering( 1329 Inst, 1330 Result.addNumComputeOps(2 * ArgShape.NumRows * ArgShape.NumColumns), 1331 Builder); 1332 } 1333 1334 /// Lower load instructions, if shape information is available. 1335 bool VisitLoad(LoadInst *Inst, Value *Ptr, IRBuilder<> &Builder) { 1336 auto I = ShapeMap.find(Inst); 1337 if (I == ShapeMap.end()) 1338 return false; 1339 1340 LowerLoad(Inst, Ptr, Builder.getInt64(I->second.getStride()), 1341 Inst->isVolatile(), I->second); 1342 return true; 1343 } 1344 1345 bool VisitStore(StoreInst *Inst, Value *StoredVal, Value *Ptr, 1346 IRBuilder<> &Builder) { 1347 auto I = ShapeMap.find(StoredVal); 1348 if (I == ShapeMap.end()) 1349 return false; 1350 1351 LowerStore(Inst, StoredVal, Ptr, Builder.getInt64(I->second.getStride()), 1352 Inst->isVolatile(), I->second); 1353 return true; 1354 } 1355 1356 /// Lower binary operators, if shape information is available. 1357 bool VisitBinaryOperator(BinaryOperator *Inst) { 1358 auto I = ShapeMap.find(Inst); 1359 if (I == ShapeMap.end()) 1360 return false; 1361 1362 Value *Lhs = Inst->getOperand(0); 1363 Value *Rhs = Inst->getOperand(1); 1364 1365 IRBuilder<> Builder(Inst); 1366 ShapeInfo &Shape = I->second; 1367 1368 MatrixTy Result; 1369 MatrixTy A = getMatrix(Lhs, Shape, Builder); 1370 MatrixTy B = getMatrix(Rhs, Shape, Builder); 1371 assert(A.isColumnMajor() == B.isColumnMajor() && 1372 Result.isColumnMajor() == A.isColumnMajor() && 1373 "operands must agree on matrix layout"); 1374 1375 // Helper to perform binary op on vectors. 1376 auto BuildVectorOp = [&Builder, Inst](Value *LHS, Value *RHS) { 1377 switch (Inst->getOpcode()) { 1378 case Instruction::Add: 1379 return Builder.CreateAdd(LHS, RHS); 1380 case Instruction::Mul: 1381 return Builder.CreateMul(LHS, RHS); 1382 case Instruction::Sub: 1383 return Builder.CreateSub(LHS, RHS); 1384 case Instruction::FAdd: 1385 return Builder.CreateFAdd(LHS, RHS); 1386 case Instruction::FMul: 1387 return Builder.CreateFMul(LHS, RHS); 1388 case Instruction::FSub: 1389 return Builder.CreateFSub(LHS, RHS); 1390 default: 1391 llvm_unreachable("Unsupported binary operator for matrix"); 1392 } 1393 }; 1394 1395 for (unsigned I = 0; I < Shape.getNumVectors(); ++I) 1396 Result.addVector(BuildVectorOp(A.getVector(I), B.getVector(I))); 1397 1398 finalizeLowering(Inst, 1399 Result.addNumComputeOps(getNumOps(Result.getVectorTy()) * 1400 Result.getNumVectors()), 1401 Builder); 1402 return true; 1403 } 1404 1405 /// Helper to linearize a matrix expression tree into a string. Currently 1406 /// matrix expressions are linarized by starting at an expression leaf and 1407 /// linearizing bottom up. 1408 struct ExprLinearizer { 1409 unsigned LengthToBreak = 100; 1410 std::string Str; 1411 raw_string_ostream Stream; 1412 unsigned LineLength = 0; 1413 const DataLayout &DL; 1414 1415 /// Mapping from instructions to matrixes. It is used to identify 1416 /// matrix instructions. 1417 const MapVector<Value *, MatrixTy> &Inst2Matrix; 1418 1419 /// Mapping from values to the leaves of all expressions that the value is 1420 /// part of. 1421 const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared; 1422 1423 /// Set of matrix expressions in the scope of a given DISubprogram. 1424 const SmallSetVector<Value *, 32> &ExprsInSubprogram; 1425 1426 /// Leaf node of the expression to linearize. 1427 Value *Leaf; 1428 1429 /// Used to keep track of sub-expressions that get reused while linearizing 1430 /// the expression. Re-used sub-expressions are marked as (reused). 1431 SmallPtrSet<Value *, 8> ReusedExprs; 1432 1433 ExprLinearizer(const DataLayout &DL, 1434 const MapVector<Value *, MatrixTy> &Inst2Matrix, 1435 const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared, 1436 const SmallSetVector<Value *, 32> &ExprsInSubprogram, 1437 Value *Leaf) 1438 : Str(), Stream(Str), DL(DL), Inst2Matrix(Inst2Matrix), Shared(Shared), 1439 ExprsInSubprogram(ExprsInSubprogram), Leaf(Leaf) {} 1440 1441 void indent(unsigned N) { 1442 LineLength += N; 1443 for (unsigned i = 0; i < N; i++) 1444 Stream << " "; 1445 } 1446 1447 void lineBreak() { 1448 Stream << "\n"; 1449 LineLength = 0; 1450 } 1451 1452 void maybeIndent(unsigned Indent) { 1453 if (LineLength >= LengthToBreak) 1454 lineBreak(); 1455 1456 if (LineLength == 0) 1457 indent(Indent); 1458 } 1459 1460 void write(StringRef S) { 1461 LineLength += S.size(); 1462 Stream << S; 1463 } 1464 1465 Value *getUnderlyingObjectThroughLoads(Value *V) { 1466 if (Value *Ptr = getPointerOperand(V)) 1467 return getUnderlyingObjectThroughLoads(Ptr); 1468 else if (V->getType()->isPointerTy()) 1469 return GetUnderlyingObject(V, DL); 1470 return V; 1471 } 1472 1473 /// Returns true if \p V is a matrix value in the given subprogram. 1474 bool isMatrix(Value *V) const { return ExprsInSubprogram.count(V); } 1475 1476 /// If \p V is a matrix value, print its shape as as NumRows x NumColumns to 1477 /// \p SS. 1478 void prettyPrintMatrixType(Value *V, raw_string_ostream &SS) { 1479 auto M = Inst2Matrix.find(V); 1480 if (M == Inst2Matrix.end()) 1481 SS << "unknown"; 1482 else { 1483 SS << M->second.getNumRows(); 1484 SS << "x"; 1485 SS << M->second.getNumColumns(); 1486 } 1487 } 1488 1489 /// Write the called function name. Handles calls to llvm.matrix.* 1490 /// specially: we write the name, followed by the dimensions of the input 1491 /// matrixes, followed by the scalar type name. 1492 void writeFnName(CallInst *CI) { 1493 if (!CI->getCalledFunction()) 1494 write("<no called fn>"); 1495 else { 1496 StringRef Name = CI->getCalledFunction()->getName(); 1497 if (!Name.startswith("llvm.matrix")) { 1498 write(Name); 1499 return; 1500 } 1501 IntrinsicInst *II = dyn_cast<IntrinsicInst>(CI); 1502 write(StringRef(Intrinsic::getName(II->getIntrinsicID(), {})) 1503 .drop_front(StringRef("llvm.matrix.").size())); 1504 write("."); 1505 std::string Tmp = ""; 1506 raw_string_ostream SS(Tmp); 1507 1508 switch (II->getIntrinsicID()) { 1509 case Intrinsic::matrix_multiply: 1510 prettyPrintMatrixType(II->getOperand(0), SS); 1511 SS << "."; 1512 prettyPrintMatrixType(II->getOperand(1), SS); 1513 SS << "." << *II->getType()->getScalarType(); 1514 break; 1515 case Intrinsic::matrix_transpose: 1516 prettyPrintMatrixType(II->getOperand(0), SS); 1517 SS << "." << *II->getType()->getScalarType(); 1518 break; 1519 case Intrinsic::matrix_column_major_load: 1520 prettyPrintMatrixType(II, SS); 1521 SS << "." << *II->getType()->getScalarType(); 1522 break; 1523 case Intrinsic::matrix_column_major_store: 1524 prettyPrintMatrixType(II->getOperand(0), SS); 1525 SS << "." << *II->getOperand(0)->getType()->getScalarType(); 1526 break; 1527 default: 1528 llvm_unreachable("Unhandled case"); 1529 } 1530 SS.flush(); 1531 write(Tmp); 1532 } 1533 } 1534 1535 unsigned getNumShapeArgs(CallInst *CI) const { 1536 if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(CI)) { 1537 switch (II->getIntrinsicID()) { 1538 case Intrinsic::matrix_multiply: 1539 return 3; 1540 case Intrinsic::matrix_transpose: 1541 return 2; 1542 case Intrinsic::matrix_column_major_load: 1543 case Intrinsic::matrix_column_major_store: 1544 return 3; 1545 default: 1546 return 0; 1547 } 1548 } 1549 return 0; 1550 } 1551 1552 /// Special printing for values: for pointers, we print if they refer to an 1553 /// (function) external address or a stack address, for other values we 1554 /// either print the constant or "scalar"/"matrix" for other values. 1555 void write(Value *V) { 1556 V = getUnderlyingObjectThroughLoads(V); 1557 if (V->getType()->isPointerTy()) { 1558 if (isa<AllocaInst>(V)) { 1559 Stream << "stack addr"; 1560 LineLength += StringRef("stack addr").size(); 1561 } else { 1562 Stream << "addr"; 1563 LineLength += StringRef("addr").size(); 1564 } 1565 if (!V->getName().empty()) { 1566 Stream << " %" << V->getName() << ""; 1567 LineLength += V->getName().size() + 2; 1568 } 1569 return; 1570 } 1571 1572 std::string Tmp; 1573 raw_string_ostream TmpStream(Tmp); 1574 1575 if (auto *CI = dyn_cast<ConstantInt>(V)) 1576 TmpStream << CI->getValue(); 1577 else if (isa<Constant>(V)) 1578 TmpStream << "constant"; 1579 else { 1580 if (isMatrix(V)) 1581 TmpStream << "matrix"; 1582 else 1583 TmpStream << "scalar"; 1584 } 1585 TmpStream.flush(); 1586 Tmp = std::string(StringRef(Tmp).trim()); 1587 LineLength += Tmp.size(); 1588 Stream << Tmp; 1589 } 1590 1591 /// Linearize expression \p Expr starting at an indentation of \p Indent. 1592 /// Expressions that are re-used multiple times are prefixed with (reused) 1593 /// at the re-used root instruction. 1594 void linearizeExpr(Value *Expr, unsigned Indent, bool ParentReused, 1595 bool ParentShared) { 1596 auto *I = cast<Instruction>(Expr); 1597 maybeIndent(Indent); 1598 SmallVector<Value *, 8> Ops; 1599 1600 // Is Expr shared with other expression leaves? 1601 bool ExprShared = false; 1602 1603 // Deal with shared subtrees. Mark them as shared, if required. 1604 if (!ParentShared) { 1605 auto SI = Shared.find(Expr); 1606 assert(SI != Shared.end() && SI->second.count(Leaf)); 1607 1608 for (Value *S : SI->second) { 1609 if (S == Leaf) 1610 continue; 1611 DebugLoc DL = cast<Instruction>(S)->getDebugLoc(); 1612 write("shared with remark at line " + std::to_string(DL.getLine()) + 1613 " column " + std::to_string(DL.getCol()) + " ("); 1614 } 1615 ExprShared = SI->second.size() > 1; 1616 } 1617 1618 bool Reused = !ReusedExprs.insert(Expr).second; 1619 if (Reused && !ParentReused) 1620 write("(reused) "); 1621 1622 if (auto *CI = dyn_cast<CallInst>(I)) { 1623 writeFnName(CI); 1624 1625 Ops.append(CI->arg_begin(), CI->arg_end() - getNumShapeArgs(CI)); 1626 } else if (isa<BitCastInst>(Expr)) { 1627 // Special case bitcasts, which are used to materialize matrixes from 1628 // non-matrix ops. 1629 write("matrix"); 1630 return; 1631 } else { 1632 Ops.append(I->value_op_begin(), I->value_op_end()); 1633 write(std::string(I->getOpcodeName())); 1634 } 1635 1636 write(std::string("(")); 1637 1638 unsigned NumOpsToBreak = 1; 1639 if (match(Expr, m_Intrinsic<Intrinsic::matrix_column_major_load>())) 1640 NumOpsToBreak = 2; 1641 1642 for (Value *Op : Ops) { 1643 if (Ops.size() > NumOpsToBreak) 1644 lineBreak(); 1645 1646 maybeIndent(Indent + 1); 1647 if (isMatrix(Op)) 1648 linearizeExpr(Op, Indent + 1, Reused, ExprShared); 1649 else 1650 write(Op); 1651 if (Op != Ops.back()) 1652 write(", "); 1653 } 1654 1655 write(")"); 1656 } 1657 1658 const std::string &getResult() { 1659 Stream.flush(); 1660 return Str; 1661 } 1662 }; 1663 1664 /// Generate remarks for matrix operations in a function. To generate remarks 1665 /// for matrix expressions, the following approach is used: 1666 /// 1. Use the inlined-at debug information to group matrix operations to the 1667 /// DISubprograms they are contained in. 1668 /// 2. Collect leaves of matrix expressions (done in 1669 /// RemarkGenerator::getExpressionLeaves) for each subprogram - expression 1670 // mapping. Leaves are lowered matrix instructions without other matrix 1671 // users (like stores) in the current subprogram. 1672 /// 3. For each leaf, create a remark containing a linearizied version of the 1673 /// matrix expression. The expression is linearized by a recursive 1674 /// bottom-up traversal of the matrix operands, starting at a leaf. Note 1675 /// that multiple leaves can share sub-expressions. Shared subexpressions 1676 /// are explicitly marked as shared(). 1677 struct RemarkGenerator { 1678 const MapVector<Value *, MatrixTy> &Inst2Matrix; 1679 OptimizationRemarkEmitter &ORE; 1680 Function &Func; 1681 const DataLayout &DL; 1682 1683 RemarkGenerator(const MapVector<Value *, MatrixTy> &Inst2Matrix, 1684 OptimizationRemarkEmitter &ORE, Function &Func) 1685 : Inst2Matrix(Inst2Matrix), ORE(ORE), Func(Func), 1686 DL(Func.getParent()->getDataLayout()) {} 1687 1688 /// Return all leaves of the expressions in \p ExprsInSubprogram. Those are 1689 /// instructions in Inst2Matrix returning void or without any users in 1690 /// \p ExprsInSubprogram. Currently that should only include stores. 1691 SmallVector<Value *, 4> 1692 getExpressionLeaves(const SmallSetVector<Value *, 32> &ExprsInSubprogram) { 1693 SmallVector<Value *, 4> Leaves; 1694 for (auto *Expr : ExprsInSubprogram) 1695 if (Expr->getType()->isVoidTy() || 1696 !any_of(Expr->users(), [&ExprsInSubprogram](User *U) { 1697 return ExprsInSubprogram.count(U); 1698 })) 1699 Leaves.push_back(Expr); 1700 return Leaves; 1701 } 1702 1703 /// Recursively traverse expression \p V starting at \p Leaf and add \p Leaf 1704 /// to all visited expressions in \p Shared. Limit the matrix operations to 1705 /// the ones in \p ExprsInSubprogram. 1706 void collectSharedInfo(Value *Leaf, Value *V, 1707 const SmallSetVector<Value *, 32> &ExprsInSubprogram, 1708 DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared) { 1709 1710 if (!ExprsInSubprogram.count(V)) 1711 return; 1712 1713 auto I = Shared.insert({V, {}}); 1714 I.first->second.insert(Leaf); 1715 1716 for (Value *Op : cast<Instruction>(V)->operand_values()) 1717 collectSharedInfo(Leaf, Op, ExprsInSubprogram, Shared); 1718 return; 1719 } 1720 1721 /// Calculate the number of exclusive and shared op counts for expression 1722 /// starting at \p V. Expressions used multiple times are counted once. 1723 /// Limit the matrix operations to the ones in \p ExprsInSubprogram. 1724 std::pair<OpInfoTy, OpInfoTy> 1725 sumOpInfos(Value *Root, SmallPtrSetImpl<Value *> &ReusedExprs, 1726 const SmallSetVector<Value *, 32> &ExprsInSubprogram, 1727 DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared) const { 1728 if (!ExprsInSubprogram.count(Root)) 1729 return {}; 1730 1731 // Already counted this expression. Stop. 1732 if (!ReusedExprs.insert(Root).second) 1733 return {}; 1734 1735 OpInfoTy SharedCount; 1736 OpInfoTy Count; 1737 1738 auto I = Shared.find(Root); 1739 auto CM = Inst2Matrix.find(Root); 1740 if (I->second.size() == 1) 1741 Count = CM->second.getOpInfo(); 1742 else 1743 SharedCount = CM->second.getOpInfo(); 1744 1745 for (Value *Op : cast<Instruction>(Root)->operand_values()) { 1746 auto C = sumOpInfos(Op, ReusedExprs, ExprsInSubprogram, Shared); 1747 Count += C.first; 1748 SharedCount += C.second; 1749 } 1750 return {Count, SharedCount}; 1751 } 1752 1753 void emitRemarks() { 1754 if (!ORE.allowExtraAnalysis(DEBUG_TYPE)) 1755 return; 1756 1757 // Map matrix operations to their containting subprograms, by traversing 1758 // the inlinedAt chain. If the function does not have a DISubprogram, we 1759 // only map them to the containing function. 1760 MapVector<DISubprogram *, SmallVector<Value *, 8>> Subprog2Exprs; 1761 for (auto &KV : Inst2Matrix) { 1762 if (Func.getSubprogram()) { 1763 auto *I = cast<Instruction>(KV.first); 1764 DILocation *Context = I->getDebugLoc(); 1765 while (Context) { 1766 auto I = 1767 Subprog2Exprs.insert({getSubprogram(Context->getScope()), {}}); 1768 I.first->second.push_back(KV.first); 1769 Context = DebugLoc(Context).getInlinedAt(); 1770 } 1771 } else { 1772 auto I = Subprog2Exprs.insert({nullptr, {}}); 1773 I.first->second.push_back(KV.first); 1774 } 1775 } 1776 for (auto &KV : Subprog2Exprs) { 1777 SmallSetVector<Value *, 32> ExprsInSubprogram(KV.second.begin(), 1778 KV.second.end()); 1779 auto Leaves = getExpressionLeaves(ExprsInSubprogram); 1780 1781 DenseMap<Value *, SmallPtrSet<Value *, 2>> Shared; 1782 for (Value *Leaf : Leaves) 1783 collectSharedInfo(Leaf, Leaf, ExprsInSubprogram, Shared); 1784 1785 // Generate remarks for each leaf. 1786 for (auto *L : Leaves) { 1787 1788 DebugLoc Loc = cast<Instruction>(L)->getDebugLoc(); 1789 DILocation *Context = cast<Instruction>(L)->getDebugLoc(); 1790 while (Context) { 1791 if (getSubprogram(Context->getScope()) == KV.first) { 1792 Loc = Context; 1793 break; 1794 } 1795 Context = DebugLoc(Context).getInlinedAt(); 1796 } 1797 1798 SmallPtrSet<Value *, 8> ReusedExprs; 1799 OpInfoTy Counts, SharedCounts; 1800 std::tie(Counts, SharedCounts) = 1801 sumOpInfos(L, ReusedExprs, ExprsInSubprogram, Shared); 1802 1803 OptimizationRemark Rem(DEBUG_TYPE, "matrix-lowered", Loc, 1804 cast<Instruction>(L)->getParent()); 1805 1806 Rem << "Lowered with "; 1807 Rem << ore::NV("NumStores", Counts.NumStores) << " stores, " 1808 << ore::NV("NumLoads", Counts.NumLoads) << " loads, " 1809 << ore::NV("NumComputeOps", Counts.NumComputeOps) 1810 << " compute ops"; 1811 1812 if (SharedCounts.NumStores > 0 || SharedCounts.NumLoads > 0 || 1813 SharedCounts.NumComputeOps > 0) { 1814 Rem << ",\nadditionally " 1815 << ore::NV("NumStores", SharedCounts.NumStores) << " stores, " 1816 << ore::NV("NumLoads", SharedCounts.NumLoads) << " loads, " 1817 << ore::NV("NumFPOps", SharedCounts.NumComputeOps) 1818 << " compute ops" 1819 << " are shared with other expressions"; 1820 } 1821 1822 Rem << ("\n" + linearize(L, Shared, ExprsInSubprogram, DL)); 1823 ORE.emit(Rem); 1824 } 1825 } 1826 } 1827 1828 std::string 1829 linearize(Value *L, 1830 const DenseMap<Value *, SmallPtrSet<Value *, 2>> &Shared, 1831 const SmallSetVector<Value *, 32> &ExprsInSubprogram, 1832 const DataLayout &DL) { 1833 ExprLinearizer Lin(DL, Inst2Matrix, Shared, ExprsInSubprogram, L); 1834 Lin.linearizeExpr(L, 0, false, false); 1835 return Lin.getResult(); 1836 } 1837 }; 1838 }; 1839 } // namespace 1840 1841 PreservedAnalyses LowerMatrixIntrinsicsPass::run(Function &F, 1842 FunctionAnalysisManager &AM) { 1843 auto &TTI = AM.getResult<TargetIRAnalysis>(F); 1844 auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F); 1845 auto &AA = AM.getResult<AAManager>(F); 1846 auto &DT = AM.getResult<DominatorTreeAnalysis>(F); 1847 auto &LI = AM.getResult<LoopAnalysis>(F); 1848 1849 LowerMatrixIntrinsics LMT(F, TTI, AA, DT, LI, ORE); 1850 if (LMT.Visit()) { 1851 PreservedAnalyses PA; 1852 PA.preserveSet<CFGAnalyses>(); 1853 return PA; 1854 } 1855 return PreservedAnalyses::all(); 1856 } 1857 1858 namespace { 1859 1860 class LowerMatrixIntrinsicsLegacyPass : public FunctionPass { 1861 public: 1862 static char ID; 1863 1864 LowerMatrixIntrinsicsLegacyPass() : FunctionPass(ID) { 1865 initializeLowerMatrixIntrinsicsLegacyPassPass( 1866 *PassRegistry::getPassRegistry()); 1867 } 1868 1869 bool runOnFunction(Function &F) override { 1870 auto &TTI = getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); 1871 auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE(); 1872 auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults(); 1873 auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree(); 1874 auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); 1875 LowerMatrixIntrinsics LMT(F, TTI, AA, DT, LI, ORE); 1876 bool C = LMT.Visit(); 1877 return C; 1878 } 1879 1880 void getAnalysisUsage(AnalysisUsage &AU) const override { 1881 AU.addRequired<TargetTransformInfoWrapperPass>(); 1882 AU.addRequired<OptimizationRemarkEmitterWrapperPass>(); 1883 AU.addRequired<AAResultsWrapperPass>(); 1884 AU.addRequired<DominatorTreeWrapperPass>(); 1885 AU.addPreserved<DominatorTreeWrapperPass>(); 1886 AU.addRequired<LoopInfoWrapperPass>(); 1887 AU.addPreserved<LoopInfoWrapperPass>(); 1888 } 1889 }; 1890 } // namespace 1891 1892 static const char pass_name[] = "Lower the matrix intrinsics"; 1893 char LowerMatrixIntrinsicsLegacyPass::ID = 0; 1894 INITIALIZE_PASS_BEGIN(LowerMatrixIntrinsicsLegacyPass, DEBUG_TYPE, pass_name, 1895 false, false) 1896 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass) 1897 INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass) 1898 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) 1899 INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) 1900 INITIALIZE_PASS_END(LowerMatrixIntrinsicsLegacyPass, DEBUG_TYPE, pass_name, 1901 false, false) 1902 1903 Pass *llvm::createLowerMatrixIntrinsicsPass() { 1904 return new LowerMatrixIntrinsicsLegacyPass(); 1905 } 1906