xref: /freebsd/contrib/llvm-project/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp (revision 8bcb0991864975618c09697b1aca10683346d9f0)
10b57cec5SDimitry Andric //===-- llvm/CodeGen/GlobalISel/Legalizer.cpp -----------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric /// \file This file implements the LegalizerHelper class to legalize individual
100b57cec5SDimitry Andric /// instructions and the LegalizePass wrapper pass for the primary
110b57cec5SDimitry Andric /// legalization.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/Legalizer.h"
160b57cec5SDimitry Andric #include "llvm/ADT/PostOrderIterator.h"
170b57cec5SDimitry Andric #include "llvm/ADT/SetVector.h"
180b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/CSEInfo.h"
190b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
200b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
210b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
220b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h"
230b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
240b57cec5SDimitry Andric #include "llvm/CodeGen/GlobalISel/Utils.h"
250b57cec5SDimitry Andric #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
260b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
270b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
280b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
290b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
300b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h"
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric #include <iterator>
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric #define DEBUG_TYPE "legalizer"
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric using namespace llvm;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric static cl::opt<bool>
390b57cec5SDimitry Andric     EnableCSEInLegalizer("enable-cse-in-legalizer",
400b57cec5SDimitry Andric                          cl::desc("Should enable CSE in Legalizer"),
410b57cec5SDimitry Andric                          cl::Optional, cl::init(false));
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric char Legalizer::ID = 0;
440b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
450b57cec5SDimitry Andric                       "Legalize the Machine IR a function's Machine IR", false,
460b57cec5SDimitry Andric                       false)
470b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
480b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
490b57cec5SDimitry Andric INITIALIZE_PASS_END(Legalizer, DEBUG_TYPE,
500b57cec5SDimitry Andric                     "Legalize the Machine IR a function's Machine IR", false,
510b57cec5SDimitry Andric                     false)
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric Legalizer::Legalizer() : MachineFunctionPass(ID) { }
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric void Legalizer::getAnalysisUsage(AnalysisUsage &AU) const {
560b57cec5SDimitry Andric   AU.addRequired<TargetPassConfig>();
570b57cec5SDimitry Andric   AU.addRequired<GISelCSEAnalysisWrapperPass>();
580b57cec5SDimitry Andric   AU.addPreserved<GISelCSEAnalysisWrapperPass>();
590b57cec5SDimitry Andric   getSelectionDAGFallbackAnalysisUsage(AU);
600b57cec5SDimitry Andric   MachineFunctionPass::getAnalysisUsage(AU);
610b57cec5SDimitry Andric }
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric void Legalizer::init(MachineFunction &MF) {
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric static bool isArtifact(const MachineInstr &MI) {
670b57cec5SDimitry Andric   switch (MI.getOpcode()) {
680b57cec5SDimitry Andric   default:
690b57cec5SDimitry Andric     return false;
700b57cec5SDimitry Andric   case TargetOpcode::G_TRUNC:
710b57cec5SDimitry Andric   case TargetOpcode::G_ZEXT:
720b57cec5SDimitry Andric   case TargetOpcode::G_ANYEXT:
730b57cec5SDimitry Andric   case TargetOpcode::G_SEXT:
740b57cec5SDimitry Andric   case TargetOpcode::G_MERGE_VALUES:
750b57cec5SDimitry Andric   case TargetOpcode::G_UNMERGE_VALUES:
760b57cec5SDimitry Andric   case TargetOpcode::G_CONCAT_VECTORS:
770b57cec5SDimitry Andric   case TargetOpcode::G_BUILD_VECTOR:
780b57cec5SDimitry Andric   case TargetOpcode::G_EXTRACT:
790b57cec5SDimitry Andric     return true;
800b57cec5SDimitry Andric   }
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric using InstListTy = GISelWorkList<256>;
830b57cec5SDimitry Andric using ArtifactListTy = GISelWorkList<128>;
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric namespace {
860b57cec5SDimitry Andric class LegalizerWorkListManager : public GISelChangeObserver {
870b57cec5SDimitry Andric   InstListTy &InstList;
880b57cec5SDimitry Andric   ArtifactListTy &ArtifactList;
890b57cec5SDimitry Andric #ifndef NDEBUG
900b57cec5SDimitry Andric   SmallVector<MachineInstr *, 4> NewMIs;
910b57cec5SDimitry Andric #endif
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric public:
940b57cec5SDimitry Andric   LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts)
950b57cec5SDimitry Andric       : InstList(Insts), ArtifactList(Arts) {}
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   void createdOrChangedInstr(MachineInstr &MI) {
980b57cec5SDimitry Andric     // Only legalize pre-isel generic instructions.
990b57cec5SDimitry Andric     // Legalization process could generate Target specific pseudo
1000b57cec5SDimitry Andric     // instructions with generic types. Don't record them
1010b57cec5SDimitry Andric     if (isPreISelGenericOpcode(MI.getOpcode())) {
1020b57cec5SDimitry Andric       if (isArtifact(MI))
1030b57cec5SDimitry Andric         ArtifactList.insert(&MI);
1040b57cec5SDimitry Andric       else
1050b57cec5SDimitry Andric         InstList.insert(&MI);
1060b57cec5SDimitry Andric     }
1070b57cec5SDimitry Andric   }
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   void createdInstr(MachineInstr &MI) override {
1100b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << ".. .. New MI: " << MI);
1110b57cec5SDimitry Andric     LLVM_DEBUG(NewMIs.push_back(&MI));
1120b57cec5SDimitry Andric     createdOrChangedInstr(MI);
1130b57cec5SDimitry Andric   }
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   void printNewInstrs() {
1160b57cec5SDimitry Andric     LLVM_DEBUG({
1170b57cec5SDimitry Andric       for (const auto *MI : NewMIs)
1180b57cec5SDimitry Andric         dbgs() << ".. .. New MI: " << *MI;
1190b57cec5SDimitry Andric       NewMIs.clear();
1200b57cec5SDimitry Andric     });
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric   void erasingInstr(MachineInstr &MI) override {
1240b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << ".. .. Erasing: " << MI);
1250b57cec5SDimitry Andric     InstList.remove(&MI);
1260b57cec5SDimitry Andric     ArtifactList.remove(&MI);
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   void changingInstr(MachineInstr &MI) override {
1300b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << ".. .. Changing MI: " << MI);
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   void changedInstr(MachineInstr &MI) override {
1340b57cec5SDimitry Andric     // When insts change, we want to revisit them to legalize them again.
1350b57cec5SDimitry Andric     // We'll consider them the same as created.
1360b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << ".. .. Changed MI: " << MI);
1370b57cec5SDimitry Andric     createdOrChangedInstr(MI);
1380b57cec5SDimitry Andric   }
1390b57cec5SDimitry Andric };
1400b57cec5SDimitry Andric } // namespace
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
1430b57cec5SDimitry Andric   // If the ISel pipeline failed, do not bother running that pass.
1440b57cec5SDimitry Andric   if (MF.getProperties().hasProperty(
1450b57cec5SDimitry Andric           MachineFunctionProperties::Property::FailedISel))
1460b57cec5SDimitry Andric     return false;
1470b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "Legalize Machine IR for: " << MF.getName() << '\n');
1480b57cec5SDimitry Andric   init(MF);
1490b57cec5SDimitry Andric   const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
1500b57cec5SDimitry Andric   GISelCSEAnalysisWrapper &Wrapper =
1510b57cec5SDimitry Andric       getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
1520b57cec5SDimitry Andric   MachineOptimizationRemarkEmitter MORE(MF, /*MBFI=*/nullptr);
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   const size_t NumBlocks = MF.size();
1550b57cec5SDimitry Andric   MachineRegisterInfo &MRI = MF.getRegInfo();
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric   // Populate Insts
1580b57cec5SDimitry Andric   InstListTy InstList;
1590b57cec5SDimitry Andric   ArtifactListTy ArtifactList;
1600b57cec5SDimitry Andric   ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
1610b57cec5SDimitry Andric   // Perform legalization bottom up so we can DCE as we legalize.
1620b57cec5SDimitry Andric   // Traverse BB in RPOT and within each basic block, add insts top down,
1630b57cec5SDimitry Andric   // so when we pop_back_val in the legalization process, we traverse bottom-up.
1640b57cec5SDimitry Andric   for (auto *MBB : RPOT) {
1650b57cec5SDimitry Andric     if (MBB->empty())
1660b57cec5SDimitry Andric       continue;
1670b57cec5SDimitry Andric     for (MachineInstr &MI : *MBB) {
1680b57cec5SDimitry Andric       // Only legalize pre-isel generic instructions: others don't have types
1690b57cec5SDimitry Andric       // and are assumed to be legal.
1700b57cec5SDimitry Andric       if (!isPreISelGenericOpcode(MI.getOpcode()))
1710b57cec5SDimitry Andric         continue;
1720b57cec5SDimitry Andric       if (isArtifact(MI))
1730b57cec5SDimitry Andric         ArtifactList.deferred_insert(&MI);
1740b57cec5SDimitry Andric       else
1750b57cec5SDimitry Andric         InstList.deferred_insert(&MI);
1760b57cec5SDimitry Andric     }
1770b57cec5SDimitry Andric   }
1780b57cec5SDimitry Andric   ArtifactList.finalize();
1790b57cec5SDimitry Andric   InstList.finalize();
1800b57cec5SDimitry Andric   std::unique_ptr<MachineIRBuilder> MIRBuilder;
1810b57cec5SDimitry Andric   GISelCSEInfo *CSEInfo = nullptr;
1820b57cec5SDimitry Andric   bool EnableCSE = EnableCSEInLegalizer.getNumOccurrences()
1830b57cec5SDimitry Andric                        ? EnableCSEInLegalizer
1840b57cec5SDimitry Andric                        : TPC.isGISelCSEEnabled();
1850b57cec5SDimitry Andric 
1860b57cec5SDimitry Andric   if (EnableCSE) {
187*8bcb0991SDimitry Andric     MIRBuilder = std::make_unique<CSEMIRBuilder>();
1880b57cec5SDimitry Andric     CSEInfo = &Wrapper.get(TPC.getCSEConfig());
1890b57cec5SDimitry Andric     MIRBuilder->setCSEInfo(CSEInfo);
1900b57cec5SDimitry Andric   } else
191*8bcb0991SDimitry Andric     MIRBuilder = std::make_unique<MachineIRBuilder>();
1920b57cec5SDimitry Andric   // This observer keeps the worklist updated.
1930b57cec5SDimitry Andric   LegalizerWorkListManager WorkListObserver(InstList, ArtifactList);
1940b57cec5SDimitry Andric   // We want both WorkListObserver as well as CSEInfo to observe all changes.
1950b57cec5SDimitry Andric   // Use the wrapper observer.
1960b57cec5SDimitry Andric   GISelObserverWrapper WrapperObserver(&WorkListObserver);
1970b57cec5SDimitry Andric   if (EnableCSE && CSEInfo)
1980b57cec5SDimitry Andric     WrapperObserver.addObserver(CSEInfo);
1990b57cec5SDimitry Andric   // Now install the observer as the delegate to MF.
2000b57cec5SDimitry Andric   // This will keep all the observers notified about new insertions/deletions.
2010b57cec5SDimitry Andric   RAIIDelegateInstaller DelInstall(MF, &WrapperObserver);
2020b57cec5SDimitry Andric   LegalizerHelper Helper(MF, WrapperObserver, *MIRBuilder.get());
2030b57cec5SDimitry Andric   const LegalizerInfo &LInfo(Helper.getLegalizerInfo());
2040b57cec5SDimitry Andric   LegalizationArtifactCombiner ArtCombiner(*MIRBuilder.get(), MF.getRegInfo(),
2050b57cec5SDimitry Andric                                            LInfo);
2060b57cec5SDimitry Andric   auto RemoveDeadInstFromLists = [&WrapperObserver](MachineInstr *DeadMI) {
2070b57cec5SDimitry Andric     WrapperObserver.erasingInstr(*DeadMI);
2080b57cec5SDimitry Andric   };
209*8bcb0991SDimitry Andric   auto stopLegalizing = [&](MachineInstr &MI) {
210*8bcb0991SDimitry Andric     Helper.MIRBuilder.stopObservingChanges();
211*8bcb0991SDimitry Andric     reportGISelFailure(MF, TPC, MORE, "gisel-legalize",
212*8bcb0991SDimitry Andric                        "unable to legalize instruction", MI);
213*8bcb0991SDimitry Andric   };
2140b57cec5SDimitry Andric   bool Changed = false;
215*8bcb0991SDimitry Andric   SmallVector<MachineInstr *, 128> RetryList;
2160b57cec5SDimitry Andric   do {
217*8bcb0991SDimitry Andric     assert(RetryList.empty() && "Expected no instructions in RetryList");
218*8bcb0991SDimitry Andric     unsigned NumArtifacts = ArtifactList.size();
2190b57cec5SDimitry Andric     while (!InstList.empty()) {
2200b57cec5SDimitry Andric       MachineInstr &MI = *InstList.pop_back_val();
2210b57cec5SDimitry Andric       assert(isPreISelGenericOpcode(MI.getOpcode()) && "Expecting generic opcode");
2220b57cec5SDimitry Andric       if (isTriviallyDead(MI, MRI)) {
2230b57cec5SDimitry Andric         LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n");
2240b57cec5SDimitry Andric         MI.eraseFromParentAndMarkDBGValuesForRemoval();
2250b57cec5SDimitry Andric         continue;
2260b57cec5SDimitry Andric       }
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric       // Do the legalization for this instruction.
2290b57cec5SDimitry Andric       auto Res = Helper.legalizeInstrStep(MI);
2300b57cec5SDimitry Andric       // Error out if we couldn't legalize this instruction. We may want to
2310b57cec5SDimitry Andric       // fall back to DAG ISel instead in the future.
2320b57cec5SDimitry Andric       if (Res == LegalizerHelper::UnableToLegalize) {
233*8bcb0991SDimitry Andric         // Move illegal artifacts to RetryList instead of aborting because
234*8bcb0991SDimitry Andric         // legalizing InstList may generate artifacts that allow
235*8bcb0991SDimitry Andric         // ArtifactCombiner to combine away them.
236*8bcb0991SDimitry Andric         if (isArtifact(MI)) {
237*8bcb0991SDimitry Andric           RetryList.push_back(&MI);
238*8bcb0991SDimitry Andric           continue;
239*8bcb0991SDimitry Andric         }
240*8bcb0991SDimitry Andric         stopLegalizing(MI);
2410b57cec5SDimitry Andric         return false;
2420b57cec5SDimitry Andric       }
2430b57cec5SDimitry Andric       WorkListObserver.printNewInstrs();
2440b57cec5SDimitry Andric       Changed |= Res == LegalizerHelper::Legalized;
2450b57cec5SDimitry Andric     }
246*8bcb0991SDimitry Andric     // Try to combine the instructions in RetryList again if there
247*8bcb0991SDimitry Andric     // are new artifacts. If not, stop legalizing.
248*8bcb0991SDimitry Andric     if (!RetryList.empty()) {
249*8bcb0991SDimitry Andric       if (ArtifactList.size() > NumArtifacts) {
250*8bcb0991SDimitry Andric         while (!RetryList.empty())
251*8bcb0991SDimitry Andric           ArtifactList.insert(RetryList.pop_back_val());
252*8bcb0991SDimitry Andric       } else {
253*8bcb0991SDimitry Andric         MachineInstr *MI = *RetryList.begin();
254*8bcb0991SDimitry Andric         stopLegalizing(*MI);
255*8bcb0991SDimitry Andric         return false;
256*8bcb0991SDimitry Andric       }
257*8bcb0991SDimitry Andric     }
2580b57cec5SDimitry Andric     while (!ArtifactList.empty()) {
2590b57cec5SDimitry Andric       MachineInstr &MI = *ArtifactList.pop_back_val();
2600b57cec5SDimitry Andric       assert(isPreISelGenericOpcode(MI.getOpcode()) && "Expecting generic opcode");
2610b57cec5SDimitry Andric       if (isTriviallyDead(MI, MRI)) {
2620b57cec5SDimitry Andric         LLVM_DEBUG(dbgs() << MI << "Is dead\n");
2630b57cec5SDimitry Andric         RemoveDeadInstFromLists(&MI);
2640b57cec5SDimitry Andric         MI.eraseFromParentAndMarkDBGValuesForRemoval();
2650b57cec5SDimitry Andric         continue;
2660b57cec5SDimitry Andric       }
2670b57cec5SDimitry Andric       SmallVector<MachineInstr *, 4> DeadInstructions;
2680b57cec5SDimitry Andric       if (ArtCombiner.tryCombineInstruction(MI, DeadInstructions,
2690b57cec5SDimitry Andric                                             WrapperObserver)) {
2700b57cec5SDimitry Andric         WorkListObserver.printNewInstrs();
2710b57cec5SDimitry Andric         for (auto *DeadMI : DeadInstructions) {
2720b57cec5SDimitry Andric           LLVM_DEBUG(dbgs() << *DeadMI << "Is dead\n");
2730b57cec5SDimitry Andric           RemoveDeadInstFromLists(DeadMI);
2740b57cec5SDimitry Andric           DeadMI->eraseFromParentAndMarkDBGValuesForRemoval();
2750b57cec5SDimitry Andric         }
2760b57cec5SDimitry Andric         Changed = true;
2770b57cec5SDimitry Andric         continue;
2780b57cec5SDimitry Andric       }
2790b57cec5SDimitry Andric       // If this was not an artifact (that could be combined away), this might
2800b57cec5SDimitry Andric       // need special handling. Add it to InstList, so when it's processed
2810b57cec5SDimitry Andric       // there, it has to be legal or specially handled.
2820b57cec5SDimitry Andric       else
2830b57cec5SDimitry Andric         InstList.insert(&MI);
2840b57cec5SDimitry Andric     }
2850b57cec5SDimitry Andric   } while (!InstList.empty());
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric   // For now don't support if new blocks are inserted - we would need to fix the
2880b57cec5SDimitry Andric   // outerloop for that.
2890b57cec5SDimitry Andric   if (MF.size() != NumBlocks) {
2900b57cec5SDimitry Andric     MachineOptimizationRemarkMissed R("gisel-legalize", "GISelFailure",
2910b57cec5SDimitry Andric                                       MF.getFunction().getSubprogram(),
2920b57cec5SDimitry Andric                                       /*MBB=*/nullptr);
2930b57cec5SDimitry Andric     R << "inserting blocks is not supported yet";
2940b57cec5SDimitry Andric     reportGISelFailure(MF, TPC, MORE, R);
2950b57cec5SDimitry Andric     return false;
2960b57cec5SDimitry Andric   }
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric   return Changed;
2990b57cec5SDimitry Andric }
300