xref: /llvm-project/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp (revision 978de2d6664a74864471d62244700c216fdc6741)
1540d2551SVyacheslav Levytskyy //===-- SPIRVPostLegalizer.cpp - ammend info after legalization -*- C++ -*-===//
2540d2551SVyacheslav Levytskyy //
3540d2551SVyacheslav Levytskyy // which may appear after the legalizer pass
4540d2551SVyacheslav Levytskyy //
5540d2551SVyacheslav Levytskyy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6540d2551SVyacheslav Levytskyy // See https://llvm.org/LICENSE.txt for license information.
7540d2551SVyacheslav Levytskyy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8540d2551SVyacheslav Levytskyy //
9540d2551SVyacheslav Levytskyy //===----------------------------------------------------------------------===//
10540d2551SVyacheslav Levytskyy //
11540d2551SVyacheslav Levytskyy // The pass partially apply pre-legalization logic to new instructions inserted
12540d2551SVyacheslav Levytskyy // as a result of legalization:
13540d2551SVyacheslav Levytskyy // - assigns SPIR-V types to registers for new instructions.
14540d2551SVyacheslav Levytskyy //
15540d2551SVyacheslav Levytskyy //===----------------------------------------------------------------------===//
16540d2551SVyacheslav Levytskyy 
17540d2551SVyacheslav Levytskyy #include "SPIRV.h"
18540d2551SVyacheslav Levytskyy #include "SPIRVSubtarget.h"
19540d2551SVyacheslav Levytskyy #include "SPIRVUtils.h"
20540d2551SVyacheslav Levytskyy #include "llvm/ADT/PostOrderIterator.h"
21540d2551SVyacheslav Levytskyy #include "llvm/Analysis/OptimizationRemarkEmitter.h"
220c07e7c2SNathan Gauër #include "llvm/CodeGen/MachinePostDominators.h"
23540d2551SVyacheslav Levytskyy #include "llvm/IR/Attributes.h"
24540d2551SVyacheslav Levytskyy #include "llvm/IR/Constants.h"
25540d2551SVyacheslav Levytskyy #include "llvm/IR/DebugInfoMetadata.h"
26540d2551SVyacheslav Levytskyy #include "llvm/IR/IntrinsicsSPIRV.h"
27540d2551SVyacheslav Levytskyy #include "llvm/Target/TargetIntrinsicInfo.h"
280c07e7c2SNathan Gauër #include <stack>
29540d2551SVyacheslav Levytskyy 
30540d2551SVyacheslav Levytskyy #define DEBUG_TYPE "spirv-postlegalizer"
31540d2551SVyacheslav Levytskyy 
32540d2551SVyacheslav Levytskyy using namespace llvm;
33540d2551SVyacheslav Levytskyy 
34540d2551SVyacheslav Levytskyy namespace {
35540d2551SVyacheslav Levytskyy class SPIRVPostLegalizer : public MachineFunctionPass {
36540d2551SVyacheslav Levytskyy public:
37540d2551SVyacheslav Levytskyy   static char ID;
38540d2551SVyacheslav Levytskyy   SPIRVPostLegalizer() : MachineFunctionPass(ID) {
39540d2551SVyacheslav Levytskyy     initializeSPIRVPostLegalizerPass(*PassRegistry::getPassRegistry());
40540d2551SVyacheslav Levytskyy   }
41540d2551SVyacheslav Levytskyy   bool runOnMachineFunction(MachineFunction &MF) override;
42540d2551SVyacheslav Levytskyy };
43540d2551SVyacheslav Levytskyy } // namespace
44540d2551SVyacheslav Levytskyy 
45540d2551SVyacheslav Levytskyy // Defined in SPIRVLegalizerInfo.cpp.
46540d2551SVyacheslav Levytskyy extern bool isTypeFoldingSupported(unsigned Opcode);
47540d2551SVyacheslav Levytskyy 
48540d2551SVyacheslav Levytskyy namespace llvm {
49540d2551SVyacheslav Levytskyy //  Defined in SPIRVPreLegalizer.cpp.
50540d2551SVyacheslav Levytskyy extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
51540d2551SVyacheslav Levytskyy                                   SPIRVGlobalRegistry *GR,
52540d2551SVyacheslav Levytskyy                                   MachineIRBuilder &MIB,
53540d2551SVyacheslav Levytskyy                                   MachineRegisterInfo &MRI);
54540d2551SVyacheslav Levytskyy extern void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
55540d2551SVyacheslav Levytskyy                          MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR);
56540d2551SVyacheslav Levytskyy } // namespace llvm
57540d2551SVyacheslav Levytskyy 
58540d2551SVyacheslav Levytskyy static bool mayBeInserted(unsigned Opcode) {
59540d2551SVyacheslav Levytskyy   switch (Opcode) {
60540d2551SVyacheslav Levytskyy   case TargetOpcode::G_SMAX:
61540d2551SVyacheslav Levytskyy   case TargetOpcode::G_UMAX:
62540d2551SVyacheslav Levytskyy   case TargetOpcode::G_SMIN:
63540d2551SVyacheslav Levytskyy   case TargetOpcode::G_UMIN:
64540d2551SVyacheslav Levytskyy   case TargetOpcode::G_FMINNUM:
65540d2551SVyacheslav Levytskyy   case TargetOpcode::G_FMINIMUM:
66540d2551SVyacheslav Levytskyy   case TargetOpcode::G_FMAXNUM:
67540d2551SVyacheslav Levytskyy   case TargetOpcode::G_FMAXIMUM:
68540d2551SVyacheslav Levytskyy     return true;
69540d2551SVyacheslav Levytskyy   default:
70540d2551SVyacheslav Levytskyy     return isTypeFoldingSupported(Opcode);
71540d2551SVyacheslav Levytskyy   }
72540d2551SVyacheslav Levytskyy }
73540d2551SVyacheslav Levytskyy 
74540d2551SVyacheslav Levytskyy static void processNewInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
75540d2551SVyacheslav Levytskyy                              MachineIRBuilder MIB) {
76540d2551SVyacheslav Levytskyy   MachineRegisterInfo &MRI = MF.getRegInfo();
77540d2551SVyacheslav Levytskyy 
78540d2551SVyacheslav Levytskyy   for (MachineBasicBlock &MBB : MF) {
79540d2551SVyacheslav Levytskyy     for (MachineInstr &I : MBB) {
80540d2551SVyacheslav Levytskyy       const unsigned Opcode = I.getOpcode();
81540d2551SVyacheslav Levytskyy       if (Opcode == TargetOpcode::G_UNMERGE_VALUES) {
82540d2551SVyacheslav Levytskyy         unsigned ArgI = I.getNumOperands() - 1;
83540d2551SVyacheslav Levytskyy         Register SrcReg = I.getOperand(ArgI).isReg()
84540d2551SVyacheslav Levytskyy                               ? I.getOperand(ArgI).getReg()
85540d2551SVyacheslav Levytskyy                               : Register(0);
86540d2551SVyacheslav Levytskyy         SPIRVType *DefType =
87540d2551SVyacheslav Levytskyy             SrcReg.isValid() ? GR->getSPIRVTypeForVReg(SrcReg) : nullptr;
88540d2551SVyacheslav Levytskyy         if (!DefType || DefType->getOpcode() != SPIRV::OpTypeVector)
89540d2551SVyacheslav Levytskyy           report_fatal_error(
90540d2551SVyacheslav Levytskyy               "cannot select G_UNMERGE_VALUES with a non-vector argument");
91540d2551SVyacheslav Levytskyy         SPIRVType *ScalarType =
92540d2551SVyacheslav Levytskyy             GR->getSPIRVTypeForVReg(DefType->getOperand(1).getReg());
93540d2551SVyacheslav Levytskyy         for (unsigned i = 0; i < I.getNumDefs(); ++i) {
94540d2551SVyacheslav Levytskyy           Register ResVReg = I.getOperand(i).getReg();
95540d2551SVyacheslav Levytskyy           SPIRVType *ResType = GR->getSPIRVTypeForVReg(ResVReg);
96540d2551SVyacheslav Levytskyy           if (!ResType) {
97540d2551SVyacheslav Levytskyy             // There was no "assign type" actions, let's fix this now
98540d2551SVyacheslav Levytskyy             ResType = ScalarType;
99b5132b7dSVyacheslav Levytskyy             setRegClassType(ResVReg, ResType, GR, &MRI, *GR->CurMF, true);
100540d2551SVyacheslav Levytskyy           }
101540d2551SVyacheslav Levytskyy         }
102540d2551SVyacheslav Levytskyy       } else if (mayBeInserted(Opcode) && I.getNumDefs() == 1 &&
103540d2551SVyacheslav Levytskyy                  I.getNumOperands() > 1 && I.getOperand(1).isReg()) {
104540d2551SVyacheslav Levytskyy         // Legalizer may have added a new instructions and introduced new
105540d2551SVyacheslav Levytskyy         // registers, we must decorate them as if they were introduced in a
106540d2551SVyacheslav Levytskyy         // non-automatic way
107540d2551SVyacheslav Levytskyy         Register ResVReg = I.getOperand(0).getReg();
108540d2551SVyacheslav Levytskyy         // Check if the register defined by the instruction is newly generated
109540d2551SVyacheslav Levytskyy         // or already processed
110*978de2d6SVyacheslav Levytskyy         if (MRI.getRegClassOrNull(ResVReg))
111*978de2d6SVyacheslav Levytskyy           continue;
112*978de2d6SVyacheslav Levytskyy         assert(GR->getSPIRVTypeForVReg(ResVReg) == nullptr);
113540d2551SVyacheslav Levytskyy         // Check if we have type defined for operands of the new instruction
114*978de2d6SVyacheslav Levytskyy         SPIRVType *ResVType = GR->getSPIRVTypeForVReg(I.getOperand(1).getReg());
115540d2551SVyacheslav Levytskyy         if (!ResVType)
116540d2551SVyacheslav Levytskyy           continue;
117540d2551SVyacheslav Levytskyy         // Set type & class
118b5132b7dSVyacheslav Levytskyy         setRegClassType(ResVReg, ResVType, GR, &MRI, *GR->CurMF, true);
119540d2551SVyacheslav Levytskyy         // If this is a simple operation that is to be reduced by TableGen
120540d2551SVyacheslav Levytskyy         // definition we must apply some of pre-legalizer rules here
121540d2551SVyacheslav Levytskyy         if (isTypeFoldingSupported(Opcode)) {
122540d2551SVyacheslav Levytskyy           insertAssignInstr(ResVReg, nullptr, ResVType, GR, MIB, MRI);
123540d2551SVyacheslav Levytskyy           processInstr(I, MIB, MRI, GR);
124540d2551SVyacheslav Levytskyy         }
125540d2551SVyacheslav Levytskyy       }
126540d2551SVyacheslav Levytskyy     }
127540d2551SVyacheslav Levytskyy   }
128540d2551SVyacheslav Levytskyy }
129540d2551SVyacheslav Levytskyy 
1300c07e7c2SNathan Gauër // Do a preorder traversal of the CFG starting from the BB |Start|.
1310c07e7c2SNathan Gauër // point. Calls |op| on each basic block encountered during the traversal.
1320c07e7c2SNathan Gauër void visit(MachineFunction &MF, MachineBasicBlock &Start,
1330c07e7c2SNathan Gauër            std::function<void(MachineBasicBlock *)> op) {
1340c07e7c2SNathan Gauër   std::stack<MachineBasicBlock *> ToVisit;
1350c07e7c2SNathan Gauër   SmallPtrSet<MachineBasicBlock *, 8> Seen;
1360c07e7c2SNathan Gauër 
1370c07e7c2SNathan Gauër   ToVisit.push(&Start);
1380c07e7c2SNathan Gauër   Seen.insert(ToVisit.top());
1390c07e7c2SNathan Gauër   while (ToVisit.size() != 0) {
1400c07e7c2SNathan Gauër     MachineBasicBlock *MBB = ToVisit.top();
1410c07e7c2SNathan Gauër     ToVisit.pop();
1420c07e7c2SNathan Gauër 
1430c07e7c2SNathan Gauër     op(MBB);
1440c07e7c2SNathan Gauër 
1450c07e7c2SNathan Gauër     for (auto Succ : MBB->successors()) {
1460c07e7c2SNathan Gauër       if (Seen.contains(Succ))
1470c07e7c2SNathan Gauër         continue;
1480c07e7c2SNathan Gauër       ToVisit.push(Succ);
1490c07e7c2SNathan Gauër       Seen.insert(Succ);
1500c07e7c2SNathan Gauër     }
1510c07e7c2SNathan Gauër   }
1520c07e7c2SNathan Gauër }
1530c07e7c2SNathan Gauër 
1540c07e7c2SNathan Gauër // Do a preorder traversal of the CFG starting from the given function's entry
1550c07e7c2SNathan Gauër // point. Calls |op| on each basic block encountered during the traversal.
1560c07e7c2SNathan Gauër void visit(MachineFunction &MF, std::function<void(MachineBasicBlock *)> op) {
1570c07e7c2SNathan Gauër   visit(MF, *MF.begin(), op);
1580c07e7c2SNathan Gauër }
1590c07e7c2SNathan Gauër 
160540d2551SVyacheslav Levytskyy bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
161540d2551SVyacheslav Levytskyy   // Initialize the type registry.
162540d2551SVyacheslav Levytskyy   const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
163540d2551SVyacheslav Levytskyy   SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry();
164540d2551SVyacheslav Levytskyy   GR->setCurrentFunc(MF);
165540d2551SVyacheslav Levytskyy   MachineIRBuilder MIB(MF);
166540d2551SVyacheslav Levytskyy 
167540d2551SVyacheslav Levytskyy   processNewInstrs(MF, GR, MIB);
168540d2551SVyacheslav Levytskyy 
169540d2551SVyacheslav Levytskyy   return true;
170540d2551SVyacheslav Levytskyy }
171540d2551SVyacheslav Levytskyy 
172540d2551SVyacheslav Levytskyy INITIALIZE_PASS(SPIRVPostLegalizer, DEBUG_TYPE, "SPIRV post legalizer", false,
173540d2551SVyacheslav Levytskyy                 false)
174540d2551SVyacheslav Levytskyy 
175540d2551SVyacheslav Levytskyy char SPIRVPostLegalizer::ID = 0;
176540d2551SVyacheslav Levytskyy 
177540d2551SVyacheslav Levytskyy FunctionPass *llvm::createSPIRVPostLegalizerPass() {
178540d2551SVyacheslav Levytskyy   return new SPIRVPostLegalizer();
179540d2551SVyacheslav Levytskyy }
180