| //===--------------------- HexagonPostRAHandleQFP.cpp -------------------------- | |
| //===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===---------------------------------------------------------------------===// | |
| // For v79 and above, we generate qf operations for HVX which includes vadd, | |
| // vsub and vmpy instructions. These qf operations with qf operands are fast, | |
| // maintain similar accuracy as IEEE and saves power. | |
| // | |
| // However, these qf operands should always be converted back to IEEE format | |
| // when used in non-HVX instructions. This is because of how the qf values | |
| // are stored in memory. qf operands have 4 extra bits. If used in non-HVX | |
| // operations, these bits get dropped resulting in incorrect value being | |
| // used. So, before use in any non-HVX operation we need to convert these | |
| // qf values to IEEE format. | |
| // | |
| // During register allocation, when no more physical registers are available | |
| // the qf operands may be spilled to memory. This instantly causes loss of | |
| // accuracy. This pass prevents that by: | |
| // 1. Inserting qf type to IEEE type conversion instructions before the spill. | |
| // 2. Iterating over the uses of qf def (created before the spill) and | |
| // changing their opcodes to handle IEEE type operands for saturating | |
| // instructions. This is because, the refills will use IEEE type operands, but | |
| // the instructions will still assume qf operands. For non-saturating | |
| // instructions which uses qf, we incorporate a conversion to IEEE before that. | |
| // 3. Iterating over the uses of qf def created by the spill and replacing | |
| // them with appropiate opcode (which uses IEEE operands) for saturating | |
| // instructions. For non-saturating instructions which uses qf, | |
| // we incorporate a conversion to IEEE before that. | |
| // 4. Iterating over the copy instructions and checking their uses, | |
| // inserting conversions from qf to IEEE whenever required. The conversions | |
| // are inserted after their reaching def since there can be multiple defs | |
| // for use in non-SSA form. | |
| // | |
| // To get the use-def chains, we make use of Register DataFlow Graph (RDF), | |
| // since after register allocation SSA form is lost. This can be done during | |
| // spills and fills during Frame Lowering for register allocation. However, | |
| // that was abandoned due to the intermediate state of the code. | |
| // Liveness is preserved in this pass. | |
| // | |
| // NOTE: | |
| // Saturating instructions: Instructions for which transformation involves | |
| // only changing the opcode. Eg. vmpy(qf32, sf) saturates to vmpy(sf, sf) when | |
| // we see that the first operand is now a sf type. | |
| // Non-Saturating instructions: Instructions for which conversion(s) have | |
| // to be inserted. Eg. Vd.f8=Vu.qf16. If the use operand is now hf type, | |
| // we have to insert a conversion qf16 = hf before this instruction. | |
| // | |
| // FIXME tags have been added for potential errors, along with the underlying | |
| // assumption. | |
| // FIXME Implement v81 specific optimizations as below. At the moment, we add | |
| // converts. | |
| // Vd.qf16=Vu.hf | |
| // Vd.qf16=Vu.qf16 | |
| // Vd.qf32=Vu.qf32 | |
| // Vd.qf32=Vu.sf | |
| //===---------------------------------------------------------------------===// | |
| #include "HexagonTargetMachine.h" | |
| #include "llvm/CodeGen/LiveInterval.h" | |
| #include "llvm/CodeGen/LiveIntervals.h" | |
| #include "llvm/CodeGen/LivePhysRegs.h" | |
| #include "llvm/CodeGen/MachineDominanceFrontier.h" | |
| #include "llvm/CodeGen/MachineDominators.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/CodeGen/Passes.h" | |
| #include "llvm/CodeGen/RDFGraph.h" | |
| #include "llvm/CodeGen/RDFLiveness.h" | |
| #include "llvm/CodeGen/RDFRegisters.h" | |
| #include "llvm/CodeGen/TargetInstrInfo.h" | |
| #include "llvm/CodeGen/TargetRegisterInfo.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| #define DEBUG_TYPE "handle-qfp" | |
| using namespace llvm; | |
| using namespace rdf; | |
| extern cl::opt<QFloatMode> QFloatModeValue; | |
| cl::opt<bool> DisablePostRAHandleQFloat( | |
| "disable-handle-qfp", cl::init(false), | |
| cl::desc("Disable handling of Qfloat spills/refills after register " | |
| "allocation.")); | |
| static cl::opt<bool> EnablePostRAXqfCompliance( | |
| "enable-postra-xqf-check", cl::init(false), | |
| cl::desc("Enable ABI compliance for xqf operands post regalloc.")); | |
| namespace llvm { | |
| FunctionPass *createHexagonPostRAHandleQFP(); | |
| void initializeHexagonPostRAHandleQFPPass(PassRegistry &); | |
| } // namespace llvm | |
| // QF Instructions list which need to be analyzed. | |
| // The value of the key denotes a pair | |
| // pair.first|pair.second = True if IEEE type, false otherwise. | |
| // We only need to change the opcode to handling qf/sf | |
| // misuses for these, or these instructions can be 'saturated'. | |
| DenseMap<unsigned short, std::pair<bool, bool>> QFPSatInstsMap{ | |
| {Hexagon::V6_vadd_qf16_mix, {false, true}}, | |
| {Hexagon::V6_vadd_qf16, {false, false}}, | |
| {Hexagon::V6_vadd_qf32_mix, {false, true}}, | |
| {Hexagon::V6_vadd_qf32, {false, false}}, | |
| {Hexagon::V6_vsub_qf16_mix, {false, true}}, | |
| {Hexagon::V6_vsub_hf_mix, {true, false}}, | |
| {Hexagon::V6_vsub_qf16, {false, false}}, | |
| {Hexagon::V6_vsub_qf32_mix, {false, true}}, | |
| {Hexagon::V6_vsub_sf_mix, {true, false}}, | |
| {Hexagon::V6_vsub_qf32, {false, false}}, | |
| {Hexagon::V6_vmpy_qf16_mix_hf, {false, true}}, | |
| {Hexagon::V6_vmpy_qf16, {false, false}}, | |
| {Hexagon::V6_vmpy_qf32_mix_hf, {false, true}}, | |
| {Hexagon::V6_vmpy_qf32_qf16, {false, false}}, | |
| {Hexagon::V6_vmpy_qf32, {false, false}}, | |
| {Hexagon::V6_vmpy_rt_qf16, {false, true}}, | |
| // These opcodes take a single operand only. | |
| // Second placeholder op is true always. | |
| {Hexagon::V6_vabs_qf32_qf32, {false, true}}, | |
| {Hexagon::V6_vabs_qf16_qf16, {false, true}}, | |
| {Hexagon::V6_vneg_qf32_qf32, {false, true}}, | |
| {Hexagon::V6_vneg_qf16_qf16, {false, true}}, | |
| {Hexagon::V6_vilog2_qf32, {false, true}}, | |
| {Hexagon::V6_vilog2_qf16, {false, true}}, | |
| {Hexagon::V6_vconv_qf32_qf32, {false, true}}, | |
| {Hexagon::V6_vconv_qf16_qf16, {false, true}}, | |
| }; | |
| // This holds the instruction opcodes for which there are | |
| // no 'saturating' opcodes. The only way is to insert | |
| // convert instructions before them. | |
| SmallVector<unsigned short, 5> QFNonSatInstr{ | |
| Hexagon::V6_vconv_hf_qf16, Hexagon::V6_vconv_hf_qf32, | |
| Hexagon::V6_vconv_sf_qf32, | |
| // v81 instructions | |
| Hexagon::V6_vconv_bf_qf32, Hexagon::V6_vconv_f8_qf16}; | |
| namespace { | |
| class HexagonPostRAHandleQFP : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| HexagonPostRAHandleQFP() : MachineFunctionPass(ID) { | |
| PassRegistry &R = *PassRegistry::getPassRegistry(); | |
| initializeHexagonPostRAHandleQFPPass(R); | |
| } | |
| StringRef getPassName() const override { | |
| return "Hexagon handle QFloat spills and refills post RA."; | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| AU.addRequired<MachineDominatorTreeWrapperPass>(); | |
| AU.addRequired<MachineDominanceFrontierWrapperPass>(); | |
| AU.setPreservesCFG(); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| private: | |
| // QFUses collects the instructions which uses QF operands. | |
| // These have to be deleted and transformed to opcodes | |
| // to denote usage of IEEE operands. | |
| // It might involve changing the order of the Register operands. | |
| using QFUses = std::map<MachineInstr *, std::pair<bool, bool>>; | |
| QFUses QFUsesMap; | |
| // Holds the Register Dataglow Graph. | |
| DataFlowGraph *DFG = nullptr; | |
| // Stores spill nodes and their reaching definition instructions | |
| // which generates the qf operand to be stored. | |
| std::vector<std::pair<MachineInstr *, NodeAddr<DefNode *>>> SpillMIs; | |
| // Stores the refill nodes consisting of load instructions. | |
| std::vector<NodeAddr<DefNode *>> RefillMIs; | |
| // Stores the type of op. | |
| enum ConvOperand { | |
| Undefined = 0x0, | |
| Lo = 0x1, | |
| Hi = 0x2, | |
| HiLo = 0x3, | |
| }; | |
| // Stores the convert instructions which take qf operands. | |
| MapVector<MachineInstr *, unsigned> QFNonSatMIs; | |
| // Stores the qf-generating vmul/vadd/etc. nodes with mutiple reaching defs | |
| std::set<NodeId> PossibleMultiReachDefs; | |
| // Qf generating instructions to ignore. Do not insert conversion instruction | |
| // to sf/hf from qf, if the instr is present in this list; since that means | |
| // a conversion has already been inserted after the instruction. | |
| SmallPtrSet<MachineInstr *, 4> IgnoreInsertConvList; | |
| // Register type | |
| enum class RegType { qf32, qf16, qf32_double, qf16_double, ieee, undefined }; | |
| // Stores the copy instructions which their reaching def, along with the op | |
| // type | |
| std::map<std::pair<NodeId, NodeId>, RegType> QFCopys; | |
| // Stores the reaching defs of copies whose result has to be converted to IEEE | |
| DenseMap<MachineInstr *, RegType> ReachDefOfCopies; | |
| // Stores copies which need to be converted back to qf. The uses of these | |
| // copies feed to qf type instructions and hence can be converted back to qf | |
| // type. | |
| DenseMap<MachineInstr *, std::pair<NodeAddr<DefNode *>, RegType>> | |
| ConvertToQfCopies; | |
| // Subregister kill set for a doubletype use. The pair of bool,bool | |
| // represents the hi and lo subregisters of the double register. | |
| DenseMap<MachineInstr *, std::pair<bool, bool>> SubRegKillSet; | |
| const HexagonInstrInfo *HII = nullptr; | |
| const HexagonRegisterInfo *HRI = nullptr; | |
| MachineRegisterInfo *MRI = nullptr; | |
| Liveness *LV = nullptr; | |
| const HexagonSubtarget *HST = nullptr; | |
| void collectQFPStackSpill(NodeAddr<StmtNode *> *); | |
| void collectQFPStackRefill(NodeAddr<StmtNode *> *); | |
| void collectCopies(NodeAddr<StmtNode *> *); | |
| bool HandleRefills(); | |
| bool HandleSpills(); | |
| bool HandleCopies(); | |
| bool HandleNonSatInstr(); | |
| bool HandleMultiReachingDefs(); | |
| bool HandleReachDefOfCopies(); | |
| bool HandleConvertToQfCopies(); | |
| RegType HasQfUses(NodeAddr<DefNode *>, MachineInstr *); | |
| void collectConvQFInstr(NodeAddr<DefNode *> &); | |
| void collectQFUses(NodeAddr<DefNode *>, MachineInstr *DefMI); | |
| void conditionallyInsert(MachineInstr &, Register &); | |
| // Helper functions | |
| unsigned short getreplacedQFOpcode(unsigned, bool, bool); | |
| MCPhysReg findAllocatableReg(MachineInstr *MI) const; | |
| void insertIEEEToQF(MachineInstr *, Register, MachineOperand, bool is32bit); | |
| void collectLivenessForSubregs(NodeAddr<UseNode *> &); | |
| void insertInstr(MachineInstr *, unsigned, unsigned, unsigned, RegState); | |
| }; | |
| } // namespace | |
| // This class handles spurious vector instrutions which do not | |
| // follow the ABI. For eg, vcombine(qf,qf) takes qf operands | |
| // instead of IEEE type. This diagnostic pass can be used | |
| // as a final verifier for XQF implementation. Turned off by | |
| // default | |
| class XqfPostRADiagnosis { | |
| public: | |
| XqfPostRADiagnosis(DataFlowGraph &G, Liveness &L, const HexagonInstrInfo *HII) | |
| : G(&G), L(&L), HII(HII) {} | |
| // Deleting default constructor to handle misconstruction | |
| XqfPostRADiagnosis() = delete; | |
| void runCompliance() const; | |
| void print_warning(Twine &, MachineInstr *, MachineInstr *) const; | |
| private: | |
| DataFlowGraph *G = nullptr; | |
| Liveness *L = nullptr; | |
| const HexagonInstrInfo *HII = nullptr; | |
| }; | |
| void XqfPostRADiagnosis::print_warning(Twine &wstr, MachineInstr *DefMI, | |
| MachineInstr *UseMI) const { | |
| #ifndef NDEBUG | |
| dbgs() << wstr; | |
| dbgs() << "\n\tDef:"; | |
| DefMI->dump(); | |
| // dbgs() << "\t" << DefMI->getParent()->getName(); | |
| dbgs() << "\tUse:"; | |
| UseMI->dump(); | |
| // dbgs() << "\t" << UseMI->getParent()->getName(); | |
| #endif // NDEBUG | |
| } | |
| // This static function gets all reached uses of a def. | |
| // When it encounters a phi node, it goes over the | |
| // reached uses of the phi node too. | |
| static void getAllRealUses(NodeAddr<DefNode *> DA, NodeSet &UNodeSet, | |
| Liveness *L, DataFlowGraph *G, | |
| bool comprehensive = false) { | |
| RegisterRef DR = DA.Addr->getRegRef(*G); | |
| NodeAddr<StmtNode *> DefStmt = DA.Addr->getOwner(*G); | |
| MachineInstr *Instr = DefStmt.Addr->getCode(); | |
| auto UseSet = L->getAllReachedUses(DR, DA); | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = G->addr<UseNode *>(UI); | |
| MachineFunction *MF = Instr->getMF(); | |
| const auto &HRI = MF->getSubtarget<HexagonSubtarget>().getRegisterInfo(); | |
| Register RR = UA.Addr->getRegRef(*G).Id; | |
| if (HRI->isFakeReg(RR)) | |
| continue; | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) { | |
| NodeAddr<PhiNode *> PA = UA.Addr->getOwner(*G); | |
| NodeId id = PA.Id; | |
| const Liveness::RefMap &phiUse = L->getRealUses(id); | |
| for (auto I : phiUse) { | |
| if (!G->getPRI().alias(RegisterRef(I.first), DR)) | |
| continue; | |
| auto phiUseSet = I.second; | |
| for (auto phiUI : phiUseSet) { | |
| NodeAddr<UseNode *> phiUA = G->addr<UseNode *>(phiUI.first); | |
| UNodeSet.insert(phiUA.Id); | |
| } | |
| } | |
| } else { | |
| // FIXME Due to bug in RDF, check if the reaching def of the use | |
| // reaches this instruction | |
| if (comprehensive) { | |
| UNodeSet.insert(UA.Id); | |
| continue; | |
| } | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*G); | |
| for (NodeAddr<UseNode *> UA : UseStmt.Addr->members_if(G->IsUse, *G)) { | |
| NodeId QFPDefNode = UA.Addr->getReachingDef(); | |
| NodeAddr<DefNode *> RegDef = G->addr<DefNode *>(QFPDefNode); | |
| // FIXME Reaching def computation error | |
| if (QFPDefNode == 0) | |
| continue; | |
| NodeAddr<StmtNode *> RegStmt = RegDef.Addr->getOwner(*G); | |
| MachineInstr *ReachDefInstr = RegStmt.Addr->getCode(); | |
| if (ReachDefInstr && ReachDefInstr == Instr) | |
| UNodeSet.insert(UA.Id); | |
| } | |
| } | |
| } | |
| } | |
| void XqfPostRADiagnosis::runCompliance() const { | |
| NodeAddr<FuncNode *> FA = G->getFunc(); | |
| for (NodeAddr<BlockNode *> BA : FA.Addr->members(*G)) { | |
| for (auto IA : BA.Addr->members(*G)) { | |
| if (!G->IsCode<NodeAttrs::Stmt>(IA)) | |
| continue; | |
| NodeAddr<StmtNode *> SA = IA; | |
| MachineInstr *DefMI = SA.Addr->getCode(); | |
| if (DefMI->isDebugInstr() || DefMI->isInlineAsm()) | |
| continue; | |
| auto NodeBase = SA.Addr->members_if(G->IsDef, *G); | |
| if (NodeBase.empty()) | |
| continue; | |
| NodeAddr<DefNode *> DfNode = NodeBase.front(); | |
| NodeSet UseSet; | |
| getAllRealUses(DfNode, UseSet, L, G, true); | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = G->addr<UseNode *>(UI); | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*G); | |
| MachineInstr *UseMI = UseStmt.Addr->getCode(); | |
| if (UseMI->isDebugInstr() || UseMI->isInlineAsm()) | |
| continue; | |
| unsigned OpNo = UA.Addr->getOp().getOperandNo(); | |
| if (HII->usesQF32Operand(UseMI, OpNo) && !HII->isQFP32Instr(DefMI)) { | |
| Twine wstr(Twine("Mismatch: sf type used as qf32 at operand ") | |
| .concat(Twine(OpNo))); | |
| print_warning(wstr, DefMI, UseMI); | |
| } else if (!HII->usesQF32Operand(UseMI, OpNo) && | |
| HII->isQFP32Instr(DefMI)) { | |
| Twine wstr(Twine("Mismatch: qf32 type used as sf at operand ") | |
| .concat(Twine(OpNo))); | |
| print_warning(wstr, DefMI, UseMI); | |
| } else if (HII->usesQF16Operand(UseMI, OpNo) && | |
| !HII->isQFP16Instr(DefMI)) { | |
| Twine wstr(Twine("Mismatch: hf type used as qf16 at operand ") | |
| .concat(Twine(OpNo))); | |
| print_warning(wstr, DefMI, UseMI); | |
| } else if (!HII->usesQF16Operand(UseMI, OpNo) && | |
| HII->isQFP16Instr(DefMI)) { | |
| Twine wstr(Twine("Mismatch: qf16 type used as hf at operand ") | |
| .concat(Twine(OpNo))); | |
| print_warning(wstr, DefMI, UseMI); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| char HexagonPostRAHandleQFP::ID = 0; | |
| namespace llvm { | |
| char &HexagonPostRAHandleQFPID = HexagonPostRAHandleQFP::ID; | |
| } | |
| // Check whether the instruction is added already, if not add it | |
| // along with the Register values and qf type. | |
| // If already added, then check the register values and edit them. | |
| void HexagonPostRAHandleQFP::conditionallyInsert(MachineInstr &MI, | |
| Register &DefReg) { | |
| LLVM_DEBUG(dbgs() << "\nCollecting instruction using QF: "; MI.dump()); | |
| // check if the key exists. | |
| Register Reg1 = MI.getOperand(1).getReg(); | |
| // If the use is a unary operation, make second register point to Defreg | |
| // This ensures that secondOp is always true | |
| Register Reg2 = MI.getNumOperands() == 2 ? DefReg : MI.getOperand(2).getReg(); | |
| if (QFUsesMap.find(&MI) != QFUsesMap.end()) { | |
| auto Entry = QFUsesMap[&MI]; | |
| bool firstOp = ((Reg1 == DefReg) ? true : false) | Entry.first; | |
| bool secondOp = ((Reg2 == DefReg) ? true : false) | Entry.second; | |
| QFUsesMap[&MI] = std::make_pair(firstOp, secondOp); | |
| } else { // encountered first time. | |
| // Get the default type of the operand: | |
| // True : IEEE type | |
| // False : QF type | |
| auto defaultPair = QFPSatInstsMap[MI.getOpcode()]; | |
| bool firstOp = (Reg1 == DefReg) ? true : defaultPair.first; | |
| bool secondOp = (Reg2 == DefReg) ? true : defaultPair.second; | |
| QFUsesMap[&MI] = std::make_pair(firstOp, secondOp); | |
| } | |
| } | |
| unsigned short HexagonPostRAHandleQFP::getreplacedQFOpcode(unsigned srcOpcode, | |
| bool firstOp, | |
| bool secondOp) { | |
| if (firstOp && secondOp) { | |
| switch (srcOpcode) { | |
| case Hexagon::V6_vadd_qf32: | |
| case Hexagon::V6_vadd_qf32_mix: | |
| return Hexagon::V6_vadd_sf; | |
| case Hexagon::V6_vadd_qf16: | |
| case Hexagon::V6_vadd_qf16_mix: | |
| return Hexagon::V6_vadd_hf; | |
| case Hexagon::V6_vsub_qf32: | |
| case Hexagon::V6_vsub_qf32_mix: | |
| case Hexagon::V6_vsub_sf_mix: | |
| return Hexagon::V6_vsub_sf; | |
| case Hexagon::V6_vsub_qf16: | |
| case Hexagon::V6_vsub_qf16_mix: | |
| case Hexagon::V6_vsub_hf_mix: | |
| return Hexagon::V6_vsub_hf; | |
| case Hexagon::V6_vmpy_qf32: | |
| return Hexagon::V6_vmpy_qf32_sf; | |
| case Hexagon::V6_vmpy_qf16: | |
| case Hexagon::V6_vmpy_qf16_mix_hf: | |
| return Hexagon::V6_vmpy_qf16_hf; | |
| case Hexagon::V6_vmpy_qf32_qf16: | |
| case Hexagon::V6_vmpy_qf32_mix_hf: | |
| return Hexagon::V6_vmpy_qf32_hf; | |
| case Hexagon::V6_vmpy_rt_qf16: | |
| return Hexagon::V6_vmpy_rt_hf; | |
| // v81 opcodes start | |
| case Hexagon::V6_vabs_qf32_qf32: | |
| return Hexagon::V6_vabs_qf32_sf; | |
| case Hexagon::V6_vabs_qf16_qf16: | |
| return Hexagon::V6_vabs_qf16_hf; | |
| case Hexagon::V6_vneg_qf32_qf32: | |
| return Hexagon::V6_vneg_qf32_sf; | |
| case Hexagon::V6_vneg_qf16_qf16: | |
| return Hexagon::V6_vneg_qf16_hf; | |
| case Hexagon::V6_vilog2_qf32: | |
| return Hexagon::V6_vilog2_sf; | |
| case Hexagon::V6_vilog2_qf16: | |
| return Hexagon::V6_vilog2_hf; | |
| case Hexagon::V6_vconv_qf32_qf32: | |
| return Hexagon::V6_vconv_qf32_sf; | |
| case Hexagon::V6_vconv_qf16_qf16: | |
| return Hexagon::V6_vconv_qf16_hf; | |
| // v81 opcodes end | |
| default: | |
| llvm_unreachable("Invalid qf opcode in this scenario!"); | |
| } | |
| } else if (firstOp) { | |
| switch (srcOpcode) { | |
| case Hexagon::V6_vadd_qf32: | |
| return Hexagon::V6_vadd_qf32_mix; // interchange reqd | |
| case Hexagon::V6_vadd_qf16: | |
| return Hexagon::V6_vadd_qf16_mix; // interchange reqd | |
| case Hexagon::V6_vsub_qf32: | |
| if (HST->useHVXV81Ops()) | |
| return Hexagon::V6_vsub_sf_mix; | |
| else if (HST->useHVXV79Ops()) | |
| return Hexagon::V6_vsub_sf; // conv reqd | |
| else | |
| llvm_unreachable("Invalid Hexagon Arch for this scenario!"); | |
| case Hexagon::V6_vsub_qf16: | |
| if (HST->useHVXV81Ops()) | |
| return Hexagon::V6_vsub_hf_mix; | |
| else if (HST->useHVXV79Ops()) | |
| return Hexagon::V6_vsub_hf; // conv reqd | |
| else | |
| llvm_unreachable("Invalid Hexagon Arch for this scenario!"); | |
| case Hexagon::V6_vsub_qf32_mix: | |
| return Hexagon::V6_vsub_sf; | |
| case Hexagon::V6_vsub_qf16_mix: | |
| return Hexagon::V6_vsub_hf; | |
| // This opcode does not have a mixed type. Hence if one | |
| // of op1 or op2 is IEEE type and another qf type, | |
| // send the opcode which takes in both as IEEE type. | |
| case Hexagon::V6_vmpy_qf32: | |
| return Hexagon::V6_vmpy_qf32_sf; // conv reqd | |
| case Hexagon::V6_vmpy_qf16: | |
| return Hexagon::V6_vmpy_qf16_mix_hf; // interchange reqd | |
| case Hexagon::V6_vmpy_qf32_qf16: | |
| return Hexagon::V6_vmpy_qf32_mix_hf; // interchange reqd | |
| default: | |
| return srcOpcode; | |
| } | |
| } else if (secondOp) { | |
| switch (srcOpcode) { | |
| case Hexagon::V6_vadd_qf32: | |
| return Hexagon::V6_vadd_qf32_mix; | |
| case Hexagon::V6_vadd_qf16: | |
| return Hexagon::V6_vadd_qf16_mix; | |
| case Hexagon::V6_vsub_qf32: | |
| return Hexagon::V6_vsub_qf32_mix; | |
| case Hexagon::V6_vsub_qf16: | |
| return Hexagon::V6_vsub_qf16_mix; | |
| case Hexagon::V6_vsub_sf_mix: | |
| return Hexagon::V6_vsub_sf; | |
| case Hexagon::V6_vsub_hf_mix: | |
| return Hexagon::V6_vsub_hf; | |
| case Hexagon::V6_vmpy_qf32: | |
| return Hexagon::V6_vmpy_qf32_sf; // conv reqd | |
| case Hexagon::V6_vmpy_qf16: | |
| return Hexagon::V6_vmpy_qf16_mix_hf; | |
| case Hexagon::V6_vmpy_qf32_qf16: | |
| return Hexagon::V6_vmpy_qf32_mix_hf; | |
| default: | |
| return srcOpcode; | |
| } | |
| } else | |
| return srcOpcode; | |
| } | |
| // Insert IEEE to Qf conversion instructions | |
| // is32bit: If true, SrcReg holds sf type, else a hf type | |
| void HexagonPostRAHandleQFP::insertIEEEToQF(MachineInstr *MI, Register SrcReg, | |
| MachineOperand SrcOp, | |
| bool is32bit = false) { | |
| auto MBB = MI->getParent(); | |
| MachineInstrBuilder MIB; | |
| const DebugLoc &DL = MI->getDebugLoc(); | |
| if (HST->useHVXV81Ops()) { | |
| auto Op = is32bit ? Hexagon::V6_vconv_qf32_sf : Hexagon::V6_vconv_qf16_hf; | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(Op), SrcReg) | |
| .addReg(SrcReg, RegState::Renamable | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| } else if (HST->useHVXV79Ops()) { | |
| // Get an available register | |
| auto V0_Reg = findAllocatableReg(MI); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(Hexagon::V6_vd0), V0_Reg); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| auto Op = is32bit ? Hexagon::V6_vadd_sf : Hexagon::V6_vadd_hf; | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(Op), SrcReg) | |
| .addReg(SrcReg, RegState::Renamable | RegState::Kill) | |
| .addReg(V0_Reg, RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "Inserting new instruction: "; MIB.getInstr()->dump()); | |
| } else | |
| llvm_unreachable("Not possible to insert qf = hf/sf for this unknown\ | |
| subtarget!"); | |
| } | |
| // Create a new instruction which handle sf/hf types to replace | |
| // qf type handling instructions. | |
| bool HexagonPostRAHandleQFP::HandleRefills() { | |
| bool Changed = false; | |
| LLVM_DEBUG(dbgs() << "HandleRefills: "); | |
| std::vector<MachineInstr *> eraseList; | |
| for (auto It : QFUsesMap) { | |
| // Separately handle unary qf opcodes | |
| MachineInstr *MI = It.first; | |
| auto SrcOpcode = MI->getOpcode(); | |
| auto Pair = It.second; | |
| auto SrcOp1 = MI->getOperand(1); | |
| Register DestReg = MI->getOperand(0).getReg(); | |
| auto MBB = MI->getParent(); | |
| MachineInstrBuilder MIB; | |
| LLVM_DEBUG(dbgs() << "\nProcessing: "; MI->dump()); | |
| const DebugLoc &DL = MI->getDebugLoc(); | |
| // lambda to handle unary qf operations | |
| // ieee: True if the 1st operand is sf/hf type, false if qf type | |
| auto HandleUnaryRefill = [&](MachineInstr *MI, bool isIeee) -> bool { | |
| if (isIeee) { | |
| auto finalOpcode = getreplacedQFOpcode(SrcOpcode, true, true); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)); | |
| Changed |= true; | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| } else | |
| eraseList.push_back(MI); | |
| return Changed; | |
| }; | |
| if (MI->getNumOperands() == 2) { | |
| Changed |= HandleUnaryRefill(It.first, It.second.first); | |
| continue; | |
| } | |
| auto SrcOp2 = MI->getOperand(2); | |
| // lambda to handle mixed type vsub instructions for v79 | |
| auto HandleSub = [&](auto srcOpcode) -> bool { | |
| auto ConvOp = (srcOpcode == Hexagon::V6_vsub_qf32) | |
| ? Hexagon::V6_vconv_sf_qf32 | |
| : Hexagon::V6_vconv_hf_qf16; | |
| auto SubOp = (ConvOp == Hexagon::V6_vconv_sf_qf32) ? Hexagon::V6_vsub_sf | |
| : Hexagon::V6_vsub_hf; | |
| Register SrcOp2Reg = SrcOp2.getReg(); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(ConvOp), SrcOp2Reg) | |
| .addReg(SrcOp2Reg, getRegState(SrcOp2) | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(SubOp), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)) | |
| .addReg(SrcOp2Reg, getRegState(SrcOp2)); | |
| // If Op2 is not killed, it is used after this instruction. | |
| // convert it back to original qf form. | |
| if (!SrcOp2.isKill()) | |
| insertIEEEToQF(&*(++MI->getIterator()), SrcOp2.getReg(), SrcOp2); | |
| return true; | |
| }; | |
| // If both operands are sf type, we only need to replace the opcode. | |
| if (Pair.first == true && Pair.second == true) { | |
| auto finalOpcode = getreplacedQFOpcode(SrcOpcode, true, true); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)) | |
| .addReg(SrcOp2.getReg(), getRegState(SrcOp2)); | |
| Changed |= true; | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| } else if (Pair.first == true && Pair.second == false) { | |
| auto finalOpcode = getreplacedQFOpcode(SrcOpcode, true, false); | |
| // If 2nd op is qf, first op is sf, convert the 2nd | |
| // op to sf before inserting the vmpy instruction. | |
| if (SrcOpcode == Hexagon::V6_vmpy_qf32) { | |
| Register SrcOp2Reg = SrcOp2.getReg(); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(Hexagon::V6_vconv_sf_qf32), | |
| SrcOp2Reg) | |
| .addReg(SrcOp2Reg, getRegState(SrcOp2) | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction before: "; | |
| MIB.getInstr()->dump()); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)) | |
| .addReg(SrcOp2Reg, getRegState(SrcOp2)); | |
| // If Op2 is not killed convert back to qf, since there | |
| // are uses for this qf op. | |
| if (!SrcOp2.isKill()) | |
| insertIEEEToQF(&*(++MI->getIterator()), SrcOp2.getReg(), SrcOp2, | |
| true /* sf type reg */); | |
| // if the opcode is mixed type, we use Op2 as first operand | |
| // since that takes in qf type. Op1 is taken as second op. | |
| } else if (finalOpcode == Hexagon::V6_vadd_qf16_mix || | |
| finalOpcode == Hexagon::V6_vadd_qf32_mix || | |
| finalOpcode == Hexagon::V6_vmpy_qf16_mix_hf || | |
| finalOpcode == Hexagon::V6_vmpy_qf32_mix_hf) { | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp2.getReg(), getRegState(SrcOp2)) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)); | |
| // Subtracting is not associative, so if Op1 is sf/hf type and | |
| // Op2 is qf type, we cannot interchange the operands. | |
| // For v79, we convert Op2 to IEEE and use the non-mix type | |
| // instruction for the subtraction. | |
| // For v81, we have an appropiate opcode with vsub(sf/hf, qf) type | |
| } else if ((SrcOpcode == Hexagon::V6_vsub_qf32 || | |
| SrcOpcode == Hexagon::V6_vsub_qf16) && | |
| HST->useHVXV79Ops()) { | |
| Changed |= HandleSub(SrcOpcode); | |
| } else { | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)) | |
| .addReg(SrcOp2.getReg(), getRegState(SrcOp2)); | |
| } | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| Changed |= true; | |
| } else if (Pair.first == false && Pair.second == true) { | |
| auto finalOpcode = getreplacedQFOpcode(SrcOpcode, false, true); | |
| // If 2nd op is sf, first op is qf, convert the 1st | |
| // op to sf before inserting the vmpy instruction. | |
| if (SrcOpcode == Hexagon::V6_vmpy_qf32) { | |
| Register SrcOp1Reg = SrcOp1.getReg(); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(Hexagon::V6_vconv_sf_qf32), | |
| SrcOp1Reg) | |
| .addReg(SrcOp1Reg, getRegState(SrcOp1) | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction before: "; | |
| MIB.getInstr()->dump()); | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1Reg, getRegState(SrcOp1)) | |
| .addReg(SrcOp2.getReg(), getRegState(SrcOp2)); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| // If Op1 is not killed convert back to qf, since there | |
| // are uses for this qf op. | |
| if (!SrcOp1.isKill()) | |
| insertIEEEToQF(&*(++MI->getIterator()), SrcOp1.getReg(), SrcOp1, | |
| true /*sf type reg*/); | |
| } else { | |
| MIB = BuildMI(*MBB, *MI, DL, HII->get(finalOpcode), DestReg) | |
| .addReg(SrcOp1.getReg(), getRegState(SrcOp1)) | |
| .addReg(SrcOp2.getReg(), getRegState(SrcOp2)); | |
| LLVM_DEBUG(dbgs() << "\nInserting new instruction: "; | |
| MIB.getInstr()->dump()); | |
| } | |
| Changed |= true; | |
| } else { | |
| // Both the operands of this instructions are valid, so no use of | |
| // this instruction is to be modified. We need to remove this | |
| // instruction from the action map QFUsesMap. | |
| eraseList.push_back(MI); | |
| } | |
| } | |
| for (MachineInstr *delMI : eraseList) | |
| QFUsesMap.erase(delMI); | |
| return Changed; | |
| } | |
| // Insert a new instruction. | |
| void HexagonPostRAHandleQFP::insertInstr(MachineInstr *MI, unsigned MIOpcode, | |
| unsigned SrcReg, unsigned DstReg, | |
| RegState Flags) { | |
| MachineInstrBuilder MIB; | |
| MachineBasicBlock *MBB = MI->getParent(); | |
| DebugLoc DL = MI->getDebugLoc(); | |
| MachineBasicBlock::iterator MIt = MI; | |
| auto MINext = ++MI->getIterator(); | |
| if (++MIt == MBB->end()) | |
| MIB = BuildMI(MBB, DL, HII->get(MIOpcode), DstReg).addReg(SrcReg, Flags); | |
| else | |
| MIB = BuildMI(*MBB, MINext, DL, HII->get(MIOpcode), DstReg) | |
| .addReg(SrcReg, Flags); | |
| LLVM_DEBUG(dbgs() << "\t\tInserting after conv: "; MIB.getInstr()->dump()); | |
| } | |
| // Find an available vector register to store 0x0. We have reserved vector | |
| // register v30 to be exempted from being used during register allocation | |
| // for this purpose. | |
| MCPhysReg HexagonPostRAHandleQFP::findAllocatableReg(MachineInstr *MI) const { | |
| LLVM_DEBUG(dbgs() << "\tUsing V30 register to store a vector of zeroes!"); | |
| return Hexagon::V30; | |
| } | |
| // Insert qf = sf/hf conversions before non-saturating instructions | |
| bool HexagonPostRAHandleQFP::HandleNonSatInstr() { | |
| for (auto It : QFNonSatMIs) { | |
| MachineInstr *MI = It.first; | |
| auto MIOpcode = MI->getOpcode(); | |
| auto Op = MI->getOperand(1); | |
| Register DefReg = Op.getReg(); | |
| LLVM_DEBUG(dbgs() << "Analyzing convert instruction: "; MI->dump()); | |
| // Handle hf = qf16. | |
| // Handle f8 = qf16 | |
| if (MIOpcode == Hexagon::V6_vconv_hf_qf16 || | |
| MIOpcode == Hexagon::V6_vconv_f8_qf16) { | |
| insertIEEEToQF(MI, DefReg, Op); | |
| // TODO Check if there are any reaching def which is qf generating type. | |
| // That op should be converted to sf/hf | |
| if (!Op.isKill()) | |
| insertInstr(MI, Hexagon::V6_vconv_hf_qf16, DefReg, DefReg, | |
| getRegState(Op) | RegState::Kill); | |
| // Handle hf = qf.qf. | |
| // Handle bf = qf.qf | |
| } else if (MIOpcode == Hexagon::V6_vconv_hf_qf32 || | |
| MIOpcode == Hexagon::V6_vconv_bf_qf32) { | |
| Register DefLo = HRI->getSubReg(DefReg, Hexagon::vsub_lo); | |
| Register DefHi = HRI->getSubReg(DefReg, Hexagon::vsub_hi); | |
| if (It.second == ConvOperand::HiLo) { | |
| insertIEEEToQF(MI, DefLo, Op, true /* sf type */); | |
| insertIEEEToQF(MI, DefHi, Op, true /* sf type */); | |
| // Check which subregister is live and convert it | |
| // and according insert conversion for that subreg | |
| auto KillState = SubRegKillSet[MI]; | |
| if (!KillState.first) | |
| insertInstr(MI, Hexagon::V6_vconv_sf_qf32, DefHi, DefHi, | |
| getRegState(Op) | RegState::Kill); | |
| if (!KillState.second) | |
| insertInstr(MI, Hexagon::V6_vconv_sf_qf32, DefLo, DefLo, | |
| getRegState(Op) | RegState::Kill); | |
| } else if (It.second == ConvOperand::Hi) { | |
| insertIEEEToQF(MI, DefHi, Op, true /* sf type */); | |
| if (!Op.isKill()) | |
| insertInstr(MI, Hexagon::V6_vconv_sf_qf32, DefHi, DefHi, | |
| getRegState(Op) | RegState::Kill); | |
| } else { // It.second == ConvOperand::Lo | |
| insertIEEEToQF(MI, DefLo, Op, true /* sf type */); | |
| if (!Op.isKill()) | |
| insertInstr(MI, Hexagon::V6_vconv_sf_qf32, DefLo, DefLo, | |
| getRegState(Op) | RegState::Kill); | |
| } | |
| // Handle sf = qf32. | |
| } else if (MIOpcode == Hexagon::V6_vconv_sf_qf32) { | |
| insertIEEEToQF(MI, DefReg, Op, true /* sf type */); | |
| if (!Op.isKill()) | |
| insertInstr(MI, Hexagon::V6_vconv_sf_qf32, DefReg, DefReg, | |
| getRegState(Op) | RegState::Kill); | |
| } else { | |
| llvm_unreachable("Unhandled non-saturating instruction!"); | |
| } | |
| } | |
| if (QFNonSatMIs.empty()) | |
| return false; | |
| return true; | |
| } | |
| // Calculates the liveness of subregisters (whether killed or not) | |
| // when double register is used. This is necessary because RDF | |
| // carries liveness of the superreg and not the subregisters individually | |
| void HexagonPostRAHandleQFP::collectLivenessForSubregs( | |
| NodeAddr<UseNode *> &UsedNode) { | |
| RegisterRef UR = UsedNode.Addr->getRegRef(*DFG); | |
| NodeAddr<StmtNode *> UseStmt = UsedNode.Addr->getOwner(*DFG); | |
| MachineInstr *UseInstr = UseStmt.Addr->getCode(); | |
| auto UseOp = UseInstr->getOperand(1); | |
| Register UseDefLo = HRI->getSubReg(UseOp.getReg(), Hexagon::vsub_lo); | |
| Register UseDefHi = HRI->getSubReg(UseOp.getReg(), Hexagon::vsub_hi); | |
| NodeSet Visited, Defs; | |
| bool isHiSubRegKilled = true, isLoSubRegKilled = true; | |
| const auto &P = LV->getAllReachingDefsRec(UR, UsedNode, Visited, Defs); | |
| if (!P.second) | |
| return; | |
| for (auto RD : P.first) { | |
| NodeAddr<DefNode *> RegDef = DFG->addr<DefNode *>(RD); | |
| Register RR = RegDef.Addr->getRegRef(*DFG).Id; | |
| if (HRI->isFakeReg(RR)) | |
| continue; | |
| NodeAddr<StmtNode *> RegStmt = RegDef.Addr->getOwner(*DFG); | |
| MachineInstr *ReachDefInstr = RegStmt.Addr->getCode(); | |
| if (ReachDefInstr == nullptr) | |
| continue; | |
| // If the reaching def is WReg, then the kill flag in the use is correct | |
| // since there is no subreg | |
| Register DefReg = ReachDefInstr->getOperand(0).getReg(); | |
| if (Hexagon::HvxWRRegClass.contains(DefReg)) { | |
| if (!UseOp.isKill()) | |
| isHiSubRegKilled = isLoSubRegKilled = false; | |
| // If the reaching ref is VReg, the liveness might be different between | |
| // each of the subreg. Handle them individually. | |
| // Find the other uses after this use for the reaching def. If it exists, | |
| // the subregister is live after the use. | |
| // NOTE: Assumption: The uses are in order in RDF. | |
| } else { | |
| NodeSet UseSet; | |
| getAllRealUses(RegDef, UseSet, LV, DFG); | |
| for (auto UIntr : UseSet) { | |
| NodeAddr<UseNode *> UA = DFG->addr<UseNode *>(UIntr); | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*DFG); | |
| MachineInstr *UseMI = UseStmt.Addr->getCode(); | |
| if (UseMI == nullptr) | |
| continue; | |
| // When we reach the use set a flag to see if there are other uses | |
| // after this. If yes, then the register is not killed. | |
| if (UseMI == UseInstr) | |
| continue; | |
| if (HII->isMIBefore(UseInstr, UseMI) && DefReg == UseDefLo) { | |
| isLoSubRegKilled = false; | |
| break; | |
| } | |
| if (HII->isMIBefore(UseInstr, UseMI) && DefReg == UseDefHi) { | |
| isHiSubRegKilled = false; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| SubRegKillSet[UseInstr] = std::make_pair(isHiSubRegKilled, isLoSubRegKilled); | |
| } | |
| // Store all refill instructions. | |
| void HexagonPostRAHandleQFP::collectQFPStackRefill( | |
| NodeAddr<StmtNode *> *StNode) { | |
| NodeAddr<DefNode *> DfNode = | |
| StNode->Addr->members_if(DFG->IsDef, *DFG).front(); | |
| MachineInstr *MI = StNode->Addr->getCode(); | |
| // Check if operand to this instruction is a frame index. | |
| const MachineOperand &OpFI = MI->getOperand(1); | |
| if (!OpFI.isFI()) | |
| return; | |
| // LLVM_DEBUG(dbgs() << "\n[Stack Refill]: Collecting: "; MI->dump()); | |
| RefillMIs.push_back(DfNode); | |
| } | |
| // Iterate over the uses of the qf generating instruction in RDG graph | |
| // If we get a qf to IEEE convert instruction, add it to a list. | |
| void HexagonPostRAHandleQFP::collectConvQFInstr(NodeAddr<DefNode *> &RegDef) { | |
| NodeSet UseSet; | |
| NodeAddr<StmtNode *> DefStmt = RegDef.Addr->getOwner(*DFG); | |
| MachineInstr *DefInstr = DefStmt.Addr->getCode(); | |
| getAllRealUses(RegDef, UseSet, LV, DFG); | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = DFG->addr<UseNode *>(UI); | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*DFG); | |
| MachineInstr *QFConvInstr = UseStmt.Addr->getCode(); | |
| if (std::find(QFNonSatInstr.begin(), QFNonSatInstr.end(), | |
| QFConvInstr->getOpcode()) != QFNonSatInstr.end()) { | |
| // The use is a double register type. But the def can be hi/lo or double | |
| // type. So conversion needs to be inserted only for the type | |
| // which is in IEEE form. | |
| auto UseReg = QFConvInstr->getOperand(1).getReg(); | |
| auto DefReg = DefInstr->getOperand(0).getReg(); | |
| if (Hexagon::HvxWRRegClass.contains(UseReg)) { | |
| collectLivenessForSubregs(UA); | |
| unsigned Op = ConvOperand::Undefined; | |
| if (QFNonSatMIs.contains(QFConvInstr)) | |
| Op = QFNonSatMIs[QFConvInstr]; | |
| // Def is double type | |
| if (Hexagon::HvxWRRegClass.contains(DefReg)) | |
| Op = ConvOperand::HiLo; | |
| // Def is lo of double type | |
| else if (DefReg == HRI->getSubReg(UseReg, Hexagon::vsub_lo)) | |
| Op |= ConvOperand::Lo; | |
| // Def is hi of double type | |
| else | |
| Op |= ConvOperand::Hi; | |
| QFNonSatMIs[QFConvInstr] = Op; | |
| } else // for other def-use, BothOp is used as default | |
| QFNonSatMIs[QFConvInstr] = ConvOperand::HiLo; | |
| IgnoreInsertConvList.insert(DefInstr); | |
| LLVM_DEBUG(std::string OpType = ""; switch (QFNonSatMIs[QFConvInstr]) { | |
| case ConvOperand::HiLo: | |
| OpType = "HiLo Op"; | |
| break; | |
| case ConvOperand::Lo: | |
| OpType = "Lo Op"; | |
| break; | |
| case ConvOperand::Hi: | |
| OpType = "Hi Op"; | |
| break; | |
| default: | |
| OpType = "Undefined"; | |
| } dbgs() << "Collecting convert instruction with type " | |
| << OpType << " : "; | |
| QFConvInstr->dump()); | |
| } | |
| } | |
| } | |
| // Check if the COPY statements use came from a def which generates | |
| // a qf type. If yes, collect it in a vector. Also, collect copies | |
| // with reaching def other copies (nested copies). | |
| void HexagonPostRAHandleQFP::collectCopies(NodeAddr<StmtNode *> *StNode) { | |
| NodeAddr<DefNode *> CopyDef = | |
| StNode->Addr->members_if(DFG->IsDef, *DFG).front(); | |
| MachineInstr *CopyInstr = StNode->Addr->getCode(); | |
| LLVM_DEBUG(dbgs() << "\nAnalyzing copy: "; StNode->Addr->getCode()->dump()); | |
| for (NodeAddr<UseNode *> UA : StNode->Addr->members_if(DFG->IsUse, *DFG)) { | |
| RegisterRef UR = UA.Addr->getRegRef(*DFG); | |
| NodeSet Visited, Defs; | |
| const auto &P = LV->getAllReachingDefsRec(UR, UA, Visited, Defs); | |
| if (!P.second) { | |
| LLVM_DEBUG({ | |
| dbgs() << "*** Unable to collect all reaching defs for use ***\n" | |
| << PrintNode<UseNode *>(UA, *DFG) << '\n'; | |
| }); | |
| continue; | |
| } | |
| // Note: there can be multiple reaching defs of the copy | |
| for (auto RD : P.first) { | |
| NodeAddr<DefNode *> RegDef = DFG->addr<DefNode *>(RD); | |
| Register RR = RegDef.Addr->getRegRef(*DFG).Id; | |
| if (HRI->isFakeReg(RR)) | |
| continue; | |
| NodeAddr<StmtNode *> RegStmt = RegDef.Addr->getOwner(*DFG); | |
| MachineInstr *ReachDefInstr = RegStmt.Addr->getCode(); | |
| if (ReachDefInstr == nullptr) | |
| continue; | |
| LLVM_DEBUG(dbgs() << "\t[Reaching Def]: "; ReachDefInstr->dump()); | |
| // If the reaching def is a COPY,collect it with reg type ieee | |
| if (ReachDefInstr->getOpcode() == TargetOpcode::COPY) { | |
| auto pairKey = std::make_pair(CopyDef.Id, RegDef.Id); | |
| QFCopys[pairKey] = RegType::ieee; | |
| continue; | |
| } | |
| // If the reaching def is a qf instr, collect the copy. | |
| // reg type is selected based on the op | |
| auto RegT = RegType::undefined; | |
| if (HII->isQFPInstr(ReachDefInstr)) { | |
| if (HII->isQFP32Instr(ReachDefInstr)) { | |
| // check whether the copies register is hvxWR or hvxVR type | |
| // NOTE: Assumption: A copy's reaching def shall not be 2, | |
| // i.e., for each of the subregister. | |
| if (Hexagon::HvxWRRegClass.contains( | |
| ReachDefInstr->getOperand(0).getReg())) | |
| RegT = RegType::qf32_double; | |
| else | |
| RegT = RegType::qf32; | |
| } else if (HII->isQFP16Instr(ReachDefInstr)) { | |
| // Check if qf16 instruction outputs double-wide register | |
| if (Hexagon::HvxWRRegClass.contains( | |
| ReachDefInstr->getOperand(0).getReg())) { | |
| RegT = RegType::qf16_double; | |
| } else { | |
| RegT = RegType::qf16; | |
| } | |
| } | |
| } else { | |
| // if the copy involves non-qf vector registers collect it too | |
| Register CopyReg = CopyInstr->getOperand(1).getReg(); | |
| if (Hexagon::HvxWRRegClass.contains(CopyReg) || | |
| Hexagon::HvxVRRegClass.contains(CopyReg)) | |
| RegT = RegType::ieee; | |
| else | |
| continue; | |
| } | |
| auto pairKey = std::make_pair(CopyDef.Id, RegDef.Id); | |
| QFCopys[pairKey] = RegT; | |
| } | |
| } | |
| } | |
| // Inserts an qf instruction to a list. These instruction | |
| // values are spilled to the stack. | |
| void HexagonPostRAHandleQFP::collectQFPStackSpill( | |
| NodeAddr<StmtNode *> *StNode) { | |
| MachineInstr *MI = StNode->Addr->getCode(); | |
| LLVM_DEBUG(dbgs() << "\n[Stack Spill]: Analyzing: "; MI->dump()); | |
| // Check if operand to this instruction is a frame index. | |
| const MachineOperand &OpFI = MI->getOperand(0); | |
| if (!OpFI.isFI()) | |
| return; | |
| // Pre-RegAlloc | |
| //%46:hvxwr = V6_vmpy_qf32_hf %7:hvxvr, %10:hvxvr | |
| // PS_vstorerw_ai %stack.3, 0, %46:hvxwr :: (store (s2048) into %stack.3, | |
| // align 128) | |
| // Post-RegAlloc | |
| // renamable $w4 = V6_vmpy_qf32_hf killed renamable $v1, renamable $v0 | |
| // PS_vstorerw_ai %stack.3, 0, renamable $w4 :: (store (s2048) into %stack.3, | |
| // align 128) | |
| if (!MI->getOperand(2).isReg()) | |
| return; | |
| // Iterate over the operands of the store instruction to get their reaching | |
| // defs | |
| NodeId QFPDefNode = 0; | |
| for (NodeAddr<UseNode *> UA : StNode->Addr->members_if(DFG->IsUse, *DFG)) { | |
| QFPDefNode = UA.Addr->getReachingDef(); | |
| // Get the defining instruction node(s) | |
| NodeAddr<DefNode *> RegDef = DFG->addr<DefNode *>(QFPDefNode); | |
| assert(QFPDefNode != 0 && "Reaching def computation error"); | |
| NodeAddr<StmtNode *> RegStmt = RegDef.Addr->getOwner(*DFG); | |
| MachineInstr *ReachDefInstr = RegStmt.Addr->getCode(); | |
| if (ReachDefInstr == nullptr) | |
| continue; | |
| LLVM_DEBUG(dbgs() << "[Stack Spill]:\tReaching Def of operand:"; | |
| ReachDefInstr->dump()); | |
| // Reaching Def cannot be a phi instruction. | |
| if (RegDef.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| if (!HII->isQFPInstr(ReachDefInstr)) | |
| continue; | |
| auto RR = RegDef.Addr->getRegRef(*DFG).Id; | |
| if (HRI->isFakeReg(RR)) | |
| continue; | |
| LLVM_DEBUG(dbgs() << "Found a QFPStackSpill via \n"; MI->dump(); | |
| dbgs() << "The corresponding XQF instruction is:\n"; | |
| ReachDefInstr->dump()); | |
| // Collect the spills. | |
| SpillMIs.push_back(std::make_pair(MI, RegDef)); | |
| } | |
| } | |
| // Find the uses of qf generating instructions and conditionally add them | |
| // to a list. | |
| void HexagonPostRAHandleQFP::collectQFUses(NodeAddr<DefNode *> RegDef, | |
| MachineInstr *DefMI) { | |
| NodeSet UseSet; | |
| LLVM_DEBUG(dbgs() << " Finding uses of: "; DefMI->dump();); | |
| getAllRealUses(RegDef, UseSet, LV, DFG); | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = DFG->addr<UseNode *>(UI); | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*DFG); | |
| MachineInstr *UseMI = UseStmt.Addr->getCode(); | |
| LLVM_DEBUG(dbgs() << "\t\t\t[Reached Use of QF operand]: "; UseMI->dump()); | |
| Register UsedReg = UA.Addr->getRegRef(*DFG).Id; | |
| if (QFPSatInstsMap.find(UseMI->getOpcode()) != QFPSatInstsMap.end()) { | |
| if (PossibleMultiReachDefs.count(UseStmt.Id) == 0) { | |
| PossibleMultiReachDefs.insert(UseStmt.Id); | |
| LLVM_DEBUG(dbgs() << "\n[Collect instr with possible multidef]:"; | |
| UseMI->dump()); | |
| } | |
| conditionallyInsert(*UseMI, UsedReg); | |
| } | |
| } | |
| } | |
| // Process the list which can have multiple definitions. A possible case | |
| // can be reaching defs to be a copy and a qf-generating instr respectively. | |
| // Only handle the qf-generating instruction by inserting convert to sf/hf | |
| // after it. Additionally, then handle the reached uses of this reaching | |
| // def since the type has changed to sf/hf from qf after the conversion. | |
| bool HexagonPostRAHandleQFP::HandleMultiReachingDefs() { | |
| bool Changed = false; | |
| // Note: It may seem this loop can further add to PossibleMultiReachDefs. | |
| // But it is not expected to since if any instruction has multiple | |
| // definitions it should already be present in it. | |
| for (auto It : PossibleMultiReachDefs) { | |
| NodeAddr<StmtNode *> Stmt = DFG->addr<StmtNode *>(It); | |
| MachineInstr *Instr = Stmt.Addr->getCode(); | |
| // get the op type for the original instruction. | |
| // True is sf/hf, false is qf | |
| auto Pair = QFUsesMap[Instr]; | |
| unsigned short UseNo = 1; | |
| // Iterate over the operands | |
| for (NodeAddr<UseNode *> UA : Stmt.Addr->members_if(DFG->IsUse, *DFG)) { | |
| // If the type is qf for the operand, | |
| // we skip since there is no scope for mismatch | |
| if ((UseNo == 1 && Pair.first == false) || | |
| (UseNo == 2 && Pair.second == false)) { | |
| ++UseNo; | |
| continue; | |
| } | |
| RegisterRef UR = UA.Addr->getRegRef(*DFG); | |
| NodeSet Visited, Defs; | |
| const auto &P = LV->getAllReachingDefsRec(UR, UA, Visited, Defs); | |
| if (!P.second) { | |
| LLVM_DEBUG({ | |
| dbgs() << "*** Unable to collect all reaching defs for use ***\n" | |
| << PrintNode<UseNode *>(UA, *DFG) << '\n'; | |
| }); | |
| continue; | |
| } | |
| // Iterate over the reaching defs and process the ones which | |
| // generate qf. Ignore the ones which have already been handled | |
| for (auto RD : P.first) { | |
| NodeAddr<DefNode *> RegDef = DFG->addr<DefNode *>(RD); | |
| // Ignore fake reaches | |
| auto RR = RegDef.Addr->getRegRef(*DFG).Id; | |
| if (HRI->isFakeReg(RR)) | |
| continue; | |
| NodeAddr<StmtNode *> RegStmt = RegDef.Addr->getOwner(*DFG); | |
| MachineInstr *ReachDefInstr = RegStmt.Addr->getCode(); | |
| if (ReachDefInstr == nullptr) | |
| continue; | |
| if (!HII->isQFPInstr(ReachDefInstr)) | |
| continue; | |
| if (IgnoreInsertConvList.find(ReachDefInstr) != | |
| IgnoreInsertConvList.end()) | |
| continue; | |
| LLVM_DEBUG(dbgs() << "[Multidef] Handling reaching def:"; | |
| ReachDefInstr->dump()); | |
| auto *MBB = ReachDefInstr->getParent(); | |
| auto &dl = ReachDefInstr->getDebugLoc(); | |
| auto NextReachMI = ++ReachDefInstr->getIterator(); | |
| auto DefOp = ReachDefInstr->getOperand(0); | |
| Register OpReg = DefOp.getReg(); | |
| MachineInstrBuilder MIB; | |
| // For double vector regs, two conversions are inserted. Single | |
| // conversion for qf32 type | |
| if (HII->isQFP32Instr(ReachDefInstr)) { | |
| // if the reaching def is a qf double type | |
| if (Hexagon::HvxWRRegClass.contains( | |
| ReachDefInstr->getOperand(0).getReg())) { | |
| Register RegLo = HRI->getSubReg(OpReg, Hexagon::vsub_lo); | |
| Register RegHi = HRI->getSubReg(OpReg, Hexagon::vsub_hi); | |
| MIB = BuildMI(*MBB, NextReachMI, dl, | |
| HII->get(Hexagon::V6_vconv_sf_qf32), RegLo) | |
| .addReg(RegLo, RegState::Renamable | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "[MultiDef] Inserting convert instruction: "; | |
| MIB.getInstr()->dump()); | |
| MIB = BuildMI(*MBB, NextReachMI, dl, | |
| HII->get(Hexagon::V6_vconv_sf_qf32), RegHi) | |
| .addReg(RegHi, RegState::Renamable | RegState::Kill); | |
| } else { // If the reaching def is a qf type | |
| MIB = BuildMI(*MBB, NextReachMI, dl, | |
| HII->get(Hexagon::V6_vconv_sf_qf32), OpReg) | |
| .addReg(OpReg, RegState::Renamable | RegState::Kill); | |
| } | |
| } | |
| if (HII->isQFP16Instr(ReachDefInstr)) { | |
| MIB = BuildMI(*MBB, NextReachMI, dl, | |
| HII->get(Hexagon::V6_vconv_hf_qf16), OpReg) | |
| .addReg(OpReg, RegState::Renamable | RegState::Kill); | |
| } | |
| LLVM_DEBUG(dbgs() << "[MultiDef] Inserting convert instruction: "; | |
| MIB.getInstr()->dump(); dbgs() << "\tafter instruction: "; | |
| ReachDefInstr->dump()); | |
| // find the uses of the newly transformed to sf/hf and handle | |
| // accordingly. Uses can be vmul/vadd/etc. types or converts which take | |
| // in qf types. | |
| collectQFUses(RegDef, ReachDefInstr); | |
| collectConvQFInstr(RegDef); | |
| IgnoreInsertConvList.insert(ReachDefInstr); | |
| Changed = true; | |
| } | |
| UseNo++; | |
| } | |
| } | |
| return Changed; | |
| } | |
| bool HexagonPostRAHandleQFP::HandleConvertToQfCopies() { | |
| if (ConvertToQfCopies.empty()) | |
| return false; | |
| LLVM_DEBUG( | |
| dbgs() << "\n*** Inserting convert to qf for selected copies ***\n"); | |
| // Any reached use of the copy should not already be collected to be | |
| // converted to IEEE. If present, it means that the reached use has | |
| // other reaching def with type IEEE, other than this copy. | |
| auto CanTransform = [&](MachineInstr *MI, unsigned OpNo) -> bool { | |
| if (QFUsesMap.find(MI) != QFUsesMap.end()) { | |
| auto Entry = QFUsesMap[MI]; | |
| if (OpNo == 1 && Entry.first == true) | |
| return false; | |
| if (OpNo == 2 && Entry.second == true) | |
| return false; | |
| } | |
| return true; | |
| }; | |
| for (auto It : ConvertToQfCopies) { | |
| NodeSet UseSet; | |
| getAllRealUses(It.second.first, UseSet, LV, DFG); | |
| bool transform = true; | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = DFG->addr<UseNode *>(UI); | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*DFG); | |
| MachineInstr *UseMI = UseStmt.Addr->getCode(); | |
| unsigned OpNo = UA.Addr->getOp().getOperandNo(); | |
| if (!CanTransform(UseMI, OpNo)) { | |
| transform = false; | |
| break; | |
| } | |
| } | |
| if (transform) { | |
| LLVM_DEBUG(dbgs() << "\n[HandleConvertToQfCopies]\tProcessing Copy:"; | |
| It.first->dump()); | |
| auto CopyOp = It.first->getOperand(0); | |
| auto NextMIIter = std::next(It.first->getIterator()); | |
| switch (It.second.second) { | |
| case RegType::qf32_double: { | |
| Register DefLo = HRI->getSubReg(CopyOp.getReg(), Hexagon::vsub_lo); | |
| Register DefHi = HRI->getSubReg(CopyOp.getReg(), Hexagon::vsub_hi); | |
| insertIEEEToQF(&*NextMIIter, DefLo, CopyOp, /*is32bit=*/true); | |
| insertIEEEToQF(&*NextMIIter, DefHi, CopyOp, /*is32bit=*/true); | |
| break; | |
| } | |
| case RegType::qf16_double: { | |
| Register DefLo = HRI->getSubReg(CopyOp.getReg(), Hexagon::vsub_lo); | |
| Register DefHi = HRI->getSubReg(CopyOp.getReg(), Hexagon::vsub_hi); | |
| insertIEEEToQF(&*NextMIIter, DefLo, CopyOp, /*is32bit=*/false); | |
| insertIEEEToQF(&*NextMIIter, DefHi, CopyOp, /*is32bit=*/false); | |
| break; | |
| } | |
| case RegType::qf16: | |
| insertIEEEToQF(&*NextMIIter, CopyOp.getReg(), CopyOp, | |
| /*is32bit=*/false); | |
| break; | |
| case RegType::qf32: | |
| insertIEEEToQF(&*NextMIIter, CopyOp.getReg(), CopyOp, /*is32bit=*/true); | |
| break; | |
| default: | |
| break; | |
| } | |
| } else { | |
| collectQFUses(It.second.first, It.first); | |
| collectConvQFInstr(It.second.first); | |
| } | |
| } | |
| return true; | |
| } | |
| bool HexagonPostRAHandleQFP::HandleReachDefOfCopies() { | |
| if (ReachDefOfCopies.empty()) | |
| return false; | |
| MachineInstrBuilder MIB; | |
| for (auto It : ReachDefOfCopies) { | |
| auto *MBB = It.first->getParent(); | |
| auto &dl = It.first->getDebugLoc(); | |
| auto NextMI = ++(It.first)->getIterator(); | |
| auto RegOp = It.first->getOperand(0); | |
| Register OpReg = RegOp.getReg(); | |
| if (It.second == RegType::qf32) | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_sf_qf32), OpReg) | |
| .addReg(OpReg, RegState::Renamable | RegState::Kill); | |
| else if (It.second == RegType::qf16) | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_hf_qf16), OpReg) | |
| .addReg(OpReg, RegState::Renamable | RegState::Kill); | |
| else if (It.second == RegType::qf32_double) { | |
| Register RegLo = HRI->getSubReg(OpReg, Hexagon::vsub_lo); | |
| Register RegHi = HRI->getSubReg(OpReg, Hexagon::vsub_hi); | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_sf_qf32), RegLo) | |
| .addReg(RegLo, RegState::Renamable | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "Inserting convert instruction: "; | |
| MIB.getInstr()->dump()); | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_sf_qf32), RegHi) | |
| .addReg(RegHi, RegState::Renamable | RegState::Kill); | |
| } else if (It.second == RegType::qf16_double) { | |
| Register RegLo = HRI->getSubReg(OpReg, Hexagon::vsub_lo); | |
| Register RegHi = HRI->getSubReg(OpReg, Hexagon::vsub_hi); | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_hf_qf16), RegLo) | |
| .addReg(RegLo, RegState::Renamable | RegState::Kill); | |
| LLVM_DEBUG(dbgs() << "Inserting convert instruction: "; | |
| MIB.getInstr()->dump()); | |
| MIB = | |
| BuildMI(*MBB, NextMI, dl, HII->get(Hexagon::V6_vconv_hf_qf16), RegHi) | |
| .addReg(RegHi, RegState::Renamable | RegState::Kill); | |
| } | |
| LLVM_DEBUG(dbgs() << "Inserting convert instruction: "; | |
| MIB.getInstr()->dump(); dbgs() << "\tafter instruction: "; | |
| It.first->dump()); | |
| } | |
| return true; | |
| } | |
| HexagonPostRAHandleQFP::RegType | |
| HexagonPostRAHandleQFP::HasQfUses(NodeAddr<DefNode *> CopyDef, | |
| MachineInstr *CopyMI) { | |
| NodeSet UseSet; | |
| getAllRealUses(CopyDef, UseSet, LV, DFG); | |
| if (UseSet.size() == 0) | |
| return RegType::undefined; | |
| bool hasQf16Use = false; | |
| bool hasQf32Use = false; | |
| LLVM_DEBUG(dbgs() << "[COPY]\nUses of the copy are: "); | |
| for (auto UI : UseSet) { | |
| NodeAddr<UseNode *> UA = DFG->addr<UseNode *>(UI); | |
| if (UA.Addr->getFlags() & NodeAttrs::PhiRef) | |
| continue; | |
| NodeAddr<StmtNode *> UseStmt = UA.Addr->getOwner(*DFG); | |
| MachineInstr *UseMI = UseStmt.Addr->getCode(); | |
| unsigned OpNo = UA.Addr->getOp().getOperandNo(); | |
| LLVM_DEBUG(dbgs() << "\nCopy's use: "; UseMI->dump()); | |
| // Any reached use should not be a non-qf instruction | |
| if (!HII->usesQFOperand(UseMI, OpNo)) | |
| return RegType::ieee; | |
| // Determine the qf type from the use | |
| if (HII->usesQF16Operand(UseMI, OpNo)) | |
| hasQf16Use = true; | |
| else if (HII->usesQF32Operand(UseMI, OpNo)) | |
| hasQf32Use = true; | |
| // Any reached use should not already be converted to IEEE. | |
| // If present, it means that the reached use has other reaching def | |
| // other than the copy. | |
| if (QFUsesMap.find(UseMI) != QFUsesMap.end()) { | |
| auto Entry = QFUsesMap[UseMI]; | |
| if (OpNo == 1 && Entry.first == true) | |
| return RegType::ieee; | |
| if (OpNo == 2 && Entry.second == true) | |
| return RegType::ieee; | |
| } | |
| } | |
| // Set the output type based on uses | |
| if (hasQf16Use) { | |
| // Check if copy destination is double-wide | |
| if (Hexagon::HvxWRRegClass.contains(CopyMI->getOperand(0).getReg())) | |
| return RegType::qf16_double; | |
| else | |
| return RegType::qf16; | |
| } else if (hasQf32Use) { | |
| if (Hexagon::HvxWRRegClass.contains(CopyMI->getOperand(0).getReg())) | |
| return RegType::qf32_double; | |
| else | |
| return RegType::qf32; | |
| } | |
| return RegType::undefined; | |
| } | |
| // Go through the collected copies and insert conversion to sf/hf | |
| // conditionally *after their reaching defs*. This is done because there | |
| // can be mutliple reaching defs of the copies. Also, check for the uses | |
| // of the reaching def and handle qf uses too by changing opcode or | |
| // inserting converts. | |
| // Additionally, check for the uses of the copy | |
| // and handle them via changing opcode or inserting converts. | |
| bool HexagonPostRAHandleQFP::HandleCopies() { | |
| bool Changed = false; | |
| // If a convert is inserted after a reaching def, add it to ignorelist. | |
| // This is because this reaching def can be reaching def of other copies | |
| // due to non-SSA form. | |
| for (auto It : QFCopys) { | |
| // Get details of the copy node | |
| NodeAddr<DefNode *> CopyNode = DFG->addr<DefNode *>(It.first.first); | |
| NodeAddr<StmtNode *> StNode = CopyNode.Addr->getOwner(*DFG); | |
| [[maybe_unused]] auto *CopyMI = StNode.Addr->getCode(); | |
| LLVM_DEBUG(dbgs() << "\nHandling Reaching Defs of COPY: "; CopyMI->dump(); | |
| std::string Type; switch (It.second) { | |
| case RegType::qf32_double: | |
| Type = "qf32_double"; | |
| break; | |
| case RegType::qf32: | |
| Type = "qf32"; | |
| break; | |
| case RegType::qf16: | |
| Type = "qf16"; | |
| break; | |
| case RegType::qf16_double: | |
| Type = "qf16_double"; | |
| break; | |
| default: | |
| Type = "ieee"; | |
| } dbgs() << "\t Type: " | |
| << Type << "\n"); | |
| // insert convert to IEEE after the reaching def if it generates qf type | |
| RegType RTy = It.second; | |
| if (RTy != RegType::ieee) { | |
| // get details of the reaching def node | |
| NodeAddr<DefNode *> ReachDefNode = DFG->addr<DefNode *>(It.first.second); | |
| NodeAddr<StmtNode *> StNode = ReachDefNode.Addr->getOwner(*DFG); | |
| auto *ReachingDef = StNode.Addr->getCode(); | |
| if (IgnoreInsertConvList.find(ReachingDef) != IgnoreInsertConvList.end()) | |
| continue; | |
| // Collect the reaching defs to be processed later. | |
| ReachDefOfCopies.insert(std::make_pair(ReachingDef, RTy)); | |
| // Process the reached uses of the reaching def now for | |
| // incorrect usage, since the register type has changed | |
| // following the conversion. | |
| LLVM_DEBUG(dbgs() << "\n[COPY]\tAnalyzing uses of the reaching defs \ | |
| of the copy..."); | |
| collectQFUses(ReachDefNode, ReachingDef); | |
| collectConvQFInstr(ReachDefNode); | |
| IgnoreInsertConvList.insert(ReachingDef); | |
| Changed = true; | |
| } | |
| } | |
| // Loop through copies with qf uses | |
| for (auto It : QFCopys) { | |
| // Get details of the copy node | |
| NodeAddr<DefNode *> CopyNode = DFG->addr<DefNode *>(It.first.first); | |
| NodeAddr<StmtNode *> StNode = CopyNode.Addr->getOwner(*DFG); | |
| auto *CopyMI = StNode.Addr->getCode(); | |
| LLVM_DEBUG(dbgs() << "\nHandling COPY: "; CopyMI->dump()); | |
| RegType RTy = It.second; | |
| // Process the reached uses of the copy to find any incorrect | |
| // qf uses. If the copy's uses are all qf types, we need to convert | |
| // its result back to qf | |
| // FIXME: don't include the copy if its the last instruction since | |
| // it is *probably* not possible to insert via BuildMI at the end of BB | |
| RTy = HasQfUses(CopyNode, CopyMI); | |
| if (RTy != RegType::ieee && RTy != RegType::undefined && | |
| (++CopyMI->getIterator() != CopyMI->getParent()->end())) { | |
| if (!ConvertToQfCopies.contains(CopyMI)) { | |
| ConvertToQfCopies[CopyMI] = std::make_pair(CopyNode, RTy); | |
| LLVM_DEBUG(dbgs() << "\n[ConvertToQfCopies]\tAdded copy: "; | |
| CopyMI->dump(); std::string Type; switch (RTy) { | |
| case RegType::qf32_double: | |
| Type = "qf32_double"; | |
| break; | |
| case RegType::qf32: | |
| Type = "qf32"; | |
| break; | |
| case RegType::qf16: | |
| Type = "qf16"; | |
| break; | |
| case RegType::qf16_double: | |
| Type = "qf16_double"; | |
| break; | |
| default: | |
| Type = "ieee"; | |
| } dbgs() << "\t Type: " | |
| << Type << "\n"); | |
| } | |
| continue; | |
| } | |
| LLVM_DEBUG(dbgs() << "\n[COPY]\tAnalyzing uses of the copy..."); | |
| collectQFUses(CopyNode, CopyMI); | |
| collectConvQFInstr(CopyNode); | |
| } | |
| Changed |= HandleReachDefOfCopies(); | |
| Changed |= HandleMultiReachingDefs(); | |
| Changed |= HandleConvertToQfCopies(); | |
| return Changed; | |
| } | |
| // Inserts conversion instruction sf/hf = qf before spilling | |
| // Uses the same physical register for conversion. | |
| // Additinally checks for the uses of the register; and | |
| // conditionally store them to handle later. | |
| bool HexagonPostRAHandleQFP::HandleSpills() { | |
| LLVM_DEBUG(dbgs() << "\n[Handling Spill]\n"); | |
| bool Changed = false; | |
| for (auto It : SpillMIs) { | |
| MachineInstr *MI = It.first; | |
| auto OpC = MI->getOpcode(); | |
| auto NodeDef = It.second; | |
| NodeAddr<StmtNode *> Stmt = NodeDef.Addr->getOwner(*DFG); | |
| MachineInstr *DefMI = Stmt.Addr->getCode(); | |
| auto RegOp = MI->getOperand(2); | |
| Register DefR = RegOp.getReg(); | |
| // handles widened qf16/qf32 instructions. | |
| if (OpC == Hexagon::PS_vstorerw_ai) { | |
| if (!Hexagon::HvxWRRegClass.contains(DefR)) | |
| assert(false && " Unhandled Vector Register class passed\n"); | |
| // Walk through the uses of DefLo and DefHi and if there is QFP | |
| // instructions, the instruction needs to be updated to use sf operands | |
| // instead of qf operands. | |
| collectQFUses(NodeDef, DefMI); | |
| if (IgnoreInsertConvList.find(DefMI) != IgnoreInsertConvList.end()) | |
| continue; | |
| // Collect the reached uses of ReachDefInstr | |
| // which are sf/hf = qf conversion instructions. | |
| collectConvQFInstr(NodeDef); | |
| Register DefLo = HRI->getSubReg(DefR, Hexagon::vsub_lo); | |
| Register DefHi = HRI->getSubReg(DefR, Hexagon::vsub_hi); | |
| // Create two copy instructions, one each for Hi and Lo conditionally. | |
| // Liveness is the same is for the store instruction for the register. | |
| // If both are double registers, two insertions are done. | |
| // If one of the subregs are reaching to the store, conversion is done | |
| // for that subreg. | |
| Register DReg = DefMI->getOperand(0).getReg(); | |
| if (HII->isQFP16Instr(DefMI)) { | |
| if (DefLo == DReg || Hexagon::HvxWRRegClass.contains(DReg)) | |
| insertInstr(DefMI, Hexagon::V6_vconv_hf_qf16, DefLo, DefLo, | |
| getRegState(RegOp) | RegState::Kill); | |
| if (DefHi == DReg || Hexagon::HvxWRRegClass.contains(DReg)) | |
| insertInstr(DefMI, Hexagon::V6_vconv_hf_qf16, DefHi, DefHi, | |
| getRegState(RegOp) | RegState::Kill); | |
| } else if (HII->isQFP32Instr(DefMI)) { | |
| if (DefLo == DReg || Hexagon::HvxWRRegClass.contains(DReg)) | |
| insertInstr(DefMI, Hexagon::V6_vconv_sf_qf32, DefLo, DefLo, | |
| getRegState(RegOp) | RegState::Kill); | |
| if (DefHi == DReg || Hexagon::HvxWRRegClass.contains(DReg)) | |
| insertInstr(DefMI, Hexagon::V6_vconv_sf_qf32, DefHi, DefHi, | |
| getRegState(RegOp) | RegState::Kill); | |
| } | |
| IgnoreInsertConvList.insert(DefMI); | |
| Changed = true; | |
| // Handles instructions which output qf32 type. | |
| } else if (OpC == Hexagon::PS_vstorerv_ai && HII->isQFP32Instr(DefMI)) { | |
| collectQFUses(NodeDef, DefMI); | |
| if (IgnoreInsertConvList.find(DefMI) != IgnoreInsertConvList.end()) | |
| continue; | |
| collectConvQFInstr(NodeDef); | |
| insertInstr(DefMI, Hexagon::V6_vconv_sf_qf32, DefR, DefR, | |
| getRegState(RegOp) | RegState::Kill); | |
| IgnoreInsertConvList.insert(DefMI); | |
| Changed = true; | |
| // Handles instructions which output qf16 type. | |
| } else if (OpC == Hexagon::PS_vstorerv_ai && HII->isQFP16Instr(DefMI)) { | |
| collectQFUses(NodeDef, DefMI); | |
| if (IgnoreInsertConvList.find(DefMI) != IgnoreInsertConvList.end()) | |
| continue; | |
| collectConvQFInstr(NodeDef); | |
| insertInstr(DefMI, Hexagon::V6_vconv_hf_qf16, DefR, DefR, | |
| getRegState(RegOp) | RegState::Kill); | |
| IgnoreInsertConvList.insert(DefMI); | |
| Changed = true; | |
| } else { | |
| LLVM_DEBUG(MI->dump()); | |
| llvm_unreachable("This case is not handled. Look above for MI\n"); | |
| } | |
| } | |
| return Changed; | |
| } | |
| bool HexagonPostRAHandleQFP::runOnMachineFunction(MachineFunction &MF) { | |
| if (DisablePostRAHandleQFloat) | |
| return false; | |
| LLVM_DEBUG( | |
| dbgs() << "\n=== Entering Hexagon Fixup QF spills and refills pass ===\n" | |
| << "Mode: "; | |
| switch (QFloatModeValue) { | |
| case QFloatMode::StrictIEEE: | |
| dbgs() << "Strict IEEE"; | |
| break; | |
| case QFloatMode::IEEE: | |
| dbgs() << "IEEE"; | |
| break; | |
| case QFloatMode::Lossy: | |
| dbgs() << "Lossy"; | |
| break; | |
| default: | |
| dbgs() << "Legacy"; | |
| break; | |
| }; | |
| dbgs() << "\n";); | |
| bool Changed = false; | |
| auto &_HST = MF.getSubtarget<HexagonSubtarget>(); | |
| if (!_HST.useHVXOps()) | |
| return false; | |
| HII = _HST.getInstrInfo(); | |
| // If the mode is legacy, the function may not contain qf instructions | |
| // check if this pass is required to run for legacy mode. | |
| if (QFloatModeValue == QFloatMode::Legacy) | |
| if (!HII->hasQFPInstrs(MF)) | |
| return false; | |
| HRI = _HST.getRegisterInfo(); | |
| MRI = &MF.getRegInfo(); | |
| const auto &MDF = getAnalysis<MachineDominanceFrontierWrapperPass>().getMDF(); | |
| MachineDominatorTree *MDT = | |
| &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree(); | |
| HST = &_HST; | |
| // We need Register Dataflow Graph(RDG) to calculate reaching definitions | |
| // since the Machine code is not in SSA. | |
| // DDG holds the graph on which we iterate for the nodes. | |
| DataFlowGraph G(MF, *HII, *HRI, *MDT, MDF); | |
| G.build(); | |
| DFG = &G; | |
| Liveness L(*MRI, *DFG); | |
| L.computePhiInfo(); | |
| LV = &L; | |
| // Find and save the list of QFP stack spills. | |
| // For refills store all refill instructions to process conditionally later. | |
| NodeAddr<FuncNode *> FA = DFG->getFunc(); | |
| LLVM_DEBUG(dbgs() << "==== [RefMap#]=====:\n " | |
| << Print<NodeAddr<FuncNode *>>(FA, *DFG) << "\n"); | |
| for (NodeAddr<BlockNode *> BA : FA.Addr->members(*DFG)) { | |
| for (auto IA : BA.Addr->members(*DFG)) { | |
| if (!DFG->IsCode<NodeAttrs::Stmt>(IA)) | |
| continue; | |
| // 'SA' holds the Statement node which contains the machine instruction. | |
| NodeAddr<StmtNode *> SA = IA; | |
| MachineInstr *I = SA.Addr->getCode(); | |
| switch (I->getOpcode()) { | |
| case Hexagon::PS_vstorerw_ai: | |
| case Hexagon::PS_vstorerv_ai: | |
| collectQFPStackSpill(&SA); | |
| break; | |
| case Hexagon::PS_vloadrw_ai: | |
| case Hexagon::PS_vloadrv_ai: | |
| collectQFPStackRefill(&SA); | |
| break; | |
| case TargetOpcode::COPY: | |
| collectCopies(&SA); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| // Walk through the spills and insert converts when necessary. | |
| // Additionally, walk though the uses of the converts and | |
| // store them conditionally for later processing. | |
| LLVM_DEBUG(dbgs() << "\nHandling spills...."); | |
| Changed |= HandleSpills(); | |
| SpillMIs.clear(); | |
| // Walk through the uses of the refill instructions. | |
| // Process them if they are used as qf operands. | |
| LLVM_DEBUG(dbgs() << "\nCollecting refills....\n"); | |
| for (NodeAddr<DefNode *> DfNode : RefillMIs) { | |
| NodeAddr<StmtNode *> Stmt = DfNode.Addr->getOwner(*DFG); | |
| MachineInstr *DefMI = Stmt.Addr->getCode(); | |
| collectQFUses(DfNode, DefMI); | |
| collectConvQFInstr(DfNode); | |
| } | |
| RefillMIs.clear(); | |
| LLVM_DEBUG(dbgs() << "\nHandling copies...."); | |
| Changed |= HandleCopies(); | |
| QFCopys.clear(); | |
| PossibleMultiReachDefs.clear(); | |
| ReachDefOfCopies.clear(); | |
| ConvertToQfCopies.clear(); | |
| LLVM_DEBUG(dbgs() << "\n === QF Uses map === "; for (auto It : QFUsesMap) { | |
| dbgs() << "\nInstruction: "; | |
| It.first->dump(); | |
| dbgs() << "\t Property: " << It.second.first << " ," << It.second.second; | |
| }); | |
| // Insert new opcodes as applicable for the refill uses. | |
| // Delete the original instructions. | |
| Changed |= HandleRefills(); | |
| // Handle non-saturating instructions by inserting convert(s) from sf to qf. | |
| Changed |= HandleNonSatInstr(); | |
| QFNonSatMIs.clear(); | |
| // Cleanup | |
| for (auto It : QFUsesMap) | |
| It.first->eraseFromParent(); | |
| QFUsesMap.clear(); | |
| IgnoreInsertConvList.clear(); | |
| // Option if enabled, checks for qf use-def mismatches | |
| if (EnablePostRAXqfCompliance) { | |
| dbgs() << "\nChecking for ABI compliance for XQF post register \ | |
| allocation for function: " | |
| << MF.getName() << "\n"; | |
| DataFlowGraph DFG(MF, *HII, *HRI, *MDT, MDF); | |
| DFG.build(); | |
| Liveness LV(*MRI, DFG); | |
| LV.computeLiveIns(); | |
| XqfPostRADiagnosis VDiag(DFG, LV, HII); | |
| VDiag.runCompliance(); | |
| } | |
| return Changed; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Public Constructor Functions | |
| //===----------------------------------------------------------------------===// | |
| INITIALIZE_PASS_BEGIN(HexagonPostRAHandleQFP, "handle-qfp-spills-refills", | |
| "Hexagon Post RA Handle QFloat", false, false) | |
| INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass) | |
| INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontierWrapperPass) | |
| INITIALIZE_PASS_END(HexagonPostRAHandleQFP, "handle-qfp-spills-refills", | |
| "Hexagon PostRA Handle QFloat", false, false) | |
| FunctionPass *llvm::createHexagonPostRAHandleQFP() { | |
| return new HexagonPostRAHandleQFP(); | |
| } |