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