brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.8 KiB · 4bd025d Raw
320 lines · cpp
1//=== AArch64PostSelectOptimize.cpp ---------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This pass does post-instruction-selection optimizations in the GlobalISel10// pipeline, before the rest of codegen runs.11//12//===----------------------------------------------------------------------===//13 14#include "AArch64.h"15#include "AArch64TargetMachine.h"16#include "MCTargetDesc/AArch64MCTargetDesc.h"17#include "llvm/ADT/STLExtras.h"18#include "llvm/CodeGen/GlobalISel/Utils.h"19#include "llvm/CodeGen/MachineBasicBlock.h"20#include "llvm/CodeGen/MachineFunctionPass.h"21#include "llvm/CodeGen/MachineInstr.h"22#include "llvm/CodeGen/MachineOperand.h"23#include "llvm/CodeGen/TargetPassConfig.h"24#include "llvm/Support/Debug.h"25#include "llvm/Support/ErrorHandling.h"26 27#define DEBUG_TYPE "aarch64-post-select-optimize"28 29using namespace llvm;30 31namespace {32class AArch64PostSelectOptimize : public MachineFunctionPass {33public:34  static char ID;35 36  AArch64PostSelectOptimize() : MachineFunctionPass(ID) {}37 38  StringRef getPassName() const override {39    return "AArch64 Post Select Optimizer";40  }41 42  bool runOnMachineFunction(MachineFunction &MF) override;43 44  void getAnalysisUsage(AnalysisUsage &AU) const override;45 46private:47  bool optimizeNZCVDefs(MachineBasicBlock &MBB);48  bool doPeepholeOpts(MachineBasicBlock &MBB);49  /// Look for cross regclass copies that can be trivially eliminated.50  bool foldSimpleCrossClassCopies(MachineInstr &MI);51  bool foldCopyDup(MachineInstr &MI);52};53} // end anonymous namespace54 55void AArch64PostSelectOptimize::getAnalysisUsage(AnalysisUsage &AU) const {56  AU.addRequired<TargetPassConfig>();57  AU.setPreservesCFG();58  getSelectionDAGFallbackAnalysisUsage(AU);59  MachineFunctionPass::getAnalysisUsage(AU);60}61 62unsigned getNonFlagSettingVariant(unsigned Opc) {63  switch (Opc) {64  default:65    return 0;66  case AArch64::SUBSXrr:67    return AArch64::SUBXrr;68  case AArch64::SUBSWrr:69    return AArch64::SUBWrr;70  case AArch64::SUBSXrs:71    return AArch64::SUBXrs;72  case AArch64::SUBSWrs:73    return AArch64::SUBWrs;74  case AArch64::SUBSXri:75    return AArch64::SUBXri;76  case AArch64::SUBSWri:77    return AArch64::SUBWri;78  case AArch64::ADDSXrr:79    return AArch64::ADDXrr;80  case AArch64::ADDSWrr:81    return AArch64::ADDWrr;82  case AArch64::ADDSXrs:83    return AArch64::ADDXrs;84  case AArch64::ADDSWrs:85    return AArch64::ADDWrs;86  case AArch64::ADDSXri:87    return AArch64::ADDXri;88  case AArch64::ADDSWri:89    return AArch64::ADDWri;90  case AArch64::SBCSXr:91    return AArch64::SBCXr;92  case AArch64::SBCSWr:93    return AArch64::SBCWr;94  case AArch64::ADCSXr:95    return AArch64::ADCXr;96  case AArch64::ADCSWr:97    return AArch64::ADCWr;98  }99}100 101bool AArch64PostSelectOptimize::doPeepholeOpts(MachineBasicBlock &MBB) {102  bool Changed = false;103  for (auto &MI : make_early_inc_range(MBB)) {104    bool CurrentIterChanged = foldSimpleCrossClassCopies(MI);105    if (!CurrentIterChanged)106      CurrentIterChanged |= foldCopyDup(MI);107    Changed |= CurrentIterChanged;108  }109  return Changed;110}111 112bool AArch64PostSelectOptimize::foldSimpleCrossClassCopies(MachineInstr &MI) {113  auto *MF = MI.getMF();114  auto &MRI = MF->getRegInfo();115 116  if (!MI.isCopy())117    return false;118 119  if (MI.getOperand(1).getSubReg())120    return false; // Don't deal with subreg copies121 122  Register Src = MI.getOperand(1).getReg();123  Register Dst = MI.getOperand(0).getReg();124 125  if (Src.isPhysical() || Dst.isPhysical())126    return false;127 128  const TargetRegisterClass *SrcRC = MRI.getRegClass(Src);129  const TargetRegisterClass *DstRC = MRI.getRegClass(Dst);130 131  if (SrcRC == DstRC)132    return false;133 134 135  if (SrcRC->hasSubClass(DstRC)) {136    // This is the case where the source class is a superclass of the dest, so137    // if the copy is the only user of the source, we can just constrain the138    // source reg to the dest class.139 140    if (!MRI.hasOneNonDBGUse(Src))141      return false; // Only constrain single uses of the source.142 143    // Constrain to dst reg class as long as it's not a weird class that only144    // has a few registers.145    if (!MRI.constrainRegClass(Src, DstRC, /* MinNumRegs */ 25))146      return false;147  } else if (DstRC->hasSubClass(SrcRC)) {148    // This is the inverse case, where the destination class is a superclass of149    // the source. Here, if the copy is the only user, we can just constrain150    // the user of the copy to use the smaller class of the source.151  } else {152    return false;153  }154 155  MRI.replaceRegWith(Dst, Src);156  MI.eraseFromParent();157  return true;158}159 160bool AArch64PostSelectOptimize::foldCopyDup(MachineInstr &MI) {161  if (!MI.isCopy())162    return false;163 164  auto *MF = MI.getMF();165  auto &MRI = MF->getRegInfo();166  auto *TII = MF->getSubtarget().getInstrInfo();167 168  // Optimize COPY(y:GPR, DUP(x:FPR, i)) -> UMOV(y:GPR, x:FPR, i).169  // Here Dst is y and Src is the result of DUP.170  Register Dst = MI.getOperand(0).getReg();171  Register Src = MI.getOperand(1).getReg();172 173  if (!Dst.isVirtual() || !Src.isVirtual())174    return false;175 176  auto TryMatchDUP = [&](const TargetRegisterClass *GPRRegClass,177                         const TargetRegisterClass *FPRRegClass, unsigned DUP,178                         unsigned UMOV) {179    if (MRI.getRegClassOrNull(Dst) != GPRRegClass ||180        MRI.getRegClassOrNull(Src) != FPRRegClass)181      return false;182 183    // There is a special case when one of the uses is COPY(z:FPR, y:GPR).184    // In this case, we get COPY(z:FPR, COPY(y:GPR, DUP(x:FPR, i))), which can185    // be folded by peephole-opt into just DUP(z:FPR, i), so this transform is186    // not worthwhile in that case.187    for (auto &Use : MRI.use_nodbg_instructions(Dst)) {188      if (!Use.isCopy())189        continue;190 191      Register UseOp0 = Use.getOperand(0).getReg();192      Register UseOp1 = Use.getOperand(1).getReg();193      if (UseOp0.isPhysical() || UseOp1.isPhysical())194        return false;195 196      if (MRI.getRegClassOrNull(UseOp0) == FPRRegClass &&197          MRI.getRegClassOrNull(UseOp1) == GPRRegClass)198        return false;199    }200 201    MachineInstr *SrcMI = MRI.getUniqueVRegDef(Src);202    if (!SrcMI || SrcMI->getOpcode() != DUP || !MRI.hasOneNonDBGUse(Src))203      return false;204 205    Register DupSrc = SrcMI->getOperand(1).getReg();206    int64_t DupImm = SrcMI->getOperand(2).getImm();207 208    BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(UMOV), Dst)209        .addReg(DupSrc)210        .addImm(DupImm);211    SrcMI->eraseFromParent();212    MI.eraseFromParent();213    return true;214  };215 216  return TryMatchDUP(&AArch64::GPR32RegClass, &AArch64::FPR32RegClass,217                     AArch64::DUPi32, AArch64::UMOVvi32) ||218         TryMatchDUP(&AArch64::GPR64RegClass, &AArch64::FPR64RegClass,219                     AArch64::DUPi64, AArch64::UMOVvi64);220}221 222bool AArch64PostSelectOptimize::optimizeNZCVDefs(MachineBasicBlock &MBB) {223  // If we find a dead NZCV implicit-def, we224  // - try to convert the operation to a non-flag-setting equivalent225  // - or mark the def as dead to aid later peephole optimizations.226 227  // Use cases:228  // 1)229  // Consider the following code:230  //  FCMPSrr %0, %1, implicit-def $nzcv231  //  %sel1:gpr32 = CSELWr %_, %_, 12, implicit $nzcv232  //  %sub:gpr32 = SUBSWrr %_, %_, implicit-def $nzcv233  //  FCMPSrr %0, %1, implicit-def $nzcv234  //  %sel2:gpr32 = CSELWr %_, %_, 12, implicit $nzcv235  // This kind of code where we have 2 FCMPs each feeding a CSEL can happen236  // when we have a single IR fcmp being used by two selects. During selection,237  // to ensure that there can be no clobbering of nzcv between the fcmp and the238  // csel, we have to generate an fcmp immediately before each csel is239  // selected.240  // However, often we can essentially CSE these together later in MachineCSE.241  // This doesn't work though if there are unrelated flag-setting instructions242  // in between the two FCMPs. In this case, the SUBS defines NZCV243  // but it doesn't have any users, being overwritten by the second FCMP.244  //245  // 2)246  // The instruction selector always emits the flag-setting variant of ADC/SBC247  // while selecting G_UADDE/G_SADDE/G_USUBE/G_SSUBE. If the carry-out of these248  // instructions is never used, we can switch to the non-flag-setting variant.249 250  bool Changed = false;251  auto &MF = *MBB.getParent();252  auto &Subtarget = MF.getSubtarget();253  const auto &TII = Subtarget.getInstrInfo();254  auto TRI = Subtarget.getRegisterInfo();255  auto RBI = Subtarget.getRegBankInfo();256  auto &MRI = MF.getRegInfo();257 258  LiveRegUnits LRU(*MBB.getParent()->getSubtarget().getRegisterInfo());259  LRU.addLiveOuts(MBB);260 261  for (auto &II : instructionsWithoutDebug(MBB.rbegin(), MBB.rend())) {262    bool NZCVDead = LRU.available(AArch64::NZCV);263    if (NZCVDead && II.definesRegister(AArch64::NZCV, /*TRI=*/nullptr)) {264      // The instruction defines NZCV, but NZCV is dead.265      unsigned NewOpc = getNonFlagSettingVariant(II.getOpcode());266      int DeadNZCVIdx =267          II.findRegisterDefOperandIdx(AArch64::NZCV, /*TRI=*/nullptr);268      if (DeadNZCVIdx != -1) {269        if (NewOpc) {270          // If there is an equivalent non-flag-setting op, we convert.271          LLVM_DEBUG(dbgs() << "Post-select optimizer: converting flag-setting "272                               "op: "273                            << II);274          II.setDesc(TII->get(NewOpc));275          II.removeOperand(DeadNZCVIdx);276          // Changing the opcode can result in differing regclass requirements,277          // e.g. SUBSWri uses gpr32 for the dest, whereas SUBWri uses gpr32sp.278          // Constrain the regclasses, possibly introducing a copy.279          constrainOperandRegClass(MF, *TRI, MRI, *TII, *RBI, II, II.getDesc(),280                                   II.getOperand(0), 0);281          Changed |= true;282        } else {283          // Otherwise, we just set the nzcv imp-def operand to be dead, so the284          // peephole optimizations can optimize them further.285          II.getOperand(DeadNZCVIdx).setIsDead();286        }287      }288    }289    LRU.stepBackward(II);290  }291  return Changed;292}293 294bool AArch64PostSelectOptimize::runOnMachineFunction(MachineFunction &MF) {295  if (MF.getProperties().hasFailedISel())296    return false;297  assert(MF.getProperties().hasSelected() && "Expected a selected MF");298 299  bool Changed = false;300  for (auto &BB : MF) {301    Changed |= optimizeNZCVDefs(BB);302    Changed |= doPeepholeOpts(BB);303  }304  return Changed;305}306 307char AArch64PostSelectOptimize::ID = 0;308INITIALIZE_PASS_BEGIN(AArch64PostSelectOptimize, DEBUG_TYPE,309                      "Optimize AArch64 selected instructions",310                      false, false)311INITIALIZE_PASS_END(AArch64PostSelectOptimize, DEBUG_TYPE,312                    "Optimize AArch64 selected instructions", false,313                    false)314 315namespace llvm {316FunctionPass *createAArch64PostSelectOptimize() {317  return new AArch64PostSelectOptimize();318}319} // end namespace llvm320