LLVM 23.0.0git
X86LowerAMXType.cpp
Go to the documentation of this file.
1//===- Target/X86/X86LowerAMXType.cpp - -------------------------*- 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/// \file Pass to transform <256 x i32> load/store
10/// <256 x i32> is bitcasted to x86_amx on X86, and AMX instruction set only
11/// provides simple operation on x86_amx. The basic elementwise operation
12/// is not supported by AMX. Since x86_amx is bitcasted from vector <256 x i32>
13/// and only AMX intrinsics can operate on the type, we need transform
14/// load/store <256 x i32> instruction to AMX load/store. If the bitcast can
15/// not be combined with load/store, we transform the bitcast to amx load/store
16/// and <256 x i32> store/load.
17///
18/// If Front End not use O0 but the Mid/Back end use O0, (e.g. "Clang -O2 -S
19/// -emit-llvm t.c" + "llc t.ll") we should make sure the amx data is volatile,
20/// because that is necessary for AMX fast register allocation. (In Fast
21/// registera allocation, register will be allocated before spill/reload, so
22/// there is no additional register for amx to identify the step in spill.)
23/// The volatileTileData() will handle this case.
24/// e.g.
25/// ----------------------------------------------------------
26/// | def %td = ... |
27/// | ... |
28/// | "use %td" |
29/// ----------------------------------------------------------
30/// will transfer to -->
31/// ----------------------------------------------------------
32/// | def %td = ... |
33/// | call void @llvm.x86.tilestored64.internal(mem, %td) |
34/// | ... |
35/// | %td2 = call x86_amx @llvm.x86.tileloadd64.internal(mem)|
36/// | "use %td2" |
37/// ----------------------------------------------------------
38//
39//===----------------------------------------------------------------------===//
40//
41#include "X86.h"
43#include "llvm/ADT/SetVector.h"
46#include "llvm/CodeGen/Passes.h"
49#include "llvm/IR/Analysis.h"
50#include "llvm/IR/DataLayout.h"
51#include "llvm/IR/Function.h"
52#include "llvm/IR/IRBuilder.h"
55#include "llvm/IR/IntrinsicsX86.h"
56#include "llvm/IR/PassManager.h"
59#include "llvm/Pass.h"
63
64#include <map>
65
66using namespace llvm;
67using namespace PatternMatch;
68
69#define DEBUG_TYPE "x86-lower-amx-type"
70
76
77static bool isAMXIntrinsic(Value *I) {
79 if (!II)
80 return false;
81 if (isAMXCast(II))
82 return false;
83 // Check if return type or parameter is x86_amx. If it is x86_amx
84 // the intrinsic must be x86 amx intrinsics.
85 if (II->getType()->isX86_AMXTy())
86 return true;
87 for (Value *V : II->args()) {
88 if (V->getType()->isX86_AMXTy())
89 return true;
90 }
91
92 return false;
93}
94
95static bool containsAMXCode(Function &F) {
96 for (BasicBlock &BB : F)
97 for (Instruction &I : BB)
98 if (I.getType()->isX86_AMXTy())
99 return true;
100 return false;
101}
102
104 Type *Ty) {
105 Function &F = *BB->getParent();
106 const DataLayout &DL = F.getDataLayout();
107
108 LLVMContext &Ctx = Builder.getContext();
109 auto AllocaAlignment = DL.getPrefTypeAlign(Type::getX86_AMXTy(Ctx));
110 unsigned AllocaAS = DL.getAllocaAddrSpace();
111 AllocaInst *AllocaRes =
112 new AllocaInst(Ty, AllocaAS, "", F.getEntryBlock().begin());
113 AllocaRes->setAlignment(AllocaAlignment);
114 return AllocaRes;
115}
116
118 for (Instruction &I : F.getEntryBlock())
119 if (!isa<AllocaInst>(&I))
120 return &I;
121 llvm_unreachable("No terminator in the entry block!");
122}
123
124static Value *getRowFromCol(Instruction *II, Value *V, unsigned Granularity) {
125 IRBuilder<> Builder(II);
126 Value *RealRow = nullptr;
127 if (isa<ConstantInt>(V))
128 RealRow =
129 Builder.getInt16((cast<ConstantInt>(V)->getSExtValue()) / Granularity);
130 else if (isa<Instruction>(V)) {
131 // When it is not a const value and it is not a function argument, we
132 // create Row after the definition of V instead of
133 // before II. For example, II is %118, we try to getshape for %117:
134 // %117 = call x86_amx @llvm.x86.cast.vector.to.tile.v256i32(<256 x
135 // i32> %115).
136 // %118 = call x86_amx @llvm.x86.tdpbf16ps.internal(i16
137 // %104, i16 %105, i16 %106, x86_amx %110, x86_amx %114, x86_amx
138 // %117).
139 // If we create %row = udiv i16 %106, 4 before %118(aka. II), then its
140 // definition is after its user(new tileload for %117).
141 // So, the best choice is to create %row right after the definition of
142 // %106.
143 Builder.SetInsertPoint(cast<Instruction>(V));
144 RealRow = Builder.CreateUDiv(V, Builder.getInt16(4));
145 cast<Instruction>(RealRow)->moveAfter(cast<Instruction>(V));
146 } else {
147 // When it is not a const value and it is a function argument, we create
148 // Row at the entry bb.
149 IRBuilder<> NewBuilder(
150 getFirstNonAllocaInTheEntryBlock(*II->getFunction()));
151 RealRow = NewBuilder.CreateUDiv(V, NewBuilder.getInt16(Granularity));
152 }
153 return RealRow;
154}
155
156// TODO: Refine the row and col-in-bytes of tile to row and col of matrix.
157std::pair<Value *, Value *> getShape(IntrinsicInst *II, unsigned OpNo) {
158 IRBuilder<> Builder(II);
159 Value *Row = nullptr, *Col = nullptr;
160 switch (II->getIntrinsicID()) {
161 default:
162 llvm_unreachable("Expect amx intrinsics");
163 case Intrinsic::x86_tileloadd64_internal:
164 case Intrinsic::x86_tileloaddt164_internal:
165 case Intrinsic::x86_tilestored64_internal:
166 case Intrinsic::x86_tileloaddrs64_internal:
167 case Intrinsic::x86_tileloaddrst164_internal: {
168 Row = II->getArgOperand(0);
169 Col = II->getArgOperand(1);
170 break;
171 }
172 // a * b + c
173 // The shape depends on which operand.
174 case Intrinsic::x86_tcmmimfp16ps_internal:
175 case Intrinsic::x86_tcmmrlfp16ps_internal:
176 case Intrinsic::x86_tdpbssd_internal:
177 case Intrinsic::x86_tdpbsud_internal:
178 case Intrinsic::x86_tdpbusd_internal:
179 case Intrinsic::x86_tdpbuud_internal:
180 case Intrinsic::x86_tdpbf16ps_internal:
181 case Intrinsic::x86_tdpfp16ps_internal:
182 case Intrinsic::x86_tdpbf8ps_internal:
183 case Intrinsic::x86_tdpbhf8ps_internal:
184 case Intrinsic::x86_tdphbf8ps_internal:
185 case Intrinsic::x86_tdphf8ps_internal: {
186 switch (OpNo) {
187 case 3:
188 Row = II->getArgOperand(0);
189 Col = II->getArgOperand(1);
190 break;
191 case 4:
192 Row = II->getArgOperand(0);
193 Col = II->getArgOperand(2);
194 break;
195 case 5:
196 Row = getRowFromCol(II, II->getArgOperand(2), 4);
197 Col = II->getArgOperand(1);
198 break;
199 }
200 break;
201 }
202 case Intrinsic::x86_tcvtrowd2ps_internal:
203 case Intrinsic::x86_tcvtrowps2bf16h_internal:
204 case Intrinsic::x86_tcvtrowps2bf16l_internal:
205 case Intrinsic::x86_tcvtrowps2phh_internal:
206 case Intrinsic::x86_tcvtrowps2phl_internal:
207 case Intrinsic::x86_tilemovrow_internal: {
208 assert(OpNo == 2 && "Illegal Operand Number.");
209 Row = II->getArgOperand(0);
210 Col = II->getArgOperand(1);
211 break;
212 }
213 }
214
215 return std::make_pair(Row, Col);
216}
217
218static std::pair<Value *, Value *> getShape(PHINode *Phi) {
219 Use &U = *(Phi->use_begin());
220 unsigned OpNo = U.getOperandNo();
221 User *V = U.getUser();
222 // TODO We don't traverse all users. To make the algorithm simple, here we
223 // just traverse the first user. If we can find shape, then return the shape,
224 // otherwise just return nullptr and the optimization for undef/zero will be
225 // abandoned.
226 while (V) {
228 if (V->use_empty())
229 break;
230 Use &U = *(V->use_begin());
231 OpNo = U.getOperandNo();
232 V = U.getUser();
233 } else if (isAMXIntrinsic(V)) {
234 return getShape(cast<IntrinsicInst>(V), OpNo);
235 } else if (isa<PHINode>(V)) {
236 if (V->use_empty())
237 break;
238 Use &U = *(V->use_begin());
239 V = U.getUser();
240 } else {
241 break;
242 }
243 }
244
245 return std::make_pair(nullptr, nullptr);
246}
247
248namespace {
249class X86LowerAMXType {
250 Function &Func;
251
252 // In AMX intrinsics we let Shape = {Row, Col}, but the
253 // RealCol = Col / ElementSize. We may use the RealCol
254 // as a new Row for other new created AMX intrinsics.
255 std::map<Value *, Value *> Col2Row;
256
257public:
258 X86LowerAMXType(Function &F) : Func(F) {}
259 bool visit();
260 void combineLoadBitcast(LoadInst *LD, BitCastInst *Bitcast);
261 void combineBitcastStore(BitCastInst *Bitcast, StoreInst *ST);
262 bool transformBitcast(BitCastInst *Bitcast);
263};
264
265// %src = load <256 x i32>, <256 x i32>* %addr, align 64
266// %2 = bitcast <256 x i32> %src to x86_amx
267// -->
268// %2 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
269// i8* %addr, i64 %stride64)
270void X86LowerAMXType::combineLoadBitcast(LoadInst *LD, BitCastInst *Bitcast) {
271 Value *Row = nullptr, *Col = nullptr;
272 Use &U = *(Bitcast->use_begin());
273 unsigned OpNo = U.getOperandNo();
274 auto *II = cast<IntrinsicInst>(U.getUser());
275 std::tie(Row, Col) = getShape(II, OpNo);
276 IRBuilder<> Builder(Bitcast);
277 // Use the maximun column as stride.
278 Value *Stride = Builder.getInt64(64);
279 Value *I8Ptr = LD->getOperand(0);
280 std::array<Value *, 4> Args = {Row, Col, I8Ptr, Stride};
281
282 Value *NewInst =
283 Builder.CreateIntrinsic(Intrinsic::x86_tileloadd64_internal, Args);
284 Bitcast->replaceAllUsesWith(NewInst);
285}
286
287// %src = call x86_amx @llvm.x86.tileloadd64.internal(%row, %col, %addr,
288// %stride);
289// %13 = bitcast x86_amx %src to <256 x i32>
290// store <256 x i32> %13, <256 x i32>* %addr, align 64
291// -->
292// call void @llvm.x86.tilestored64.internal(%row, %col, %addr,
293// %stride64, %13)
294void X86LowerAMXType::combineBitcastStore(BitCastInst *Bitcast, StoreInst *ST) {
295
296 Value *Tile = Bitcast->getOperand(0);
297 auto *II = cast<IntrinsicInst>(Tile);
298 // Tile is output from AMX intrinsic. The first operand of the
299 // intrinsic is row, the second operand of the intrinsic is column.
300 Value *Row = II->getOperand(0);
301 Value *Col = II->getOperand(1);
302 IRBuilder<> Builder(ST);
303 // Use the maximum column as stride. It must be the same with load
304 // stride.
305 Value *Stride = Builder.getInt64(64);
306 Value *I8Ptr = ST->getOperand(1);
307 std::array<Value *, 5> Args = {Row, Col, I8Ptr, Stride, Tile};
308 Builder.CreateIntrinsic(Intrinsic::x86_tilestored64_internal, Args);
309 if (Bitcast->hasOneUse())
310 return;
311 // %13 = bitcast x86_amx %src to <256 x i32>
312 // store <256 x i32> %13, <256 x i32>* %addr, align 64
313 // %add = <256 x i32> %13, <256 x i32> %src2
314 // -->
315 // %13 = bitcast x86_amx %src to <256 x i32>
316 // call void @llvm.x86.tilestored64.internal(%row, %col, %addr,
317 // %stride64, %13)
318 // %14 = load <256 x i32>, %addr
319 // %add = <256 x i32> %14, <256 x i32> %src2
320 Value *Vec = Builder.CreateLoad(Bitcast->getType(), ST->getOperand(1));
321 Bitcast->replaceAllUsesWith(Vec);
322}
323
324// transform bitcast to <store, load> instructions.
325bool X86LowerAMXType::transformBitcast(BitCastInst *Bitcast) {
326 IRBuilder<> Builder(Bitcast);
327 AllocaInst *AllocaAddr;
328 Value *I8Ptr, *Stride;
329 auto *Src = Bitcast->getOperand(0);
330
331 auto Prepare = [&](Type *MemTy) {
332 AllocaAddr = createAllocaInstAtEntry(Builder, Bitcast->getParent(), MemTy);
333 I8Ptr = AllocaAddr;
334 Stride = Builder.getInt64(64);
335 };
336
337 if (Bitcast->getType()->isX86_AMXTy()) {
338 // %2 = bitcast <256 x i32> %src to x86_amx
339 // -->
340 // %addr = alloca <256 x i32>, align 64
341 // store <256 x i32> %src, <256 x i32>* %addr, align 64
342 // %addr2 = bitcast <256 x i32>* to i8*
343 // %2 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
344 // i8* %addr2,
345 // i64 64)
346 Use &U = *(Bitcast->use_begin());
347 unsigned OpNo = U.getOperandNo();
348 auto *II = dyn_cast<IntrinsicInst>(U.getUser());
349 if (!II)
350 return false; // May be bitcast from x86amx to <256 x i32>.
351 Prepare(Bitcast->getOperand(0)->getType());
352 Builder.CreateStore(Src, AllocaAddr);
353 // TODO we can pick an constant operand for the shape.
354 Value *Row = nullptr, *Col = nullptr;
355 std::tie(Row, Col) = getShape(II, OpNo);
356 std::array<Value *, 4> Args = {Row, Col, I8Ptr, Stride};
357 Value *NewInst =
358 Builder.CreateIntrinsic(Intrinsic::x86_tileloadd64_internal, Args);
359 Bitcast->replaceAllUsesWith(NewInst);
360 } else {
361 // %2 = bitcast x86_amx %src to <256 x i32>
362 // -->
363 // %addr = alloca <256 x i32>, align 64
364 // %addr2 = bitcast <256 x i32>* to i8*
365 // call void @llvm.x86.tilestored64.internal(i16 %row, i16 %col,
366 // i8* %addr2, i64 %stride)
367 // %2 = load <256 x i32>, <256 x i32>* %addr, align 64
368 auto *II = dyn_cast<IntrinsicInst>(Src);
369 if (!II)
370 return false; // May be bitcast from <256 x i32> to x86amx.
371 Prepare(Bitcast->getType());
372 Value *Row = II->getOperand(0);
373 Value *Col = II->getOperand(1);
374 std::array<Value *, 5> Args = {Row, Col, I8Ptr, Stride, Src};
375 Builder.CreateIntrinsic(Intrinsic::x86_tilestored64_internal, Args);
376 Value *NewInst = Builder.CreateLoad(Bitcast->getType(), AllocaAddr);
377 Bitcast->replaceAllUsesWith(NewInst);
378 }
379
380 return true;
381}
382
383bool X86LowerAMXType::visit() {
384 SmallVector<Instruction *, 8> DeadInsts;
385 Col2Row.clear();
386
387 for (BasicBlock *BB : post_order(&Func)) {
388 for (Instruction &Inst : llvm::make_early_inc_range(llvm::reverse(*BB))) {
389 auto *Bitcast = dyn_cast<BitCastInst>(&Inst);
390 if (!Bitcast)
391 continue;
392
393 Value *Src = Bitcast->getOperand(0);
394 if (Bitcast->getType()->isX86_AMXTy()) {
395 if (Bitcast->user_empty()) {
396 DeadInsts.push_back(Bitcast);
397 continue;
398 }
399 LoadInst *LD = dyn_cast<LoadInst>(Src);
400 if (!LD) {
401 if (transformBitcast(Bitcast))
402 DeadInsts.push_back(Bitcast);
403 continue;
404 }
405 // If load has multi-user, duplicate a vector load.
406 // %src = load <256 x i32>, <256 x i32>* %addr, align 64
407 // %2 = bitcast <256 x i32> %src to x86_amx
408 // %add = add <256 x i32> %src, <256 x i32> %src2
409 // -->
410 // %src = load <256 x i32>, <256 x i32>* %addr, align 64
411 // %2 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
412 // i8* %addr, i64 %stride64)
413 // %add = add <256 x i32> %src, <256 x i32> %src2
414
415 // If load has one user, the load will be eliminated in DAG ISel.
416 // %src = load <256 x i32>, <256 x i32>* %addr, align 64
417 // %2 = bitcast <256 x i32> %src to x86_amx
418 // -->
419 // %2 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
420 // i8* %addr, i64 %stride64)
421 combineLoadBitcast(LD, Bitcast);
422 DeadInsts.push_back(Bitcast);
423 if (LD->hasOneUse())
424 DeadInsts.push_back(LD);
425 } else if (Src->getType()->isX86_AMXTy()) {
426 if (Bitcast->user_empty()) {
427 DeadInsts.push_back(Bitcast);
428 continue;
429 }
430 StoreInst *ST = nullptr;
431 for (Use &U : Bitcast->uses()) {
432 ST = dyn_cast<StoreInst>(U.getUser());
433 if (ST)
434 break;
435 }
436 if (!ST) {
437 if (transformBitcast(Bitcast))
438 DeadInsts.push_back(Bitcast);
439 continue;
440 }
441 // If bitcast (%13) has one use, combine bitcast and store to amx store.
442 // %src = call x86_amx @llvm.x86.tileloadd64.internal(%row, %col, %addr,
443 // %stride);
444 // %13 = bitcast x86_amx %src to <256 x i32>
445 // store <256 x i32> %13, <256 x i32>* %addr, align 64
446 // -->
447 // call void @llvm.x86.tilestored64.internal(%row, %col, %addr,
448 // %stride64, %13)
449 //
450 // If bitcast (%13) has multi-use, transform as below.
451 // %13 = bitcast x86_amx %src to <256 x i32>
452 // store <256 x i32> %13, <256 x i32>* %addr, align 64
453 // %add = <256 x i32> %13, <256 x i32> %src2
454 // -->
455 // %13 = bitcast x86_amx %src to <256 x i32>
456 // call void @llvm.x86.tilestored64.internal(%row, %col, %addr,
457 // %stride64, %13)
458 // %14 = load <256 x i32>, %addr
459 // %add = <256 x i32> %14, <256 x i32> %src2
460 //
461 combineBitcastStore(Bitcast, ST);
462 // Delete user first.
463 DeadInsts.push_back(ST);
464 DeadInsts.push_back(Bitcast);
465 }
466 }
467 }
468
469 bool C = !DeadInsts.empty();
470
471 for (auto *Inst : DeadInsts)
472 Inst->eraseFromParent();
473
474 return C;
475}
476} // anonymous namespace
477
479 Function *F = BB->getParent();
480 IRBuilder<> Builder(&F->getEntryBlock().front());
481 const DataLayout &DL = F->getDataLayout();
482 unsigned AllocaAS = DL.getAllocaAddrSpace();
483 Type *V256I32Ty = VectorType::get(Builder.getInt32Ty(), 256, false);
484 AllocaInst *AllocaRes =
485 new AllocaInst(V256I32Ty, AllocaAS, "", F->getEntryBlock().begin());
486 BasicBlock::iterator Iter = AllocaRes->getIterator();
487 ++Iter;
488 Builder.SetInsertPoint(&*Iter);
489 Value *I8Ptr = Builder.CreateBitCast(AllocaRes, Builder.getPtrTy());
490 return I8Ptr;
491}
492
494 assert(TileDef->getType()->isX86_AMXTy() && "Not define tile!");
495 auto *II = cast<IntrinsicInst>(TileDef);
496
497 assert(II && "Not tile intrinsic!");
498 Value *Row = II->getOperand(0);
499 Value *Col = II->getOperand(1);
500
501 BasicBlock *BB = TileDef->getParent();
502 BasicBlock::iterator Iter = TileDef->getIterator();
503 IRBuilder<> Builder(BB, ++Iter);
504 Value *Stride = Builder.getInt64(64);
505 std::array<Value *, 5> Args = {Row, Col, Ptr, Stride, TileDef};
506
507 Instruction *TileStore = Builder.CreateIntrinsicWithoutFolding(
508 Intrinsic::x86_tilestored64_internal, Args);
509 return TileStore;
510}
511
512static void replaceWithTileLoad(Use &U, Value *Ptr, bool IsPHI = false) {
513 Value *V = U.get();
514 assert(V->getType()->isX86_AMXTy() && "Not define tile!");
515
516 // Get tile shape.
517 IntrinsicInst *II = nullptr;
518 if (IsPHI) {
519 Value *PhiOp = cast<PHINode>(V)->getIncomingValue(0);
520 II = cast<IntrinsicInst>(PhiOp);
521 } else {
523 }
524 Value *Row = II->getOperand(0);
525 Value *Col = II->getOperand(1);
526
527 Instruction *UserI = cast<Instruction>(U.getUser());
528 IRBuilder<> Builder(UserI);
529 Value *Stride = Builder.getInt64(64);
530 std::array<Value *, 4> Args = {Row, Col, Ptr, Stride};
531
532 Value *TileLoad =
533 Builder.CreateIntrinsic(Intrinsic::x86_tileloadd64_internal, Args);
534 UserI->replaceUsesOfWith(V, TileLoad);
535}
536
538 for (Use &U : I->uses()) {
539 User *V = U.getUser();
540 if (isa<PHINode>(V))
541 return true;
542 }
543 return false;
544}
545
546// Let all AMX tile data become volatile data, shorten the life range
547// of each tile register before fast register allocation.
548namespace {
549class X86VolatileTileData {
550 Function &F;
551
552public:
553 X86VolatileTileData(Function &Func) : F(Func) {}
554 Value *updatePhiIncomings(BasicBlock *BB,
555 SmallVector<Instruction *, 2> &Incomings);
556 void replacePhiDefWithLoad(Instruction *PHI, Value *StorePtr);
557 bool volatileTileData();
558 void volatileTilePHI(PHINode *PHI);
559 void volatileTileNonPHI(Instruction *I);
560};
561
562Value *X86VolatileTileData::updatePhiIncomings(
563 BasicBlock *BB, SmallVector<Instruction *, 2> &Incomings) {
564 Value *I8Ptr = getAllocaPos(BB);
565
566 for (auto *I : Incomings) {
567 User *Store = createTileStore(I, I8Ptr);
568
569 // All its uses (except phi) should load from stored mem.
570 for (Use &U : I->uses()) {
571 User *V = U.getUser();
572 if (isa<PHINode>(V) || V == Store)
573 continue;
574 replaceWithTileLoad(U, I8Ptr);
575 }
576 }
577 return I8Ptr;
578}
579
580void X86VolatileTileData::replacePhiDefWithLoad(Instruction *PHI,
581 Value *StorePtr) {
582 for (Use &U : PHI->uses())
583 replaceWithTileLoad(U, StorePtr, true);
584 PHI->eraseFromParent();
585}
586
587// Smilar with volatileTileNonPHI, this function only handle PHI Nodes
588// and their related AMX intrinsics.
589// 1) PHI Def should change to tileload.
590// 2) PHI Incoming Values should tilestored in just after their def.
591// 3) The mem of these tileload and tilestores should be same.
592// e.g.
593// ------------------------------------------------------
594// bb_dom:
595// ...
596// br i1 %bool.cond, label %if.else, label %if.then
597//
598// if.then:
599// def %t0 = ...
600// ...
601// use %t0
602// ...
603// br label %if.end
604//
605// if.else:
606// def %t1 = ...
607// br label %if.end
608//
609// if.end:
610// %td = phi x86_amx [ %t1, %if.else ], [ %t0, %if.then ]
611// ...
612// use %td
613// ------------------------------------------------------
614// -->
615// ------------------------------------------------------
616// bb_entry:
617// %mem = alloca <256 x i32>, align 1024 *
618// ...
619// bb_dom:
620// ...
621// br i1 %bool.cond, label %if.else, label %if.then
622//
623// if.then:
624// def %t0 = ...
625// call void @llvm.x86.tilestored64.internal(mem, %t0) *
626// ...
627// %t0` = call x86_amx @llvm.x86.tileloadd64.internal(mem)*
628// use %t0` *
629// ...
630// br label %if.end
631//
632// if.else:
633// def %t1 = ...
634// call void @llvm.x86.tilestored64.internal(mem, %t1) *
635// br label %if.end
636//
637// if.end:
638// ...
639// %td = call x86_amx @llvm.x86.tileloadd64.internal(mem) *
640// use %td
641// ------------------------------------------------------
642void X86VolatileTileData::volatileTilePHI(PHINode *PHI) {
643 BasicBlock *BB = PHI->getParent();
644 SmallVector<Instruction *, 2> Incomings;
645
646 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I != E; ++I) {
647 Value *Op = PHI->getIncomingValue(I);
649 assert(Inst && "We shouldn't fold AMX instrution!");
650 Incomings.push_back(Inst);
651 }
652
653 Value *StorePtr = updatePhiIncomings(BB, Incomings);
654 replacePhiDefWithLoad(PHI, StorePtr);
655}
656
657// Store the defined tile and load it before use.
658// All its users are not PHI.
659// e.g.
660// ------------------------------------------------------
661// def %td = ...
662// ...
663// "use %td"
664// ------------------------------------------------------
665// -->
666// ------------------------------------------------------
667// def %td = ...
668// call void @llvm.x86.tilestored64.internal(mem, %td)
669// ...
670// %td2 = call x86_amx @llvm.x86.tileloadd64.internal(mem)
671// "use %td2"
672// ------------------------------------------------------
673void X86VolatileTileData::volatileTileNonPHI(Instruction *I) {
674 BasicBlock *BB = I->getParent();
675 Value *I8Ptr = getAllocaPos(BB);
676 User *Store = createTileStore(I, I8Ptr);
677
678 // All its uses should load from stored mem.
679 for (Use &U : I->uses()) {
680 User *V = U.getUser();
681 assert(!isa<PHINode>(V) && "PHI Nodes should be excluded!");
682 if (V != Store)
683 replaceWithTileLoad(U, I8Ptr);
684 }
685}
686
687// Volatile Tile Model:
688// 1) All the uses of tile data comes from tileload in time.
689// 2) All the defs of tile data tilestore into mem immediately.
690// For example:
691// --------------------------------------------------------------------------
692// %t1 = call x86_amx @llvm.x86.tileloadd64.internal(m, k, ...) key
693// %t2 = call x86_amx @llvm.x86.tileloadd64.internal(k, n, ...)
694// %t3 = call x86_amx @llvm.x86.tileloadd64.internal(m, n, ...) amx
695// %td = tail call x86_amx @llvm.x86.tdpbssd.internal(m, n, k, t1, t2, t3)
696// call void @llvm.x86.tilestored64.internal(... td) area
697// --------------------------------------------------------------------------
698// 3) No terminator, call or other amx instructions in the key amx area.
699bool X86VolatileTileData::volatileTileData() {
700 bool Changed = false;
701 for (BasicBlock &BB : F) {
702 SmallVector<Instruction *, 2> PHIInsts;
703 SmallVector<Instruction *, 8> AMXDefInsts;
704
705 for (Instruction &I : BB) {
706 if (!I.getType()->isX86_AMXTy())
707 continue;
708 if (isa<PHINode>(&I))
709 PHIInsts.push_back(&I);
710 else
711 AMXDefInsts.push_back(&I);
712 }
713
714 // First we "volatile" the non-phi related amx intrinsics.
715 for (Instruction *I : AMXDefInsts) {
716 if (isIncomingOfPHI(I))
717 continue;
718 volatileTileNonPHI(I);
719 Changed = true;
720 }
721
722 for (Instruction *I : PHIInsts) {
723 volatileTilePHI(dyn_cast<PHINode>(I));
724 Changed = true;
725 }
726 }
727 return Changed;
728}
729
730} // anonymous namespace
731
732namespace {
733
734class X86LowerAMXCast {
735 Function &Func;
736 std::unique_ptr<DominatorTree> DT;
737
738public:
739 X86LowerAMXCast(Function &F) : Func(F), DT(nullptr) {}
740 bool combineCastStore(IntrinsicInst *Cast, StoreInst *ST);
741 bool combineLoadCast(IntrinsicInst *Cast, LoadInst *LD);
742 bool combineTilezero(IntrinsicInst *Cast);
743 bool combineLdSt(SmallVectorImpl<Instruction *> &Casts);
744 bool combineAMXcast(TargetLibraryInfo *TLI);
745 bool transformAMXCast(IntrinsicInst *AMXCast);
746 bool transformAllAMXCast();
747 bool optimizeAMXCastFromPhi(IntrinsicInst *CI, PHINode *PN,
748 SmallSetVector<Instruction *, 16> &DeadInst);
749};
750
751static bool DCEInstruction(Instruction *I,
752 SmallSetVector<Instruction *, 16> &WorkList,
753 const TargetLibraryInfo *TLI) {
754 if (isInstructionTriviallyDead(I, TLI)) {
757
758 // Null out all of the instruction's operands to see if any operand becomes
759 // dead as we go.
760 for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) {
761 Value *OpV = I->getOperand(i);
762 I->setOperand(i, nullptr);
763
764 if (!OpV->use_empty() || I == OpV)
765 continue;
766
767 // If the operand is an instruction that became dead as we nulled out the
768 // operand, and if it is 'trivially' dead, delete it in a future loop
769 // iteration.
770 if (Instruction *OpI = dyn_cast<Instruction>(OpV)) {
771 if (isInstructionTriviallyDead(OpI, TLI)) {
772 WorkList.insert(OpI);
773 }
774 }
775 }
776 I->eraseFromParent();
777 return true;
778 }
779 return false;
780}
781
782/// This function handles following case
783///
784/// A -> B amxcast
785/// PHI
786/// B -> A amxcast
787///
788/// All the related PHI nodes can be replaced by new PHI nodes with type A.
789/// The uses of \p CI can be changed to the new PHI node corresponding to \p PN.
790bool X86LowerAMXCast::optimizeAMXCastFromPhi(
791 IntrinsicInst *CI, PHINode *PN,
792 SmallSetVector<Instruction *, 16> &DeadInst) {
793 IRBuilder<> Builder(CI);
794 Value *Src = CI->getOperand(0);
795 Type *SrcTy = Src->getType(); // Type B
796 Type *DestTy = CI->getType(); // Type A
797
798 SmallVector<PHINode *, 4> PhiWorklist;
799 SmallSetVector<PHINode *, 4> OldPhiNodes;
800
801 // Find all of the A->B casts and PHI nodes.
802 // We need to inspect all related PHI nodes, but PHIs can be cyclic, so
803 // OldPhiNodes is used to track all known PHI nodes, before adding a new
804 // PHI to PhiWorklist, it is checked against and added to OldPhiNodes first.
805 PhiWorklist.push_back(PN);
806 OldPhiNodes.insert(PN);
807 while (!PhiWorklist.empty()) {
808 auto *OldPN = PhiWorklist.pop_back_val();
809 for (unsigned I = 0; I < OldPN->getNumOperands(); ++I) {
810 Value *IncValue = OldPN->getIncomingValue(I);
811 // TODO: currently, We ignore cases where it is a const. In the future, we
812 // might support const.
813 if (isa<Constant>(IncValue)) {
814 auto *IncConst = dyn_cast<Constant>(IncValue);
815 if (!isa<UndefValue>(IncValue) && !IncConst->isNullValue())
816 return false;
817 Value *Row = nullptr, *Col = nullptr;
818 std::tie(Row, Col) = getShape(OldPN);
819 // TODO: If it is not constant the Row and Col must domoniate tilezero
820 // that we are going to create.
821 if (!Row || !Col || !isa<Constant>(Row) || !isa<Constant>(Col))
822 return false;
823 // Create tilezero at the end of incoming block.
824 auto *Block = OldPN->getIncomingBlock(I);
825 BasicBlock::iterator Iter = Block->getTerminator()->getIterator();
826 Instruction *NewInst = Builder.CreateIntrinsicWithoutFolding(
827 Intrinsic::x86_tilezero_internal, {}, {Row, Col});
828 NewInst->moveBefore(Iter);
829 NewInst = Builder.CreateIntrinsicWithoutFolding(
830 Intrinsic::x86_cast_tile_to_vector, {IncValue->getType()},
831 {NewInst});
832 NewInst->moveBefore(Iter);
833 // Replace InValue with new Value.
834 OldPN->setIncomingValue(I, NewInst);
835 IncValue = NewInst;
836 }
837
838 if (auto *PNode = dyn_cast<PHINode>(IncValue)) {
839 if (OldPhiNodes.insert(PNode))
840 PhiWorklist.push_back(PNode);
841 continue;
842 }
843 Instruction *ACI = dyn_cast<Instruction>(IncValue);
844 if (ACI && isAMXCast(ACI)) {
845 // Verify it's a A->B cast.
846 Type *TyA = ACI->getOperand(0)->getType();
847 Type *TyB = ACI->getType();
848 if (TyA != DestTy || TyB != SrcTy)
849 return false;
850 continue;
851 }
852 return false;
853 }
854 }
855
856 // Check that each user of each old PHI node is something that we can
857 // rewrite, so that all of the old PHI nodes can be cleaned up afterwards.
858 for (auto *OldPN : OldPhiNodes) {
859 for (User *V : OldPN->users()) {
861 if (ACI && isAMXCast(ACI)) {
862 // Verify it's a B->A cast.
863 Type *TyB = ACI->getOperand(0)->getType();
864 Type *TyA = ACI->getType();
865 if (TyA != DestTy || TyB != SrcTy)
866 return false;
867 } else if (auto *PHI = dyn_cast<PHINode>(V)) {
868 // As long as the user is another old PHI node, then even if we don't
869 // rewrite it, the PHI web we're considering won't have any users
870 // outside itself, so it'll be dead.
871 // example:
872 // bb.0:
873 // %0 = amxcast ...
874 // bb.1:
875 // %1 = amxcast ...
876 // bb.2:
877 // %goodphi = phi %0, %1
878 // %3 = amxcast %goodphi
879 // bb.3:
880 // %goodphi2 = phi %0, %goodphi
881 // %4 = amxcast %goodphi2
882 // When optimizeAMXCastFromPhi process %3 and %goodphi, %goodphi2 is
883 // outside the phi-web, so the combination stop When
884 // optimizeAMXCastFromPhi process %4 and %goodphi2, the optimization
885 // will be done.
886 if (OldPhiNodes.count(PHI) == 0)
887 return false;
888 } else
889 return false;
890 }
891 }
892
893 // For each old PHI node, create a corresponding new PHI node with a type A.
894 SmallDenseMap<PHINode *, PHINode *> NewPNodes;
895 for (auto *OldPN : OldPhiNodes) {
896 Builder.SetInsertPoint(OldPN);
897 PHINode *NewPN = Builder.CreatePHI(DestTy, OldPN->getNumOperands());
898 NewPNodes[OldPN] = NewPN;
899 }
900
901 // Fill in the operands of new PHI nodes.
902 for (auto *OldPN : OldPhiNodes) {
903 PHINode *NewPN = NewPNodes[OldPN];
904 for (unsigned j = 0, e = OldPN->getNumOperands(); j != e; ++j) {
905 Value *V = OldPN->getOperand(j);
906 Value *NewV = nullptr;
908 // There should not be a AMXcast from a const.
909 if (ACI && isAMXCast(ACI))
910 NewV = ACI->getOperand(0);
911 else if (auto *PrevPN = dyn_cast<PHINode>(V))
912 NewV = NewPNodes[PrevPN];
913 assert(NewV);
914 NewPN->addIncoming(NewV, OldPN->getIncomingBlock(j));
915 }
916 }
917
918 // Traverse all accumulated PHI nodes and process its users,
919 // which are Stores and BitcCasts. Without this processing
920 // NewPHI nodes could be replicated and could lead to extra
921 // moves generated after DeSSA.
922 // If there is a store with type B, change it to type A.
923
924 // Replace users of BitCast B->A with NewPHI. These will help
925 // later to get rid of a closure formed by OldPHI nodes.
926 for (auto *OldPN : OldPhiNodes) {
927 PHINode *NewPN = NewPNodes[OldPN];
928 for (User *V : make_early_inc_range(OldPN->users())) {
930 if (ACI && isAMXCast(ACI)) {
931 Type *TyB = ACI->getOperand(0)->getType();
932 Type *TyA = ACI->getType();
933 assert(TyA == DestTy && TyB == SrcTy);
934 (void)TyA;
935 (void)TyB;
936 ACI->replaceAllUsesWith(NewPN);
937 DeadInst.insert(ACI);
938 } else if (auto *PHI = dyn_cast<PHINode>(V)) {
939 // We don't need to push PHINode into DeadInst since they are operands
940 // of rootPN DCE can safely delete rootPN's operands if rootPN is dead.
941 assert(OldPhiNodes.contains(PHI));
942 (void)PHI;
943 } else
944 llvm_unreachable("all uses should be handled");
945 }
946 }
947 return true;
948}
949
950// %43 = call <256 x i32> @llvm.x86.cast.tile.to.vector.v256i32(x86_amx %42)
951// store <256 x i32> %43, <256 x i32>* %p, align 64
952// -->
953// call void @llvm.x86.tilestored64.internal(i16 %row, i16 %col, i8* %p,
954// i64 64, x86_amx %42)
955bool X86LowerAMXCast::combineCastStore(IntrinsicInst *Cast, StoreInst *ST) {
956 Value *Tile = Cast->getOperand(0);
957
958 assert(Tile->getType()->isX86_AMXTy() && "Not Tile Operand!");
959
960 // TODO: Specially handle the multi-use case.
961 if (!Tile->hasOneUse())
962 return false;
963
964 auto *II = cast<IntrinsicInst>(Tile);
965 // Tile is output from AMX intrinsic. The first operand of the
966 // intrinsic is row, the second operand of the intrinsic is column.
967 Value *Row = II->getOperand(0);
968 Value *Col = II->getOperand(1);
969
970 IRBuilder<> Builder(ST);
971
972 // Stride should be equal to col(measured by bytes)
973 Value *Stride = Builder.CreateSExt(Col, Builder.getInt64Ty());
974 Value *I8Ptr = Builder.CreateBitCast(ST->getOperand(1), Builder.getPtrTy());
975 std::array<Value *, 5> Args = {Row, Col, I8Ptr, Stride, Tile};
976 Builder.CreateIntrinsic(Intrinsic::x86_tilestored64_internal, Args);
977 return true;
978}
979
980// %65 = load <256 x i32>, <256 x i32>* %p, align 64
981// %66 = call x86_amx @llvm.x86.cast.vector.to.tile(<256 x i32> %65)
982// -->
983// %66 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
984// i8* %p, i64 64)
985bool X86LowerAMXCast::combineLoadCast(IntrinsicInst *Cast, LoadInst *LD) {
986 bool EraseLoad = true;
987 Value *Row = nullptr, *Col = nullptr;
988 Use &U = *(Cast->use_begin());
989 unsigned OpNo = U.getOperandNo();
990 auto *II = cast<IntrinsicInst>(U.getUser());
991 // TODO: If it is cast intrinsic or phi node, we can propagate the
992 // shape information through def-use chain.
993 if (!isAMXIntrinsic(II))
994 return false;
995 std::tie(Row, Col) = getShape(II, OpNo);
996 IRBuilder<> Builder(LD);
997 Value *I8Ptr;
998
999 // To save compiling time, we create dominator tree when it is really needed.
1000 if (!DT)
1001 DT.reset(new DominatorTree(Func));
1002 if (!DT->dominates(Row, LD) || !DT->dominates(Col, LD)) {
1003 // store the value to stack and reload it from stack before cast.
1004 auto *AllocaAddr =
1005 createAllocaInstAtEntry(Builder, Cast->getParent(), LD->getType());
1006 Builder.SetInsertPoint(&*std::next(LD->getIterator()));
1007 Builder.CreateStore(LD, AllocaAddr);
1008
1009 Builder.SetInsertPoint(Cast);
1010 I8Ptr = Builder.CreateBitCast(AllocaAddr, Builder.getPtrTy());
1011 EraseLoad = false;
1012 } else {
1013 I8Ptr = Builder.CreateBitCast(LD->getOperand(0), Builder.getPtrTy());
1014 }
1015 // Stride should be equal to col(measured by bytes)
1016 Value *Stride = Builder.CreateSExt(Col, Builder.getInt64Ty());
1017 std::array<Value *, 4> Args = {Row, Col, I8Ptr, Stride};
1018
1019 Value *NewInst =
1020 Builder.CreateIntrinsic(Intrinsic::x86_tileloadd64_internal, Args);
1021 Cast->replaceAllUsesWith(NewInst);
1022
1023 return EraseLoad;
1024}
1025
1026// %19 = tail call x86_amx @llvm.x86.cast.vector.to.tile.v256i32(<256 x i32> zeroinitializer)
1027// -->
1028// %19 = tail call x86_amx @llvm.x86.tilezero.internal(i16 %row, i16 %col)
1029bool X86LowerAMXCast::combineTilezero(IntrinsicInst *Cast) {
1030 Value *Row = nullptr, *Col = nullptr;
1031 Use &U = *(Cast->use_begin());
1032 unsigned OpNo = U.getOperandNo();
1033 auto *II = cast<IntrinsicInst>(U.getUser());
1034 if (!isAMXIntrinsic(II))
1035 return false;
1036
1037 std::tie(Row, Col) = getShape(II, OpNo);
1038
1039 IRBuilder<> Builder(Cast);
1040 Value *NewInst =
1041 Builder.CreateIntrinsic(Intrinsic::x86_tilezero_internal, {}, {Row, Col});
1042 Cast->replaceAllUsesWith(NewInst);
1043 return true;
1044}
1045
1046bool X86LowerAMXCast::combineLdSt(SmallVectorImpl<Instruction *> &Casts) {
1047 bool Change = false;
1048 for (auto *Cast : Casts) {
1049 auto *II = cast<IntrinsicInst>(Cast);
1050 // %43 = call <256 x i32> @llvm.x86.cast.tile.to.vector(x86_amx %42)
1051 // store <256 x i32> %43, <256 x i32>* %p, align 64
1052 // -->
1053 // call void @llvm.x86.tilestored64.internal(i16 %row, i16 %col, i8* %p,
1054 // i64 64, x86_amx %42)
1055 if (II->getIntrinsicID() == Intrinsic::x86_cast_tile_to_vector) {
1056 SmallVector<Instruction *, 2> DeadStores;
1057 for (User *U : Cast->users()) {
1058 StoreInst *Store = dyn_cast<StoreInst>(U);
1059 if (!Store)
1060 continue;
1061 if (combineCastStore(cast<IntrinsicInst>(Cast), Store)) {
1062 DeadStores.push_back(Store);
1063 Change = true;
1064 }
1065 }
1066 for (auto *Store : DeadStores)
1067 Store->eraseFromParent();
1068 } else { // x86_cast_vector_to_tile
1069 // %19 = tail call x86_amx @llvm.x86.cast.vector.to.tile.v256i32(<256 x i32> zeroinitializer)
1070 // -->
1071 // %19 = tail call x86_amx @llvm.x86.tilezero.internal(i16 %row, i16 %col)
1072 if (isa<ConstantAggregateZero>(Cast->getOperand(0))) {
1073 Change |= combineTilezero(cast<IntrinsicInst>(Cast));
1074 continue;
1075 }
1076
1077 auto *Load = dyn_cast<LoadInst>(Cast->getOperand(0));
1078 if (!Load || !Load->hasOneUse())
1079 continue;
1080 // %65 = load <256 x i32>, <256 x i32>* %p, align 64
1081 // %66 = call x86_amx @llvm.x86.cast.vector.to.tile(<256 x i32> %65)
1082 // -->
1083 // %66 = call x86_amx @llvm.x86.tileloadd64.internal(i16 %row, i16 %col,
1084 // i8* %p, i64 64)
1085 if (combineLoadCast(cast<IntrinsicInst>(Cast), Load)) {
1086 // Set the operand is null so that load instruction can be erased.
1087 Cast->setOperand(0, nullptr);
1088 Load->eraseFromParent();
1089 Change = true;
1090 }
1091 }
1092 }
1093 return Change;
1094}
1095
1096bool X86LowerAMXCast::combineAMXcast(TargetLibraryInfo *TLI) {
1097 bool Change = false;
1098 // Collect tile cast instruction.
1099 SmallVector<Instruction *, 8> Vec2TileInsts;
1100 SmallVector<Instruction *, 8> Tile2VecInsts;
1101 SmallVector<Instruction *, 8> PhiCastWorkList;
1102 SmallSetVector<Instruction *, 16> DeadInst;
1103 for (BasicBlock &BB : Func) {
1104 for (Instruction &I : BB) {
1105 Value *Vec;
1106 if (match(&I,
1108 Vec2TileInsts.push_back(&I);
1110 m_Value(Vec))))
1111 Tile2VecInsts.push_back(&I);
1112 }
1113 }
1114
1115 auto Convert = [&](SmallVectorImpl<Instruction *> &Insts, Intrinsic::ID IID) {
1116 for (auto *Inst : Insts) {
1117 for (User *U : Inst->users()) {
1118 IntrinsicInst *II = dyn_cast<IntrinsicInst>(U);
1119 if (!II || II->getIntrinsicID() != IID)
1120 continue;
1121 // T1 = vec2tile V0
1122 // V2 = tile2vec T1
1123 // V3 = OP V2
1124 // -->
1125 // T1 = vec2tile V0
1126 // V2 = tile2vec T1
1127 // V3 = OP V0
1128 II->replaceAllUsesWith(Inst->getOperand(0));
1129 Change = true;
1130 }
1131 }
1132 };
1133
1134 Convert(Vec2TileInsts, Intrinsic::x86_cast_tile_to_vector);
1135 Convert(Tile2VecInsts, Intrinsic::x86_cast_vector_to_tile);
1136
1137 SmallVector<Instruction *, 8> LiveCasts;
1138 auto EraseInst = [&](SmallVectorImpl<Instruction *> &Insts) {
1139 for (auto *Inst : Insts) {
1140 if (Inst->use_empty()) {
1141 Inst->eraseFromParent();
1142 Change = true;
1143 } else {
1144 LiveCasts.push_back(Inst);
1145 }
1146 }
1147 };
1148
1149 EraseInst(Vec2TileInsts);
1150 EraseInst(Tile2VecInsts);
1151 LLVM_DEBUG(dbgs() << "[LowerAMXTYpe][combineAMXcast] IR dump after combine "
1152 "Vec2Tile and Tile2Vec:\n";
1153 Func.dump());
1154 Change |= combineLdSt(LiveCasts);
1155 EraseInst(LiveCasts);
1156 LLVM_DEBUG(dbgs() << "[LowerAMXTYpe][combineAMXcast] IR dump after combine "
1157 "AMXCast and load/store:\n";
1158 Func.dump());
1159
1160 // Handle the A->B->A cast, and there is an intervening PHI node.
1161 for (BasicBlock &BB : Func) {
1162 for (Instruction &I : BB) {
1163 if (isAMXCast(&I)) {
1164 if (isa<PHINode>(I.getOperand(0)))
1165 PhiCastWorkList.push_back(&I);
1166 }
1167 }
1168 }
1169 for (auto *I : PhiCastWorkList) {
1170 // We skip the dead Amxcast.
1171 if (DeadInst.contains(I))
1172 continue;
1173 PHINode *PN = cast<PHINode>(I->getOperand(0));
1174 if (optimizeAMXCastFromPhi(cast<IntrinsicInst>(I), PN, DeadInst)) {
1175 DeadInst.insert(PN);
1176 Change = true;
1177 }
1178 }
1179
1180 // Since we create new phi and merge AMXCast, some old phis and AMXCast might
1181 // have no uses. We do some DeadCodeElimination for them.
1182 while (!DeadInst.empty()) {
1183 Instruction *I = DeadInst.pop_back_val();
1184 Change |= DCEInstruction(I, DeadInst, TLI);
1185 }
1186 LLVM_DEBUG(dbgs() << "[LowerAMXTYpe][combineAMXcast] IR dump after "
1187 "optimizeAMXCastFromPhi:\n";
1188 Func.dump());
1189 return Change;
1190}
1191
1192// There might be remaining AMXcast after combineAMXcast and they should be
1193// handled elegantly.
1194bool X86LowerAMXCast::transformAMXCast(IntrinsicInst *AMXCast) {
1195 IRBuilder<> Builder(AMXCast);
1196 AllocaInst *AllocaAddr;
1197 Value *I8Ptr, *Stride;
1198 auto *Src = AMXCast->getOperand(0);
1199
1200 auto Prepare = [&](Type *MemTy) {
1201 AllocaAddr = createAllocaInstAtEntry(Builder, AMXCast->getParent(), MemTy);
1202 I8Ptr = Builder.CreateBitCast(AllocaAddr, Builder.getPtrTy());
1203 Stride = Builder.getInt64(64);
1204 };
1205
1206 if (AMXCast->getType()->isX86_AMXTy()) {
1207 // %2 = amxcast <225 x i32> %src to x86_amx
1208 // call void @llvm.x86.tilestored64.internal(i16 15, i16 60,
1209 // i8* %addr3, i64 60, x86_amx %2)
1210 // -->
1211 // %addr = alloca <225 x i32>, align 64
1212 // store <225 x i32> %src, <225 x i32>* %addr, align 64
1213 // %addr2 = bitcast <225 x i32>* %addr to i8*
1214 // %2 = call x86_amx @llvm.x86.tileloadd64.internal(i16 15, i16 60,
1215 // i8* %addr2,
1216 // i64 60)
1217 // call void @llvm.x86.tilestored64.internal(i16 15, i16 60,
1218 // i8* %addr3, i64 60, x86_amx %2)
1219 if (AMXCast->use_empty()) {
1220 AMXCast->eraseFromParent();
1221 return true;
1222 }
1223 Use &U = *(AMXCast->use_begin());
1224 unsigned OpNo = U.getOperandNo();
1225 auto *II = dyn_cast<IntrinsicInst>(U.getUser());
1226 if (!II)
1227 return false; // May be bitcast from x86amx to <256 x i32>.
1228 Prepare(AMXCast->getOperand(0)->getType());
1229 Builder.CreateStore(Src, AllocaAddr);
1230 // TODO we can pick an constant operand for the shape.
1231 Value *Row = nullptr, *Col = nullptr;
1232 std::tie(Row, Col) = getShape(II, OpNo);
1233 std::array<Value *, 4> Args = {
1234 Row, Col, I8Ptr, Builder.CreateSExt(Col, Builder.getInt64Ty())};
1235 Value *NewInst =
1236 Builder.CreateIntrinsic(Intrinsic::x86_tileloadd64_internal, Args);
1237 AMXCast->replaceAllUsesWith(NewInst);
1238 AMXCast->eraseFromParent();
1239 } else {
1240 // %2 = amxcast x86_amx %src to <225 x i32>
1241 // -->
1242 // %addr = alloca <225 x i32>, align 64
1243 // %addr2 = bitcast <225 x i32>* to i8*
1244 // call void @llvm.x86.tilestored64.internal(i16 %row, i16 %col,
1245 // i8* %addr2, i64 %stride)
1246 // %2 = load <225 x i32>, <225 x i32>* %addr, align 64
1247 auto *II = dyn_cast<IntrinsicInst>(Src);
1248 if (!II)
1249 return false; // May be bitcast from <256 x i32> to x86amx.
1250 Prepare(AMXCast->getType());
1251 Value *Row = II->getOperand(0);
1252 Value *Col = II->getOperand(1);
1253 std::array<Value *, 5> Args = {
1254 Row, Col, I8Ptr, Builder.CreateSExt(Col, Builder.getInt64Ty()), Src};
1255 Builder.CreateIntrinsic(Intrinsic::x86_tilestored64_internal, Args);
1256 Value *NewInst = Builder.CreateLoad(AMXCast->getType(), AllocaAddr);
1257 AMXCast->replaceAllUsesWith(NewInst);
1258 AMXCast->eraseFromParent();
1259 }
1260
1261 return true;
1262}
1263
1264bool X86LowerAMXCast::transformAllAMXCast() {
1265 bool Change = false;
1266 // Collect tile cast instruction.
1267 SmallVector<Instruction *, 8> WorkLists;
1268 for (BasicBlock &BB : Func) {
1269 for (Instruction &I : BB) {
1270 if (isAMXCast(&I))
1271 WorkLists.push_back(&I);
1272 }
1273 }
1274
1275 for (auto *Inst : WorkLists) {
1276 Change |= transformAMXCast(cast<IntrinsicInst>(Inst));
1277 }
1278
1279 return Change;
1280}
1281
1282bool lowerAmxType(Function &F, const TargetMachine *TM,
1283 TargetLibraryInfo *TLI) {
1284 // Performance optimization: most code doesn't use AMX, so return early if
1285 // there are no instructions that produce AMX values. This is sufficient, as
1286 // AMX arguments and constants are not allowed -- so any producer of an AMX
1287 // value must be an instruction.
1288 // TODO: find a cheaper way for this, without looking at all instructions.
1289 if (!containsAMXCode(F))
1290 return false;
1291
1292 bool C = false;
1293 X86LowerAMXCast LAC(F);
1294 C |= LAC.combineAMXcast(TLI);
1295 // There might be remaining AMXcast after combineAMXcast and they should be
1296 // handled elegantly.
1297 C |= LAC.transformAllAMXCast();
1298
1299 X86LowerAMXType LAT(F);
1300 C |= LAT.visit();
1301
1302 // Prepare for fast register allocation at O0.
1303 // Todo: May better check the volatile model of AMX code, not just
1304 // by checking Attribute::OptimizeNone and CodeGenOptLevel::None.
1305 if (TM->getOptLevel() == CodeGenOptLevel::None) {
1306 // If Front End not use O0 but the Mid/Back end use O0, (e.g.
1307 // "Clang -O2 -S -emit-llvm t.c" + "llc t.ll") we should make
1308 // sure the amx data is volatile, that is necessary for AMX fast
1309 // register allocation.
1310 if (!F.hasFnAttribute(Attribute::OptimizeNone)) {
1311 X86VolatileTileData VTD(F);
1312 C = VTD.volatileTileData() || C;
1313 }
1314 }
1315
1316 return C;
1317}
1318
1319} // anonymous namespace
1320
1323 TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
1324 bool Changed = lowerAmxType(F, TM, &TLI);
1325 if (!Changed)
1326 return PreservedAnalyses::all();
1327
1330 return PA;
1331}
1332
1333namespace {
1334
1335class X86LowerAMXTypeLegacyPass : public FunctionPass {
1336public:
1337 static char ID;
1338
1339 X86LowerAMXTypeLegacyPass() : FunctionPass(ID) {}
1340
1341 bool runOnFunction(Function &F) override {
1342 TargetMachine *TM = &getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
1343 TargetLibraryInfo *TLI =
1344 &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
1345 return lowerAmxType(F, TM, TLI);
1346 }
1347
1348 void getAnalysisUsage(AnalysisUsage &AU) const override {
1349 AU.setPreservesCFG();
1350 AU.addRequired<TargetPassConfig>();
1351 AU.addRequired<TargetLibraryInfoWrapperPass>();
1352 }
1353};
1354
1355} // anonymous namespace
1356
1357static const char PassName[] = "Lower AMX type for load/store";
1358char X86LowerAMXTypeLegacyPass::ID = 0;
1359INITIALIZE_PASS_BEGIN(X86LowerAMXTypeLegacyPass, DEBUG_TYPE, PassName, false,
1360 false)
1363INITIALIZE_PASS_END(X86LowerAMXTypeLegacyPass, DEBUG_TYPE, PassName, false,
1364 false)
1365
1367 return new X86LowerAMXTypeLegacyPass();
1368}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static bool DCEInstruction(Instruction *I, SmallSetVector< Instruction *, 16 > &WorkList, const TargetLibraryInfo *TLI)
Definition DCE.cpp:55
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
This header defines various interfaces for pass management in LLVM.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.
static void visit(BasicBlock &Start, std::function< bool(BasicBlock *)> op)
This file implements a set that has insertion order iteration characteristics.
#define LLVM_DEBUG(...)
Definition Debug.h:119
Target-Independent Code Generator Pass Configuration Options pass.
This pass exposes codegen information to IR-level passes.
static ShapeT getShape(MachineRegisterInfo *MRI, Register TileReg)
static const char PassName[]
static bool isAMXCast(Instruction *II)
static Value * getRowFromCol(Instruction *II, Value *V, unsigned Granularity)
static void replaceWithTileLoad(Use &U, Value *Ptr, bool IsPHI=false)
static Instruction * createTileStore(Instruction *TileDef, Value *Ptr)
static Value * getAllocaPos(BasicBlock *BB)
static bool containsAMXCode(Function &F)
std::pair< Value *, Value * > getShape(IntrinsicInst *II, unsigned OpNo)
static bool isIncomingOfPHI(Instruction *I)
static bool isAMXIntrinsic(Value *I)
static Instruction * getFirstNonAllocaInTheEntryBlock(Function &F)
static AllocaInst * createAllocaInstAtEntry(IRBuilder<> &Builder, BasicBlock *BB, Type *Ty)
an instruction to allocate memory on the stack
void setAlignment(Align Align)
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:275
LLVM Basic Block Representation.
Definition BasicBlock.h:62
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
This class represents a no-op cast from one type to another.
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
Value * CreateUDiv(Value *LHS, Value *RHS, const Twine &Name="", bool isExact=false)
Definition IRBuilder.h:1473
ConstantInt * getInt16(uint16_t C)
Get a constant 16-bit value.
Definition IRBuilder.h:472
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2893
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
A wrapper class for inspecting calls to intrinsic functions.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
bool contains(const_arg_type key) const
Check if the SetVector contains the given key.
Definition SetVector.h:252
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:151
value_type pop_back_val()
Definition SetVector.h:279
void push_back(const T &Elt)
Analysis pass providing the TargetLibraryInfo.
Provides information about what library functions are available for the current target.
Primary interface to the complete machine description for the target machine.
CodeGenOptLevel getOptLevel() const
Returns the optimization level: None, Less, Default, or Aggressive.
Target-Independent Code Generator Pass Configuration Options.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI Type * getX86_AMXTy(LLVMContext &C)
Definition Type.cpp:293
bool isX86_AMXTy() const
Return true if this is X86 AMX.
Definition Type.h:202
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
LLVM_ABI unsigned getOperandNo() const
Return the operand # of this use in its User.
Definition Use.cpp:36
User * getUser() const
Returns the User that contains this Use.
Definition Use.h:61
void setOperand(unsigned i, Value *Val)
Definition User.h:212
LLVM_ABI bool replaceUsesOfWith(Value *From, Value *To)
Replace uses of one Value with another.
Definition User.cpp:25
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
use_iterator use_begin()
Definition Value.h:364
bool use_empty() const
Definition Value.h:346
static LLVM_ABI VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
Changed
Pass manager infrastructure for declaring and invalidating analyses.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
@ Bitcast
Perform the operation on a different, but equivalently sized type.
bool match(Val *V, const Pattern &P)
auto m_Value()
Match an arbitrary value and ignore it.
auto m_Intrinsic(const Ts &...Ops)
Match intrinsic calls like this: m_Intrinsic<Intrinsic::fabs>(m_Value(X))
@ User
could "use" a pointer
NodeAddr< UseNode * > Use
Definition RDFGraph.h:387
NodeAddr< FuncNode * > Func
Definition RDFGraph.h:395
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
LLVM_ABI void salvageDebugInfo(const MachineRegisterInfo &MRI, MachineInstr &MI)
Assuming the instruction MI is going to be deleted, attempt to salvage debug users of MI by writing t...
Definition Utils.cpp:1690
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
LLVM_ABI bool isInstructionTriviallyDead(Instruction *I, const TargetLibraryInfo *TLI=nullptr)
Return true if the result produced by the instruction is not used, and the instruction will return.
Definition Local.cpp:403
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
auto post_order(const T &G)
Post-order traversal of a graph.
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
LLVM_ABI bool salvageKnowledge(Instruction *I, AssumptionCache *AC=nullptr, DominatorTree *DT=nullptr)
Calls BuildAssumeFromInst and if the resulting llvm.assume is valid insert if before I.
DWARFExpression::Operation Op
FunctionPass * createX86LowerAMXTypeLegacyPass()
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.