| 1 | //===-- RISCVQCRelaxMarking.cpp - Mark Instructions for QC Relaxations ----===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This pass adds access tags to some instructions which are used by the |
| 10 | // assembler to emit marker relocations, which enable some code-size relaxations |
| 11 | // for Xqcilo/Xqcili. |
| 12 | // |
| 13 | // The pass is looking for the following sequences: |
| 14 | // |
| 15 | // $dst1 = QC_E_LI sym |
| 16 | // $dst2 = Load killed $dst1, 0 |
| 17 | // |
| 18 | // $dst1 = QC_E_LI sym |
| 19 | // Store $dst2, killed $dst1, 0 |
| 20 | // |
| 21 | // In either case, the Load/Store is modified to become a |
| 22 | // PseudoQCAccess<Load/Store>, with an additional operand that represents the |
| 23 | // accessed symbolic address, which will become the contents of a |
| 24 | // `R_RISCV_QC_ACCESS_*` relocation on the emitted instruction. |
| 25 | // |
| 26 | // FIXME: The intention is this pass does not change the size of any |
| 27 | // instructions, but right now it has to do instruction compression as the |
| 28 | // CompressPat infrastructure cannot handle compressing the `%qc.access(...)` |
| 29 | // operand. Symbolic operands are not usually compressible, but this one is as |
| 30 | // we have relocations for both 32-bit and 16-bit instructions (and the |
| 31 | // relocation does not care about the fields of the instruction). |
| 32 | |
| 33 | #include "RISCV.h" |
| 34 | #include "RISCVSubtarget.h" |
| 35 | #include "llvm/ADT/STLExtras.h" |
| 36 | #include "llvm/ADT/Statistic.h" |
| 37 | #include "llvm/CodeGen/Passes.h" |
| 38 | #include "llvm/CodeGen/RegisterScavenging.h" |
| 39 | #include "llvm/MC/TargetRegistry.h" |
| 40 | #include "llvm/Support/Debug.h" |
| 41 | #include "llvm/Support/ErrorHandling.h" |
| 42 | |
| 43 | using namespace llvm; |
| 44 | |
| 45 | #define DEBUG_TYPE "riscv-qc-relax-marking" |
| 46 | #define RISCV_QC_RELAX_MARKING_NAME "RISC-V QC Relaxation Marking" |
| 47 | |
| 48 | STATISTIC(NumMarked, "Number of Loads/Stores Marked" ); |
| 49 | |
| 50 | namespace { |
| 51 | |
| 52 | struct RISCVQCRelaxMarking : public MachineFunctionPass { |
| 53 | static char ID; |
| 54 | |
| 55 | bool runOnMachineFunction(MachineFunction &) override; |
| 56 | |
| 57 | RISCVQCRelaxMarking() : MachineFunctionPass(ID) {} |
| 58 | |
| 59 | StringRef getPassName() const override { return RISCV_QC_RELAX_MARKING_NAME; } |
| 60 | }; |
| 61 | |
| 62 | } // end namespace |
| 63 | |
| 64 | char RISCVQCRelaxMarking::ID = 0; |
| 65 | |
| 66 | INITIALIZE_PASS(RISCVQCRelaxMarking, DEBUG_TYPE, RISCV_QC_RELAX_MARKING_NAME, |
| 67 | false, false) |
| 68 | |
| 69 | /// Returns an instance of the Make Compressible Optimization pass. |
| 70 | FunctionPass *llvm::createRISCVQCRelaxMarkingPass() { |
| 71 | return new RISCVQCRelaxMarking(); |
| 72 | } |
| 73 | |
| 74 | static bool isUImm7LSB000(const MachineOperand &MO) { |
| 75 | return MO.isImm() && isShiftedUInt<4, 3>(x: MO.getImm()); |
| 76 | } |
| 77 | |
| 78 | static bool isUImm2LSB0(const MachineOperand &MO) { |
| 79 | return MO.isImm() && isShiftedUInt<1, 1>(x: MO.getImm()); |
| 80 | } |
| 81 | |
| 82 | static bool isUImm2(const MachineOperand &MO) { |
| 83 | return MO.isImm() && isUInt<2>(x: MO.getImm()); |
| 84 | } |
| 85 | |
| 86 | static bool isGPRC(const MachineOperand &MO) { |
| 87 | return RISCV::GPRCRegClass.contains(Reg: MO.getReg()); |
| 88 | } |
| 89 | |
| 90 | static unsigned getQCMarkedOpcode(const MachineInstr &MI, |
| 91 | const RISCVSubtarget &STI) { |
| 92 | switch (MI.getOpcode()) { |
| 93 | case RISCV::LB: |
| 94 | // No c.lb |
| 95 | return RISCV::PseudoQCAccessLB; |
| 96 | case RISCV::LBU: |
| 97 | if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 98 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm2(MO: MI.getOperand(i: 2))) |
| 99 | return RISCV::PseudoQCAccessC_LBU; |
| 100 | return RISCV::PseudoQCAccessLBU; |
| 101 | case RISCV::LH: |
| 102 | if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 103 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2))) |
| 104 | return RISCV::PseudoQCAccessC_LH; |
| 105 | return RISCV::PseudoQCAccessLH; |
| 106 | case RISCV::LHU: |
| 107 | if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 108 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2))) |
| 109 | return RISCV::PseudoQCAccessC_LHU; |
| 110 | return RISCV::PseudoQCAccessLHU; |
| 111 | case RISCV::LW: |
| 112 | if (STI.hasStdExtZca() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 113 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm7LSB000(MO: MI.getOperand(i: 2))) |
| 114 | return RISCV::PseudoQCAccessC_LW; |
| 115 | return RISCV::PseudoQCAccessLW; |
| 116 | case RISCV::SB: |
| 117 | if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 118 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm2(MO: MI.getOperand(i: 2))) |
| 119 | return RISCV::PseudoQCAccessC_SB; |
| 120 | return RISCV::PseudoQCAccessSB; |
| 121 | case RISCV::SH: |
| 122 | if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 123 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2))) |
| 124 | return RISCV::PseudoQCAccessC_SH; |
| 125 | return RISCV::PseudoQCAccessSH; |
| 126 | case RISCV::SW: |
| 127 | if (STI.hasStdExtZca() && isGPRC(MO: MI.getOperand(i: 0)) && |
| 128 | isGPRC(MO: MI.getOperand(i: 1)) && isUImm7LSB000(MO: MI.getOperand(i: 2))) |
| 129 | return RISCV::PseudoQCAccessC_SW; |
| 130 | return RISCV::PseudoQCAccessSW; |
| 131 | default: |
| 132 | reportFatalInternalError( |
| 133 | reason: "Unhandled Opcode: No Corresponding Marked Opcode" ); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) { |
| 138 | if (skipFunction(F: MF.getFunction())) |
| 139 | return false; |
| 140 | |
| 141 | // This is only relevant for QC.E.LI with a symbol, which we only use in the |
| 142 | // small code model. |
| 143 | if (MF.getTarget().getCodeModel() != CodeModel::Small) |
| 144 | return false; |
| 145 | |
| 146 | auto &STI = MF.getSubtarget<RISCVSubtarget>(); |
| 147 | // We need QC.E.LI instructions to perform this optimisation, which needs |
| 148 | // 32-bit and Xqcili. The markers are only needed when linker relaxations are |
| 149 | // enabled. |
| 150 | if (STI.is64Bit() || !STI.hasVendorXqcili() || !STI.enableLinkerRelax()) |
| 151 | return false; |
| 152 | |
| 153 | const RISCVInstrInfo *TII = STI.getInstrInfo(); |
| 154 | |
| 155 | bool Changed = false; |
| 156 | for (MachineBasicBlock &MBB : MF) { |
| 157 | for (auto MI = MBB.begin(), E = MBB.end(); MI != E; MI++) { |
| 158 | auto NextMI = std::next(x: MI); |
| 159 | if (NextMI == E) |
| 160 | break; |
| 161 | |
| 162 | // Looking for QC.E.LI followed by a load or store |
| 163 | if (MI->getOpcode() != RISCV::QC_E_LI || |
| 164 | !(RISCVInstrInfo::isBaseLoad(MI: *NextMI) || |
| 165 | RISCVInstrInfo::isBaseStore(MI: *NextMI))) |
| 166 | continue; |
| 167 | |
| 168 | LLVM_DEBUG(dbgs() << "Found QC_E_LI " << *MI); |
| 169 | LLVM_DEBUG(dbgs() << "Followed by Load/Store " << *NextMI); |
| 170 | |
| 171 | if (MI->getOperand(i: 0).getReg() != NextMI->getOperand(i: 1).getReg()) |
| 172 | continue; |
| 173 | if (!NextMI->getOperand(i: 1).isKill()) |
| 174 | continue; |
| 175 | |
| 176 | // This is unsafe for stores where the access address is being stored. |
| 177 | if (RISCVInstrInfo::isBaseStore(MI: *NextMI) && |
| 178 | MI->getOperand(i: 0).getReg() == NextMI->getOperand(i: 0).getReg()) |
| 179 | continue; |
| 180 | |
| 181 | MachineOperand &SymOp = MI->getOperand(i: 1); |
| 182 | if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() && |
| 183 | !SymOp.isCPI()) |
| 184 | continue; |
| 185 | |
| 186 | unsigned NewOpc = getQCMarkedOpcode(MI: *NextMI, STI); |
| 187 | LLVM_DEBUG(dbgs() << "Load/Store " << TII->getName(NextMI->getOpcode()) |
| 188 | << " will become " << TII->getName(NewOpc) << "\n" ); |
| 189 | MachineInstrBuilder MIB = |
| 190 | BuildMI(BB&: MBB, I: NextMI, MIMD: NextMI->getDebugLoc(), MCID: TII->get(Opcode: NewOpc)) |
| 191 | .add(MO: NextMI->getOperand(i: 0)) |
| 192 | .add(MO: NextMI->getOperand(i: 1)) |
| 193 | .add(MO: NextMI->getOperand(i: 2)) |
| 194 | .cloneMemRefs(OtherMI: *NextMI); |
| 195 | |
| 196 | if (SymOp.isSymbol()) { |
| 197 | MIB.addExternalSymbol(FnName: SymOp.getSymbolName(), TargetFlags: RISCVII::MO_QC_ACCESS); |
| 198 | } else if (SymOp.isGlobal()) { |
| 199 | MIB.addGlobalAddress(GV: SymOp.getGlobal(), Offset: SymOp.getOffset(), |
| 200 | TargetFlags: RISCVII::MO_QC_ACCESS); |
| 201 | } else if (SymOp.isMCSymbol()) { |
| 202 | MachineOperand MO = MachineOperand::CreateMCSymbol( |
| 203 | Sym: SymOp.getMCSymbol(), TargetFlags: RISCVII::MO_QC_ACCESS); |
| 204 | MO.setOffset(SymOp.getOffset()); |
| 205 | MIB.add(MO); |
| 206 | } else if (SymOp.isCPI()) { |
| 207 | MIB.addConstantPoolIndex(Idx: SymOp.getIndex(), Offset: SymOp.getOffset(), |
| 208 | TargetFlags: RISCVII::MO_QC_ACCESS); |
| 209 | } else { |
| 210 | reportFatalInternalError(reason: "Unhandled SymOp Kind" ); |
| 211 | } |
| 212 | |
| 213 | NextMI->removeFromParent(); |
| 214 | NumMarked++; |
| 215 | Changed |= true; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return Changed; |
| 220 | } |
| 221 | |