407 lines · cpp
1//===-- SPIRVPostLegalizer.cpp - ammend info after legalization -*- C++ -*-===//2//3// which may appear after the legalizer pass4//5// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.6// See https://llvm.org/LICENSE.txt for license information.7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception8//9//===----------------------------------------------------------------------===//10//11// The pass partially apply pre-legalization logic to new instructions inserted12// as a result of legalization:13// - assigns SPIR-V types to registers for new instructions.14//15//===----------------------------------------------------------------------===//16 17#include "SPIRV.h"18#include "SPIRVSubtarget.h"19#include "SPIRVUtils.h"20#include "llvm/IR/IntrinsicsSPIRV.h"21#include "llvm/Support/Debug.h"22#include <stack>23 24#define DEBUG_TYPE "spirv-postlegalizer"25 26using namespace llvm;27 28namespace {29class SPIRVPostLegalizer : public MachineFunctionPass {30public:31 static char ID;32 SPIRVPostLegalizer() : MachineFunctionPass(ID) {}33 bool runOnMachineFunction(MachineFunction &MF) override;34};35} // namespace36 37namespace llvm {38// Defined in SPIRVPreLegalizer.cpp.39extern void insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,40 SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB,41 MachineRegisterInfo &MRI);42extern void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,43 MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR,44 SPIRVType *KnownResType);45} // namespace llvm46 47static SPIRVType *deduceIntTypeFromResult(Register ResVReg,48 MachineIRBuilder &MIB,49 SPIRVGlobalRegistry *GR) {50 const LLT &Ty = MIB.getMRI()->getType(ResVReg);51 return GR->getOrCreateSPIRVIntegerType(Ty.getScalarSizeInBits(), MIB);52}53 54static bool deduceAndAssignTypeForGUnmerge(MachineInstr *I, MachineFunction &MF,55 SPIRVGlobalRegistry *GR) {56 MachineRegisterInfo &MRI = MF.getRegInfo();57 Register SrcReg = I->getOperand(I->getNumOperands() - 1).getReg();58 SPIRVType *ScalarType = nullptr;59 if (SPIRVType *DefType = GR->getSPIRVTypeForVReg(SrcReg)) {60 assert(DefType->getOpcode() == SPIRV::OpTypeVector);61 ScalarType = GR->getSPIRVTypeForVReg(DefType->getOperand(1).getReg());62 }63 64 if (!ScalarType) {65 // If we could not deduce the type from the source, try to deduce it from66 // the uses of the results.67 for (unsigned i = 0; i < I->getNumDefs() && !ScalarType; ++i) {68 for (const auto &Use :69 MRI.use_nodbg_instructions(I->getOperand(i).getReg())) {70 assert(Use.getOpcode() == TargetOpcode::G_BUILD_VECTOR &&71 "Expected use of G_UNMERGE_VALUES to be a G_BUILD_VECTOR");72 if (auto *VecType =73 GR->getSPIRVTypeForVReg(Use.getOperand(0).getReg())) {74 ScalarType = GR->getScalarOrVectorComponentType(VecType);75 break;76 }77 }78 }79 }80 81 if (!ScalarType)82 return false;83 84 for (unsigned i = 0; i < I->getNumDefs(); ++i) {85 Register DefReg = I->getOperand(i).getReg();86 if (GR->getSPIRVTypeForVReg(DefReg))87 continue;88 89 LLT DefLLT = MRI.getType(DefReg);90 SPIRVType *ResType =91 DefLLT.isVector()92 ? GR->getOrCreateSPIRVVectorType(93 ScalarType, DefLLT.getNumElements(), *I,94 *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo())95 : ScalarType;96 setRegClassType(DefReg, ResType, GR, &MRI, MF);97 }98 return true;99}100 101static SPIRVType *deduceTypeFromSingleOperand(MachineInstr *I,102 MachineIRBuilder &MIB,103 SPIRVGlobalRegistry *GR,104 unsigned OpIdx) {105 Register OpReg = I->getOperand(OpIdx).getReg();106 if (SPIRVType *OpType = GR->getSPIRVTypeForVReg(OpReg)) {107 if (SPIRVType *CompType = GR->getScalarOrVectorComponentType(OpType)) {108 Register ResVReg = I->getOperand(0).getReg();109 const LLT &ResLLT = MIB.getMRI()->getType(ResVReg);110 if (ResLLT.isVector())111 return GR->getOrCreateSPIRVVectorType(CompType, ResLLT.getNumElements(),112 MIB, false);113 return CompType;114 }115 }116 return nullptr;117}118 119static SPIRVType *deduceTypeFromOperandRange(MachineInstr *I,120 MachineIRBuilder &MIB,121 SPIRVGlobalRegistry *GR,122 unsigned StartOp, unsigned EndOp) {123 SPIRVType *ResType = nullptr;124 for (unsigned i = StartOp; i < EndOp; ++i) {125 if (SPIRVType *Type = deduceTypeFromSingleOperand(I, MIB, GR, i)) {126#ifdef EXPENSIVE_CHECKS127 assert(!ResType || Type == ResType && "Conflicting type from operands.");128 ResType = Type;129#else130 return Type;131#endif132 }133 }134 return ResType;135}136 137static SPIRVType *deduceTypeForResultRegister(MachineInstr *Use,138 Register UseRegister,139 SPIRVGlobalRegistry *GR,140 MachineIRBuilder &MIB) {141 for (const MachineOperand &MO : Use->defs()) {142 if (!MO.isReg())143 continue;144 if (SPIRVType *OpType = GR->getSPIRVTypeForVReg(MO.getReg())) {145 if (SPIRVType *CompType = GR->getScalarOrVectorComponentType(OpType)) {146 const LLT &ResLLT = MIB.getMRI()->getType(UseRegister);147 if (ResLLT.isVector())148 return GR->getOrCreateSPIRVVectorType(149 CompType, ResLLT.getNumElements(), MIB, false);150 return CompType;151 }152 }153 }154 return nullptr;155}156 157static SPIRVType *deduceTypeFromUses(Register Reg, MachineFunction &MF,158 SPIRVGlobalRegistry *GR,159 MachineIRBuilder &MIB) {160 MachineRegisterInfo &MRI = MF.getRegInfo();161 for (MachineInstr &Use : MRI.use_nodbg_instructions(Reg)) {162 SPIRVType *ResType = nullptr;163 switch (Use.getOpcode()) {164 case TargetOpcode::G_BUILD_VECTOR:165 case TargetOpcode::G_EXTRACT_VECTOR_ELT:166 case TargetOpcode::G_UNMERGE_VALUES:167 LLVM_DEBUG(dbgs() << "Looking at use " << Use << "\n");168 ResType = deduceTypeForResultRegister(&Use, Reg, GR, MIB);169 break;170 }171 if (ResType)172 return ResType;173 }174 return nullptr;175}176 177static SPIRVType *deduceResultTypeFromOperands(MachineInstr *I,178 SPIRVGlobalRegistry *GR,179 MachineIRBuilder &MIB) {180 Register ResVReg = I->getOperand(0).getReg();181 switch (I->getOpcode()) {182 case TargetOpcode::G_CONSTANT:183 case TargetOpcode::G_ANYEXT:184 return deduceIntTypeFromResult(ResVReg, MIB, GR);185 case TargetOpcode::G_BUILD_VECTOR:186 return deduceTypeFromOperandRange(I, MIB, GR, 1, I->getNumOperands());187 case TargetOpcode::G_SHUFFLE_VECTOR:188 return deduceTypeFromOperandRange(I, MIB, GR, 1, 3);189 default:190 if (I->getNumDefs() == 1 && I->getNumOperands() > 1 &&191 I->getOperand(1).isReg())192 return deduceTypeFromSingleOperand(I, MIB, GR, 1);193 return nullptr;194 }195}196 197static bool deduceAndAssignSpirvType(MachineInstr *I, MachineFunction &MF,198 SPIRVGlobalRegistry *GR,199 MachineIRBuilder &MIB) {200 LLVM_DEBUG(dbgs() << "\nProcessing instruction: " << *I);201 MachineRegisterInfo &MRI = MF.getRegInfo();202 Register ResVReg = I->getOperand(0).getReg();203 204 // G_UNMERGE_VALUES is handled separately because it has multiple definitions,205 // unlike the other instructions which have a single result register. The main206 // deduction logic is designed for the single-definition case.207 if (I->getOpcode() == TargetOpcode::G_UNMERGE_VALUES)208 return deduceAndAssignTypeForGUnmerge(I, MF, GR);209 210 LLVM_DEBUG(dbgs() << "Inferring type from operands\n");211 SPIRVType *ResType = deduceResultTypeFromOperands(I, GR, MIB);212 if (!ResType) {213 LLVM_DEBUG(dbgs() << "Inferring type from uses\n");214 ResType = deduceTypeFromUses(ResVReg, MF, GR, MIB);215 }216 217 if (!ResType)218 return false;219 220 LLVM_DEBUG(dbgs() << "Assigned type to " << *I << ": " << *ResType);221 GR->assignSPIRVTypeToVReg(ResType, ResVReg, MF);222 223 if (!MRI.getRegClassOrNull(ResVReg)) {224 LLVM_DEBUG(dbgs() << "Updating the register class.\n");225 setRegClassType(ResVReg, ResType, GR, &MRI, *GR->CurMF, true);226 }227 return true;228}229 230static bool requiresSpirvType(MachineInstr &I, SPIRVGlobalRegistry *GR,231 MachineRegisterInfo &MRI) {232 LLVM_DEBUG(dbgs() << "Checking if instruction requires a SPIR-V type: "233 << I;);234 if (I.getNumDefs() == 0) {235 LLVM_DEBUG(dbgs() << "Instruction does not have a definition.\n");236 return false;237 }238 239 if (!I.isPreISelOpcode()) {240 LLVM_DEBUG(dbgs() << "Instruction is not a generic instruction.\n");241 return false;242 }243 244 Register ResultRegister = I.defs().begin()->getReg();245 if (GR->getSPIRVTypeForVReg(ResultRegister)) {246 LLVM_DEBUG(dbgs() << "Instruction already has a SPIR-V type.\n");247 if (!MRI.getRegClassOrNull(ResultRegister)) {248 LLVM_DEBUG(dbgs() << "Updating the register class.\n");249 setRegClassType(ResultRegister, GR->getSPIRVTypeForVReg(ResultRegister),250 GR, &MRI, *GR->CurMF, true);251 }252 return false;253 }254 255 return true;256}257 258static void registerSpirvTypeForNewInstructions(MachineFunction &MF,259 SPIRVGlobalRegistry *GR) {260 MachineRegisterInfo &MRI = MF.getRegInfo();261 SmallVector<MachineInstr *, 8> Worklist;262 for (MachineBasicBlock &MBB : MF) {263 for (MachineInstr &I : MBB) {264 if (requiresSpirvType(I, GR, MRI)) {265 Worklist.push_back(&I);266 }267 }268 }269 270 if (Worklist.empty()) {271 LLVM_DEBUG(dbgs() << "Initial worklist is empty.\n");272 return;273 }274 275 LLVM_DEBUG(dbgs() << "Initial worklist:\n";276 for (auto *I : Worklist) { I->dump(); });277 278 bool Changed;279 do {280 Changed = false;281 SmallVector<MachineInstr *, 8> NextWorklist;282 283 for (MachineInstr *I : Worklist) {284 MachineIRBuilder MIB(*I);285 if (deduceAndAssignSpirvType(I, MF, GR, MIB)) {286 Changed = true;287 } else {288 NextWorklist.push_back(I);289 }290 }291 Worklist = std::move(NextWorklist);292 LLVM_DEBUG(dbgs() << "Worklist size: " << Worklist.size() << "\n");293 } while (Changed);294 295 if (Worklist.empty())296 return;297 298 for (auto *I : Worklist) {299 MachineIRBuilder MIB(*I);300 Register ResVReg = I->getOperand(0).getReg();301 const LLT &ResLLT = MRI.getType(ResVReg);302 SPIRVType *ResType = nullptr;303 if (ResLLT.isVector()) {304 SPIRVType *CompType = GR->getOrCreateSPIRVIntegerType(305 ResLLT.getElementType().getSizeInBits(), MIB);306 ResType = GR->getOrCreateSPIRVVectorType(307 CompType, ResLLT.getNumElements(), MIB, false);308 } else {309 ResType = GR->getOrCreateSPIRVIntegerType(ResLLT.getSizeInBits(), MIB);310 }311 LLVM_DEBUG(dbgs() << "Could not determine type for " << *I312 << ", defaulting to " << *ResType << "\n");313 setRegClassType(ResVReg, ResType, GR, &MRI, MF, true);314 }315}316 317static void ensureAssignTypeForTypeFolding(MachineFunction &MF,318 SPIRVGlobalRegistry *GR) {319 LLVM_DEBUG(dbgs() << "Entering ensureAssignTypeForTypeFolding for function "320 << MF.getName() << "\n");321 MachineRegisterInfo &MRI = MF.getRegInfo();322 for (MachineBasicBlock &MBB : MF) {323 for (MachineInstr &MI : MBB) {324 if (!isTypeFoldingSupported(MI.getOpcode()))325 continue;326 if (MI.getNumOperands() == 1 || !MI.getOperand(1).isReg())327 continue;328 329 LLVM_DEBUG(dbgs() << "Processing instruction: " << MI);330 331 // Check uses of MI to see if it already has an use in SPIRV::ASSIGN_TYPE332 bool HasAssignType = false;333 Register ResultRegister = MI.defs().begin()->getReg();334 // All uses of Result register335 for (MachineInstr &UseInstr :336 MRI.use_nodbg_instructions(ResultRegister)) {337 if (UseInstr.getOpcode() == SPIRV::ASSIGN_TYPE) {338 HasAssignType = true;339 LLVM_DEBUG(dbgs() << " Instruction already has an ASSIGN_TYPE use: "340 << UseInstr);341 break;342 }343 }344 345 if (!HasAssignType) {346 Register ResultRegister = MI.defs().begin()->getReg();347 SPIRVType *ResultType = GR->getSPIRVTypeForVReg(ResultRegister);348 LLVM_DEBUG(349 dbgs() << " Adding ASSIGN_TYPE for ResultRegister: "350 << printReg(ResultRegister, MRI.getTargetRegisterInfo())351 << " with type: " << *ResultType);352 MachineIRBuilder MIB(MI);353 insertAssignInstr(ResultRegister, nullptr, ResultType, GR, MIB, MRI);354 }355 }356 }357}358 359// Do a preorder traversal of the CFG starting from the BB |Start|.360// point. Calls |op| on each basic block encountered during the traversal.361void visit(MachineFunction &MF, MachineBasicBlock &Start,362 std::function<void(MachineBasicBlock *)> op) {363 std::stack<MachineBasicBlock *> ToVisit;364 SmallPtrSet<MachineBasicBlock *, 8> Seen;365 366 ToVisit.push(&Start);367 Seen.insert(ToVisit.top());368 while (ToVisit.size() != 0) {369 MachineBasicBlock *MBB = ToVisit.top();370 ToVisit.pop();371 372 op(MBB);373 374 for (auto Succ : MBB->successors()) {375 if (Seen.contains(Succ))376 continue;377 ToVisit.push(Succ);378 Seen.insert(Succ);379 }380 }381}382 383// Do a preorder traversal of the CFG starting from the given function's entry384// point. Calls |op| on each basic block encountered during the traversal.385void visit(MachineFunction &MF, std::function<void(MachineBasicBlock *)> op) {386 visit(MF, *MF.begin(), std::move(op));387}388 389bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {390 // Initialize the type registry.391 const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();392 SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry();393 GR->setCurrentFunc(MF);394 registerSpirvTypeForNewInstructions(MF, GR);395 ensureAssignTypeForTypeFolding(MF, GR);396 return true;397}398 399INITIALIZE_PASS(SPIRVPostLegalizer, DEBUG_TYPE, "SPIRV post legalizer", false,400 false)401 402char SPIRVPostLegalizer::ID = 0;403 404FunctionPass *llvm::createSPIRVPostLegalizerPass() {405 return new SPIRVPostLegalizer();406}407