Skip to content

Commit

Permalink
[globalisel][tablegen] Partially fix compile-time regressions by conv…
Browse files Browse the repository at this point in the history
…erting matcher to state-machine(s)

Summary:
Replace the matcher if-statements for each rule with a state-machine. This
significantly reduces compile time, memory allocations, and cumulative memory
allocation when compiling AArch64InstructionSelector.cpp.o after r303259 is
recommitted.

The following patches will expand on this further to fully fix the regressions.

Reviewers: rovka, ab, t.p.northover, qcolombet, aditya_nandakumar

Reviewed By: ab

Subscribers: vitalybuka, aemerson, javed.absar, igorb, llvm-commits, kristof.beyls

Differential Revision: https://reviews.llvm.org/D33758

llvm-svn: 307079
  • Loading branch information
dsandersllvm committed Jul 4, 2017
1 parent 90f69ab commit 6ab0daa
Show file tree
Hide file tree
Showing 8 changed files with 1,062 additions and 519 deletions.
86 changes: 86 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H

#include "llvm/ADT/SmallVector.h"
#include <bitset>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <vector>

namespace llvm {

class LLT;
class MachineInstr;
class MachineInstrBuilder;
class MachineOperand;
Expand Down Expand Up @@ -58,6 +61,61 @@ class PredicateBitsetImpl : public std::bitset<MaxPredicates> {
}
};

enum {
/// Record the specified instruction
/// - NewInsnID - Instruction ID to define
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_RecordInsn,

/// Check the feature bits
/// - Expected features
GIM_CheckFeatures,

/// Check the opcode on the specified instruction
/// - InsnID - Instruction ID
/// - Expected opcode
GIM_CheckOpcode,
/// Check the instruction has the right number of operands
/// - InsnID - Instruction ID
/// - Expected number of operands
GIM_CheckNumOperands,

/// Check the type for the specified operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected type
GIM_CheckType,
/// Check the register bank for the specified operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected register bank (specified as a register class)
GIM_CheckRegBankForClass,
/// Check the operand matches a complex predicate
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - RendererID - The renderer to hold the result
/// - Complex predicate ID
GIM_CheckComplexPattern,
/// Check the operand is a specific integer
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected integer
GIM_CheckConstantInt,
/// Check the operand is a specific literal integer (i.e. MO.isImm() or MO.isCImm() is true).
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected integer
GIM_CheckLiteralInt,
/// Check the specified operand is an MBB
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_CheckIsMBB,

/// A successful match
GIM_Accept,
};

/// Provides the logic to select generic machine instructions.
class InstructionSelector {
public:
Expand All @@ -78,9 +136,37 @@ class InstructionSelector {

protected:
using ComplexRendererFn = std::function<void(MachineInstrBuilder &)>;
using RecordedMIVector = SmallVector<MachineInstr *, 4>;

struct MatcherState {
std::vector<ComplexRendererFn> Renderers;
RecordedMIVector MIs;

MatcherState(unsigned MaxRenderers);
};

public:
template <class PredicateBitset, class ComplexMatcherMemFn>
struct MatcherInfoTy {
const LLT *TypeObjects;
const PredicateBitset *FeatureBitsets;
const std::vector<ComplexMatcherMemFn> ComplexPredicates;
};

protected:
InstructionSelector();

/// Execute a given matcher table and return true if the match was successful
/// and false otherwise.
template <class TgtInstructionSelector, class PredicateBitset,
class ComplexMatcherMemFn>
bool executeMatchTable(
TgtInstructionSelector &ISel, MatcherState &State,
const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
const int64_t *MatchTable, MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI,
const PredicateBitset &AvailableFeatures) const;

/// Constrain a register operand of an instruction \p I to a specified
/// register class. This could involve inserting COPYs before (for uses) or
/// after (for defs) and may replace the operand of \p I.
Expand Down
173 changes: 173 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//==-- llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h ---------*- 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 declares the API for the instruction selector.
/// This class is responsible for selecting machine instructions.
/// It's implemented by the target. It's used by the InstructionSelect pass.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H

namespace llvm {
template <class TgtInstructionSelector, class PredicateBitset,
class ComplexMatcherMemFn>
bool InstructionSelector::executeMatchTable(
TgtInstructionSelector &ISel, MatcherState &State,
const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
const int64_t *MatchTable, MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI,
const PredicateBitset &AvailableFeatures) const {
const int64_t *Command = MatchTable;
while (true) {
switch (*Command++) {
case GIM_RecordInsn: {
int64_t NewInsnID = *Command++;
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;

MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx);
if (!MO.isReg()) {
DEBUG(dbgs() << "Rejected (not a register)\n");
return false;
}
if (TRI.isPhysicalRegister(MO.getReg())) {
DEBUG(dbgs() << "Rejected (is a physical register)\n");
return false;
}

assert((size_t)NewInsnID == State.MIs.size() &&
"Expected to store MIs in order");
State.MIs.push_back(MRI.getVRegDef(MO.getReg()));
DEBUG(dbgs() << "MIs[" << NewInsnID << "] = GIM_RecordInsn(" << InsnID
<< ", " << OpIdx << ")\n");
break;
}

case GIM_CheckFeatures: {
int64_t ExpectedBitsetID = *Command++;
DEBUG(dbgs() << "GIM_CheckFeatures(ExpectedBitsetID=" << ExpectedBitsetID
<< ")\n");
if ((AvailableFeatures & MatcherInfo.FeatureBitsets[ExpectedBitsetID]) !=
MatcherInfo.FeatureBitsets[ExpectedBitsetID]) {
DEBUG(dbgs() << "Rejected\n");
return false;
}
break;
}

case GIM_CheckOpcode: {
int64_t InsnID = *Command++;
int64_t Expected = *Command++;
DEBUG(dbgs() << "GIM_CheckOpcode(MIs[" << InsnID
<< "], ExpectedOpcode=" << Expected << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (State.MIs[InsnID]->getOpcode() != Expected)
return false;
break;
}
case GIM_CheckNumOperands: {
int64_t InsnID = *Command++;
int64_t Expected = *Command++;
DEBUG(dbgs() << "GIM_CheckNumOperands(MIs[" << InsnID
<< "], Expected=" << Expected << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (State.MIs[InsnID]->getNumOperands() != Expected)
return false;
break;
}

case GIM_CheckType: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
int64_t TypeID = *Command++;
DEBUG(dbgs() << "GIM_CheckType(MIs[" << InsnID << "]->getOperand("
<< OpIdx << "), TypeID=" << TypeID << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (MRI.getType(State.MIs[InsnID]->getOperand(OpIdx).getReg()) !=
MatcherInfo.TypeObjects[TypeID])
return false;
break;
}
case GIM_CheckRegBankForClass: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
int64_t RCEnum = *Command++;
DEBUG(dbgs() << "GIM_CheckRegBankForClass(MIs[" << InsnID
<< "]->getOperand(" << OpIdx << "), RCEnum=" << RCEnum
<< ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (&RBI.getRegBankFromRegClass(*TRI.getRegClass(RCEnum)) !=
RBI.getRegBank(State.MIs[InsnID]->getOperand(OpIdx).getReg(), MRI, TRI))
return false;
break;
}
case GIM_CheckComplexPattern: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
int64_t RendererID = *Command++;
int64_t ComplexPredicateID = *Command++;
DEBUG(dbgs() << "State.Renderers[" << RendererID
<< "] = GIM_CheckComplexPattern(MIs[" << InsnID
<< "]->getOperand(" << OpIdx
<< "), ComplexPredicateID=" << ComplexPredicateID << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
// FIXME: Use std::invoke() when it's available.
if (!(State.Renderers[RendererID] =
(ISel.*MatcherInfo.ComplexPredicates[ComplexPredicateID])(
State.MIs[InsnID]->getOperand(OpIdx))))
return false;
break;
}
case GIM_CheckConstantInt: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
int64_t Value = *Command++;
DEBUG(dbgs() << "GIM_CheckConstantInt(MIs[" << InsnID << "]->getOperand("
<< OpIdx << "), Value=" << Value << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (!isOperandImmEqual(State.MIs[InsnID]->getOperand(OpIdx), Value, MRI))
return false;
break;
}
case GIM_CheckLiteralInt: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
int64_t Value = *Command++;
DEBUG(dbgs() << "GIM_CheckLiteralInt(MIs[" << InsnID << "]->getOperand(" << OpIdx
<< "), Value=" << Value << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
MachineOperand &OM = State.MIs[InsnID]->getOperand(OpIdx);
if (!OM.isCImm() || !OM.getCImm()->equalsInt(Value))
return false;
break;
}
case GIM_CheckIsMBB: {
int64_t InsnID = *Command++;
int64_t OpIdx = *Command++;
DEBUG(dbgs() << "GIM_CheckIsMBB(MIs[" << InsnID << "]->getOperand("
<< OpIdx << "))\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
if (!State.MIs[InsnID]->getOperand(OpIdx).isMBB())
return false;
break;
}

case GIM_Accept:
DEBUG(dbgs() << "GIM_Accept");
return true;
default:
llvm_unreachable("Unexpected command");
}
}
}
} // end namespace llvm

#endif // LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

using namespace llvm;

InstructionSelector::MatcherState::MatcherState(unsigned MaxRenderers)
: Renderers(MaxRenderers, nullptr), MIs() {}

InstructionSelector::InstructionSelector() = default;

bool InstructionSelector::constrainOperandRegToRegClass(
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

#define DEBUG_TYPE "aarch64-isel"

#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"

using namespace llvm;

#ifndef LLVM_BUILD_GLOBAL_ISEL
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#define DEBUG_TYPE "arm-isel"

#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"

using namespace llvm;

#ifndef LLVM_BUILD_GLOBAL_ISEL
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/X86/X86InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

#define DEBUG_TYPE "X86-isel"

#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"

using namespace llvm;

#ifndef LLVM_BUILD_GLOBAL_ISEL
Expand Down
Loading

0 comments on commit 6ab0daa

Please sign in to comment.