LCOV - code coverage report
Current view: top level - lib/CodeGen/GlobalISel - CombinerHelper.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 75 104 72.1 %
Date: 2018-10-20 13:21:21 Functions: 3 8 37.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //== ---lib/CodeGen/GlobalISel/GICombinerHelper.cpp --------------------- == //
       2             : //
       3             : //                     The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : #include "llvm/CodeGen/GlobalISel/Combiner.h"
      10             : #include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
      11             : #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
      12             : #include "llvm/CodeGen/GlobalISel/Utils.h"
      13             : #include "llvm/CodeGen/MachineInstr.h"
      14             : #include "llvm/CodeGen/MachineRegisterInfo.h"
      15             : #include "llvm/CodeGen/TargetInstrInfo.h"
      16             : 
      17             : #define DEBUG_TYPE "gi-combine"
      18             : 
      19             : using namespace llvm;
      20             : 
      21        1821 : CombinerHelper::CombinerHelper(CombinerChangeObserver &Observer,
      22        1821 :                                MachineIRBuilder &B)
      23        1821 :     : Builder(B), MRI(Builder.getMF().getRegInfo()), Observer(Observer) {}
      24             : 
      25           0 : void CombinerHelper::eraseInstr(MachineInstr &MI) {
      26           0 :   Observer.erasedInstr(MI);
      27           0 : }
      28           0 : void CombinerHelper::scheduleForVisit(MachineInstr &MI) {
      29           0 :   Observer.createdInstr(MI);
      30           0 : }
      31             : 
      32           0 : bool CombinerHelper::tryCombineCopy(MachineInstr &MI) {
      33           0 :   if (MI.getOpcode() != TargetOpcode::COPY)
      34             :     return false;
      35           0 :   unsigned DstReg = MI.getOperand(0).getReg();
      36           0 :   unsigned SrcReg = MI.getOperand(1).getReg();
      37           0 :   LLT DstTy = MRI.getType(DstReg);
      38             :   LLT SrcTy = MRI.getType(SrcReg);
      39             :   // Simple Copy Propagation.
      40             :   // a(sx) = COPY b(sx) -> Replace all uses of a with b.
      41           0 :   if (DstTy.isValid() && SrcTy.isValid() && DstTy == SrcTy) {
      42           0 :     MI.eraseFromParent();
      43           0 :     MRI.replaceRegWith(DstReg, SrcReg);
      44           0 :     return true;
      45             :   }
      46             :   return false;
      47             : }
      48             : 
      49             : namespace {
      50             : struct PreferredTuple {
      51             :   LLT Ty;                // The result type of the extend.
      52             :   unsigned ExtendOpcode; // G_ANYEXT/G_SEXT/G_ZEXT
      53             :   MachineInstr *MI;
      54             : };
      55             : 
      56             : /// Select a preference between two uses. CurrentUse is the current preference
      57             : /// while *ForCandidate is attributes of the candidate under consideration.
      58          44 : PreferredTuple ChoosePreferredUse(PreferredTuple &CurrentUse,
      59             :                                   const LLT &TyForCandidate,
      60             :                                   unsigned OpcodeForCandidate,
      61             :                                   MachineInstr *MIForCandidate) {
      62          44 :   if (!CurrentUse.Ty.isValid()) {
      63          31 :     if (CurrentUse.ExtendOpcode == OpcodeForCandidate ||
      64             :         CurrentUse.ExtendOpcode == TargetOpcode::G_ANYEXT)
      65          29 :       return {TyForCandidate, OpcodeForCandidate, MIForCandidate};
      66           2 :     return CurrentUse;
      67             :   }
      68             : 
      69             :   // We permit the extend to hoist through basic blocks but this is only
      70             :   // sensible if the target has extending loads. If you end up lowering back
      71             :   // into a load and extend during the legalizer then the end result is
      72             :   // hoisting the extend up to the load.
      73             : 
      74             :   // Prefer defined extensions to undefined extensions as these are more
      75             :   // likely to reduce the number of instructions.
      76          13 :   if (OpcodeForCandidate == TargetOpcode::G_ANYEXT &&
      77           4 :       CurrentUse.ExtendOpcode != TargetOpcode::G_ANYEXT)
      78           1 :     return CurrentUse;
      79          12 :   else if (CurrentUse.ExtendOpcode == TargetOpcode::G_ANYEXT &&
      80             :            OpcodeForCandidate != TargetOpcode::G_ANYEXT)
      81           8 :     return {TyForCandidate, OpcodeForCandidate, MIForCandidate};
      82             : 
      83             :   // Prefer sign extensions to zero extensions as sign-extensions tend to be
      84             :   // more expensive.
      85           0 :   if (CurrentUse.Ty == TyForCandidate) {
      86           4 :     if (CurrentUse.ExtendOpcode == TargetOpcode::G_SEXT &&
      87             :         OpcodeForCandidate == TargetOpcode::G_ZEXT)
      88           0 :       return CurrentUse;
      89           4 :     else if (CurrentUse.ExtendOpcode == TargetOpcode::G_ZEXT &&
      90             :              OpcodeForCandidate == TargetOpcode::G_SEXT)
      91           1 :       return {TyForCandidate, OpcodeForCandidate, MIForCandidate};
      92             :   }
      93             : 
      94             :   // This is potentially target specific. We've chosen the largest type
      95             :   // because G_TRUNC is usually free. One potential catch with this is that
      96             :   // some targets have a reduced number of larger registers than smaller
      97             :   // registers and this choice potentially increases the live-range for the
      98             :   // larger value.
      99           3 :   if (TyForCandidate.getSizeInBits() > CurrentUse.Ty.getSizeInBits()) {
     100           0 :     return {TyForCandidate, OpcodeForCandidate, MIForCandidate};
     101             :   }
     102           3 :   return CurrentUse;
     103             : }
     104             : 
     105             : /// Find a suitable place to insert some instructions and insert them. This
     106             : /// function accounts for special cases like inserting before a PHI node.
     107             : /// The current strategy for inserting before PHI's is to duplicate the
     108             : /// instructions for each predecessor. However, while that's ok for G_TRUNC
     109             : /// on most targets since it generally requires no code, other targets/cases may
     110             : /// want to try harder to find a dominating block.
     111           0 : static void InsertInsnsWithoutSideEffectsBeforeUse(
     112             :     MachineIRBuilder &Builder, MachineInstr &DefMI, MachineOperand &UseMO,
     113             :     std::function<void(MachineBasicBlock *, MachineBasicBlock::iterator)>
     114             :         Inserter) {
     115           0 :   MachineInstr &UseMI = *UseMO.getParent();
     116             : 
     117           0 :   MachineBasicBlock *InsertBB = UseMI.getParent();
     118             : 
     119             :   // If the use is a PHI then we want the predecessor block instead.
     120             :   if (UseMI.isPHI()) {
     121             :     MachineOperand *PredBB = std::next(&UseMO);
     122           0 :     InsertBB = PredBB->getMBB();
     123             :   }
     124             : 
     125             :   // If the block is the same block as the def then we want to insert just after
     126             :   // the def instead of at the start of the block.
     127           0 :   if (InsertBB == DefMI.getParent()) {
     128             :     MachineBasicBlock::iterator InsertPt = &DefMI;
     129           0 :     Inserter(InsertBB, std::next(InsertPt));
     130           0 :     return;
     131             :   }
     132             : 
     133             :   // Otherwise we want the start of the BB
     134           0 :   Inserter(InsertBB, InsertBB->getFirstNonPHI());
     135             : }
     136             : } // end anonymous namespace
     137             : 
     138         202 : bool CombinerHelper::tryCombineExtendingLoads(MachineInstr &MI) {
     139             :   struct InsertionPoint {
     140             :     MachineOperand *UseMO;
     141             :     MachineBasicBlock *InsertIntoBB;
     142             :     MachineBasicBlock::iterator InsertBefore;
     143             :     InsertionPoint(MachineOperand *UseMO, MachineBasicBlock *InsertIntoBB,
     144             :                    MachineBasicBlock::iterator InsertBefore)
     145          16 :         : UseMO(UseMO), InsertIntoBB(InsertIntoBB), InsertBefore(InsertBefore) {
     146             :     }
     147             :   };
     148             : 
     149             :   // We match the loads and follow the uses to the extend instead of matching
     150             :   // the extends and following the def to the load. This is because the load
     151             :   // must remain in the same position for correctness (unless we also add code
     152             :   // to find a safe place to sink it) whereas the extend is freely movable.
     153             :   // It also prevents us from duplicating the load for the volatile case or just
     154             :   // for performance.
     155             : 
     156         202 :   if (MI.getOpcode() != TargetOpcode::G_LOAD &&
     157         209 :       MI.getOpcode() != TargetOpcode::G_SEXTLOAD &&
     158             :       MI.getOpcode() != TargetOpcode::G_ZEXTLOAD)
     159             :     return false;
     160             : 
     161         202 :   auto &LoadValue = MI.getOperand(0);
     162             :   assert(LoadValue.isReg() && "Result wasn't a register?");
     163             : 
     164         202 :   LLT LoadValueTy = MRI.getType(LoadValue.getReg());
     165             :   if (!LoadValueTy.isScalar())
     166          19 :     return false;
     167             : 
     168             :   // Find the preferred type aside from the any-extends (unless it's the only
     169             :   // one) and non-extending ops. We'll emit an extending load to that type and
     170             :   // and emit a variant of (extend (trunc X)) for the others according to the
     171             :   // relative type sizes. At the same time, pick an extend to use based on the
     172             :   // extend involved in the chosen type.
     173             :   unsigned PreferredOpcode = MI.getOpcode() == TargetOpcode::G_LOAD
     174         183 :                                  ? TargetOpcode::G_ANYEXT
     175             :                                  : MI.getOpcode() == TargetOpcode::G_SEXTLOAD
     176             :                                        ? TargetOpcode::G_SEXT
     177             :                                        : TargetOpcode::G_ZEXT;
     178         366 :   PreferredTuple Preferred = {LLT(), PreferredOpcode, nullptr};
     179         409 :   for (auto &UseMI : MRI.use_instructions(LoadValue.getReg())) {
     180         226 :     if (UseMI.getOpcode() == TargetOpcode::G_SEXT ||
     181         427 :         UseMI.getOpcode() == TargetOpcode::G_ZEXT ||
     182             :         UseMI.getOpcode() == TargetOpcode::G_ANYEXT) {
     183             :       Preferred = ChoosePreferredUse(Preferred,
     184          44 :                                      MRI.getType(UseMI.getOperand(0).getReg()),
     185          44 :                                      UseMI.getOpcode(), &UseMI);
     186             :     }
     187             :   }
     188             : 
     189             :   // There were no extends
     190         183 :   if (!Preferred.MI)
     191             :     return false;
     192             :   // It should be impossible to chose an extend without selecting a different
     193             :   // type since by definition the result of an extend is larger.
     194             :   assert(Preferred.Ty != LoadValueTy && "Extending to same type?");
     195             : 
     196             :   // Rewrite the load to the chosen extending load.
     197          29 :   unsigned ChosenDstReg = Preferred.MI->getOperand(0).getReg();
     198          29 :   MI.setDesc(
     199          29 :       Builder.getTII().get(Preferred.ExtendOpcode == TargetOpcode::G_SEXT
     200          12 :                                ? TargetOpcode::G_SEXTLOAD
     201             :                                : Preferred.ExtendOpcode == TargetOpcode::G_ZEXT
     202             :                                      ? TargetOpcode::G_ZEXTLOAD
     203             :                                      : TargetOpcode::G_LOAD));
     204             : 
     205             :   // Rewrite all the uses to fix up the types.
     206             :   SmallVector<MachineInstr *, 1> ScheduleForErase;
     207          29 :   SmallVector<InsertionPoint, 4> ScheduleForInsert;
     208         109 :   for (auto &UseMO : MRI.use_operands(LoadValue.getReg())) {
     209          51 :     MachineInstr *UseMI = UseMO.getParent();
     210             : 
     211             :     // If the extend is compatible with the preferred extend then we should fix
     212             :     // up the type and extend so that it uses the preferred use.
     213         102 :     if (UseMI->getOpcode() == Preferred.ExtendOpcode ||
     214             :         UseMI->getOpcode() == TargetOpcode::G_ANYEXT) {
     215          41 :       unsigned UseDstReg = UseMI->getOperand(0).getReg();
     216          41 :       unsigned UseSrcReg = UseMI->getOperand(1).getReg();
     217          41 :       const LLT &UseDstTy = MRI.getType(UseDstReg);
     218          41 :       if (UseDstReg != ChosenDstReg) {
     219          12 :         if (Preferred.Ty == UseDstTy) {
     220             :           // If the use has the same type as the preferred use, then merge
     221             :           // the vregs and erase the extend. For example:
     222             :           //    %1:_(s8) = G_LOAD ...
     223             :           //    %2:_(s32) = G_SEXT %1(s8)
     224             :           //    %3:_(s32) = G_ANYEXT %1(s8)
     225             :           //    ... = ... %3(s32)
     226             :           // rewrites to:
     227             :           //    %2:_(s32) = G_SEXTLOAD ...
     228             :           //    ... = ... %2(s32)
     229           4 :           MRI.replaceRegWith(UseDstReg, ChosenDstReg);
     230           4 :           ScheduleForErase.push_back(UseMO.getParent());
     231           8 :         } else if (Preferred.Ty.getSizeInBits() < UseDstTy.getSizeInBits()) {
     232             :           // If the preferred size is smaller, then keep the extend but extend
     233             :           // from the result of the extending load. For example:
     234             :           //    %1:_(s8) = G_LOAD ...
     235             :           //    %2:_(s32) = G_SEXT %1(s8)
     236             :           //    %3:_(s64) = G_ANYEXT %1(s8)
     237             :           //    ... = ... %3(s64)
     238             :           /// rewrites to:
     239             :           //    %2:_(s32) = G_SEXTLOAD ...
     240             :           //    %3:_(s64) = G_ANYEXT %2:_(s32)
     241             :           //    ... = ... %3(s64)
     242           2 :           MRI.replaceRegWith(UseSrcReg, ChosenDstReg);
     243             :         } else {
     244             :           // If the preferred size is large, then insert a truncate. For
     245             :           // example:
     246             :           //    %1:_(s8) = G_LOAD ...
     247             :           //    %2:_(s64) = G_SEXT %1(s8)
     248             :           //    %3:_(s32) = G_ZEXT %1(s8)
     249             :           //    ... = ... %3(s32)
     250             :           /// rewrites to:
     251             :           //    %2:_(s64) = G_SEXTLOAD ...
     252             :           //    %4:_(s8) = G_TRUNC %2:_(s32)
     253             :           //    %3:_(s64) = G_ZEXT %2:_(s8)
     254             :           //    ... = ... %3(s64)
     255          12 :           InsertInsnsWithoutSideEffectsBeforeUse(
     256             :               Builder, MI, UseMO,
     257             :               [&](MachineBasicBlock *InsertIntoBB,
     258             :                   MachineBasicBlock::iterator InsertBefore) {
     259           6 :                 ScheduleForInsert.emplace_back(&UseMO, InsertIntoBB, InsertBefore);
     260             :               });
     261             :         }
     262          12 :         continue;
     263             :       }
     264             :       // The use is (one of) the uses of the preferred use we chose earlier.
     265             :       // We're going to update the load to def this value later so just erase
     266             :       // the old extend.
     267          29 :       ScheduleForErase.push_back(UseMO.getParent());
     268          29 :       continue;
     269             :     }
     270             : 
     271             :     // The use isn't an extend. Truncate back to the type we originally loaded.
     272             :     // This is free on many targets.
     273          20 :     InsertInsnsWithoutSideEffectsBeforeUse(
     274             :         Builder, MI, UseMO,
     275             :         [&](MachineBasicBlock *InsertIntoBB,
     276             :             MachineBasicBlock::iterator InsertBefore) {
     277          10 :           ScheduleForInsert.emplace_back(&UseMO, InsertIntoBB, InsertBefore);
     278             :         });
     279             :   }
     280             : 
     281             :   DenseMap<MachineBasicBlock *, MachineInstr *> EmittedInsns;
     282          45 :   for (auto &InsertionInfo : ScheduleForInsert) {
     283          16 :     MachineOperand *UseMO = InsertionInfo.UseMO;
     284          16 :     MachineBasicBlock *InsertIntoBB = InsertionInfo.InsertIntoBB;
     285          16 :     MachineBasicBlock::iterator InsertBefore = InsertionInfo.InsertBefore;
     286             : 
     287          15 :     MachineInstr *PreviouslyEmitted = EmittedInsns.lookup(InsertIntoBB);
     288           1 :     if (PreviouslyEmitted) {
     289           1 :       UseMO->setReg(PreviouslyEmitted->getOperand(0).getReg());
     290           1 :       continue;
     291             :     }
     292             : 
     293          15 :     Builder.setInsertPt(*InsertIntoBB, InsertBefore);
     294          30 :     unsigned NewDstReg = MRI.cloneVirtualRegister(MI.getOperand(0).getReg());
     295          15 :     MachineInstr *NewMI = Builder.buildTrunc(NewDstReg, ChosenDstReg);
     296          15 :     EmittedInsns[InsertIntoBB] = NewMI;
     297          15 :     UseMO->setReg(NewDstReg);
     298          15 :     Observer.createdInstr(*NewMI);
     299             :   }
     300          62 :   for (auto &EraseMI : ScheduleForErase) {
     301          33 :     EraseMI->eraseFromParent();
     302          33 :     Observer.erasedInstr(*EraseMI);
     303             :   }
     304          29 :   MI.getOperand(0).setReg(ChosenDstReg);
     305             : 
     306             :   return true;
     307             : }
     308             : 
     309           0 : bool CombinerHelper::tryCombine(MachineInstr &MI) {
     310           0 :   if (tryCombineCopy(MI))
     311             :     return true;
     312           0 :   return tryCombineExtendingLoads(MI);
     313             : }

Generated by: LCOV version 1.13