| //===-- RISCVQCRelaxMarking.cpp - Mark Instructions for QC Relaxations ----===// |
| // |
| // 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 pass adds access tags to some instructions which are used by the |
| // assembler to emit marker relocations, which enable some code-size relaxations |
| // for Xqcilo/Xqcili. |
| // |
| // The pass is looking for the following sequences: |
| // |
| // $dst1 = QC_E_LI sym |
| // $dst2 = Load killed $dst1, 0 |
| // |
| // $dst1 = QC_E_LI sym |
| // Store $dst2, killed $dst1, 0 |
| // |
| // In either case, the Load/Store is modified to become a |
| // PseudoQCAccess<Load/Store>, with an additional operand that represents the |
| // accessed symbolic address, which will become the contents of a |
| // `R_RISCV_QC_ACCESS_*` relocation on the emitted instruction. |
| // |
| // FIXME: The intention is this pass does not change the size of any |
| // instructions, but right now it has to do instruction compression as the |
| // CompressPat infrastructure cannot handle compressing the `%qc.access(...)` |
| // operand. Symbolic operands are not usually compressible, but this one is as |
| // we have relocations for both 32-bit and 16-bit instructions (and the |
| // relocation does not care about the fields of the instruction). |
| |
| #include "RISCV.h" |
| #include "RISCVSubtarget.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/CodeGen/Passes.h" |
| #include "llvm/CodeGen/RegisterScavenging.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "riscv-qc-relax-marking" |
| #define RISCV_QC_RELAX_MARKING_NAME "RISC-V QC Relaxation Marking" |
| |
| STATISTIC(NumMarked, "Number of Loads/Stores Marked"); |
| |
| namespace { |
| |
| struct RISCVQCRelaxMarking : public MachineFunctionPass { |
| static char ID; |
| |
| bool runOnMachineFunction(MachineFunction &) override; |
| |
| RISCVQCRelaxMarking() : MachineFunctionPass(ID) {} |
| |
| StringRef getPassName() const override { return RISCV_QC_RELAX_MARKING_NAME; } |
| }; |
| |
| } // end namespace |
| |
| char RISCVQCRelaxMarking::ID = 0; |
| |
| INITIALIZE_PASS(RISCVQCRelaxMarking, DEBUG_TYPE, RISCV_QC_RELAX_MARKING_NAME, |
| false, false) |
| |
| /// Returns an instance of the Make Compressible Optimization pass. |
| FunctionPass *llvm::createRISCVQCRelaxMarkingPass() { |
| return new RISCVQCRelaxMarking(); |
| } |
| |
| static bool isUImm7LSB000(const MachineOperand &MO) { |
| return MO.isImm() && isShiftedUInt<4, 3>(MO.getImm()); |
| } |
| |
| static bool isUImm2LSB0(const MachineOperand &MO) { |
| return MO.isImm() && isShiftedUInt<1, 1>(MO.getImm()); |
| } |
| |
| static bool isUImm2(const MachineOperand &MO) { |
| return MO.isImm() && isUInt<2>(MO.getImm()); |
| } |
| |
| static bool isGPRC(const MachineOperand &MO) { |
| return RISCV::GPRCRegClass.contains(MO.getReg()); |
| } |
| |
| static unsigned getQCMarkedOpcode(const MachineInstr &MI, |
| const RISCVSubtarget &STI) { |
| switch (MI.getOpcode()) { |
| case RISCV::LB: |
| // No c.lb |
| return RISCV::PseudoQCAccessLB; |
| case RISCV::LBU: |
| if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm2(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_LBU; |
| return RISCV::PseudoQCAccessLBU; |
| case RISCV::LH: |
| if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_LH; |
| return RISCV::PseudoQCAccessLH; |
| case RISCV::LHU: |
| if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_LHU; |
| return RISCV::PseudoQCAccessLHU; |
| case RISCV::LW: |
| if (STI.hasStdExtZca() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm7LSB000(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_LW; |
| return RISCV::PseudoQCAccessLW; |
| case RISCV::SB: |
| if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm2(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_SB; |
| return RISCV::PseudoQCAccessSB; |
| case RISCV::SH: |
| if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_SH; |
| return RISCV::PseudoQCAccessSH; |
| case RISCV::SW: |
| if (STI.hasStdExtZca() && isGPRC(MI.getOperand(0)) && |
| isGPRC(MI.getOperand(1)) && isUImm7LSB000(MI.getOperand(2))) |
| return RISCV::PseudoQCAccessC_SW; |
| return RISCV::PseudoQCAccessSW; |
| default: |
| reportFatalInternalError( |
| "Unhandled Opcode: No Corresponding Marked Opcode"); |
| } |
| } |
| |
| bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) { |
| if (skipFunction(MF.getFunction())) |
| return false; |
| |
| // This is only relevant for QC.E.LI with a symbol, which we only use in the |
| // small code model. |
| if (MF.getTarget().getCodeModel() != CodeModel::Small) |
| return false; |
| |
| auto &STI = MF.getSubtarget<RISCVSubtarget>(); |
| // We need QC.E.LI instructions to perform this optimisation, which needs |
| // 32-bit and Xqcili. The markers are only needed when linker relaxations are |
| // enabled. |
| if (STI.is64Bit() || !STI.hasVendorXqcili() || !STI.enableLinkerRelax()) |
| return false; |
| |
| const RISCVInstrInfo *TII = STI.getInstrInfo(); |
| |
| bool Changed = false; |
| for (MachineBasicBlock &MBB : MF) { |
| for (auto MI = MBB.begin(), E = MBB.end(); MI != E; MI++) { |
| auto NextMI = std::next(MI); |
| if (NextMI == E) |
| break; |
| |
| // Looking for QC.E.LI followed by a load or store |
| if (MI->getOpcode() != RISCV::QC_E_LI || |
| !(RISCVInstrInfo::isBaseLoad(*NextMI) || |
| RISCVInstrInfo::isBaseStore(*NextMI))) |
| continue; |
| |
| LLVM_DEBUG(dbgs() << "Found QC_E_LI " << *MI); |
| LLVM_DEBUG(dbgs() << "Followed by Load/Store " << *NextMI); |
| |
| if (MI->getOperand(0).getReg() != NextMI->getOperand(1).getReg()) |
| continue; |
| if (!NextMI->getOperand(1).isKill()) |
| continue; |
| |
| // This is unsafe for stores where the access address is being stored. |
| if (RISCVInstrInfo::isBaseStore(*NextMI) && |
| MI->getOperand(0).getReg() == NextMI->getOperand(0).getReg()) |
| continue; |
| |
| MachineOperand &SymOp = MI->getOperand(1); |
| if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() && |
| !SymOp.isCPI()) |
| continue; |
| |
| unsigned NewOpc = getQCMarkedOpcode(*NextMI, STI); |
| LLVM_DEBUG(dbgs() << "Load/Store " << TII->getName(NextMI->getOpcode()) |
| << " will become " << TII->getName(NewOpc) << "\n"); |
| MachineInstrBuilder MIB = |
| BuildMI(MBB, NextMI, NextMI->getDebugLoc(), TII->get(NewOpc)) |
| .add(NextMI->getOperand(0)) |
| .add(NextMI->getOperand(1)) |
| .add(NextMI->getOperand(2)) |
| .cloneMemRefs(*NextMI); |
| |
| if (SymOp.isSymbol()) { |
| MIB.addExternalSymbol(SymOp.getSymbolName(), RISCVII::MO_QC_ACCESS); |
| } else if (SymOp.isGlobal()) { |
| MIB.addGlobalAddress(SymOp.getGlobal(), SymOp.getOffset(), |
| RISCVII::MO_QC_ACCESS); |
| } else if (SymOp.isMCSymbol()) { |
| MachineOperand MO = MachineOperand::CreateMCSymbol( |
| SymOp.getMCSymbol(), RISCVII::MO_QC_ACCESS); |
| MO.setOffset(SymOp.getOffset()); |
| MIB.add(MO); |
| } else if (SymOp.isCPI()) { |
| MIB.addConstantPoolIndex(SymOp.getIndex(), SymOp.getOffset(), |
| RISCVII::MO_QC_ACCESS); |
| } else { |
| reportFatalInternalError("Unhandled SymOp Kind"); |
| } |
| |
| NextMI->removeFromParent(); |
| NumMarked++; |
| Changed |= true; |
| } |
| } |
| |
| return Changed; |
| } |