1*5f7ddb14SDimitry Andric //===- Target/X86/X86PreAMXConfig.cpp - ------------------------*- C++ -*-===//
2*5f7ddb14SDimitry Andric //
3*5f7ddb14SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5f7ddb14SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5f7ddb14SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5f7ddb14SDimitry Andric //
7*5f7ddb14SDimitry Andric //===----------------------------------------------------------------------===//
8*5f7ddb14SDimitry Andric //
9*5f7ddb14SDimitry Andric /// Insert tilecfg for each area of key AMX intrinsic.
10*5f7ddb14SDimitry Andric /// All the key AMX intrinsic's tile operand must come from tileload. And the
11*5f7ddb14SDimitry Andric /// def tile of key AMX intrinsic must be tilestored.
12*5f7ddb14SDimitry Andric /// take tdpbssd for example:
13*5f7ddb14SDimitry Andric /// --------------------------------------------------------------------------
14*5f7ddb14SDimitry Andric /// %t1 = call x86_amx @llvm.x86.tileloadd64.internal(...) key
15*5f7ddb14SDimitry Andric /// %t2 = call x86_amx @llvm.x86.tileloadd64.internal(...) |
16*5f7ddb14SDimitry Andric /// %t3 = call x86_amx @llvm.x86.tileloadd64.internal(...) amx
17*5f7ddb14SDimitry Andric /// %td = tail call x86_amx @llvm.x86.tdpbssd.internal(t1, t2, t3) |
18*5f7ddb14SDimitry Andric /// call void @llvm.x86.tilestored64.internal(... td) area
19*5f7ddb14SDimitry Andric /// --------------------------------------------------------------------------
20*5f7ddb14SDimitry Andric /// This pass will insert tilecfg before every key-amx-area, some like:
21*5f7ddb14SDimitry Andric /// --------------------------------------------------------------------------
22*5f7ddb14SDimitry Andric /// %cfgmem = alloca <16 x i32>, align 4 * allocate mem
23*5f7ddb14SDimitry Andric /// store <16 x i32> zeroinitializer, <16 x i32>* %cfgmem * zero init
24*5f7ddb14SDimitry Andric /// ...
25*5f7ddb14SDimitry Andric /// ... pre-config shape of %t1 *
26*5f7ddb14SDimitry Andric /// store volatile i8 %m, i8* %amx.tmm.0.shape.row, align 1 *
27*5f7ddb14SDimitry Andric /// store volatile i16 %k, i16* %amx.tmm.0.shape.col, align 2 * pre-config
28*5f7ddb14SDimitry Andric /// ... *
29*5f7ddb14SDimitry Andric /// ... pre-config shape of %t2 * shapes
30*5f7ddb14SDimitry Andric /// store volatile i8 %k, i8* %amx.tmm.1.shape.row, align 1 *
31*5f7ddb14SDimitry Andric /// store volatile i16 %n, i16* %amx.tmm.1.shape.col, align 2 *
32*5f7ddb14SDimitry Andric /// ...
33*5f7ddb14SDimitry Andric /// call void @llvm.x86.ldtilecfg(i8* %cfgmem) * tile config
34*5f7ddb14SDimitry Andric //
35*5f7ddb14SDimitry Andric //===----------------------------------------------------------------------===//
36*5f7ddb14SDimitry Andric //
37*5f7ddb14SDimitry Andric #include "X86.h"
38*5f7ddb14SDimitry Andric #include "llvm/ADT/SmallSet.h"
39*5f7ddb14SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h"
40*5f7ddb14SDimitry Andric #include "llvm/CodeGen/Passes.h"
41*5f7ddb14SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
42*5f7ddb14SDimitry Andric #include "llvm/CodeGen/ValueTypes.h"
43*5f7ddb14SDimitry Andric #include "llvm/IR/DataLayout.h"
44*5f7ddb14SDimitry Andric #include "llvm/IR/Function.h"
45*5f7ddb14SDimitry Andric #include "llvm/IR/IRBuilder.h"
46*5f7ddb14SDimitry Andric #include "llvm/IR/Instructions.h"
47*5f7ddb14SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
48*5f7ddb14SDimitry Andric #include "llvm/IR/IntrinsicsX86.h"
49*5f7ddb14SDimitry Andric #include "llvm/IR/PatternMatch.h"
50*5f7ddb14SDimitry Andric #include "llvm/InitializePasses.h"
51*5f7ddb14SDimitry Andric #include "llvm/Pass.h"
52*5f7ddb14SDimitry Andric #include "llvm/Support/raw_ostream.h"
53*5f7ddb14SDimitry Andric #include "llvm/Target/TargetMachine.h"
54*5f7ddb14SDimitry Andric
55*5f7ddb14SDimitry Andric using namespace llvm;
56*5f7ddb14SDimitry Andric using namespace PatternMatch;
57*5f7ddb14SDimitry Andric
58*5f7ddb14SDimitry Andric #define DEBUG_TYPE "pre-amx-config"
59*5f7ddb14SDimitry Andric
isAMXIntrinsic(IntrinsicInst * II)60*5f7ddb14SDimitry Andric static bool isAMXIntrinsic(IntrinsicInst *II) {
61*5f7ddb14SDimitry Andric for (Value *Operand : II->operands())
62*5f7ddb14SDimitry Andric if (Operand->getType()->isX86_AMXTy())
63*5f7ddb14SDimitry Andric return true;
64*5f7ddb14SDimitry Andric return II->getType()->isX86_AMXTy();
65*5f7ddb14SDimitry Andric }
66*5f7ddb14SDimitry Andric
isTileLoad(IntrinsicInst * II)67*5f7ddb14SDimitry Andric static bool isTileLoad(IntrinsicInst *II) {
68*5f7ddb14SDimitry Andric return II->getIntrinsicID() == Intrinsic::x86_tileloadd64_internal ||
69*5f7ddb14SDimitry Andric II->getIntrinsicID() == Intrinsic::x86_tileloaddt164_internal;
70*5f7ddb14SDimitry Andric }
71*5f7ddb14SDimitry Andric
isTileStore(IntrinsicInst * II)72*5f7ddb14SDimitry Andric static bool isTileStore(IntrinsicInst *II) {
73*5f7ddb14SDimitry Andric return II->getIntrinsicID() == Intrinsic::x86_tilestored64_internal;
74*5f7ddb14SDimitry Andric }
75*5f7ddb14SDimitry Andric
76*5f7ddb14SDimitry Andric #ifndef NDEBUG
onlyTileDef(IntrinsicInst * II)77*5f7ddb14SDimitry Andric static bool onlyTileDef(IntrinsicInst *II) {
78*5f7ddb14SDimitry Andric for (Value *Operand : II->operands())
79*5f7ddb14SDimitry Andric if (Operand->getType()->isX86_AMXTy())
80*5f7ddb14SDimitry Andric return false;
81*5f7ddb14SDimitry Andric return II->getType()->isX86_AMXTy();
82*5f7ddb14SDimitry Andric }
83*5f7ddb14SDimitry Andric
brokenVolatile(Instruction * I)84*5f7ddb14SDimitry Andric static bool brokenVolatile(Instruction *I) {
85*5f7ddb14SDimitry Andric // Todo: it is weak to identify a normal call here.
86*5f7ddb14SDimitry Andric if ((isa<CallInst>(I) && !isa<IntrinsicInst>(I)) || I->isTerminator())
87*5f7ddb14SDimitry Andric return true;
88*5f7ddb14SDimitry Andric return false;
89*5f7ddb14SDimitry Andric }
90*5f7ddb14SDimitry Andric #endif
91*5f7ddb14SDimitry Andric
92*5f7ddb14SDimitry Andric namespace {
93*5f7ddb14SDimitry Andric class X86PreAMXConfig {
94*5f7ddb14SDimitry Andric Function &F;
95*5f7ddb14SDimitry Andric
96*5f7ddb14SDimitry Andric public:
X86PreAMXConfig(Function & Func)97*5f7ddb14SDimitry Andric X86PreAMXConfig(Function &Func) : F(Func) {}
98*5f7ddb14SDimitry Andric bool preTileConfig();
99*5f7ddb14SDimitry Andric bool addTileConfig(Instruction *ModelStart, SmallVector<Value *, 8> &Shapes);
100*5f7ddb14SDimitry Andric bool findConfigShapes(
101*5f7ddb14SDimitry Andric DenseMap<Instruction *, SmallVector<Value *, 8>> &PosAndShapes);
102*5f7ddb14SDimitry Andric bool getKeyAMXShapes(IntrinsicInst *KeyAMX, SmallVector<Value *, 8> &Shapes);
103*5f7ddb14SDimitry Andric bool preWriteTileCfg(Value *I8Ptr, Instruction *Pos,
104*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes);
105*5f7ddb14SDimitry Andric BasicBlock::iterator
106*5f7ddb14SDimitry Andric getShapesAndConfigPosEnd(BasicBlock::iterator Iter,
107*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes);
108*5f7ddb14SDimitry Andric bool checkVolatileModel(SmallSet<Value *, 4> &Loads, IntrinsicInst *Store,
109*5f7ddb14SDimitry Andric IntrinsicInst *KeyAMX);
110*5f7ddb14SDimitry Andric };
111*5f7ddb14SDimitry Andric
112*5f7ddb14SDimitry Andric // Orderly write the shapes in tilecfg's mem. This maybe not right.
113*5f7ddb14SDimitry Andric // Because the first shape may not corresponding to the first tmm register,
114*5f7ddb14SDimitry Andric // so we need to handle at at X86FastTileConfig::materializeTileCfg()
115*5f7ddb14SDimitry Andric // after register allocation.
116*5f7ddb14SDimitry Andric // For example:
117*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
118*5f7ddb14SDimitry Andric // zeroinitialize tilecfg's mem (of ldtilecfg)
119*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
120*5f7ddb14SDimitry Andric // ... pre-config shape of %t1 *
121*5f7ddb14SDimitry Andric // %amx.tmm.0.shape.row = getelementptr i8, i8* %mem, i64 48 *
122*5f7ddb14SDimitry Andric // %amx.tmm.0.shape.col = getelementptr i16, i16* %mem, i64 16 *
123*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.0.shape.row, align 1 *
124*5f7ddb14SDimitry Andric // store volatile i16 %k, i16* %amx.tmm.0.shape.col, align 2 * pre-config
125*5f7ddb14SDimitry Andric // ... *
126*5f7ddb14SDimitry Andric // ... pre-config shape of %t2 *
127*5f7ddb14SDimitry Andric // %amx.tmm.1.shape.row = getelementptr i8, i8* %mem, i64 49 *
128*5f7ddb14SDimitry Andric // %amx.tmm.1.shape.col = getelementptr i16, i16* %mem, i64 18 *
129*5f7ddb14SDimitry Andric // store volatile i8 %k, i8* %amx.tmm.1.shape.row, align 1 * shapes
130*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.1.shape.col, align 2 *
131*5f7ddb14SDimitry Andric // ... *
132*5f7ddb14SDimitry Andric // ... pre-config shape of %t3 * of
133*5f7ddb14SDimitry Andric // %amx.tmm.2.shape.row = getelementptr i8, i8* %mem, i64 50 *
134*5f7ddb14SDimitry Andric // %amx.tmm.2.shape.col = getelementptr i16, i16* %mem, i64 20 *
135*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.2.shape.row, align 1 *
136*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.2.shape.col, align 2 *
137*5f7ddb14SDimitry Andric // ... * tiles
138*5f7ddb14SDimitry Andric // ... pre-config shape of %td *
139*5f7ddb14SDimitry Andric // %amx.tmm.3.shape.row = getelementptr i8, i8* %mem, i64 51 *
140*5f7ddb14SDimitry Andric // %amx.tmm.3.shape.col = getelementptr i16, i16* %mem, i64 22 *
141*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.3.shape.row, align 1 *
142*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.3.shape.col, align 2 *
143*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
144*5f7ddb14SDimitry Andric // call void @llvm.x86.ldtilecfg(i8* %mem) * tile config
145*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
146*5f7ddb14SDimitry Andric // %t1 = call x86_amx @llvm.x86.tileloadd64.internal(m, k, ...) key
147*5f7ddb14SDimitry Andric // %t2 = call x86_amx @llvm.x86.tileloadd64.internal(k, n, ...)
148*5f7ddb14SDimitry Andric // %t3 = call x86_amx @llvm.x86.tileloadd64.internal(m, n, ...) amx
149*5f7ddb14SDimitry Andric // %td = tail call x86_amx @llvm.x86.tdpbssd.internal(m, n, k, t1, t2, t3)
150*5f7ddb14SDimitry Andric // call void @llvm.x86.tilestored64.internal(... td) area
151*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
preWriteTileCfg(Value * I8Ptr,Instruction * Pos,SmallVector<Value *,8> & Shapes)152*5f7ddb14SDimitry Andric bool X86PreAMXConfig::preWriteTileCfg(Value *I8Ptr, Instruction *Pos,
153*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes) {
154*5f7ddb14SDimitry Andric bool Write = false;
155*5f7ddb14SDimitry Andric LLVMContext &Ctx = Pos->getParent()->getContext();
156*5f7ddb14SDimitry Andric Type *I8Ty = Type::getInt8Ty(Ctx);
157*5f7ddb14SDimitry Andric Type *I16Ty = Type::getInt16Ty(Ctx);
158*5f7ddb14SDimitry Andric
159*5f7ddb14SDimitry Andric // TODO: Currently we defaultly set Palette = 1, it may be assigned to
160*5f7ddb14SDimitry Andric // other value in the future.
161*5f7ddb14SDimitry Andric Value *PaletteOffset = ConstantInt::get(Type::getInt64Ty(Ctx), 0);
162*5f7ddb14SDimitry Andric Value *PaletteValue = ConstantInt::get(Type::getInt8Ty(Ctx), 1);
163*5f7ddb14SDimitry Andric Value *PalettePos =
164*5f7ddb14SDimitry Andric GetElementPtrInst::Create(I8Ty, I8Ptr, PaletteOffset, "", Pos);
165*5f7ddb14SDimitry Andric new StoreInst(PaletteValue, PalettePos, Pos);
166*5f7ddb14SDimitry Andric
167*5f7ddb14SDimitry Andric for (int I = 0, E = Shapes.size() / 2; I < E; I++) {
168*5f7ddb14SDimitry Andric Value *RowOffset = ConstantInt::get(Type::getInt64Ty(Ctx), 48 + I);
169*5f7ddb14SDimitry Andric Value *ColOffset = ConstantInt::get(Type::getInt64Ty(Ctx), 16 + I * 2);
170*5f7ddb14SDimitry Andric const std::string ShapeName = "amx.tmm." + itostr(I);
171*5f7ddb14SDimitry Andric Value *RowPos = GetElementPtrInst::Create(I8Ty, I8Ptr, RowOffset,
172*5f7ddb14SDimitry Andric ShapeName + ".shape.row", Pos);
173*5f7ddb14SDimitry Andric Value *ColPos = GetElementPtrInst::Create(I8Ty, I8Ptr, ColOffset, "", Pos);
174*5f7ddb14SDimitry Andric ColPos = new BitCastInst(ColPos, PointerType::get(I16Ty, 0),
175*5f7ddb14SDimitry Andric ShapeName + ".shape.col", Pos);
176*5f7ddb14SDimitry Andric Value *Row = Shapes[I * 2];
177*5f7ddb14SDimitry Andric Value *Col = Shapes[I * 2 + 1];
178*5f7ddb14SDimitry Andric Row = new TruncInst(Row, I8Ty, "", Pos);
179*5f7ddb14SDimitry Andric new StoreInst(Row, RowPos, Pos);
180*5f7ddb14SDimitry Andric new StoreInst(Col, ColPos, Pos);
181*5f7ddb14SDimitry Andric Write = true;
182*5f7ddb14SDimitry Andric }
183*5f7ddb14SDimitry Andric return Write;
184*5f7ddb14SDimitry Andric }
185*5f7ddb14SDimitry Andric
addTileConfig(Instruction * ModelStart,SmallVector<Value *,8> & Shapes)186*5f7ddb14SDimitry Andric bool X86PreAMXConfig::addTileConfig(Instruction *ModelStart,
187*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes) {
188*5f7ddb14SDimitry Andric Module *M = F.getParent();
189*5f7ddb14SDimitry Andric IRBuilder<> Builder(ModelStart);
190*5f7ddb14SDimitry Andric const DataLayout &DL = M->getDataLayout();
191*5f7ddb14SDimitry Andric unsigned AddrSpace = DL.getAllocaAddrSpace();
192*5f7ddb14SDimitry Andric LLVMContext &Ctx = Builder.getContext();
193*5f7ddb14SDimitry Andric Type *V512Ty = VectorType::get(Builder.getInt32Ty(), 16, false);
194*5f7ddb14SDimitry Andric Align Alignment = DL.getPrefTypeAlign(Type::getInt32Ty(Ctx));
195*5f7ddb14SDimitry Andric
196*5f7ddb14SDimitry Andric AllocaInst *Addr =
197*5f7ddb14SDimitry Andric new AllocaInst(V512Ty, AddrSpace, "", &F.getEntryBlock().front());
198*5f7ddb14SDimitry Andric Addr->setAlignment(Alignment);
199*5f7ddb14SDimitry Andric Value *I8Ptr = Builder.CreateBitCast(Addr, Builder.getInt8PtrTy());
200*5f7ddb14SDimitry Andric
201*5f7ddb14SDimitry Andric std::array<Value *, 1> Args = {I8Ptr};
202*5f7ddb14SDimitry Andric Instruction *Cfg =
203*5f7ddb14SDimitry Andric Builder.CreateIntrinsic(Intrinsic::x86_ldtilecfg_internal, None, Args);
204*5f7ddb14SDimitry Andric
205*5f7ddb14SDimitry Andric Value *Val0 = Constant::getNullValue(V512Ty);
206*5f7ddb14SDimitry Andric Instruction *Init0 = new StoreInst(Val0, Addr, false, Alignment, Cfg);
207*5f7ddb14SDimitry Andric assert(Init0 && "Not Zero initilizate the cfg mem!");
208*5f7ddb14SDimitry Andric
209*5f7ddb14SDimitry Andric preWriteTileCfg(I8Ptr, Cfg, Shapes);
210*5f7ddb14SDimitry Andric
211*5f7ddb14SDimitry Andric return Init0;
212*5f7ddb14SDimitry Andric }
213*5f7ddb14SDimitry Andric
214*5f7ddb14SDimitry Andric // Todo: We may need to handle "more than one store" case in the future.
checkVolatileModel(SmallSet<Value *,4> & Loads,IntrinsicInst * Store,IntrinsicInst * KeyAMX)215*5f7ddb14SDimitry Andric bool X86PreAMXConfig::checkVolatileModel(SmallSet<Value *, 4> &Loads,
216*5f7ddb14SDimitry Andric IntrinsicInst *Store,
217*5f7ddb14SDimitry Andric IntrinsicInst *KeyAMX) {
218*5f7ddb14SDimitry Andric Value *ST = Store->getOperand(4);
219*5f7ddb14SDimitry Andric
220*5f7ddb14SDimitry Andric // Only has tileload and tilestore.
221*5f7ddb14SDimitry Andric if (!KeyAMX)
222*5f7ddb14SDimitry Andric return (Loads.size() == 1) && Loads.contains(ST);
223*5f7ddb14SDimitry Andric
224*5f7ddb14SDimitry Andric // All Loads should be operands of KeyAMX.
225*5f7ddb14SDimitry Andric // All tile operands of KeyAMX should come from Loads.
226*5f7ddb14SDimitry Andric for (Value *Op : KeyAMX->operands()) {
227*5f7ddb14SDimitry Andric if (Op->getType()->isX86_AMXTy())
228*5f7ddb14SDimitry Andric if (!Loads.erase(Op))
229*5f7ddb14SDimitry Andric return false;
230*5f7ddb14SDimitry Andric }
231*5f7ddb14SDimitry Andric
232*5f7ddb14SDimitry Andric // The def of KeyAMX should be stored into mem.
233*5f7ddb14SDimitry Andric // Todo: is it key amx can be no def?
234*5f7ddb14SDimitry Andric return Loads.empty() && (ST == cast<Value>(KeyAMX));
235*5f7ddb14SDimitry Andric }
236*5f7ddb14SDimitry Andric
getKeyAMXShapes(IntrinsicInst * KeyAMX,SmallVector<Value *,8> & Shapes)237*5f7ddb14SDimitry Andric bool X86PreAMXConfig::getKeyAMXShapes(IntrinsicInst *KeyAMX,
238*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes) {
239*5f7ddb14SDimitry Andric for (unsigned I = 0; I < KeyAMX->getNumOperands(); I++) {
240*5f7ddb14SDimitry Andric Value *Op = KeyAMX->getOperand(I);
241*5f7ddb14SDimitry Andric if (!Op->getType()->isX86_AMXTy())
242*5f7ddb14SDimitry Andric continue;
243*5f7ddb14SDimitry Andric IntrinsicInst *TileDef = dyn_cast<IntrinsicInst>(Op);
244*5f7ddb14SDimitry Andric assert((TileDef && isTileLoad(TileDef)) &&
245*5f7ddb14SDimitry Andric "All KeyAMX's tile definiation should comes from TileLoad!");
246*5f7ddb14SDimitry Andric Shapes.push_back(TileDef->getOperand(0));
247*5f7ddb14SDimitry Andric Shapes.push_back(TileDef->getOperand(1));
248*5f7ddb14SDimitry Andric }
249*5f7ddb14SDimitry Andric if (!isTileStore(KeyAMX)) {
250*5f7ddb14SDimitry Andric Shapes.push_back(KeyAMX->getOperand(0));
251*5f7ddb14SDimitry Andric Shapes.push_back(KeyAMX->getOperand(1));
252*5f7ddb14SDimitry Andric }
253*5f7ddb14SDimitry Andric return Shapes.size() != 0;
254*5f7ddb14SDimitry Andric }
255*5f7ddb14SDimitry Andric
256*5f7ddb14SDimitry Andric // Collect the shapes and skip the area of current key amx intrinsic.
257*5f7ddb14SDimitry Andric //
258*5f7ddb14SDimitry Andric // For example:
259*5f7ddb14SDimitry Andric // ...
260*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
261*5f7ddb14SDimitry Andric // %t1 = call x86_amx @llvm.x86.tileloadd64.internal(m, k, ...) record (m,k)
262*5f7ddb14SDimitry Andric // %t2 = call x86_amx @llvm.x86.tileloadd64.internal(k, n, ...) record (m,k)
263*5f7ddb14SDimitry Andric // %t3 = call x86_amx @llvm.x86.tileloadd64.internal(m, n, ...) record (m,k)
264*5f7ddb14SDimitry Andric // %td = call x86_amx @llvm.x86.tdpbssd.internal(...t1, t2, t3)
265*5f7ddb14SDimitry Andric // call void @llvm.x86.tilestored64.internal(m, n,... td) <--PosEnd record (m,k)
266*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
267*5f7ddb14SDimitry Andric BasicBlock::iterator
getShapesAndConfigPosEnd(BasicBlock::iterator Iter,SmallVector<Value *,8> & Shapes)268*5f7ddb14SDimitry Andric X86PreAMXConfig::getShapesAndConfigPosEnd(BasicBlock::iterator Iter,
269*5f7ddb14SDimitry Andric SmallVector<Value *, 8> &Shapes) {
270*5f7ddb14SDimitry Andric IntrinsicInst *KeyAMX = nullptr;
271*5f7ddb14SDimitry Andric BasicBlock *BB = Iter->getParent();
272*5f7ddb14SDimitry Andric BasicBlock::iterator PosEnd = BB->end();
273*5f7ddb14SDimitry Andric SmallSet<Value *, 4> Loads;
274*5f7ddb14SDimitry Andric
275*5f7ddb14SDimitry Andric // See TileStore as "Config Position End" and check volatile model.
276*5f7ddb14SDimitry Andric for (auto I = Iter, E = BB->end(); I != E; ++I) {
277*5f7ddb14SDimitry Andric assert(!brokenVolatile(&*I) && "Not reach tile store!");
278*5f7ddb14SDimitry Andric IntrinsicInst *II = dyn_cast<IntrinsicInst>(&*I);
279*5f7ddb14SDimitry Andric if (!II || !isAMXIntrinsic(II))
280*5f7ddb14SDimitry Andric continue;
281*5f7ddb14SDimitry Andric
282*5f7ddb14SDimitry Andric if (isTileLoad(II)) {
283*5f7ddb14SDimitry Andric Loads.insert(II);
284*5f7ddb14SDimitry Andric } else if (isTileStore(II)) {
285*5f7ddb14SDimitry Andric if (!checkVolatileModel(Loads, II, KeyAMX))
286*5f7ddb14SDimitry Andric report_fatal_error("Not Volatile AMX Model!");
287*5f7ddb14SDimitry Andric PosEnd = I;
288*5f7ddb14SDimitry Andric break;
289*5f7ddb14SDimitry Andric } else {
290*5f7ddb14SDimitry Andric assert(!KeyAMX && "Too many key amx intrinsic!");
291*5f7ddb14SDimitry Andric KeyAMX = II;
292*5f7ddb14SDimitry Andric }
293*5f7ddb14SDimitry Andric }
294*5f7ddb14SDimitry Andric assert(PosEnd != BB->end() && "Not find TileStore!");
295*5f7ddb14SDimitry Andric
296*5f7ddb14SDimitry Andric // See KeyAMX as TileStore if only TileLoad and TileStore.
297*5f7ddb14SDimitry Andric if (!KeyAMX)
298*5f7ddb14SDimitry Andric KeyAMX = dyn_cast<IntrinsicInst>(&*PosEnd);
299*5f7ddb14SDimitry Andric
300*5f7ddb14SDimitry Andric // Get Shapes in order.
301*5f7ddb14SDimitry Andric assert(Shapes.empty() && "Shapes should be clean.");
302*5f7ddb14SDimitry Andric getKeyAMXShapes(KeyAMX, Shapes);
303*5f7ddb14SDimitry Andric
304*5f7ddb14SDimitry Andric return PosEnd;
305*5f7ddb14SDimitry Andric }
306*5f7ddb14SDimitry Andric
307*5f7ddb14SDimitry Andric // Record a key amx area's shapes with its position.
308*5f7ddb14SDimitry Andric // Use the first tileload as its position.
309*5f7ddb14SDimitry Andric // For example:
310*5f7ddb14SDimitry Andric // ...
311*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
312*5f7ddb14SDimitry Andric // %t1 = call x86_amx @llvm.x86.tileloadd64.internal(m, k, ...) <-- pos
313*5f7ddb14SDimitry Andric // %t2 = call x86_amx @llvm.x86.tileloadd64.internal(k, n, ...) /
314*5f7ddb14SDimitry Andric // %t3 = call x86_amx @llvm.x86.tileloadd64.internal(m, n, ...) shapes:
315*5f7ddb14SDimitry Andric // %td = call x86_amx @llvm.x86.tdpbssd.internal(...t1, t2, t3) (m,k)(k,n)
316*5f7ddb14SDimitry Andric // call void @llvm.x86.tilestored64.internal(m, n,... td) (m,n)(m,n)
317*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
findConfigShapes(DenseMap<Instruction *,SmallVector<Value *,8>> & PosAndShapes)318*5f7ddb14SDimitry Andric bool X86PreAMXConfig::findConfigShapes(
319*5f7ddb14SDimitry Andric DenseMap<Instruction *, SmallVector<Value *, 8>> &PosAndShapes) {
320*5f7ddb14SDimitry Andric bool Find = false;
321*5f7ddb14SDimitry Andric for (BasicBlock &BB : F) {
322*5f7ddb14SDimitry Andric for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I) {
323*5f7ddb14SDimitry Andric IntrinsicInst *II = dyn_cast<IntrinsicInst>(&*I);
324*5f7ddb14SDimitry Andric if (!II)
325*5f7ddb14SDimitry Andric continue;
326*5f7ddb14SDimitry Andric if (!isAMXIntrinsic(II))
327*5f7ddb14SDimitry Andric continue;
328*5f7ddb14SDimitry Andric assert(onlyTileDef(II) && "Not volatile model for AMX at O0!");
329*5f7ddb14SDimitry Andric
330*5f7ddb14SDimitry Andric I = getShapesAndConfigPosEnd(I, PosAndShapes[&*I]);
331*5f7ddb14SDimitry Andric Find = true;
332*5f7ddb14SDimitry Andric }
333*5f7ddb14SDimitry Andric }
334*5f7ddb14SDimitry Andric return Find;
335*5f7ddb14SDimitry Andric }
336*5f7ddb14SDimitry Andric
337*5f7ddb14SDimitry Andric // Insert ldtilecfg and preconfig the shapes for each area of key AMX intrinsic.
338*5f7ddb14SDimitry Andric // e.g. (key amx = tdpbssd)
339*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
340*5f7ddb14SDimitry Andric // %cfgmem = alloca <16 x i32>, align 4 * allocate mem
341*5f7ddb14SDimitry Andric // store <16 x i32> zeroinitializer, <16 x i32>* %cfgmem * zero init
342*5f7ddb14SDimitry Andric // ...
343*5f7ddb14SDimitry Andric // ... pre-config shape of %t1 *
344*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.0.shape.row, align 1 *
345*5f7ddb14SDimitry Andric // store volatile i16 %k, i16* %amx.tmm.0.shape.col, align 2 * pre-config
346*5f7ddb14SDimitry Andric // ... *
347*5f7ddb14SDimitry Andric // ... pre-config shape of %t2 *
348*5f7ddb14SDimitry Andric // store volatile i8 %k, i8* %amx.tmm.1.shape.row, align 1 * shapes
349*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.1.shape.col, align 2 *
350*5f7ddb14SDimitry Andric // ... *
351*5f7ddb14SDimitry Andric // ... pre-config shape of %t3 * of
352*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.2.shape.row, align 1 *
353*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.2.shape.col, align 2 *
354*5f7ddb14SDimitry Andric // ... * tiles
355*5f7ddb14SDimitry Andric // ... pre-config shape of %td *
356*5f7ddb14SDimitry Andric // store volatile i8 %m, i8* %amx.tmm.3.shape.row, align 1 *
357*5f7ddb14SDimitry Andric // store volatile i16 %n, i16* %amx.tmm.3.shape.col, align 2 *
358*5f7ddb14SDimitry Andric //
359*5f7ddb14SDimitry Andric // call void @llvm.x86.ldtilecfg(i8* %cfgmem) * pre-config
360*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
361*5f7ddb14SDimitry Andric // %t1 = call x86_amx @llvm.x86.tileloadd64.internal(m, k, ...) key
362*5f7ddb14SDimitry Andric // %t2 = call x86_amx @llvm.x86.tileloadd64.internal(k, n, ...)
363*5f7ddb14SDimitry Andric // %t3 = call x86_amx @llvm.x86.tileloadd64.internal(m, n, ...) amx
364*5f7ddb14SDimitry Andric // %td = tail call x86_amx @llvm.x86.tdpbssd.internal(m, n, k, t1, t2, t3)
365*5f7ddb14SDimitry Andric // call void @llvm.x86.tilestored64.internal(... td) area
366*5f7ddb14SDimitry Andric // --------------------------------------------------------------------------
preTileConfig()367*5f7ddb14SDimitry Andric bool X86PreAMXConfig::preTileConfig() {
368*5f7ddb14SDimitry Andric DenseMap<Instruction *, SmallVector<Value *, 8>> PosAndShapes;
369*5f7ddb14SDimitry Andric bool NeedCfg = findConfigShapes(PosAndShapes);
370*5f7ddb14SDimitry Andric if (!NeedCfg)
371*5f7ddb14SDimitry Andric return false;
372*5f7ddb14SDimitry Andric for (auto &IPAndShapes : PosAndShapes)
373*5f7ddb14SDimitry Andric addTileConfig(IPAndShapes.first, IPAndShapes.second);
374*5f7ddb14SDimitry Andric
375*5f7ddb14SDimitry Andric return true;
376*5f7ddb14SDimitry Andric }
377*5f7ddb14SDimitry Andric } // anonymous namespace
378*5f7ddb14SDimitry Andric
379*5f7ddb14SDimitry Andric namespace {
380*5f7ddb14SDimitry Andric
381*5f7ddb14SDimitry Andric class X86PreAMXConfigPass : public FunctionPass {
382*5f7ddb14SDimitry Andric public:
383*5f7ddb14SDimitry Andric static char ID;
384*5f7ddb14SDimitry Andric
X86PreAMXConfigPass()385*5f7ddb14SDimitry Andric X86PreAMXConfigPass() : FunctionPass(ID) {
386*5f7ddb14SDimitry Andric initializeX86PreAMXConfigPassPass(*PassRegistry::getPassRegistry());
387*5f7ddb14SDimitry Andric }
388*5f7ddb14SDimitry Andric
runOnFunction(Function & F)389*5f7ddb14SDimitry Andric bool runOnFunction(Function &F) override {
390*5f7ddb14SDimitry Andric TargetMachine *TM = &getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
391*5f7ddb14SDimitry Andric bool C = false;
392*5f7ddb14SDimitry Andric
393*5f7ddb14SDimitry Andric // Prepare for fast register allocation at O0.
394*5f7ddb14SDimitry Andric if (TM->getOptLevel() == CodeGenOpt::None) {
395*5f7ddb14SDimitry Andric
396*5f7ddb14SDimitry Andric // We pre-config each key AMX intrinsic at O0.
397*5f7ddb14SDimitry Andric // In theory, one tile config can cover several AMX intrinsics, but
398*5f7ddb14SDimitry Andric // it is very diffcult to classify the tile shapes at O0. So here we
399*5f7ddb14SDimitry Andric // let thing be easy, pre-config every key AMX intrinsic.
400*5f7ddb14SDimitry Andric X86PreAMXConfig PCFG(F);
401*5f7ddb14SDimitry Andric C = PCFG.preTileConfig();
402*5f7ddb14SDimitry Andric }
403*5f7ddb14SDimitry Andric
404*5f7ddb14SDimitry Andric return C;
405*5f7ddb14SDimitry Andric }
406*5f7ddb14SDimitry Andric
getAnalysisUsage(AnalysisUsage & AU) const407*5f7ddb14SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
408*5f7ddb14SDimitry Andric AU.setPreservesCFG();
409*5f7ddb14SDimitry Andric AU.addRequired<TargetPassConfig>();
410*5f7ddb14SDimitry Andric }
411*5f7ddb14SDimitry Andric };
412*5f7ddb14SDimitry Andric
413*5f7ddb14SDimitry Andric } // anonymous namespace
414*5f7ddb14SDimitry Andric
415*5f7ddb14SDimitry Andric static const char PassName[] = "Pre AMX Tile Config";
416*5f7ddb14SDimitry Andric char X86PreAMXConfigPass::ID = 0;
INITIALIZE_PASS_BEGIN(X86PreAMXConfigPass,DEBUG_TYPE,PassName,false,false)417*5f7ddb14SDimitry Andric INITIALIZE_PASS_BEGIN(X86PreAMXConfigPass, DEBUG_TYPE, PassName, false, false)
418*5f7ddb14SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
419*5f7ddb14SDimitry Andric INITIALIZE_PASS_END(X86PreAMXConfigPass, DEBUG_TYPE, PassName, false, false)
420*5f7ddb14SDimitry Andric
421*5f7ddb14SDimitry Andric FunctionPass *llvm::createX86PreAMXConfigPass() {
422*5f7ddb14SDimitry Andric return new X86PreAMXConfigPass();
423*5f7ddb14SDimitry Andric }
424