//===- RISCVInsertVSETVLI.cpp - Insert VSETVLI instructions ---------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements a function pass that inserts VSETVLI instructions where // needed and expands the vl outputs of VLEFF/VLSEGFF to PseudoReadVL // instructions. // // This pass consists of 3 phases: // // Phase 1 collects how each basic block affects VL/VTYPE. // // Phase 2 uses the information from phase 1 to do a data flow analysis to // propagate the VL/VTYPE changes through the function. This gives us the // VL/VTYPE at the start of each basic block. // // Phase 3 inserts VSETVLI instructions in each basic block. Information from // phase 2 is used to prevent inserting a VSETVLI before the first vector // instruction in the block if possible. // //===----------------------------------------------------------------------===// #include "RISCV.h" #include "RISCVSubtarget.h" #include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/LiveDebugVariables.h" #include "llvm/CodeGen/LiveIntervals.h" #include "llvm/CodeGen/LiveStacks.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include <queue> usingnamespacellvm; #define DEBUG_TYPE … #define RISCV_INSERT_VSETVLI_NAME … #define RISCV_COALESCE_VSETVLI_NAME … STATISTIC(NumInsertedVSETVL, "Number of VSETVL inst inserted"); STATISTIC(NumCoalescedVSETVL, "Number of VSETVL inst coalesced"); namespace { /// Given a virtual register \p Reg, return the corresponding VNInfo for it. /// This will return nullptr if the virtual register is an implicit_def or /// if LiveIntervals is not available. static VNInfo *getVNInfoFromReg(Register Reg, const MachineInstr &MI, const LiveIntervals *LIS) { … } static unsigned getVLOpNum(const MachineInstr &MI) { … } static unsigned getSEWOpNum(const MachineInstr &MI) { … } static bool isVectorConfigInstr(const MachineInstr &MI) { … } /// Return true if this is 'vsetvli x0, x0, vtype' which preserves /// VL and only sets VTYPE. static bool isVLPreservingConfig(const MachineInstr &MI) { … } static bool isFloatScalarMoveOrScalarSplatInstr(const MachineInstr &MI) { … } static bool isScalarExtractInstr(const MachineInstr &MI) { … } static bool isScalarInsertInstr(const MachineInstr &MI) { … } static bool isScalarSplatInstr(const MachineInstr &MI) { … } static bool isVSlideInstr(const MachineInstr &MI) { … } /// Get the EEW for a load or store instruction. Return std::nullopt if MI is /// not a load or store which ignores SEW. static std::optional<unsigned> getEEWForLoadStore(const MachineInstr &MI) { … } static bool isNonZeroLoadImmediate(const MachineInstr &MI) { … } /// Return true if this is an operation on mask registers. Note that /// this includes both arithmetic/logical ops and load/store (vlm/vsm). static bool isMaskRegOp(const MachineInstr &MI) { … } /// Return true if the inactive elements in the result are entirely undefined. /// Note that this is different from "agnostic" as defined by the vector /// specification. Agnostic requires each lane to either be undisturbed, or /// take the value -1; no other value is allowed. static bool hasUndefinedPassthru(const MachineInstr &MI) { … } /// Which subfields of VL or VTYPE have values we need to preserve? struct DemandedFields { … }; #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_ATTRIBUTE_USED inline raw_ostream &operator<<(raw_ostream &OS, const DemandedFields &DF) { DF.print(OS); return OS; } #endif static bool isLMUL1OrSmaller(RISCVII::VLMUL LMUL) { … } /// Return true if moving from CurVType to NewVType is /// indistinguishable from the perspective of an instruction (or set /// of instructions) which use only the Used subfields and properties. static bool areCompatibleVTYPEs(uint64_t CurVType, uint64_t NewVType, const DemandedFields &Used) { … } /// Return the fields and properties demanded by the provided instruction. DemandedFields getDemanded(const MachineInstr &MI, const RISCVSubtarget *ST) { … } /// Defines the abstract state with which the forward dataflow models the /// values of the VL and VTYPE registers after insertion. class VSETVLIInfo { … }; #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_ATTRIBUTE_USED inline raw_ostream &operator<<(raw_ostream &OS, const VSETVLIInfo &V) { V.print(OS); return OS; } #endif struct BlockData { … }; class RISCVInsertVSETVLI : public MachineFunctionPass { … }; } // end anonymous namespace char RISCVInsertVSETVLI::ID = …; char &llvm::RISCVInsertVSETVLIID = …; INITIALIZE_PASS(…) // If the AVL is defined by a vsetvli's output vl with the same VLMAX, we can // replace the AVL operand with the AVL of the defining vsetvli. E.g. // // %vl = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1 // $x0 = PseudoVSETVLI %vl:gpr, SEW=32, LMUL=M1 // -> // %vl = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1 // $x0 = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1 void RISCVInsertVSETVLI::forwardVSETVLIAVL(VSETVLIInfo &Info) const { … } // Return a VSETVLIInfo representing the changes made by this VSETVLI or // VSETIVLI instruction. VSETVLIInfo RISCVInsertVSETVLI::getInfoForVSETVLI(const MachineInstr &MI) const { … } static unsigned computeVLMAX(unsigned VLEN, unsigned SEW, RISCVII::VLMUL VLMul) { … } VSETVLIInfo RISCVInsertVSETVLI::computeInfoForInstr(const MachineInstr &MI) const { … } void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertPt, DebugLoc DL, const VSETVLIInfo &Info, const VSETVLIInfo &PrevInfo) { … } /// Return true if a VSETVLI is required to transition from CurInfo to Require /// given a set of DemandedFields \p Used. bool RISCVInsertVSETVLI::needVSETVLI(const DemandedFields &Used, const VSETVLIInfo &Require, const VSETVLIInfo &CurInfo) const { … } // If we don't use LMUL or the SEW/LMUL ratio, then adjust LMUL so that we // maintain the SEW/LMUL ratio. This allows us to eliminate VL toggles in more // places. static VSETVLIInfo adjustIncoming(VSETVLIInfo PrevInfo, VSETVLIInfo NewInfo, DemandedFields &Demanded) { … } // Given an incoming state reaching MI, minimally modifies that state so that it // is compatible with MI. The resulting state is guaranteed to be semantically // legal for MI, but may not be the state requested by MI. void RISCVInsertVSETVLI::transferBefore(VSETVLIInfo &Info, const MachineInstr &MI) const { … } // Given a state with which we evaluated MI (see transferBefore above for why // this might be different that the state MI requested), modify the state to // reflect the changes MI might make. void RISCVInsertVSETVLI::transferAfter(VSETVLIInfo &Info, const MachineInstr &MI) const { … } bool RISCVInsertVSETVLI::computeVLVTYPEChanges(const MachineBasicBlock &MBB, VSETVLIInfo &Info) const { … } void RISCVInsertVSETVLI::computeIncomingVLVTYPE(const MachineBasicBlock &MBB) { … } // If we weren't able to prove a vsetvli was directly unneeded, it might still // be unneeded if the AVL was a phi node where all incoming values are VL // outputs from the last VSETVLI in their respective basic blocks. bool RISCVInsertVSETVLI::needVSETVLIPHI(const VSETVLIInfo &Require, const MachineBasicBlock &MBB) const { … } void RISCVInsertVSETVLI::emitVSETVLIs(MachineBasicBlock &MBB) { … } /// Perform simple partial redundancy elimination of the VSETVLI instructions /// we're about to insert by looking for cases where we can PRE from the /// beginning of one block to the end of one of its predecessors. Specifically, /// this is geared to catch the common case of a fixed length vsetvl in a single /// block loop when it could execute once in the preheader instead. void RISCVInsertVSETVLI::doPRE(MachineBasicBlock &MBB) { … } // Return true if we can mutate PrevMI to match MI without changing any the // fields which would be observed. bool RISCVInsertVSETVLI::canMutatePriorConfig( const MachineInstr &PrevMI, const MachineInstr &MI, const DemandedFields &Used) const { … } void RISCVInsertVSETVLI::coalesceVSETVLIs(MachineBasicBlock &MBB) const { … } void RISCVInsertVSETVLI::insertReadVL(MachineBasicBlock &MBB) { … } bool RISCVInsertVSETVLI::runOnMachineFunction(MachineFunction &MF) { … } /// Returns an instance of the Insert VSETVLI pass. FunctionPass *llvm::createRISCVInsertVSETVLIPass() { … }