From: Quentin Colombet Date: Sat, 1 Apr 2017 01:21:28 +0000 (+0000) Subject: Localizer fun X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3bae06e77eb9ae215e42912598450076eb43b025;p=llvm Localizer fun WIP git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@299283 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/CodeGen/GlobalISel/Localizer.h b/include/llvm/CodeGen/GlobalISel/Localizer.h new file mode 100644 index 00000000000..284d9838af7 --- /dev/null +++ b/include/llvm/CodeGen/GlobalISel/Localizer.h @@ -0,0 +1,58 @@ +//== llvm/CodeGen/GlobalISel/Localizer.h - Localizer -------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file This file describes the interface of the +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_LOCALIZER_H +#define LLVM_CODEGEN_GLOBALISEL_LOCALIZER_H + +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +namespace llvm { +// Forward declarations. +class MachineRegisterInfo; + +/// This pass implements the reg bank selector pass used in the GlobalISel +/// pipeline. At the end of this pass, all register operands have been assigned +class Localizer : public MachineFunctionPass { +public: + static char ID; + +private: + /// MRI contains all the register class/bank information that this + /// pass uses and updates. + MachineRegisterInfo *MRI; + + static bool shouldLocalize(const MachineInstr &MI); + + static bool isLocalUse(MachineOperand &MOUse, const MachineInstr &Def, MachineBasicBlock **InsertMBB); + + /// Initialize the field members using \p MF. + void init(MachineFunction &MF); + +public: + Localizer(); + + StringRef getPassName() const override { return "Localizer"; } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties() + .set(MachineFunctionProperties::Property::IsSSA) + .set(MachineFunctionProperties::Property::Legalized) + .set(MachineFunctionProperties::Property::RegBankSelected); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; + +} // End namespace llvm. + +#endif diff --git a/include/llvm/CodeGen/MachineRegisterInfo.h b/include/llvm/CodeGen/MachineRegisterInfo.h index 6e5c6473ff4..1026654da3d 100644 --- a/include/llvm/CodeGen/MachineRegisterInfo.h +++ b/include/llvm/CodeGen/MachineRegisterInfo.h @@ -642,6 +642,11 @@ public: /// void setRegBank(unsigned Reg, const RegisterBank &RegBank); + void setRegClassOrRegBank(unsigned Reg, + const RegClassOrRegBank &RCOrRB){ + VRegInfo[Reg].first = RCOrRB; + } + /// constrainRegClass - Constrain the register class of the specified virtual /// register to be a common subclass of RC and the current register class, /// but only if the new class has at least MinNumRegs registers. Return the diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 1b76c02ef04..4809c6a7c1b 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -192,6 +192,7 @@ void initializeLiveVariablesPass(PassRegistry&); void initializeLoadCombinePass(PassRegistry&); void initializeLoadStoreVectorizerPass(PassRegistry&); void initializeLoaderPassPass(PassRegistry&); +void initializeLocalizerPass(PassRegistry&); void initializeLocalStackSlotPassPass(PassRegistry&); void initializeLoopAccessLegacyAnalysisPass(PassRegistry&); void initializeLoopDataPrefetchLegacyPassPass(PassRegistry&); diff --git a/lib/CodeGen/GlobalISel/CMakeLists.txt b/lib/CodeGen/GlobalISel/CMakeLists.txt index 03a8c4f5f90..705ef74aac7 100644 --- a/lib/CodeGen/GlobalISel/CMakeLists.txt +++ b/lib/CodeGen/GlobalISel/CMakeLists.txt @@ -8,6 +8,7 @@ set(GLOBAL_ISEL_FILES LegalizerHelper.cpp Legalizer.cpp LegalizerInfo.cpp + Localizer.cpp RegBankSelect.cpp RegisterBank.cpp RegisterBankInfo.cpp diff --git a/lib/CodeGen/GlobalISel/GlobalISel.cpp b/lib/CodeGen/GlobalISel/GlobalISel.cpp index fcd2722f1c2..29d1209bb02 100644 --- a/lib/CodeGen/GlobalISel/GlobalISel.cpp +++ b/lib/CodeGen/GlobalISel/GlobalISel.cpp @@ -26,6 +26,7 @@ void llvm::initializeGlobalISel(PassRegistry &Registry) { void llvm::initializeGlobalISel(PassRegistry &Registry) { initializeIRTranslatorPass(Registry); initializeLegalizerPass(Registry); + initializeLocalizerPass(Registry); initializeRegBankSelectPass(Registry); initializeInstructionSelectPass(Registry); } diff --git a/lib/CodeGen/GlobalISel/Localizer.cpp b/lib/CodeGen/GlobalISel/Localizer.cpp new file mode 100644 index 00000000000..e5b1449cdae --- /dev/null +++ b/lib/CodeGen/GlobalISel/Localizer.cpp @@ -0,0 +1,120 @@ +//===- Localizer.cpp ---------------------- Localize some instrs -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the Localizer class. +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/CodeGen/GlobalISel/Localizer.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "localizer" + +using namespace llvm; + +char Localizer::ID = 0; +INITIALIZE_PASS(Localizer, DEBUG_TYPE, + "Move/duplicate certain instructions close to their use", + false, false); + +Localizer::Localizer() : MachineFunctionPass(ID) { + initializeLocalizerPass(*PassRegistry::getPassRegistry()); +} + +void Localizer::init(MachineFunction &MF) { + MRI = &MF.getRegInfo(); +} + +bool Localizer::shouldLocalize(const MachineInstr &MI) { + switch(MI.getOpcode()) { + default: + return false; + // Constants-like instructions should be close to their users. + // We don't want long live-ranges for them. + case TargetOpcode::G_CONSTANT: + case TargetOpcode::G_FRAME_INDEX: + return true; + } +} + +bool Localizer::isLocalUse(MachineOperand &MOUse, const MachineInstr &Def, MachineBasicBlock **InsertMBB) { + MachineInstr &MIUse = *MOUse.getParent(); + *InsertMBB = MIUse.getParent(); + if (MIUse.isPHI()) + *InsertMBB = MIUse.getOperand(MIUse.getOperandNo(&MOUse) + 1).getMBB(); + return *InsertMBB == Def.getParent(); +} + +bool Localizer::runOnMachineFunction(MachineFunction &MF) { + // If the ISel pipeline failed, do not bother running that pass. + if (MF.getProperties().hasProperty( + MachineFunctionProperties::Property::FailedISel)) + return false; + + DEBUG(dbgs() << "Localize instructions for: " << MF.getName() << '\n'); + + init(MF); + + bool Changed = false; + // Keep track of the instructions we localized. + // We won't need to process them if we see them later in the CFG. + SmallPtrSet LocalizedInstrs; + DenseMap, unsigned> MBBWithLocalDef; + // TODO: Do bottom up traversal. + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (LocalizedInstrs.count(&MI) || !shouldLocalize(MI)) + continue; + DEBUG(dbgs() << "Should localize: " << MI); + assert(MI.getDesc().getNumDefs() == 1 && "More than one definition not supported yet"); + unsigned Reg = MI.getOperand(0).getReg(); + // Check if all the users of MI are local. + // We are going to invalidation the list of use operands, so we + // can't use range iterator. + for (auto MOIt = MRI->use_begin(Reg), MOItEnd = MRI->use_end(); + MOIt != MOItEnd;) { + MachineOperand &MOUse = *MOIt++; + // Check if the use is already local. + MachineBasicBlock *InsertMBB; + DEBUG(MachineInstr &MIUse = *MOUse.getParent(); + dbgs() << "Checking use: " << MIUse << " #Opd: " << MIUse.getOperandNo(&MOUse) << '\n'); + if (isLocalUse(MOUse, MI, &InsertMBB)) + continue; + DEBUG(dbgs() << "Fixing non-local use\n"); + Changed = true; + auto MBBAndReg = std::make_pair(InsertMBB, Reg); + auto NewVRegIt = MBBWithLocalDef.find(MBBAndReg); + if (NewVRegIt == MBBWithLocalDef.end()) { + // Create the localized instruction. + MachineInstr *LocalizedMI = MF.CloneMachineInstr(&MI); + LocalizedInstrs.insert(LocalizedMI); + // Move it at the right place. + MachineInstr &MIUse = *MOUse.getParent(); + if (MIUse.getParent() == InsertMBB) + InsertMBB->insert(MIUse, LocalizedMI); + else + InsertMBB->insert(InsertMBB->getFirstNonPHI(), LocalizedMI); + + // Set a new register for the definition. + unsigned NewReg = MRI->createGenericVirtualRegister(MRI->getType(Reg)); + MRI->setRegClassOrRegBank(NewReg, MRI->getRegClassOrRegBank(Reg)); + LocalizedMI->getOperand(0).setReg(NewReg); + NewVRegIt = MBBWithLocalDef.insert(std::make_pair(MBBAndReg, NewReg)).first; + DEBUG(dbgs() << "Inserted: " << *LocalizedMI); + } + DEBUG(dbgs() << "Update use with: " << PrintReg(NewVRegIt->second) << '\n'); + // Update the user reg. + MOUse.setReg(NewVRegIt->second); + } + } + } + return Changed; +} diff --git a/test/CodeGen/AArch64/GlobalISel/localizer.mir b/test/CodeGen/AArch64/GlobalISel/localizer.mir new file mode 100644 index 00000000000..ea60f5fe135 --- /dev/null +++ b/test/CodeGen/AArch64/GlobalISel/localizer.mir @@ -0,0 +1,260 @@ +# RUN: llc -O0 -mtriple=aarch64-apple-ios -run-pass=localizer -verify-machineinstrs -global-isel %s -o - | FileCheck %s -check-prefix=CHECK + +# Test the localizer. + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + + define void @local_use() { ret void } + define void @non_local_1use() { ret void } + define void @non_local_2uses() { ret void } + define void @non_local_phi_use() { ret void } + define void @non_local_phi_use_followed_by_use() { ret void } + define void @non_local_phi_use_followed_by_use_fi() { ret void } +... + +--- +# CHECK-LABEL: name: local_use +name: local_use +legalized: true +regBankSelected: true + +# CHECK: registers: +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_CONSTANT 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 +body: | + bb.0: + %0(s32) = G_CONSTANT 1 + %1(s32) = G_ADD %0, %0 +... + +--- +# CHECK-LABEL: name: non_local_1use +name: non_local_1use +legalized: true +regBankSelected: true + +# CHECK: registers: +# Existing registers should be left untouched +# CHECK: - { id: 0, class: gpr } +#CHECK-NEXT: - { id: 1, class: gpr } +#CHECK-NEXT: - { id: 2, class: gpr } +# The newly created reg should be on the same regbank/regclass as its origin. +#CHECK-NEXT: - { id: 3, class: gpr } + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_CONSTANT 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 + +# CHECK: bb.1: +# CHECK: %3(s32) = G_CONSTANT 1 +# CHECK-NEXT: %2(s32) = G_ADD %3, %1 +body: | + bb.0: + successors: %bb.1 + + %0(s32) = G_CONSTANT 1 + %1(s32) = G_ADD %0, %0 + + bb.1: + %2(s32) = G_ADD %0, %1 +... + + +--- +# CHECK-LABEL: name: non_local_2uses +name: non_local_2uses +legalized: true +regBankSelected: true + +# CHECK: registers: +# Existing registers should be left untouched +# CHECK: - { id: 0, class: gpr } +#CHECK-NEXT: - { id: 1, class: gpr } +#CHECK-NEXT: - { id: 2, class: gpr } +# The newly created reg should be on the same regbank/regclass as its origin. +#CHECK-NEXT: - { id: 3, class: gpr } + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_CONSTANT 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 + +# CHECK: bb.1: +# CHECK: %3(s32) = G_CONSTANT 1 +# CHECK-NEXT: %2(s32) = G_ADD %3, %3 +body: | + bb.0: + successors: %bb.1 + + %0(s32) = G_CONSTANT 1 + %1(s32) = G_ADD %0, %0 + + bb.1: + %2(s32) = G_ADD %0, %0 +... + +--- +# CHECK-LABEL: name: non_local_phi_use +name: non_local_phi_use +legalized: true +regBankSelected: true +tracksRegLiveness: true + +# CHECK: registers: +# Existing registers should be left untouched +# CHECK: - { id: 0, class: gpr } +#CHECK-NEXT: - { id: 1, class: gpr } +#CHECK-NEXT: - { id: 2, class: gpr } +#CHECK-NEXT: - { id: 3, class: gpr } +#CHECK-NEXT: - { id: 4, class: gpr } +# The newly created reg should be on the same regbank/regclass as its origin. +#CHECK-NEXT: - { id: 5, class: gpr } + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + - { id: 4, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_CONSTANT 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 + +# CHECK: bb.1: +# CHECK: %5(s32) = G_CONSTANT 1 + +# CHECK: bb.2: +# CHECK: %3(s32) = PHI %5(s32), %bb.1 +body: | + bb.0: + successors: %bb.1 + + %0(s32) = G_CONSTANT 1 + %1(s32) = G_ADD %0, %0 + + bb.1: + successors: %bb.2 + + bb.2: + %3(s32) = PHI %0(s32), %bb.1 + %2(s32) = G_ADD %3, %3 +... + +--- +# CHECK-LABEL: name: non_local_phi_use_followed_by_use +name: non_local_phi_use_followed_by_use +legalized: true +regBankSelected: true +tracksRegLiveness: true + +# CHECK: registers: +# Existing registers should be left untouched +# CHECK: - { id: 0, class: gpr } +#CHECK-NEXT: - { id: 1, class: gpr } +#CHECK-NEXT: - { id: 2, class: gpr } +#CHECK-NEXT: - { id: 3, class: gpr } +#CHECK-NEXT: - { id: 4, class: gpr } +# The newly created regs should be on the same regbank/regclass as its origin. +#CHECK-NEXT: - { id: 5, class: gpr } +#CHECK-NEXT: - { id: 6, class: gpr } + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + - { id: 4, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_CONSTANT 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 + +# CHECK: bb.1: +# CHECK: %5(s32) = G_CONSTANT 1 + +# CHECK: bb.2: +# CHECK: %3(s32) = PHI %5(s32), %bb.1 +# CHECK-NEXT: %6(s32) = G_CONSTANT 1 +# CHECK-NEXT: %2(s32) = G_ADD %3, %6 +body: | + bb.0: + successors: %bb.1 + + %0(s32) = G_CONSTANT 1 + %1(s32) = G_ADD %0, %0 + + bb.1: + successors: %bb.2 + + bb.2: + %3(s32) = PHI %0(s32), %bb.1 + %2(s32) = G_ADD %3, %0 +... + +--- +# CHECK-LABEL: name: non_local_phi_use_followed_by_use_fi +name: non_local_phi_use_followed_by_use_fi +legalized: true +regBankSelected: true +tracksRegLiveness: true + +# CHECK: registers: +# Existing registers should be left untouched +# CHECK: - { id: 0, class: gpr } +#CHECK-NEXT: - { id: 1, class: gpr } +#CHECK-NEXT: - { id: 2, class: gpr } +#CHECK-NEXT: - { id: 3, class: gpr } +#CHECK-NEXT: - { id: 4, class: gpr } +# The newly created reg should be on the same regbank/regclass as its origin. +#CHECK-NEXT: - { id: 5, class: gpr } +#CHECK-NEXT: - { id: 6, class: gpr } + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + - { id: 4, class: gpr } + +# CHECK: body: +# CHECK: %0(s32) = G_FRAME_INDEX 1 +# CHECK-NEXT: %1(s32) = G_ADD %0, %0 + +# CHECK: bb.1: +# CHECK: %5(s32) = G_FRAME_INDEX 1 + +# CHECK: bb.2: +# CHECK: %3(s32) = PHI %5(s32), %bb.1 +# CHECK-NEXT: %6(s32) = G_FRAME_INDEX 1 +# CHECK-NEXT: %2(s32) = G_ADD %3, %6 +body: | + bb.0: + successors: %bb.1 + + %0(s32) = G_FRAME_INDEX 1 + %1(s32) = G_ADD %0, %0 + + bb.1: + successors: %bb.2 + + bb.2: + %3(s32) = PHI %0(s32), %bb.1 + %2(s32) = G_ADD %3, %0 +...