2012-07-07 12:00:00 +08:00
|
|
|
//===- CodeGenSchedule.cpp - Scheduling MachineModels ---------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2012-07-07 12:00:00 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2014-01-25 01:20:08 +08:00
|
|
|
// This file defines structures to encapsulate the machine model as described in
|
2012-07-07 12:00:00 +08:00
|
|
|
// the target description.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "CodeGenSchedule.h"
|
2018-01-24 07:05:04 +08:00
|
|
|
#include "CodeGenInstruction.h"
|
2012-07-07 12:00:00 +08:00
|
|
|
#include "CodeGenTarget.h"
|
2018-03-21 10:48:34 +08:00
|
|
|
#include "llvm/ADT/MapVector.h"
|
2018-01-24 07:05:04 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2016-12-01 01:48:10 +08:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
|
|
#include "llvm/ADT/SmallSet.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
2012-07-07 12:00:00 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2012-10-04 07:06:32 +08:00
|
|
|
#include "llvm/Support/Regex.h"
|
2018-01-24 07:05:04 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2012-12-04 18:37:14 +08:00
|
|
|
#include "llvm/TableGen/Error.h"
|
2016-12-01 01:48:10 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
#include <utility>
|
2012-07-07 12:00:00 +08:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2014-04-22 11:06:00 +08:00
|
|
|
#define DEBUG_TYPE "subtarget-emitter"
|
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
#ifndef NDEBUG
|
2015-10-24 20:46:49 +08:00
|
|
|
static void dumpIdxVec(ArrayRef<unsigned> V) {
|
|
|
|
for (unsigned Idx : V)
|
|
|
|
dbgs() << Idx << ", ";
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
#endif
|
|
|
|
|
2013-11-19 11:08:35 +08:00
|
|
|
namespace {
|
2016-12-01 01:48:10 +08:00
|
|
|
|
2012-10-04 07:06:32 +08:00
|
|
|
// (instrs a, b, ...) Evaluate and union all arguments. Identical to AddOp.
|
|
|
|
struct InstrsOp : public SetTheory::Operator {
|
2014-03-05 13:17:42 +08:00
|
|
|
void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts,
|
|
|
|
ArrayRef<SMLoc> Loc) override {
|
2013-11-19 11:08:35 +08:00
|
|
|
ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts, Loc);
|
|
|
|
}
|
|
|
|
};
|
2013-11-19 08:57:56 +08:00
|
|
|
|
2012-10-04 07:06:32 +08:00
|
|
|
// (instregex "OpcPat",...) Find all instructions matching an opcode pattern.
|
|
|
|
struct InstRegexOp : public SetTheory::Operator {
|
|
|
|
const CodeGenTarget &Target;
|
|
|
|
InstRegexOp(const CodeGenTarget &t): Target(t) {}
|
|
|
|
|
2018-01-24 07:05:04 +08:00
|
|
|
/// Remove any text inside of parentheses from S.
|
|
|
|
static std::string removeParens(llvm::StringRef S) {
|
|
|
|
std::string Result;
|
|
|
|
unsigned Paren = 0;
|
|
|
|
// NB: We don't care about escaped parens here.
|
|
|
|
for (char C : S) {
|
|
|
|
switch (C) {
|
|
|
|
case '(':
|
|
|
|
++Paren;
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
--Paren;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (Paren == 0)
|
|
|
|
Result += C;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2013-11-19 11:08:35 +08:00
|
|
|
void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts,
|
2014-03-05 13:17:42 +08:00
|
|
|
ArrayRef<SMLoc> Loc) override {
|
2018-05-24 04:45:43 +08:00
|
|
|
ArrayRef<const CodeGenInstruction *> Instructions =
|
|
|
|
Target.getInstructionsByEnumValue();
|
|
|
|
|
|
|
|
unsigned NumGeneric = Target.getNumFixedInstructions();
|
2018-05-24 06:10:21 +08:00
|
|
|
unsigned NumPseudos = Target.getNumPseudoInstructions();
|
2018-05-24 04:45:43 +08:00
|
|
|
auto Generics = Instructions.slice(0, NumGeneric);
|
2018-05-24 06:10:21 +08:00
|
|
|
auto Pseudos = Instructions.slice(NumGeneric, NumPseudos);
|
|
|
|
auto NonPseudos = Instructions.slice(NumGeneric + NumPseudos);
|
2018-05-24 04:45:43 +08:00
|
|
|
|
2017-10-05 21:27:43 +08:00
|
|
|
for (Init *Arg : make_range(Expr->arg_begin(), Expr->arg_end())) {
|
|
|
|
StringInit *SI = dyn_cast<StringInit>(Arg);
|
2013-11-19 11:08:35 +08:00
|
|
|
if (!SI)
|
2018-01-24 07:05:04 +08:00
|
|
|
PrintFatalError(Loc, "instregex requires pattern string: " +
|
|
|
|
Expr->getAsString());
|
2018-03-21 06:20:28 +08:00
|
|
|
StringRef Original = SI->getValue();
|
|
|
|
|
2018-01-24 07:05:04 +08:00
|
|
|
// Extract a prefix that we can binary search on.
|
|
|
|
static const char RegexMetachars[] = "()^$|*+?.[]\\{}";
|
2018-03-21 06:20:28 +08:00
|
|
|
auto FirstMeta = Original.find_first_of(RegexMetachars);
|
|
|
|
|
2018-01-24 07:05:04 +08:00
|
|
|
// Look for top-level | or ?. We cannot optimize them to binary search.
|
2018-03-21 06:20:28 +08:00
|
|
|
if (removeParens(Original).find_first_of("|?") != std::string::npos)
|
2018-01-24 07:05:04 +08:00
|
|
|
FirstMeta = 0;
|
2018-03-21 06:20:28 +08:00
|
|
|
|
|
|
|
Optional<Regex> Regexpr = None;
|
|
|
|
StringRef Prefix = Original.substr(0, FirstMeta);
|
2018-03-25 05:04:20 +08:00
|
|
|
StringRef PatStr = Original.substr(FirstMeta);
|
|
|
|
if (!PatStr.empty()) {
|
2018-03-21 06:20:28 +08:00
|
|
|
// For the rest use a python-style prefix match.
|
2018-03-25 05:04:20 +08:00
|
|
|
std::string pat = PatStr;
|
2018-03-21 06:20:28 +08:00
|
|
|
if (pat[0] != '^') {
|
|
|
|
pat.insert(0, "^(");
|
|
|
|
pat.insert(pat.end(), ')');
|
|
|
|
}
|
|
|
|
Regexpr = Regex(pat);
|
2013-11-19 11:08:35 +08:00
|
|
|
}
|
2018-03-21 06:20:28 +08:00
|
|
|
|
2018-03-26 03:20:08 +08:00
|
|
|
int NumMatches = 0;
|
|
|
|
|
2018-01-24 07:05:04 +08:00
|
|
|
// The generic opcodes are unsorted, handle them manually.
|
2018-03-21 06:20:28 +08:00
|
|
|
for (auto *Inst : Generics) {
|
|
|
|
StringRef InstName = Inst->TheDef->getName();
|
|
|
|
if (InstName.startswith(Prefix) &&
|
2018-03-26 03:20:08 +08:00
|
|
|
(!Regexpr || Regexpr->match(InstName.substr(Prefix.size())))) {
|
2018-01-24 07:05:04 +08:00
|
|
|
Elts.insert(Inst->TheDef);
|
2018-03-26 03:20:08 +08:00
|
|
|
NumMatches++;
|
|
|
|
}
|
2018-01-24 07:05:04 +08:00
|
|
|
}
|
|
|
|
|
2018-05-24 06:10:21 +08:00
|
|
|
// Target instructions are split into two ranges: pseudo instructions
|
|
|
|
// first, than non-pseudos. Each range is in lexicographical order
|
|
|
|
// sorted by name. Find the sub-ranges that start with our prefix.
|
2018-01-24 07:05:04 +08:00
|
|
|
struct Comp {
|
|
|
|
bool operator()(const CodeGenInstruction *LHS, StringRef RHS) {
|
|
|
|
return LHS->TheDef->getName() < RHS;
|
|
|
|
}
|
|
|
|
bool operator()(StringRef LHS, const CodeGenInstruction *RHS) {
|
|
|
|
return LHS < RHS->TheDef->getName() &&
|
|
|
|
!RHS->TheDef->getName().startswith(LHS);
|
|
|
|
}
|
|
|
|
};
|
2018-05-24 06:10:21 +08:00
|
|
|
auto Range1 =
|
|
|
|
std::equal_range(Pseudos.begin(), Pseudos.end(), Prefix, Comp());
|
|
|
|
auto Range2 = std::equal_range(NonPseudos.begin(), NonPseudos.end(),
|
|
|
|
Prefix, Comp());
|
2018-01-24 07:05:04 +08:00
|
|
|
|
2018-05-24 06:10:21 +08:00
|
|
|
// For these ranges we know that instruction names start with the prefix.
|
|
|
|
// Check if there's a regex that needs to be checked.
|
2018-05-24 04:45:43 +08:00
|
|
|
const auto HandleNonGeneric = [&](const CodeGenInstruction *Inst) {
|
2018-03-21 06:20:28 +08:00
|
|
|
StringRef InstName = Inst->TheDef->getName();
|
2018-03-26 03:20:08 +08:00
|
|
|
if (!Regexpr || Regexpr->match(InstName.substr(Prefix.size()))) {
|
2014-12-09 16:05:51 +08:00
|
|
|
Elts.insert(Inst->TheDef);
|
2018-03-26 03:20:08 +08:00
|
|
|
NumMatches++;
|
|
|
|
}
|
2018-05-24 04:45:43 +08:00
|
|
|
};
|
2018-05-24 06:10:21 +08:00
|
|
|
std::for_each(Range1.first, Range1.second, HandleNonGeneric);
|
|
|
|
std::for_each(Range2.first, Range2.second, HandleNonGeneric);
|
2018-03-26 03:20:08 +08:00
|
|
|
|
|
|
|
if (0 == NumMatches)
|
|
|
|
PrintFatalError(Loc, "instregex has no matches: " + Original);
|
2012-10-04 07:06:32 +08:00
|
|
|
}
|
|
|
|
}
|
2013-11-19 11:08:35 +08:00
|
|
|
};
|
2016-12-01 01:48:10 +08:00
|
|
|
|
2013-11-19 11:08:35 +08:00
|
|
|
} // end anonymous namespace
|
2012-10-04 07:06:32 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
/// CodeGenModels ctor interprets machine model records and populates maps.
|
2012-07-07 12:00:00 +08:00
|
|
|
CodeGenSchedModels::CodeGenSchedModels(RecordKeeper &RK,
|
|
|
|
const CodeGenTarget &TGT):
|
2013-03-17 02:58:55 +08:00
|
|
|
Records(RK), Target(TGT) {
|
2012-09-15 08:19:57 +08:00
|
|
|
|
2012-10-04 07:06:32 +08:00
|
|
|
Sets.addFieldExpander("InstRW", "Instrs");
|
|
|
|
|
|
|
|
// Allow Set evaluation to recognize the dags used in InstRW records:
|
|
|
|
// (instrs Op1, Op1...)
|
2015-04-24 14:49:44 +08:00
|
|
|
Sets.addOperator("instrs", llvm::make_unique<InstrsOp>());
|
|
|
|
Sets.addOperator("instregex", llvm::make_unique<InstRegexOp>(Target));
|
2012-10-04 07:06:32 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Instantiate a CodeGenProcModel for each SchedMachineModel with the values
|
|
|
|
// that are explicitly referenced in tablegen records. Resources associated
|
|
|
|
// with each processor will be derived later. Populate ProcModelMap with the
|
|
|
|
// CodeGenProcModel instances.
|
|
|
|
collectProcModels();
|
|
|
|
|
|
|
|
// Instantiate a CodeGenSchedRW for each SchedReadWrite record explicitly
|
|
|
|
// defined, and populate SchedReads and SchedWrites vectors. Implicit
|
|
|
|
// SchedReadWrites that represent sequences derived from expanded variant will
|
|
|
|
// be inferred later.
|
|
|
|
collectSchedRW();
|
|
|
|
|
|
|
|
// Instantiate a CodeGenSchedClass for each unique SchedRW signature directly
|
|
|
|
// required by an instruction definition, and populate SchedClassIdxMap. Set
|
|
|
|
// NumItineraryClasses to the number of explicit itinerary classes referenced
|
|
|
|
// by instructions. Set NumInstrSchedClasses to the number of itinerary
|
|
|
|
// classes plus any classes implied by instructions that derive from class
|
|
|
|
// Sched and provide SchedRW list. This does not infer any new classes from
|
|
|
|
// SchedVariant.
|
|
|
|
collectSchedClasses();
|
|
|
|
|
|
|
|
// Find instruction itineraries for each processor. Sort and populate
|
2012-09-22 10:24:21 +08:00
|
|
|
// CodeGenProcModel::ItinDefList. (Cycle-to-cycle itineraries). This requires
|
2012-09-15 08:19:57 +08:00
|
|
|
// all itinerary classes to be discovered.
|
|
|
|
collectProcItins();
|
|
|
|
|
|
|
|
// Find ItinRW records for each processor and itinerary class.
|
|
|
|
// (For per-operand resources mapped to itinerary classes).
|
|
|
|
collectProcItinRW();
|
2012-09-15 08:19:59 +08:00
|
|
|
|
2016-06-24 16:43:27 +08:00
|
|
|
// Find UnsupportedFeatures records for each processor.
|
|
|
|
// (For per-operand resources mapped to itinerary classes).
|
|
|
|
collectProcUnsupportedFeatures();
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
// Infer new SchedClasses from SchedVariant.
|
|
|
|
inferSchedClasses();
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
// Populate each CodeGenProcModel's WriteResDefs, ReadAdvanceDefs, and
|
|
|
|
// ProcResourceDefs.
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "\n+++ RESOURCE DEFINITIONS (collectProcResources) +++\n");
|
2012-09-15 08:20:02 +08:00
|
|
|
collectProcResources();
|
2016-03-02 04:03:21 +08:00
|
|
|
|
2018-04-05 23:41:41 +08:00
|
|
|
// Collect optional processor description.
|
|
|
|
collectOptionalProcessorInfo();
|
|
|
|
|
2018-08-15 02:36:54 +08:00
|
|
|
// Check MCInstPredicate definitions.
|
|
|
|
checkMCInstPredicates();
|
|
|
|
|
[TableGen][SubtargetEmitter] Add the ability for processor models to describe dependency breaking instructions.
This patch adds the ability for processor models to describe dependency breaking
instructions.
Different processors may specify a different set of dependency-breaking
instructions.
That means, we cannot assume that all processors of the same target would use
the same rules to classify dependency breaking instructions.
The main goal of this patch is to provide the means to describe dependency
breaking instructions directly via tablegen, and have the following
TargetSubtargetInfo hooks redefined in overrides by tabegen'd
XXXGenSubtargetInfo classes (here, XXX is a Target name).
```
virtual bool isZeroIdiom(const MachineInstr *MI, APInt &Mask) const {
return false;
}
virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
return isZeroIdiom(MI);
}
```
An instruction MI is a dependency-breaking instruction if a call to method
isDependencyBreaking(MI) on the STI (TargetSubtargetInfo object) evaluates to
true. Similarly, an instruction MI is a special case of zero-idiom dependency
breaking instruction if a call to STI.isZeroIdiom(MI) returns true.
The extra APInt is used for those targets that may want to select which machine
operands have their dependency broken (see comments in code).
Note that by default, subtargets don't know about the existence of
dependency-breaking. In the absence of external information, those method calls
would always return false.
A new tablegen class named STIPredicate has been added by this patch to let
processor models classify instructions that have properties in common. The idea
is that, a MCInstrPredicate definition can be used to "generate" an instruction
equivalence class, with the idea that instructions of a same class all have a
property in common.
STIPredicate definitions are essentially a collection of instruction equivalence
classes.
Also, different processor models can specify a different variant of the same
STIPredicate with different rules (i.e. predicates) to classify instructions.
Tablegen backends (in this particular case, the SubtargetEmitter) will be able
to process STIPredicate definitions, and automatically generate functions in
XXXGenSubtargetInfo.
This patch introduces two special kind of STIPredicate classes named
IsZeroIdiomFunction and IsDepBreakingFunction in tablegen. It also adds a
definition for those in the BtVer2 scheduling model only.
This patch supersedes the one committed at r338372 (phabricator review: D49310).
The main advantages are:
- We can describe subtarget predicates via tablegen using STIPredicates.
- We can describe zero-idioms / dep-breaking instructions directly via
tablegen in the scheduling models.
In future, the STIPredicates framework can be used for solving other problems.
Examples of future developments are:
- Teach how to identify optimizable register-register moves
- Teach how to identify slow LEA instructions (each subtarget defining its own
concept of "slow" LEA).
- Teach how to identify instructions that have undocumented false dependencies
on the output registers on some processors only.
It is also (in my opinion) an elegant way to expose knowledge to both external
tools like llvm-mca, and codegen passes.
For example, machine schedulers in LLVM could reuse that information when
internally constructing the data dependency graph for a code region.
This new design feature is also an "opt-in" feature. Processor models don't have
to use the new STIPredicates. It has all been designed to be as unintrusive as
possible.
Differential Revision: https://reviews.llvm.org/D52174
llvm-svn: 342555
2018-09-19 23:57:45 +08:00
|
|
|
// Check STIPredicate definitions.
|
|
|
|
checkSTIPredicates();
|
|
|
|
|
|
|
|
// Find STIPredicate definitions for each processor model, and construct
|
|
|
|
// STIPredicateFunction objects.
|
|
|
|
collectSTIPredicates();
|
|
|
|
|
2018-04-05 23:41:41 +08:00
|
|
|
checkCompleteness();
|
|
|
|
}
|
|
|
|
|
[TableGen][SubtargetEmitter] Add the ability for processor models to describe dependency breaking instructions.
This patch adds the ability for processor models to describe dependency breaking
instructions.
Different processors may specify a different set of dependency-breaking
instructions.
That means, we cannot assume that all processors of the same target would use
the same rules to classify dependency breaking instructions.
The main goal of this patch is to provide the means to describe dependency
breaking instructions directly via tablegen, and have the following
TargetSubtargetInfo hooks redefined in overrides by tabegen'd
XXXGenSubtargetInfo classes (here, XXX is a Target name).
```
virtual bool isZeroIdiom(const MachineInstr *MI, APInt &Mask) const {
return false;
}
virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
return isZeroIdiom(MI);
}
```
An instruction MI is a dependency-breaking instruction if a call to method
isDependencyBreaking(MI) on the STI (TargetSubtargetInfo object) evaluates to
true. Similarly, an instruction MI is a special case of zero-idiom dependency
breaking instruction if a call to STI.isZeroIdiom(MI) returns true.
The extra APInt is used for those targets that may want to select which machine
operands have their dependency broken (see comments in code).
Note that by default, subtargets don't know about the existence of
dependency-breaking. In the absence of external information, those method calls
would always return false.
A new tablegen class named STIPredicate has been added by this patch to let
processor models classify instructions that have properties in common. The idea
is that, a MCInstrPredicate definition can be used to "generate" an instruction
equivalence class, with the idea that instructions of a same class all have a
property in common.
STIPredicate definitions are essentially a collection of instruction equivalence
classes.
Also, different processor models can specify a different variant of the same
STIPredicate with different rules (i.e. predicates) to classify instructions.
Tablegen backends (in this particular case, the SubtargetEmitter) will be able
to process STIPredicate definitions, and automatically generate functions in
XXXGenSubtargetInfo.
This patch introduces two special kind of STIPredicate classes named
IsZeroIdiomFunction and IsDepBreakingFunction in tablegen. It also adds a
definition for those in the BtVer2 scheduling model only.
This patch supersedes the one committed at r338372 (phabricator review: D49310).
The main advantages are:
- We can describe subtarget predicates via tablegen using STIPredicates.
- We can describe zero-idioms / dep-breaking instructions directly via
tablegen in the scheduling models.
In future, the STIPredicates framework can be used for solving other problems.
Examples of future developments are:
- Teach how to identify optimizable register-register moves
- Teach how to identify slow LEA instructions (each subtarget defining its own
concept of "slow" LEA).
- Teach how to identify instructions that have undocumented false dependencies
on the output registers on some processors only.
It is also (in my opinion) an elegant way to expose knowledge to both external
tools like llvm-mca, and codegen passes.
For example, machine schedulers in LLVM could reuse that information when
internally constructing the data dependency graph for a code region.
This new design feature is also an "opt-in" feature. Processor models don't have
to use the new STIPredicates. It has all been designed to be as unintrusive as
possible.
Differential Revision: https://reviews.llvm.org/D52174
llvm-svn: 342555
2018-09-19 23:57:45 +08:00
|
|
|
void CodeGenSchedModels::checkSTIPredicates() const {
|
|
|
|
DenseMap<StringRef, const Record *> Declarations;
|
|
|
|
|
|
|
|
// There cannot be multiple declarations with the same name.
|
|
|
|
const RecVec Decls = Records.getAllDerivedDefinitions("STIPredicateDecl");
|
|
|
|
for (const Record *R : Decls) {
|
|
|
|
StringRef Name = R->getValueAsString("Name");
|
|
|
|
const auto It = Declarations.find(Name);
|
|
|
|
if (It == Declarations.end()) {
|
|
|
|
Declarations[Name] = R;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintError(R->getLoc(), "STIPredicate " + Name + " multiply declared.");
|
|
|
|
PrintNote(It->second->getLoc(), "Previous declaration was here.");
|
|
|
|
PrintFatalError(R->getLoc(), "Invalid STIPredicateDecl found.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disallow InstructionEquivalenceClasses with an empty instruction list.
|
|
|
|
const RecVec Defs =
|
|
|
|
Records.getAllDerivedDefinitions("InstructionEquivalenceClass");
|
|
|
|
for (const Record *R : Defs) {
|
|
|
|
RecVec Opcodes = R->getValueAsListOfDefs("Opcodes");
|
|
|
|
if (Opcodes.empty()) {
|
|
|
|
PrintFatalError(R->getLoc(), "Invalid InstructionEquivalenceClass "
|
|
|
|
"defined with an empty opcode list.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used by function `processSTIPredicate` to construct a mask of machine
|
|
|
|
// instruction operands.
|
|
|
|
static APInt constructOperandMask(ArrayRef<int64_t> Indices) {
|
|
|
|
APInt OperandMask;
|
|
|
|
if (Indices.empty())
|
|
|
|
return OperandMask;
|
|
|
|
|
|
|
|
int64_t MaxIndex = *std::max_element(Indices.begin(), Indices.end());
|
|
|
|
assert(MaxIndex >= 0 && "Invalid negative indices in input!");
|
|
|
|
OperandMask = OperandMask.zext(MaxIndex + 1);
|
|
|
|
for (const int64_t Index : Indices) {
|
|
|
|
assert(Index >= 0 && "Invalid negative indices!");
|
|
|
|
OperandMask.setBit(Index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return OperandMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
processSTIPredicate(STIPredicateFunction &Fn,
|
|
|
|
const DenseMap<Record *, unsigned> &ProcModelMap) {
|
|
|
|
DenseMap<const Record *, unsigned> Opcode2Index;
|
|
|
|
using OpcodeMapPair = std::pair<const Record *, OpcodeInfo>;
|
|
|
|
std::vector<OpcodeMapPair> OpcodeMappings;
|
|
|
|
std::vector<std::pair<APInt, APInt>> OpcodeMasks;
|
|
|
|
|
|
|
|
DenseMap<const Record *, unsigned> Predicate2Index;
|
|
|
|
unsigned NumUniquePredicates = 0;
|
|
|
|
|
|
|
|
// Number unique predicates and opcodes used by InstructionEquivalenceClass
|
|
|
|
// definitions. Each unique opcode will be associated with an OpcodeInfo
|
|
|
|
// object.
|
|
|
|
for (const Record *Def : Fn.getDefinitions()) {
|
|
|
|
RecVec Classes = Def->getValueAsListOfDefs("Classes");
|
|
|
|
for (const Record *EC : Classes) {
|
|
|
|
const Record *Pred = EC->getValueAsDef("Predicate");
|
|
|
|
if (Predicate2Index.find(Pred) == Predicate2Index.end())
|
|
|
|
Predicate2Index[Pred] = NumUniquePredicates++;
|
|
|
|
|
|
|
|
RecVec Opcodes = EC->getValueAsListOfDefs("Opcodes");
|
|
|
|
for (const Record *Opcode : Opcodes) {
|
|
|
|
if (Opcode2Index.find(Opcode) == Opcode2Index.end()) {
|
|
|
|
Opcode2Index[Opcode] = OpcodeMappings.size();
|
|
|
|
OpcodeMappings.emplace_back(Opcode, OpcodeInfo());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize vector `OpcodeMasks` with default values. We want to keep track
|
|
|
|
// of which processors "use" which opcodes. We also want to be able to
|
|
|
|
// identify predicates that are used by different processors for a same
|
|
|
|
// opcode.
|
|
|
|
// This information is used later on by this algorithm to sort OpcodeMapping
|
|
|
|
// elements based on their processor and predicate sets.
|
|
|
|
OpcodeMasks.resize(OpcodeMappings.size());
|
|
|
|
APInt DefaultProcMask(ProcModelMap.size(), 0);
|
|
|
|
APInt DefaultPredMask(NumUniquePredicates, 0);
|
|
|
|
for (std::pair<APInt, APInt> &MaskPair : OpcodeMasks)
|
|
|
|
MaskPair = std::make_pair(DefaultProcMask, DefaultPredMask);
|
|
|
|
|
|
|
|
// Construct a OpcodeInfo object for every unique opcode declared by an
|
|
|
|
// InstructionEquivalenceClass definition.
|
|
|
|
for (const Record *Def : Fn.getDefinitions()) {
|
|
|
|
RecVec Classes = Def->getValueAsListOfDefs("Classes");
|
|
|
|
const Record *SchedModel = Def->getValueAsDef("SchedModel");
|
|
|
|
unsigned ProcIndex = ProcModelMap.find(SchedModel)->second;
|
|
|
|
APInt ProcMask(ProcModelMap.size(), 0);
|
|
|
|
ProcMask.setBit(ProcIndex);
|
|
|
|
|
|
|
|
for (const Record *EC : Classes) {
|
|
|
|
RecVec Opcodes = EC->getValueAsListOfDefs("Opcodes");
|
|
|
|
|
|
|
|
std::vector<int64_t> OpIndices =
|
|
|
|
EC->getValueAsListOfInts("OperandIndices");
|
|
|
|
APInt OperandMask = constructOperandMask(OpIndices);
|
|
|
|
|
|
|
|
const Record *Pred = EC->getValueAsDef("Predicate");
|
|
|
|
APInt PredMask(NumUniquePredicates, 0);
|
|
|
|
PredMask.setBit(Predicate2Index[Pred]);
|
|
|
|
|
|
|
|
for (const Record *Opcode : Opcodes) {
|
|
|
|
unsigned OpcodeIdx = Opcode2Index[Opcode];
|
|
|
|
if (OpcodeMasks[OpcodeIdx].first[ProcIndex]) {
|
|
|
|
std::string Message =
|
2018-10-25 15:44:01 +08:00
|
|
|
"Opcode " + Opcode->getName().str() +
|
[TableGen][SubtargetEmitter] Add the ability for processor models to describe dependency breaking instructions.
This patch adds the ability for processor models to describe dependency breaking
instructions.
Different processors may specify a different set of dependency-breaking
instructions.
That means, we cannot assume that all processors of the same target would use
the same rules to classify dependency breaking instructions.
The main goal of this patch is to provide the means to describe dependency
breaking instructions directly via tablegen, and have the following
TargetSubtargetInfo hooks redefined in overrides by tabegen'd
XXXGenSubtargetInfo classes (here, XXX is a Target name).
```
virtual bool isZeroIdiom(const MachineInstr *MI, APInt &Mask) const {
return false;
}
virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
return isZeroIdiom(MI);
}
```
An instruction MI is a dependency-breaking instruction if a call to method
isDependencyBreaking(MI) on the STI (TargetSubtargetInfo object) evaluates to
true. Similarly, an instruction MI is a special case of zero-idiom dependency
breaking instruction if a call to STI.isZeroIdiom(MI) returns true.
The extra APInt is used for those targets that may want to select which machine
operands have their dependency broken (see comments in code).
Note that by default, subtargets don't know about the existence of
dependency-breaking. In the absence of external information, those method calls
would always return false.
A new tablegen class named STIPredicate has been added by this patch to let
processor models classify instructions that have properties in common. The idea
is that, a MCInstrPredicate definition can be used to "generate" an instruction
equivalence class, with the idea that instructions of a same class all have a
property in common.
STIPredicate definitions are essentially a collection of instruction equivalence
classes.
Also, different processor models can specify a different variant of the same
STIPredicate with different rules (i.e. predicates) to classify instructions.
Tablegen backends (in this particular case, the SubtargetEmitter) will be able
to process STIPredicate definitions, and automatically generate functions in
XXXGenSubtargetInfo.
This patch introduces two special kind of STIPredicate classes named
IsZeroIdiomFunction and IsDepBreakingFunction in tablegen. It also adds a
definition for those in the BtVer2 scheduling model only.
This patch supersedes the one committed at r338372 (phabricator review: D49310).
The main advantages are:
- We can describe subtarget predicates via tablegen using STIPredicates.
- We can describe zero-idioms / dep-breaking instructions directly via
tablegen in the scheduling models.
In future, the STIPredicates framework can be used for solving other problems.
Examples of future developments are:
- Teach how to identify optimizable register-register moves
- Teach how to identify slow LEA instructions (each subtarget defining its own
concept of "slow" LEA).
- Teach how to identify instructions that have undocumented false dependencies
on the output registers on some processors only.
It is also (in my opinion) an elegant way to expose knowledge to both external
tools like llvm-mca, and codegen passes.
For example, machine schedulers in LLVM could reuse that information when
internally constructing the data dependency graph for a code region.
This new design feature is also an "opt-in" feature. Processor models don't have
to use the new STIPredicates. It has all been designed to be as unintrusive as
possible.
Differential Revision: https://reviews.llvm.org/D52174
llvm-svn: 342555
2018-09-19 23:57:45 +08:00
|
|
|
" used by multiple InstructionEquivalenceClass definitions.";
|
|
|
|
PrintFatalError(EC->getLoc(), Message);
|
|
|
|
}
|
|
|
|
OpcodeMasks[OpcodeIdx].first |= ProcMask;
|
|
|
|
OpcodeMasks[OpcodeIdx].second |= PredMask;
|
|
|
|
OpcodeInfo &OI = OpcodeMappings[OpcodeIdx].second;
|
|
|
|
|
|
|
|
OI.addPredicateForProcModel(ProcMask, OperandMask, Pred);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort OpcodeMappings elements based on their CPU and predicate masks.
|
|
|
|
// As a last resort, order elements by opcode identifier.
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(OpcodeMappings,
|
[TableGen][SubtargetEmitter] Add the ability for processor models to describe dependency breaking instructions.
This patch adds the ability for processor models to describe dependency breaking
instructions.
Different processors may specify a different set of dependency-breaking
instructions.
That means, we cannot assume that all processors of the same target would use
the same rules to classify dependency breaking instructions.
The main goal of this patch is to provide the means to describe dependency
breaking instructions directly via tablegen, and have the following
TargetSubtargetInfo hooks redefined in overrides by tabegen'd
XXXGenSubtargetInfo classes (here, XXX is a Target name).
```
virtual bool isZeroIdiom(const MachineInstr *MI, APInt &Mask) const {
return false;
}
virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
return isZeroIdiom(MI);
}
```
An instruction MI is a dependency-breaking instruction if a call to method
isDependencyBreaking(MI) on the STI (TargetSubtargetInfo object) evaluates to
true. Similarly, an instruction MI is a special case of zero-idiom dependency
breaking instruction if a call to STI.isZeroIdiom(MI) returns true.
The extra APInt is used for those targets that may want to select which machine
operands have their dependency broken (see comments in code).
Note that by default, subtargets don't know about the existence of
dependency-breaking. In the absence of external information, those method calls
would always return false.
A new tablegen class named STIPredicate has been added by this patch to let
processor models classify instructions that have properties in common. The idea
is that, a MCInstrPredicate definition can be used to "generate" an instruction
equivalence class, with the idea that instructions of a same class all have a
property in common.
STIPredicate definitions are essentially a collection of instruction equivalence
classes.
Also, different processor models can specify a different variant of the same
STIPredicate with different rules (i.e. predicates) to classify instructions.
Tablegen backends (in this particular case, the SubtargetEmitter) will be able
to process STIPredicate definitions, and automatically generate functions in
XXXGenSubtargetInfo.
This patch introduces two special kind of STIPredicate classes named
IsZeroIdiomFunction and IsDepBreakingFunction in tablegen. It also adds a
definition for those in the BtVer2 scheduling model only.
This patch supersedes the one committed at r338372 (phabricator review: D49310).
The main advantages are:
- We can describe subtarget predicates via tablegen using STIPredicates.
- We can describe zero-idioms / dep-breaking instructions directly via
tablegen in the scheduling models.
In future, the STIPredicates framework can be used for solving other problems.
Examples of future developments are:
- Teach how to identify optimizable register-register moves
- Teach how to identify slow LEA instructions (each subtarget defining its own
concept of "slow" LEA).
- Teach how to identify instructions that have undocumented false dependencies
on the output registers on some processors only.
It is also (in my opinion) an elegant way to expose knowledge to both external
tools like llvm-mca, and codegen passes.
For example, machine schedulers in LLVM could reuse that information when
internally constructing the data dependency graph for a code region.
This new design feature is also an "opt-in" feature. Processor models don't have
to use the new STIPredicates. It has all been designed to be as unintrusive as
possible.
Differential Revision: https://reviews.llvm.org/D52174
llvm-svn: 342555
2018-09-19 23:57:45 +08:00
|
|
|
[&](const OpcodeMapPair &Lhs, const OpcodeMapPair &Rhs) {
|
|
|
|
unsigned LhsIdx = Opcode2Index[Lhs.first];
|
|
|
|
unsigned RhsIdx = Opcode2Index[Rhs.first];
|
2019-02-27 02:50:49 +08:00
|
|
|
const std::pair<APInt, APInt> &LhsMasks = OpcodeMasks[LhsIdx];
|
|
|
|
const std::pair<APInt, APInt> &RhsMasks = OpcodeMasks[RhsIdx];
|
|
|
|
|
|
|
|
auto LessThan = [](const APInt &Lhs, const APInt &Rhs) {
|
|
|
|
unsigned LhsCountPopulation = Lhs.countPopulation();
|
|
|
|
unsigned RhsCountPopulation = Rhs.countPopulation();
|
|
|
|
return ((LhsCountPopulation < RhsCountPopulation) ||
|
|
|
|
((LhsCountPopulation == RhsCountPopulation) &&
|
|
|
|
(Lhs.countLeadingZeros() > Rhs.countLeadingZeros())));
|
|
|
|
};
|
|
|
|
|
|
|
|
if (LhsMasks.first != RhsMasks.first)
|
|
|
|
return LessThan(LhsMasks.first, RhsMasks.first);
|
|
|
|
|
|
|
|
if (LhsMasks.second != RhsMasks.second)
|
|
|
|
return LessThan(LhsMasks.second, RhsMasks.second);
|
[TableGen][SubtargetEmitter] Add the ability for processor models to describe dependency breaking instructions.
This patch adds the ability for processor models to describe dependency breaking
instructions.
Different processors may specify a different set of dependency-breaking
instructions.
That means, we cannot assume that all processors of the same target would use
the same rules to classify dependency breaking instructions.
The main goal of this patch is to provide the means to describe dependency
breaking instructions directly via tablegen, and have the following
TargetSubtargetInfo hooks redefined in overrides by tabegen'd
XXXGenSubtargetInfo classes (here, XXX is a Target name).
```
virtual bool isZeroIdiom(const MachineInstr *MI, APInt &Mask) const {
return false;
}
virtual bool isDependencyBreaking(const MachineInstr *MI, APInt &Mask) const {
return isZeroIdiom(MI);
}
```
An instruction MI is a dependency-breaking instruction if a call to method
isDependencyBreaking(MI) on the STI (TargetSubtargetInfo object) evaluates to
true. Similarly, an instruction MI is a special case of zero-idiom dependency
breaking instruction if a call to STI.isZeroIdiom(MI) returns true.
The extra APInt is used for those targets that may want to select which machine
operands have their dependency broken (see comments in code).
Note that by default, subtargets don't know about the existence of
dependency-breaking. In the absence of external information, those method calls
would always return false.
A new tablegen class named STIPredicate has been added by this patch to let
processor models classify instructions that have properties in common. The idea
is that, a MCInstrPredicate definition can be used to "generate" an instruction
equivalence class, with the idea that instructions of a same class all have a
property in common.
STIPredicate definitions are essentially a collection of instruction equivalence
classes.
Also, different processor models can specify a different variant of the same
STIPredicate with different rules (i.e. predicates) to classify instructions.
Tablegen backends (in this particular case, the SubtargetEmitter) will be able
to process STIPredicate definitions, and automatically generate functions in
XXXGenSubtargetInfo.
This patch introduces two special kind of STIPredicate classes named
IsZeroIdiomFunction and IsDepBreakingFunction in tablegen. It also adds a
definition for those in the BtVer2 scheduling model only.
This patch supersedes the one committed at r338372 (phabricator review: D49310).
The main advantages are:
- We can describe subtarget predicates via tablegen using STIPredicates.
- We can describe zero-idioms / dep-breaking instructions directly via
tablegen in the scheduling models.
In future, the STIPredicates framework can be used for solving other problems.
Examples of future developments are:
- Teach how to identify optimizable register-register moves
- Teach how to identify slow LEA instructions (each subtarget defining its own
concept of "slow" LEA).
- Teach how to identify instructions that have undocumented false dependencies
on the output registers on some processors only.
It is also (in my opinion) an elegant way to expose knowledge to both external
tools like llvm-mca, and codegen passes.
For example, machine schedulers in LLVM could reuse that information when
internally constructing the data dependency graph for a code region.
This new design feature is also an "opt-in" feature. Processor models don't have
to use the new STIPredicates. It has all been designed to be as unintrusive as
possible.
Differential Revision: https://reviews.llvm.org/D52174
llvm-svn: 342555
2018-09-19 23:57:45 +08:00
|
|
|
|
|
|
|
return LhsIdx < RhsIdx;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Now construct opcode groups. Groups are used by the SubtargetEmitter when
|
|
|
|
// expanding the body of a STIPredicate function. In particular, each opcode
|
|
|
|
// group is expanded into a sequence of labels in a switch statement.
|
|
|
|
// It identifies opcodes for which different processors define same predicates
|
|
|
|
// and same opcode masks.
|
|
|
|
for (OpcodeMapPair &Info : OpcodeMappings)
|
|
|
|
Fn.addOpcode(Info.first, std::move(Info.second));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeGenSchedModels::collectSTIPredicates() {
|
|
|
|
// Map STIPredicateDecl records to elements of vector
|
|
|
|
// CodeGenSchedModels::STIPredicates.
|
|
|
|
DenseMap<const Record *, unsigned> Decl2Index;
|
|
|
|
|
|
|
|
RecVec RV = Records.getAllDerivedDefinitions("STIPredicate");
|
|
|
|
for (const Record *R : RV) {
|
|
|
|
const Record *Decl = R->getValueAsDef("Declaration");
|
|
|
|
|
|
|
|
const auto It = Decl2Index.find(Decl);
|
|
|
|
if (It == Decl2Index.end()) {
|
|
|
|
Decl2Index[Decl] = STIPredicates.size();
|
|
|
|
STIPredicateFunction Predicate(Decl);
|
|
|
|
Predicate.addDefinition(R);
|
|
|
|
STIPredicates.emplace_back(std::move(Predicate));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
STIPredicateFunction &PreviousDef = STIPredicates[It->second];
|
|
|
|
PreviousDef.addDefinition(R);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (STIPredicateFunction &Fn : STIPredicates)
|
|
|
|
processSTIPredicate(Fn, ProcModelMap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpcodeInfo::addPredicateForProcModel(const llvm::APInt &CpuMask,
|
|
|
|
const llvm::APInt &OperandMask,
|
|
|
|
const Record *Predicate) {
|
|
|
|
auto It = llvm::find_if(
|
|
|
|
Predicates, [&OperandMask, &Predicate](const PredicateInfo &P) {
|
|
|
|
return P.Predicate == Predicate && P.OperandMask == OperandMask;
|
|
|
|
});
|
|
|
|
if (It == Predicates.end()) {
|
|
|
|
Predicates.emplace_back(CpuMask, OperandMask, Predicate);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
It->ProcModelMask |= CpuMask;
|
|
|
|
}
|
|
|
|
|
2018-08-15 02:36:54 +08:00
|
|
|
void CodeGenSchedModels::checkMCInstPredicates() const {
|
|
|
|
RecVec MCPredicates = Records.getAllDerivedDefinitions("TIIPredicate");
|
|
|
|
if (MCPredicates.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// A target cannot have multiple TIIPredicate definitions with a same name.
|
|
|
|
llvm::StringMap<const Record *> TIIPredicates(MCPredicates.size());
|
|
|
|
for (const Record *TIIPred : MCPredicates) {
|
|
|
|
StringRef Name = TIIPred->getValueAsString("FunctionName");
|
|
|
|
StringMap<const Record *>::const_iterator It = TIIPredicates.find(Name);
|
|
|
|
if (It == TIIPredicates.end()) {
|
|
|
|
TIIPredicates[Name] = TIIPred;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintError(TIIPred->getLoc(),
|
|
|
|
"TIIPredicate " + Name + " is multiply defined.");
|
|
|
|
PrintNote(It->second->getLoc(),
|
|
|
|
" Previous definition of " + Name + " was here.");
|
|
|
|
PrintFatalError(TIIPred->getLoc(),
|
|
|
|
"Found conflicting definitions of TIIPredicate.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 23:41:41 +08:00
|
|
|
void CodeGenSchedModels::collectRetireControlUnits() {
|
|
|
|
RecVec Units = Records.getAllDerivedDefinitions("RetireControlUnit");
|
|
|
|
|
|
|
|
for (Record *RCU : Units) {
|
|
|
|
CodeGenProcModel &PM = getProcModel(RCU->getValueAsDef("SchedModel"));
|
|
|
|
if (PM.RetireControlUnit) {
|
|
|
|
PrintError(RCU->getLoc(),
|
|
|
|
"Expected a single RetireControlUnit definition");
|
|
|
|
PrintNote(PM.RetireControlUnit->getLoc(),
|
|
|
|
"Previous definition of RetireControlUnit was here");
|
|
|
|
}
|
|
|
|
PM.RetireControlUnit = RCU;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[llvm-mca][MC] Add the ability to declare which processor resources model load/store queues (PR36666).
This patch adds the ability to specify via tablegen which processor resources
are load/store queue resources.
A new tablegen class named MemoryQueue can be optionally used to mark resources
that model load/store queues. Information about the load/store queue is
collected at 'CodeGenSchedule' stage, and analyzed by the 'SubtargetEmitter' to
initialize two new fields in struct MCExtraProcessorInfo named `LoadQueueID` and
`StoreQueueID`. Those two fields are identifiers for buffered resources used to
describe the load queue and the store queue.
Field `BufferSize` is interpreted as the number of entries in the queue, while
the number of units is a throughput indicator (i.e. number of available pickers
for loads/stores).
At construction time, LSUnit in llvm-mca checks for the presence of extra
processor information (i.e. MCExtraProcessorInfo) in the scheduling model. If
that information is available, and fields LoadQueueID and StoreQueueID are set
to a value different than zero (i.e. the invalid processor resource index), then
LSUnit initializes its LoadQueue/StoreQueue based on the BufferSize value
declared by the two processor resources.
With this patch, we more accurately track dynamic dispatch stalls caused by the
lack of LS tokens (i.e. load/store queue full). This is also shown by the
differences in two BdVer2 tests. Stalls that were previously classified as
generic SCHEDULER FULL stalls, are not correctly classified either as "load
queue full" or "store queue full".
About the differences in the -scheduler-stats view: those differences are
expected, because entries in the load/store queue are not released at
instruction issue stage. Instead, those are released at instruction executed
stage. This is the main reason why for the modified tests, the load/store
queues gets full before PdEx is full.
Differential Revision: https://reviews.llvm.org/D54957
llvm-svn: 347857
2018-11-29 20:15:56 +08:00
|
|
|
void CodeGenSchedModels::collectLoadStoreQueueInfo() {
|
|
|
|
RecVec Queues = Records.getAllDerivedDefinitions("MemoryQueue");
|
|
|
|
|
|
|
|
for (Record *Queue : Queues) {
|
|
|
|
CodeGenProcModel &PM = getProcModel(Queue->getValueAsDef("SchedModel"));
|
|
|
|
if (Queue->isSubClassOf("LoadQueue")) {
|
|
|
|
if (PM.LoadQueue) {
|
|
|
|
PrintError(Queue->getLoc(),
|
|
|
|
"Expected a single LoadQueue definition");
|
|
|
|
PrintNote(PM.LoadQueue->getLoc(),
|
|
|
|
"Previous definition of LoadQueue was here");
|
|
|
|
}
|
|
|
|
|
|
|
|
PM.LoadQueue = Queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Queue->isSubClassOf("StoreQueue")) {
|
|
|
|
if (PM.StoreQueue) {
|
|
|
|
PrintError(Queue->getLoc(),
|
|
|
|
"Expected a single StoreQueue definition");
|
|
|
|
PrintNote(PM.LoadQueue->getLoc(),
|
|
|
|
"Previous definition of StoreQueue was here");
|
|
|
|
}
|
|
|
|
|
|
|
|
PM.StoreQueue = Queue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 23:41:41 +08:00
|
|
|
/// Collect optional processor information.
|
|
|
|
void CodeGenSchedModels::collectOptionalProcessorInfo() {
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
// Find register file definitions for each processor.
|
|
|
|
collectRegisterFiles();
|
|
|
|
|
2018-04-05 23:41:41 +08:00
|
|
|
// Collect processor RetireControlUnit descriptors if available.
|
|
|
|
collectRetireControlUnits();
|
2018-04-10 16:16:37 +08:00
|
|
|
|
[llvm-mca][MC] Add the ability to declare which processor resources model load/store queues (PR36666).
This patch adds the ability to specify via tablegen which processor resources
are load/store queue resources.
A new tablegen class named MemoryQueue can be optionally used to mark resources
that model load/store queues. Information about the load/store queue is
collected at 'CodeGenSchedule' stage, and analyzed by the 'SubtargetEmitter' to
initialize two new fields in struct MCExtraProcessorInfo named `LoadQueueID` and
`StoreQueueID`. Those two fields are identifiers for buffered resources used to
describe the load queue and the store queue.
Field `BufferSize` is interpreted as the number of entries in the queue, while
the number of units is a throughput indicator (i.e. number of available pickers
for loads/stores).
At construction time, LSUnit in llvm-mca checks for the presence of extra
processor information (i.e. MCExtraProcessorInfo) in the scheduling model. If
that information is available, and fields LoadQueueID and StoreQueueID are set
to a value different than zero (i.e. the invalid processor resource index), then
LSUnit initializes its LoadQueue/StoreQueue based on the BufferSize value
declared by the two processor resources.
With this patch, we more accurately track dynamic dispatch stalls caused by the
lack of LS tokens (i.e. load/store queue full). This is also shown by the
differences in two BdVer2 tests. Stalls that were previously classified as
generic SCHEDULER FULL stalls, are not correctly classified either as "load
queue full" or "store queue full".
About the differences in the -scheduler-stats view: those differences are
expected, because entries in the load/store queue are not released at
instruction issue stage. Instead, those are released at instruction executed
stage. This is the main reason why for the modified tests, the load/store
queues gets full before PdEx is full.
Differential Revision: https://reviews.llvm.org/D54957
llvm-svn: 347857
2018-11-29 20:15:56 +08:00
|
|
|
// Collect information about load/store queues.
|
|
|
|
collectLoadStoreQueueInfo();
|
|
|
|
|
2018-04-10 16:16:37 +08:00
|
|
|
checkCompleteness();
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gather all processor models.
|
|
|
|
void CodeGenSchedModels::collectProcModels() {
|
|
|
|
RecVec ProcRecords = Records.getAllDerivedDefinitions("Processor");
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(ProcRecords, LessRecordFieldName());
|
2012-09-15 08:19:57 +08:00
|
|
|
|
|
|
|
// Reserve space because we can. Reallocation would be ok.
|
|
|
|
ProcModels.reserve(ProcRecords.size()+1);
|
|
|
|
|
|
|
|
// Use idx=0 for NoModel/NoItineraries.
|
|
|
|
Record *NoModelDef = Records.getDef("NoSchedModel");
|
|
|
|
Record *NoItinsDef = Records.getDef("NoItineraries");
|
2015-05-30 03:43:39 +08:00
|
|
|
ProcModels.emplace_back(0, "NoSchedModel", NoModelDef, NoItinsDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
ProcModelMap[NoModelDef] = 0;
|
|
|
|
|
|
|
|
// For each processor, find a unique machine model.
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "+++ PROCESSOR MODELs (addProcModel) +++\n");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ProcRecord : ProcRecords)
|
|
|
|
addProcModel(ProcRecord);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a unique processor model based on the defined MachineModel and
|
|
|
|
/// ProcessorItineraries.
|
|
|
|
void CodeGenSchedModels::addProcModel(Record *ProcDef) {
|
|
|
|
Record *ModelKey = getModelOrItinDef(ProcDef);
|
|
|
|
if (!ProcModelMap.insert(std::make_pair(ModelKey, ProcModels.size())).second)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::string Name = ModelKey->getName();
|
|
|
|
if (ModelKey->isSubClassOf("SchedMachineModel")) {
|
|
|
|
Record *ItinsDef = ModelKey->getValueAsDef("Itineraries");
|
2015-05-30 03:43:39 +08:00
|
|
|
ProcModels.emplace_back(ProcModels.size(), Name, ModelKey, ItinsDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// An itinerary is defined without a machine model. Infer a new model.
|
|
|
|
if (!ModelKey->getValueAsListOfDefs("IID").empty())
|
|
|
|
Name = Name + "Model";
|
2015-05-30 03:43:39 +08:00
|
|
|
ProcModels.emplace_back(ProcModels.size(), Name,
|
|
|
|
ProcDef->getValueAsDef("SchedModel"), ModelKey);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(ProcModels.back().dump());
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Recursively find all reachable SchedReadWrite records.
|
|
|
|
static void scanSchedRW(Record *RWDef, RecVec &RWDefs,
|
|
|
|
SmallPtrSet<Record*, 16> &RWSet) {
|
2014-11-19 15:49:26 +08:00
|
|
|
if (!RWSet.insert(RWDef).second)
|
2012-09-15 08:19:57 +08:00
|
|
|
return;
|
|
|
|
RWDefs.push_back(RWDef);
|
2017-09-13 18:31:10 +08:00
|
|
|
// Reads don't currently have sequence records, but it can be added later.
|
2012-09-15 08:19:57 +08:00
|
|
|
if (RWDef->isSubClassOf("WriteSequence")) {
|
|
|
|
RecVec Seq = RWDef->getValueAsListOfDefs("Writes");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *WSRec : Seq)
|
|
|
|
scanSchedRW(WSRec, RWDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
else if (RWDef->isSubClassOf("SchedVariant")) {
|
|
|
|
// Visit each variant (guarded by a different predicate).
|
|
|
|
RecVec Vars = RWDef->getValueAsListOfDefs("Variants");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *Variant : Vars) {
|
2012-09-15 08:19:57 +08:00
|
|
|
// Visit each RW in the sequence selected by the current variant.
|
2017-09-13 18:31:10 +08:00
|
|
|
RecVec Selected = Variant->getValueAsListOfDefs("Selected");
|
|
|
|
for (Record *SelDef : Selected)
|
|
|
|
scanSchedRW(SelDef, RWDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect and sort all SchedReadWrites reachable via tablegen records.
|
|
|
|
// More may be inferred later when inferring new SchedClasses from variants.
|
|
|
|
void CodeGenSchedModels::collectSchedRW() {
|
|
|
|
// Reserve idx=0 for invalid writes/reads.
|
|
|
|
SchedWrites.resize(1);
|
|
|
|
SchedReads.resize(1);
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
SmallPtrSet<Record*, 16> RWSet;
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Find all SchedReadWrites referenced by instruction defs.
|
|
|
|
RecVec SWDefs, SRDefs;
|
2016-01-18 04:38:18 +08:00
|
|
|
for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) {
|
2014-12-09 16:05:51 +08:00
|
|
|
Record *SchedDef = Inst->TheDef;
|
2013-03-16 06:51:13 +08:00
|
|
|
if (SchedDef->isValueUnset("SchedRW"))
|
2012-09-15 08:19:57 +08:00
|
|
|
continue;
|
|
|
|
RecVec RWs = SchedDef->getValueAsListOfDefs("SchedRW");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *RW : RWs) {
|
|
|
|
if (RW->isSubClassOf("SchedWrite"))
|
|
|
|
scanSchedRW(RW, SWDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
else {
|
2017-09-13 18:31:10 +08:00
|
|
|
assert(RW->isSubClassOf("SchedRead") && "Unknown SchedReadWrite");
|
|
|
|
scanSchedRW(RW, SRDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Find all ReadWrites referenced by InstRW.
|
|
|
|
RecVec InstRWDefs = Records.getAllDerivedDefinitions("InstRW");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *InstRWDef : InstRWDefs) {
|
2012-09-15 08:19:57 +08:00
|
|
|
// For all OperandReadWrites.
|
2017-09-13 18:31:10 +08:00
|
|
|
RecVec RWDefs = InstRWDef->getValueAsListOfDefs("OperandReadWrites");
|
|
|
|
for (Record *RWDef : RWDefs) {
|
|
|
|
if (RWDef->isSubClassOf("SchedWrite"))
|
|
|
|
scanSchedRW(RWDef, SWDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
else {
|
2017-09-13 18:31:10 +08:00
|
|
|
assert(RWDef->isSubClassOf("SchedRead") && "Unknown SchedReadWrite");
|
|
|
|
scanSchedRW(RWDef, SRDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Find all ReadWrites referenced by ItinRW.
|
|
|
|
RecVec ItinRWDefs = Records.getAllDerivedDefinitions("ItinRW");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ItinRWDef : ItinRWDefs) {
|
2012-09-15 08:19:57 +08:00
|
|
|
// For all OperandReadWrites.
|
2017-09-13 18:31:10 +08:00
|
|
|
RecVec RWDefs = ItinRWDef->getValueAsListOfDefs("OperandReadWrites");
|
|
|
|
for (Record *RWDef : RWDefs) {
|
|
|
|
if (RWDef->isSubClassOf("SchedWrite"))
|
|
|
|
scanSchedRW(RWDef, SWDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
else {
|
2017-09-13 18:31:10 +08:00
|
|
|
assert(RWDef->isSubClassOf("SchedRead") && "Unknown SchedReadWrite");
|
|
|
|
scanSchedRW(RWDef, SRDefs, RWSet);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-22 10:24:21 +08:00
|
|
|
// Find all ReadWrites referenced by SchedAlias. AliasDefs needs to be sorted
|
|
|
|
// for the loop below that initializes Alias vectors.
|
|
|
|
RecVec AliasDefs = Records.getAllDerivedDefinitions("SchedAlias");
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(AliasDefs, LessRecord());
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ADef : AliasDefs) {
|
|
|
|
Record *MatchDef = ADef->getValueAsDef("MatchRW");
|
|
|
|
Record *AliasDef = ADef->getValueAsDef("AliasRW");
|
2012-09-22 10:24:21 +08:00
|
|
|
if (MatchDef->isSubClassOf("SchedWrite")) {
|
|
|
|
if (!AliasDef->isSubClassOf("SchedWrite"))
|
2017-09-13 18:31:10 +08:00
|
|
|
PrintFatalError(ADef->getLoc(), "SchedWrite Alias must be SchedWrite");
|
2012-09-22 10:24:21 +08:00
|
|
|
scanSchedRW(AliasDef, SWDefs, RWSet);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(MatchDef->isSubClassOf("SchedRead") && "Unknown SchedReadWrite");
|
|
|
|
if (!AliasDef->isSubClassOf("SchedRead"))
|
2017-09-13 18:31:10 +08:00
|
|
|
PrintFatalError(ADef->getLoc(), "SchedRead Alias must be SchedRead");
|
2012-09-22 10:24:21 +08:00
|
|
|
scanSchedRW(AliasDef, SRDefs, RWSet);
|
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
// Sort and add the SchedReadWrites directly referenced by instructions or
|
|
|
|
// itinerary resources. Index reads and writes in separate domains.
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(SWDefs, LessRecord());
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *SWDef : SWDefs) {
|
|
|
|
assert(!getSchedRWIdx(SWDef, /*IsRead=*/false) && "duplicate SchedWrite");
|
|
|
|
SchedWrites.emplace_back(SchedWrites.size(), SWDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(SRDefs, LessRecord());
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *SRDef : SRDefs) {
|
|
|
|
assert(!getSchedRWIdx(SRDef, /*IsRead-*/true) && "duplicate SchedWrite");
|
|
|
|
SchedReads.emplace_back(SchedReads.size(), SRDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
// Initialize WriteSequence vectors.
|
2017-09-13 18:31:10 +08:00
|
|
|
for (CodeGenSchedRW &CGRW : SchedWrites) {
|
|
|
|
if (!CGRW.IsSequence)
|
2012-09-15 08:19:57 +08:00
|
|
|
continue;
|
2017-09-13 18:31:10 +08:00
|
|
|
findRWs(CGRW.TheDef->getValueAsListOfDefs("Writes"), CGRW.Sequence,
|
2012-09-15 08:19:57 +08:00
|
|
|
/*IsRead=*/false);
|
|
|
|
}
|
2012-09-22 10:24:21 +08:00
|
|
|
// Initialize Aliases vectors.
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ADef : AliasDefs) {
|
|
|
|
Record *AliasDef = ADef->getValueAsDef("AliasRW");
|
2012-09-22 10:24:21 +08:00
|
|
|
getSchedRW(AliasDef).IsAlias = true;
|
2017-09-13 18:31:10 +08:00
|
|
|
Record *MatchDef = ADef->getValueAsDef("MatchRW");
|
2012-09-22 10:24:21 +08:00
|
|
|
CodeGenSchedRW &RW = getSchedRW(MatchDef);
|
|
|
|
if (RW.IsAlias)
|
2017-09-13 18:31:10 +08:00
|
|
|
PrintFatalError(ADef->getLoc(), "Cannot Alias an Alias");
|
|
|
|
RW.Aliases.push_back(ADef);
|
2012-09-22 10:24:21 +08:00
|
|
|
}
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "\n+++ SCHED READS and WRITES (collectSchedRW) +++\n";
|
|
|
|
for (unsigned WIdx = 0, WEnd = SchedWrites.size(); WIdx != WEnd; ++WIdx) {
|
|
|
|
dbgs() << WIdx << ": ";
|
|
|
|
SchedWrites[WIdx].dump();
|
|
|
|
dbgs() << '\n';
|
|
|
|
} for (unsigned RIdx = 0, REnd = SchedReads.size(); RIdx != REnd;
|
|
|
|
++RIdx) {
|
|
|
|
dbgs() << RIdx << ": ";
|
|
|
|
SchedReads[RIdx].dump();
|
|
|
|
dbgs() << '\n';
|
|
|
|
} RecVec RWDefs = Records.getAllDerivedDefinitions("SchedReadWrite");
|
|
|
|
for (Record *RWDef
|
|
|
|
: RWDefs) {
|
|
|
|
if (!getSchedRWIdx(RWDef, RWDef->isSubClassOf("SchedRead"))) {
|
|
|
|
StringRef Name = RWDef->getName();
|
|
|
|
if (Name != "NoWrite" && Name != "ReadDefault")
|
|
|
|
dbgs() << "Unused SchedReadWrite " << Name << '\n';
|
|
|
|
}
|
|
|
|
});
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute a SchedWrite name from a sequence of writes.
|
2015-10-24 20:46:49 +08:00
|
|
|
std::string CodeGenSchedModels::genRWName(ArrayRef<unsigned> Seq, bool IsRead) {
|
2012-09-15 08:19:57 +08:00
|
|
|
std::string Name("(");
|
2015-10-24 20:46:49 +08:00
|
|
|
for (auto I = Seq.begin(), E = Seq.end(); I != E; ++I) {
|
2012-09-15 08:19:57 +08:00
|
|
|
if (I != Seq.begin())
|
|
|
|
Name += '_';
|
|
|
|
Name += getSchedRW(*I, IsRead).Name;
|
|
|
|
}
|
|
|
|
Name += ')';
|
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:56:26 +08:00
|
|
|
unsigned CodeGenSchedModels::getSchedRWIdx(const Record *Def,
|
|
|
|
bool IsRead) const {
|
2012-09-15 08:19:57 +08:00
|
|
|
const std::vector<CodeGenSchedRW> &RWVec = IsRead ? SchedReads : SchedWrites;
|
2018-04-26 20:56:26 +08:00
|
|
|
const auto I = find_if(
|
|
|
|
RWVec, [Def](const CodeGenSchedRW &RW) { return RW.TheDef == Def; });
|
|
|
|
return I == RWVec.end() ? 0 : std::distance(RWVec.begin(), I);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
2012-09-19 12:43:19 +08:00
|
|
|
bool CodeGenSchedModels::hasReadOfWrite(Record *WriteDef) const {
|
2017-09-13 18:31:10 +08:00
|
|
|
for (const CodeGenSchedRW &Read : SchedReads) {
|
|
|
|
Record *ReadDef = Read.TheDef;
|
2012-09-19 12:43:19 +08:00
|
|
|
if (!ReadDef || !ReadDef->isSubClassOf("ProcReadAdvance"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
RecVec ValidWrites = ReadDef->getValueAsListOfDefs("ValidWrites");
|
2016-08-12 06:21:41 +08:00
|
|
|
if (is_contained(ValidWrites, WriteDef)) {
|
2012-09-19 12:43:19 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-21 13:13:01 +08:00
|
|
|
static void splitSchedReadWrites(const RecVec &RWDefs,
|
|
|
|
RecVec &WriteDefs, RecVec &ReadDefs) {
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *RWDef : RWDefs) {
|
|
|
|
if (RWDef->isSubClassOf("SchedWrite"))
|
|
|
|
WriteDefs.push_back(RWDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
else {
|
2017-09-13 18:31:10 +08:00
|
|
|
assert(RWDef->isSubClassOf("SchedRead") && "unknown SchedReadWrite");
|
|
|
|
ReadDefs.push_back(RWDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-01 01:48:10 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Split the SchedReadWrites defs and call findRWs for each list.
|
|
|
|
void CodeGenSchedModels::findRWs(const RecVec &RWDefs,
|
|
|
|
IdxVec &Writes, IdxVec &Reads) const {
|
2018-04-26 20:56:26 +08:00
|
|
|
RecVec WriteDefs;
|
|
|
|
RecVec ReadDefs;
|
|
|
|
splitSchedReadWrites(RWDefs, WriteDefs, ReadDefs);
|
|
|
|
findRWs(WriteDefs, Writes, false);
|
|
|
|
findRWs(ReadDefs, Reads, true);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call getSchedRWIdx for all elements in a sequence of SchedRW defs.
|
|
|
|
void CodeGenSchedModels::findRWs(const RecVec &RWDefs, IdxVec &RWs,
|
|
|
|
bool IsRead) const {
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *RWDef : RWDefs) {
|
|
|
|
unsigned Idx = getSchedRWIdx(RWDef, IsRead);
|
2012-09-15 08:19:57 +08:00
|
|
|
assert(Idx && "failed to collect SchedReadWrite");
|
|
|
|
RWs.push_back(Idx);
|
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
void CodeGenSchedModels::expandRWSequence(unsigned RWIdx, IdxVec &RWSeq,
|
|
|
|
bool IsRead) const {
|
|
|
|
const CodeGenSchedRW &SchedRW = getSchedRW(RWIdx, IsRead);
|
|
|
|
if (!SchedRW.IsSequence) {
|
|
|
|
RWSeq.push_back(RWIdx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int Repeat =
|
|
|
|
SchedRW.TheDef ? SchedRW.TheDef->getValueAsInt("Repeat") : 1;
|
|
|
|
for (int i = 0; i < Repeat; ++i) {
|
2017-09-13 18:31:10 +08:00
|
|
|
for (unsigned I : SchedRW.Sequence) {
|
|
|
|
expandRWSequence(I, RWSeq, IsRead);
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-04 07:06:28 +08:00
|
|
|
// Expand a SchedWrite as a sequence following any aliases that coincide with
|
|
|
|
// the given processor model.
|
|
|
|
void CodeGenSchedModels::expandRWSeqForProc(
|
|
|
|
unsigned RWIdx, IdxVec &RWSeq, bool IsRead,
|
|
|
|
const CodeGenProcModel &ProcModel) const {
|
|
|
|
|
|
|
|
const CodeGenSchedRW &SchedWrite = getSchedRW(RWIdx, IsRead);
|
2014-04-15 15:20:03 +08:00
|
|
|
Record *AliasDef = nullptr;
|
2018-04-26 20:56:26 +08:00
|
|
|
for (const Record *Rec : SchedWrite.Aliases) {
|
|
|
|
const CodeGenSchedRW &AliasRW = getSchedRW(Rec->getValueAsDef("AliasRW"));
|
|
|
|
if (Rec->getValueInit("SchedModel")->isComplete()) {
|
|
|
|
Record *ModelDef = Rec->getValueAsDef("SchedModel");
|
2012-10-04 07:06:28 +08:00
|
|
|
if (&getProcModel(ModelDef) != &ProcModel)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (AliasDef)
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(AliasRW.TheDef->getLoc(), "Multiple aliases "
|
|
|
|
"defined for processor " + ProcModel.ModelName +
|
|
|
|
" Ensure only one SchedAlias exists per RW.");
|
2012-10-04 07:06:28 +08:00
|
|
|
AliasDef = AliasRW.TheDef;
|
|
|
|
}
|
|
|
|
if (AliasDef) {
|
|
|
|
expandRWSeqForProc(getSchedRWIdx(AliasDef, IsRead),
|
|
|
|
RWSeq, IsRead,ProcModel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!SchedWrite.IsSequence) {
|
|
|
|
RWSeq.push_back(RWIdx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int Repeat =
|
|
|
|
SchedWrite.TheDef ? SchedWrite.TheDef->getValueAsInt("Repeat") : 1;
|
2018-04-26 20:56:26 +08:00
|
|
|
for (int I = 0, E = Repeat; I < E; ++I) {
|
|
|
|
for (unsigned Idx : SchedWrite.Sequence) {
|
|
|
|
expandRWSeqForProc(Idx, RWSeq, IsRead, ProcModel);
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
// Find the existing SchedWrite that models this sequence of writes.
|
2015-10-24 20:46:49 +08:00
|
|
|
unsigned CodeGenSchedModels::findRWForSequence(ArrayRef<unsigned> Seq,
|
2012-09-15 08:19:59 +08:00
|
|
|
bool IsRead) {
|
|
|
|
std::vector<CodeGenSchedRW> &RWVec = IsRead ? SchedReads : SchedWrites;
|
|
|
|
|
2018-04-26 20:56:26 +08:00
|
|
|
auto I = find_if(RWVec, [Seq](CodeGenSchedRW &RW) {
|
|
|
|
return makeArrayRef(RW.Sequence) == Seq;
|
|
|
|
});
|
2012-09-15 08:19:59 +08:00
|
|
|
// Index zero reserved for invalid RW.
|
2018-04-26 20:56:26 +08:00
|
|
|
return I == RWVec.end() ? 0 : std::distance(RWVec.begin(), I);
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Add this ReadWrite if it doesn't already exist.
|
|
|
|
unsigned CodeGenSchedModels::findOrInsertRW(ArrayRef<unsigned> Seq,
|
|
|
|
bool IsRead) {
|
|
|
|
assert(!Seq.empty() && "cannot insert empty sequence");
|
|
|
|
if (Seq.size() == 1)
|
|
|
|
return Seq.back();
|
|
|
|
|
|
|
|
unsigned Idx = findRWForSequence(Seq, IsRead);
|
|
|
|
if (Idx)
|
|
|
|
return Idx;
|
|
|
|
|
2018-04-26 20:56:26 +08:00
|
|
|
std::vector<CodeGenSchedRW> &RWVec = IsRead ? SchedReads : SchedWrites;
|
|
|
|
unsigned RWIdx = RWVec.size();
|
2012-10-04 07:06:28 +08:00
|
|
|
CodeGenSchedRW SchedRW(RWIdx, IsRead, Seq, genRWName(Seq, IsRead));
|
2018-04-26 20:56:26 +08:00
|
|
|
RWVec.push_back(SchedRW);
|
2012-10-04 07:06:28 +08:00
|
|
|
return RWIdx;
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
/// Visit all the instruction definitions for this target to gather and
|
|
|
|
/// enumerate the itinerary classes. These are the explicitly specified
|
|
|
|
/// SchedClasses. More SchedClasses may be inferred.
|
|
|
|
void CodeGenSchedModels::collectSchedClasses() {
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// NoItinerary is always the first class at Idx=0
|
2018-03-22 14:15:08 +08:00
|
|
|
assert(SchedClasses.empty() && "Expected empty sched class");
|
|
|
|
SchedClasses.emplace_back(0, "NoInstrModel",
|
|
|
|
Records.getDef("NoItinerary"));
|
2012-09-15 08:19:57 +08:00
|
|
|
SchedClasses.back().ProcIndices.push_back(0);
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2013-03-17 02:58:55 +08:00
|
|
|
// Create a SchedClass for each unique combination of itinerary class and
|
|
|
|
// SchedRW list.
|
2016-01-18 04:38:18 +08:00
|
|
|
for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) {
|
2014-12-09 16:05:51 +08:00
|
|
|
Record *ItinDef = Inst->TheDef->getValueAsDef("Itinerary");
|
2012-09-15 08:19:57 +08:00
|
|
|
IdxVec Writes, Reads;
|
2014-12-09 16:05:51 +08:00
|
|
|
if (!Inst->TheDef->isValueUnset("SchedRW"))
|
|
|
|
findRWs(Inst->TheDef->getValueAsListOfDefs("SchedRW"), Writes, Reads);
|
2013-03-17 02:58:55 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// ProcIdx == 0 indicates the class applies to all processors.
|
2018-03-22 14:15:08 +08:00
|
|
|
unsigned SCIdx = addSchedClass(ItinDef, Writes, Reads, /*ProcIndices*/{0});
|
2014-12-09 16:05:51 +08:00
|
|
|
InstrClassMap[Inst->TheDef] = SCIdx;
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2012-09-22 10:24:21 +08:00
|
|
|
// Create classes for InstRW defs.
|
2012-09-15 08:19:57 +08:00
|
|
|
RecVec InstRWDefs = Records.getAllDerivedDefinitions("InstRW");
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(InstRWDefs, LessRecord());
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "\n+++ SCHED CLASSES (createInstRWClass) +++\n");
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *RWDef : InstRWDefs)
|
|
|
|
createInstRWClass(RWDef);
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
NumInstrSchedClasses = SchedClasses.size();
|
|
|
|
|
|
|
|
bool EnableDump = false;
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(EnableDump = true);
|
2012-09-15 08:19:57 +08:00
|
|
|
if (!EnableDump)
|
|
|
|
return;
|
2013-03-17 02:58:55 +08:00
|
|
|
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
2018-04-26 20:56:26 +08:00
|
|
|
dbgs()
|
|
|
|
<< "\n+++ ITINERARIES and/or MACHINE MODELS (collectSchedClasses) +++\n");
|
2016-01-18 04:38:18 +08:00
|
|
|
for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) {
|
2017-06-01 05:12:46 +08:00
|
|
|
StringRef InstName = Inst->TheDef->getName();
|
2018-03-22 02:09:34 +08:00
|
|
|
unsigned SCIdx = getSchedClassIdx(*Inst);
|
2013-03-17 02:58:55 +08:00
|
|
|
if (!SCIdx) {
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG({
|
2018-04-26 20:56:26 +08:00
|
|
|
if (!Inst->hasNoSchedulingInfo)
|
|
|
|
dbgs() << "No machine model for " << Inst->TheDef->getName() << '\n';
|
|
|
|
});
|
2013-03-17 02:58:55 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CodeGenSchedClass &SC = getSchedClass(SCIdx);
|
|
|
|
if (SC.ProcIndices[0] != 0)
|
2014-12-09 16:05:51 +08:00
|
|
|
PrintFatalError(Inst->TheDef->getLoc(), "Instruction's sched class "
|
2013-03-17 02:58:55 +08:00
|
|
|
"must not be subtarget specific.");
|
|
|
|
|
|
|
|
IdxVec ProcIndices;
|
|
|
|
if (SC.ItinClassDef->getName() != "NoItinerary") {
|
|
|
|
ProcIndices.push_back(0);
|
|
|
|
dbgs() << "Itinerary for " << InstName << ": "
|
|
|
|
<< SC.ItinClassDef->getName() << '\n';
|
|
|
|
}
|
|
|
|
if (!SC.Writes.empty()) {
|
|
|
|
ProcIndices.push_back(0);
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG({
|
2018-04-26 20:56:26 +08:00
|
|
|
dbgs() << "SchedRW machine model for " << InstName;
|
|
|
|
for (IdxIter WI = SC.Writes.begin(), WE = SC.Writes.end(); WI != WE;
|
|
|
|
++WI)
|
|
|
|
dbgs() << " " << SchedWrites[*WI].Name;
|
|
|
|
for (IdxIter RI = SC.Reads.begin(), RE = SC.Reads.end(); RI != RE; ++RI)
|
|
|
|
dbgs() << " " << SchedReads[*RI].Name;
|
|
|
|
dbgs() << '\n';
|
|
|
|
});
|
2013-03-17 02:58:55 +08:00
|
|
|
}
|
|
|
|
const RecVec &RWDefs = SchedClasses[SCIdx].InstRWs;
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *RWDef : RWDefs) {
|
2013-03-17 02:58:55 +08:00
|
|
|
const CodeGenProcModel &ProcModel =
|
2018-04-26 20:56:26 +08:00
|
|
|
getProcModel(RWDef->getValueAsDef("SchedModel"));
|
2013-03-17 02:58:55 +08:00
|
|
|
ProcIndices.push_back(ProcModel.Index);
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "InstRW on " << ProcModel.ModelName << " for "
|
|
|
|
<< InstName);
|
2012-09-15 08:19:57 +08:00
|
|
|
IdxVec Writes;
|
|
|
|
IdxVec Reads;
|
2017-09-13 18:31:10 +08:00
|
|
|
findRWs(RWDef->getValueAsListOfDefs("OperandReadWrites"),
|
2013-03-17 02:58:55 +08:00
|
|
|
Writes, Reads);
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG({
|
2018-04-26 20:56:26 +08:00
|
|
|
for (unsigned WIdx : Writes)
|
|
|
|
dbgs() << " " << SchedWrites[WIdx].Name;
|
|
|
|
for (unsigned RIdx : Reads)
|
|
|
|
dbgs() << " " << SchedReads[RIdx].Name;
|
|
|
|
dbgs() << '\n';
|
|
|
|
});
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2016-10-18 12:17:44 +08:00
|
|
|
// If ProcIndices contains zero, the class applies to all processors.
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG({
|
2018-04-26 20:56:26 +08:00
|
|
|
if (!std::count(ProcIndices.begin(), ProcIndices.end(), 0)) {
|
|
|
|
for (const CodeGenProcModel &PM : ProcModels) {
|
|
|
|
if (!std::count(ProcIndices.begin(), ProcIndices.end(), PM.Index))
|
|
|
|
dbgs() << "No machine model for " << Inst->TheDef->getName()
|
|
|
|
<< " on processor " << PM.ModelName << '\n';
|
|
|
|
}
|
2016-10-18 12:17:44 +08:00
|
|
|
}
|
2018-04-26 20:56:26 +08:00
|
|
|
});
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Get the SchedClass index for an instruction.
|
2018-04-26 20:56:26 +08:00
|
|
|
unsigned
|
|
|
|
CodeGenSchedModels::getSchedClassIdx(const CodeGenInstruction &Inst) const {
|
2013-03-17 02:58:55 +08:00
|
|
|
return InstrClassMap.lookup(Inst.TheDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2015-10-24 20:46:49 +08:00
|
|
|
std::string
|
|
|
|
CodeGenSchedModels::createSchedClassName(Record *ItinClassDef,
|
|
|
|
ArrayRef<unsigned> OperWrites,
|
|
|
|
ArrayRef<unsigned> OperReads) {
|
2012-09-15 08:19:57 +08:00
|
|
|
|
|
|
|
std::string Name;
|
2013-03-17 02:58:55 +08:00
|
|
|
if (ItinClassDef && ItinClassDef->getName() != "NoItinerary")
|
|
|
|
Name = ItinClassDef->getName();
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned Idx : OperWrites) {
|
2013-03-17 02:58:55 +08:00
|
|
|
if (!Name.empty())
|
2012-09-15 08:19:57 +08:00
|
|
|
Name += '_';
|
2015-10-24 20:46:49 +08:00
|
|
|
Name += SchedWrites[Idx].Name;
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned Idx : OperReads) {
|
2012-09-15 08:19:57 +08:00
|
|
|
Name += '_';
|
2015-10-24 20:46:49 +08:00
|
|
|
Name += SchedReads[Idx].Name;
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
return Name;
|
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
std::string CodeGenSchedModels::createSchedClassName(const RecVec &InstDefs) {
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
std::string Name;
|
|
|
|
for (RecIter I = InstDefs.begin(), E = InstDefs.end(); I != E; ++I) {
|
|
|
|
if (I != InstDefs.begin())
|
|
|
|
Name += '_';
|
|
|
|
Name += (*I)->getName();
|
|
|
|
}
|
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
|
2013-03-17 02:58:55 +08:00
|
|
|
/// Add an inferred sched class from an itinerary class and per-operand list of
|
|
|
|
/// SchedWrites and SchedReads. ProcIndices contains the set of IDs of
|
|
|
|
/// processors that may utilize this class.
|
|
|
|
unsigned CodeGenSchedModels::addSchedClass(Record *ItinClassDef,
|
2015-10-24 20:46:49 +08:00
|
|
|
ArrayRef<unsigned> OperWrites,
|
|
|
|
ArrayRef<unsigned> OperReads,
|
|
|
|
ArrayRef<unsigned> ProcIndices) {
|
2012-09-15 08:19:57 +08:00
|
|
|
assert(!ProcIndices.empty() && "expect at least one ProcIdx");
|
|
|
|
|
2018-04-26 20:56:26 +08:00
|
|
|
auto IsKeyEqual = [=](const CodeGenSchedClass &SC) {
|
|
|
|
return SC.isKeyEqual(ItinClassDef, OperWrites, OperReads);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto I = find_if(make_range(schedClassBegin(), schedClassEnd()), IsKeyEqual);
|
|
|
|
unsigned Idx = I == schedClassEnd() ? 0 : std::distance(schedClassBegin(), I);
|
2013-03-17 02:58:55 +08:00
|
|
|
if (Idx || SchedClasses[0].isKeyEqual(ItinClassDef, OperWrites, OperReads)) {
|
2012-09-15 08:19:57 +08:00
|
|
|
IdxVec PI;
|
|
|
|
std::set_union(SchedClasses[Idx].ProcIndices.begin(),
|
|
|
|
SchedClasses[Idx].ProcIndices.end(),
|
|
|
|
ProcIndices.begin(), ProcIndices.end(),
|
|
|
|
std::back_inserter(PI));
|
2018-03-25 06:58:00 +08:00
|
|
|
SchedClasses[Idx].ProcIndices = std::move(PI);
|
2012-09-15 08:19:57 +08:00
|
|
|
return Idx;
|
|
|
|
}
|
|
|
|
Idx = SchedClasses.size();
|
2018-03-22 14:15:08 +08:00
|
|
|
SchedClasses.emplace_back(Idx,
|
|
|
|
createSchedClassName(ItinClassDef, OperWrites,
|
|
|
|
OperReads),
|
|
|
|
ItinClassDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
CodeGenSchedClass &SC = SchedClasses.back();
|
|
|
|
SC.Writes = OperWrites;
|
|
|
|
SC.Reads = OperReads;
|
|
|
|
SC.ProcIndices = ProcIndices;
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
return Idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create classes for each set of opcodes that are in the same InstReadWrite
|
|
|
|
// definition across all processors.
|
|
|
|
void CodeGenSchedModels::createInstRWClass(Record *InstRWDef) {
|
|
|
|
// ClassInstrs will hold an entry for each subset of Instrs in InstRWDef that
|
|
|
|
// intersects with an existing class via a previous InstRWDef. Instrs that do
|
|
|
|
// not intersect with an existing class refer back to their former class as
|
|
|
|
// determined from ItinDef or SchedRW.
|
2018-03-21 10:48:34 +08:00
|
|
|
SmallMapVector<unsigned, SmallVector<Record *, 8>, 4> ClassInstrs;
|
2012-09-15 08:19:57 +08:00
|
|
|
// Sort Instrs into sets.
|
2012-10-04 07:06:32 +08:00
|
|
|
const RecVec *InstDefs = Sets.expand(InstRWDef);
|
|
|
|
if (InstDefs->empty())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(InstRWDef->getLoc(), "No matching instruction opcodes");
|
2012-10-04 07:06:32 +08:00
|
|
|
|
2018-03-18 16:38:03 +08:00
|
|
|
for (Record *InstDef : *InstDefs) {
|
2017-10-05 21:27:43 +08:00
|
|
|
InstClassMapTy::const_iterator Pos = InstrClassMap.find(InstDef);
|
2013-03-17 02:58:55 +08:00
|
|
|
if (Pos == InstrClassMap.end())
|
2017-10-05 21:27:43 +08:00
|
|
|
PrintFatalError(InstDef->getLoc(), "No sched class for instruction.");
|
2013-03-17 02:58:55 +08:00
|
|
|
unsigned SCIdx = Pos->second;
|
2018-03-21 10:48:34 +08:00
|
|
|
ClassInstrs[SCIdx].push_back(InstDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
// For each set of Instrs, create a new class if necessary, and map or remap
|
|
|
|
// the Instrs to it.
|
2018-03-21 10:48:34 +08:00
|
|
|
for (auto &Entry : ClassInstrs) {
|
|
|
|
unsigned OldSCIdx = Entry.first;
|
|
|
|
ArrayRef<Record*> InstDefs = Entry.second;
|
2012-09-15 08:19:57 +08:00
|
|
|
// If the all instrs in the current class are accounted for, then leave
|
|
|
|
// them mapped to their old class.
|
2013-06-05 14:55:20 +08:00
|
|
|
if (OldSCIdx) {
|
|
|
|
const RecVec &RWDefs = SchedClasses[OldSCIdx].InstRWs;
|
|
|
|
if (!RWDefs.empty()) {
|
|
|
|
const RecVec *OrigInstDefs = Sets.expand(RWDefs[0]);
|
2018-03-22 03:30:30 +08:00
|
|
|
unsigned OrigNumInstrs =
|
|
|
|
count_if(*OrigInstDefs, [&](Record *OIDef) {
|
|
|
|
return InstrClassMap[OIDef] == OldSCIdx;
|
|
|
|
});
|
2013-06-05 14:55:20 +08:00
|
|
|
if (OrigNumInstrs == InstDefs.size()) {
|
|
|
|
assert(SchedClasses[OldSCIdx].ProcIndices[0] == 0 &&
|
|
|
|
"expected a generic SchedClass");
|
2018-03-19 03:56:15 +08:00
|
|
|
Record *RWModelDef = InstRWDef->getValueAsDef("SchedModel");
|
|
|
|
// Make sure we didn't already have a InstRW containing this
|
|
|
|
// instruction on this model.
|
|
|
|
for (Record *RWD : RWDefs) {
|
|
|
|
if (RWD->getValueAsDef("SchedModel") == RWModelDef &&
|
|
|
|
RWModelDef->getValueAsBit("FullInstRWOverlapCheck")) {
|
|
|
|
for (Record *Inst : InstDefs) {
|
|
|
|
PrintFatalError(InstRWDef->getLoc(), "Overlapping InstRW def " +
|
|
|
|
Inst->getName() + " also matches " +
|
|
|
|
RWD->getValue("Instrs")->getValue()->getAsString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "InstRW: Reuse SC " << OldSCIdx << ":"
|
|
|
|
<< SchedClasses[OldSCIdx].Name << " on "
|
|
|
|
<< RWModelDef->getName() << "\n");
|
2013-06-05 14:55:20 +08:00
|
|
|
SchedClasses[OldSCIdx].InstRWs.push_back(InstRWDef);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
unsigned SCIdx = SchedClasses.size();
|
2018-03-22 14:15:08 +08:00
|
|
|
SchedClasses.emplace_back(SCIdx, createSchedClassName(InstDefs), nullptr);
|
2012-09-15 08:19:57 +08:00
|
|
|
CodeGenSchedClass &SC = SchedClasses.back();
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "InstRW: New SC " << SCIdx << ":" << SC.Name << " on "
|
|
|
|
<< InstRWDef->getValueAsDef("SchedModel")->getName()
|
|
|
|
<< "\n");
|
2013-06-05 14:55:20 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Preserve ItinDef and Writes/Reads for processors without an InstRW entry.
|
|
|
|
SC.ItinClassDef = SchedClasses[OldSCIdx].ItinClassDef;
|
|
|
|
SC.Writes = SchedClasses[OldSCIdx].Writes;
|
|
|
|
SC.Reads = SchedClasses[OldSCIdx].Reads;
|
|
|
|
SC.ProcIndices.push_back(0);
|
2018-03-22 03:52:13 +08:00
|
|
|
// If we had an old class, copy it's InstRWs to this new class.
|
|
|
|
if (OldSCIdx) {
|
|
|
|
Record *RWModelDef = InstRWDef->getValueAsDef("SchedModel");
|
|
|
|
for (Record *OldRWDef : SchedClasses[OldSCIdx].InstRWs) {
|
|
|
|
if (OldRWDef->getValueAsDef("SchedModel") == RWModelDef) {
|
|
|
|
for (Record *InstDef : InstDefs) {
|
2018-03-22 03:30:31 +08:00
|
|
|
PrintFatalError(OldRWDef->getLoc(), "Overlapping InstRW def " +
|
|
|
|
InstDef->getName() + " also matches " +
|
|
|
|
OldRWDef->getValue("Instrs")->getValue()->getAsString());
|
2012-10-04 07:06:32 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-22 03:52:13 +08:00
|
|
|
assert(OldRWDef != InstRWDef &&
|
|
|
|
"SchedClass has duplicate InstRW def");
|
|
|
|
SC.InstRWs.push_back(OldRWDef);
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-22 03:52:13 +08:00
|
|
|
// Map each Instr to this new class.
|
|
|
|
for (Record *InstDef : InstDefs)
|
|
|
|
InstrClassMap[InstDef] = SCIdx;
|
2012-09-15 08:19:57 +08:00
|
|
|
SC.InstRWs.push_back(InstRWDef);
|
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
|
|
|
|
2013-03-17 02:58:55 +08:00
|
|
|
// True if collectProcItins found anything.
|
|
|
|
bool CodeGenSchedModels::hasItineraries() const {
|
2018-04-26 20:56:26 +08:00
|
|
|
for (const CodeGenProcModel &PM : make_range(procModelBegin(),procModelEnd()))
|
2017-09-13 18:31:10 +08:00
|
|
|
if (PM.hasItineraries())
|
2013-03-17 02:58:55 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-07 12:00:00 +08:00
|
|
|
// Gather the processor itineraries.
|
2012-09-15 08:19:57 +08:00
|
|
|
void CodeGenSchedModels::collectProcItins() {
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "\n+++ PROBLEM ITINERARIES (collectProcItins) +++\n");
|
2014-12-09 16:05:51 +08:00
|
|
|
for (CodeGenProcModel &ProcModel : ProcModels) {
|
2013-03-17 02:58:55 +08:00
|
|
|
if (!ProcModel.hasItineraries())
|
2012-09-15 08:19:57 +08:00
|
|
|
continue;
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2013-03-17 02:58:55 +08:00
|
|
|
RecVec ItinRecords = ProcModel.ItinsDef->getValueAsListOfDefs("IID");
|
|
|
|
assert(!ItinRecords.empty() && "ProcModel.hasItineraries is incorrect");
|
|
|
|
|
|
|
|
// Populate ItinDefList with Itinerary records.
|
|
|
|
ProcModel.ItinDefList.resize(NumInstrSchedClasses);
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Insert each itinerary data record in the correct position within
|
|
|
|
// the processor model's ItinDefList.
|
2017-10-05 21:27:43 +08:00
|
|
|
for (Record *ItinData : ItinRecords) {
|
2018-04-26 20:56:26 +08:00
|
|
|
const Record *ItinDef = ItinData->getValueAsDef("TheClass");
|
2013-03-19 04:42:25 +08:00
|
|
|
bool FoundClass = false;
|
2018-04-26 20:56:26 +08:00
|
|
|
|
|
|
|
for (const CodeGenSchedClass &SC :
|
|
|
|
make_range(schedClassBegin(), schedClassEnd())) {
|
2013-03-19 04:42:25 +08:00
|
|
|
// Multiple SchedClasses may share an itinerary. Update all of them.
|
2018-04-26 20:56:26 +08:00
|
|
|
if (SC.ItinClassDef == ItinDef) {
|
|
|
|
ProcModel.ItinDefList[SC.Index] = ItinData;
|
2013-03-19 04:42:25 +08:00
|
|
|
FoundClass = true;
|
2013-03-17 02:58:55 +08:00
|
|
|
}
|
|
|
|
}
|
2013-03-19 04:42:25 +08:00
|
|
|
if (!FoundClass) {
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << ProcModel.ItinsDef->getName()
|
|
|
|
<< " missing class for itinerary "
|
|
|
|
<< ItinDef->getName() << '\n');
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check for missing itinerary entries.
|
|
|
|
assert(!ProcModel.ItinDefList[0] && "NoItinerary class can't have rec");
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
for (unsigned i = 1, N = ProcModel.ItinDefList.size(); i < N; ++i) {
|
|
|
|
if (!ProcModel.ItinDefList[i])
|
|
|
|
dbgs() << ProcModel.ItinsDef->getName()
|
|
|
|
<< " missing itinerary for class " << SchedClasses[i].Name
|
|
|
|
<< '\n';
|
|
|
|
});
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
}
|
2012-07-07 12:00:00 +08:00
|
|
|
|
2012-09-15 08:19:57 +08:00
|
|
|
// Gather the read/write types for each itinerary class.
|
|
|
|
void CodeGenSchedModels::collectProcItinRW() {
|
|
|
|
RecVec ItinRWDefs = Records.getAllDerivedDefinitions("ItinRW");
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(ItinRWDefs, LessRecord());
|
2017-10-10 00:21:25 +08:00
|
|
|
for (Record *RWDef : ItinRWDefs) {
|
2017-10-09 01:23:30 +08:00
|
|
|
if (!RWDef->getValueInit("SchedModel")->isComplete())
|
|
|
|
PrintFatalError(RWDef->getLoc(), "SchedModel is undefined");
|
|
|
|
Record *ModelDef = RWDef->getValueAsDef("SchedModel");
|
2012-09-15 08:19:57 +08:00
|
|
|
ProcModelMapTy::const_iterator I = ProcModelMap.find(ModelDef);
|
|
|
|
if (I == ProcModelMap.end()) {
|
2017-10-09 01:23:30 +08:00
|
|
|
PrintFatalError(RWDef->getLoc(), "Undefined SchedMachineModel "
|
2012-09-15 08:19:57 +08:00
|
|
|
+ ModelDef->getName());
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
2017-10-09 01:23:30 +08:00
|
|
|
ProcModels[I->second].ItinRWDefs.push_back(RWDef);
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
|
|
|
|
2016-06-24 16:43:27 +08:00
|
|
|
// Gather the unsupported features for processor models.
|
|
|
|
void CodeGenSchedModels::collectProcUnsupportedFeatures() {
|
|
|
|
for (CodeGenProcModel &ProcModel : ProcModels) {
|
|
|
|
for (Record *Pred : ProcModel.ModelDef->getValueAsListOfDefs("UnsupportedFeatures")) {
|
|
|
|
ProcModel.UnsupportedFeaturesDefs.push_back(Pred);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
/// Infer new classes from existing classes. In the process, this may create new
|
|
|
|
/// SchedWrites from sequences of existing SchedWrites.
|
|
|
|
void CodeGenSchedModels::inferSchedClasses() {
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "\n+++ INFERRING SCHED CLASSES (inferSchedClasses) +++\n");
|
|
|
|
LLVM_DEBUG(dbgs() << NumInstrSchedClasses << " instr sched classes.\n");
|
2013-03-17 02:58:55 +08:00
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
// Visit all existing classes and newly created classes.
|
|
|
|
for (unsigned Idx = 0; Idx != SchedClasses.size(); ++Idx) {
|
2013-03-17 02:58:55 +08:00
|
|
|
assert(SchedClasses[Idx].Index == Idx && "bad SCIdx");
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
if (SchedClasses[Idx].ItinClassDef)
|
|
|
|
inferFromItinClass(SchedClasses[Idx].ItinClassDef, Idx);
|
2013-03-17 02:58:55 +08:00
|
|
|
if (!SchedClasses[Idx].InstRWs.empty())
|
2012-09-15 08:19:59 +08:00
|
|
|
inferFromInstRWs(Idx);
|
2013-03-17 02:58:55 +08:00
|
|
|
if (!SchedClasses[Idx].Writes.empty()) {
|
2012-09-15 08:19:59 +08:00
|
|
|
inferFromRW(SchedClasses[Idx].Writes, SchedClasses[Idx].Reads,
|
|
|
|
Idx, SchedClasses[Idx].ProcIndices);
|
|
|
|
}
|
|
|
|
assert(SchedClasses.size() < (NumInstrSchedClasses*6) &&
|
|
|
|
"too many SchedVariants");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Infer classes from per-processor itinerary resources.
|
|
|
|
void CodeGenSchedModels::inferFromItinClass(Record *ItinClassDef,
|
|
|
|
unsigned FromClassIdx) {
|
|
|
|
for (unsigned PIdx = 0, PEnd = ProcModels.size(); PIdx != PEnd; ++PIdx) {
|
|
|
|
const CodeGenProcModel &PM = ProcModels[PIdx];
|
|
|
|
// For all ItinRW entries.
|
|
|
|
bool HasMatch = false;
|
2018-04-26 20:56:26 +08:00
|
|
|
for (const Record *Rec : PM.ItinRWDefs) {
|
|
|
|
RecVec Matched = Rec->getValueAsListOfDefs("MatchedItinClasses");
|
2012-09-15 08:19:59 +08:00
|
|
|
if (!std::count(Matched.begin(), Matched.end(), ItinClassDef))
|
|
|
|
continue;
|
|
|
|
if (HasMatch)
|
2018-04-26 20:56:26 +08:00
|
|
|
PrintFatalError(Rec->getLoc(), "Duplicate itinerary class "
|
2012-09-15 08:19:59 +08:00
|
|
|
+ ItinClassDef->getName()
|
|
|
|
+ " in ItinResources for " + PM.ModelName);
|
|
|
|
HasMatch = true;
|
|
|
|
IdxVec Writes, Reads;
|
2018-04-26 20:56:26 +08:00
|
|
|
findRWs(Rec->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);
|
2018-03-25 05:57:35 +08:00
|
|
|
inferFromRW(Writes, Reads, FromClassIdx, PIdx);
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Infer classes from per-processor InstReadWrite definitions.
|
|
|
|
void CodeGenSchedModels::inferFromInstRWs(unsigned SCIdx) {
|
2013-06-09 23:20:23 +08:00
|
|
|
for (unsigned I = 0, E = SchedClasses[SCIdx].InstRWs.size(); I != E; ++I) {
|
2013-06-11 04:19:35 +08:00
|
|
|
assert(SchedClasses[SCIdx].InstRWs.size() == E && "InstrRWs was mutated!");
|
2013-06-09 23:20:23 +08:00
|
|
|
Record *Rec = SchedClasses[SCIdx].InstRWs[I];
|
|
|
|
const RecVec *InstDefs = Sets.expand(Rec);
|
2012-10-04 07:06:32 +08:00
|
|
|
RecIter II = InstDefs->begin(), IE = InstDefs->end();
|
2012-09-15 08:19:59 +08:00
|
|
|
for (; II != IE; ++II) {
|
|
|
|
if (InstrClassMap[*II] == SCIdx)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// If this class no longer has any instructions mapped to it, it has become
|
|
|
|
// irrelevant.
|
|
|
|
if (II == IE)
|
|
|
|
continue;
|
|
|
|
IdxVec Writes, Reads;
|
2013-06-09 23:20:23 +08:00
|
|
|
findRWs(Rec->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);
|
|
|
|
unsigned PIdx = getProcModel(Rec->getValueAsDef("SchedModel")).Index;
|
2018-03-25 05:57:35 +08:00
|
|
|
inferFromRW(Writes, Reads, SCIdx, PIdx); // May mutate SchedClasses.
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2016-12-01 01:48:10 +08:00
|
|
|
|
2012-09-22 10:24:21 +08:00
|
|
|
// Helper for substituteVariantOperand.
|
|
|
|
struct TransVariant {
|
2012-10-04 07:06:28 +08:00
|
|
|
Record *VarOrSeqDef; // Variant or sequence.
|
|
|
|
unsigned RWIdx; // Index of this variant or sequence's matched type.
|
2012-09-22 10:24:21 +08:00
|
|
|
unsigned ProcIdx; // Processor model index or zero for any.
|
|
|
|
unsigned TransVecIdx; // Index into PredTransitions::TransVec.
|
|
|
|
|
|
|
|
TransVariant(Record *def, unsigned rwi, unsigned pi, unsigned ti):
|
2012-10-04 07:06:28 +08:00
|
|
|
VarOrSeqDef(def), RWIdx(rwi), ProcIdx(pi), TransVecIdx(ti) {}
|
2012-09-22 10:24:21 +08:00
|
|
|
};
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
// Associate a predicate with the SchedReadWrite that it guards.
|
|
|
|
// RWIdx is the index of the read/write variant.
|
|
|
|
struct PredCheck {
|
|
|
|
bool IsRead;
|
|
|
|
unsigned RWIdx;
|
|
|
|
Record *Predicate;
|
|
|
|
|
|
|
|
PredCheck(bool r, unsigned w, Record *p): IsRead(r), RWIdx(w), Predicate(p) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
// A Predicate transition is a list of RW sequences guarded by a PredTerm.
|
|
|
|
struct PredTransition {
|
|
|
|
// A predicate term is a conjunction of PredChecks.
|
|
|
|
SmallVector<PredCheck, 4> PredTerm;
|
|
|
|
SmallVector<SmallVector<unsigned,4>, 16> WriteSequences;
|
|
|
|
SmallVector<SmallVector<unsigned,4>, 16> ReadSequences;
|
2012-09-22 10:24:21 +08:00
|
|
|
SmallVector<unsigned, 4> ProcIndices;
|
2012-09-15 08:19:59 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Encapsulate a set of partially constructed transitions.
|
|
|
|
// The results are built by repeated calls to substituteVariants.
|
|
|
|
class PredTransitions {
|
|
|
|
CodeGenSchedModels &SchedModels;
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::vector<PredTransition> TransVec;
|
|
|
|
|
|
|
|
PredTransitions(CodeGenSchedModels &sm): SchedModels(sm) {}
|
|
|
|
|
|
|
|
void substituteVariantOperand(const SmallVectorImpl<unsigned> &RWSeq,
|
|
|
|
bool IsRead, unsigned StartIdx);
|
|
|
|
|
|
|
|
void substituteVariants(const PredTransition &Trans);
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
void dump() const;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool mutuallyExclusive(Record *PredDef, ArrayRef<PredCheck> Term);
|
2012-10-04 07:06:28 +08:00
|
|
|
void getIntersectingVariants(
|
|
|
|
const CodeGenSchedRW &SchedRW, unsigned TransIdx,
|
|
|
|
std::vector<TransVariant> &IntersectingVariants);
|
2012-09-22 10:24:21 +08:00
|
|
|
void pushVariant(const TransVariant &VInfo, bool IsRead);
|
2012-09-15 08:19:59 +08:00
|
|
|
};
|
2016-12-01 01:48:10 +08:00
|
|
|
|
|
|
|
} // end anonymous namespace
|
2012-09-15 08:19:59 +08:00
|
|
|
|
|
|
|
// Return true if this predicate is mutually exclusive with a PredTerm. This
|
|
|
|
// degenerates into checking if the predicate is mutually exclusive with any
|
|
|
|
// predicate in the Term's conjunction.
|
|
|
|
//
|
|
|
|
// All predicates associated with a given SchedRW are considered mutually
|
|
|
|
// exclusive. This should work even if the conditions expressed by the
|
|
|
|
// predicates are not exclusive because the predicates for a given SchedWrite
|
|
|
|
// are always checked in the order they are defined in the .td file. Later
|
|
|
|
// conditions implicitly negate any prior condition.
|
|
|
|
bool PredTransitions::mutuallyExclusive(Record *PredDef,
|
|
|
|
ArrayRef<PredCheck> Term) {
|
2017-10-10 00:21:25 +08:00
|
|
|
for (const PredCheck &PC: Term) {
|
2017-10-05 21:27:43 +08:00
|
|
|
if (PC.Predicate == PredDef)
|
2012-09-15 08:19:59 +08:00
|
|
|
return false;
|
|
|
|
|
2017-10-05 21:27:43 +08:00
|
|
|
const CodeGenSchedRW &SchedRW = SchedModels.getSchedRW(PC.RWIdx, PC.IsRead);
|
2012-09-15 08:19:59 +08:00
|
|
|
assert(SchedRW.HasVariants && "PredCheck must refer to a SchedVariant");
|
|
|
|
RecVec Variants = SchedRW.TheDef->getValueAsListOfDefs("Variants");
|
2018-04-26 20:56:26 +08:00
|
|
|
if (any_of(Variants, [PredDef](const Record *R) {
|
|
|
|
return R->getValueAsDef("Predicate") == PredDef;
|
|
|
|
}))
|
|
|
|
return true;
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-10-04 07:06:28 +08:00
|
|
|
static bool hasAliasedVariants(const CodeGenSchedRW &RW,
|
|
|
|
CodeGenSchedModels &SchedModels) {
|
|
|
|
if (RW.HasVariants)
|
|
|
|
return true;
|
|
|
|
|
2017-10-10 00:21:25 +08:00
|
|
|
for (Record *Alias : RW.Aliases) {
|
2012-10-04 07:06:28 +08:00
|
|
|
const CodeGenSchedRW &AliasRW =
|
2017-10-05 21:27:43 +08:00
|
|
|
SchedModels.getSchedRW(Alias->getValueAsDef("AliasRW"));
|
2012-10-04 07:06:28 +08:00
|
|
|
if (AliasRW.HasVariants)
|
|
|
|
return true;
|
|
|
|
if (AliasRW.IsSequence) {
|
|
|
|
IdxVec ExpandedRWs;
|
|
|
|
SchedModels.expandRWSequence(AliasRW.Index, ExpandedRWs, AliasRW.IsRead);
|
2018-04-26 20:56:26 +08:00
|
|
|
for (unsigned SI : ExpandedRWs) {
|
|
|
|
if (hasAliasedVariants(SchedModels.getSchedRW(SI, AliasRW.IsRead),
|
|
|
|
SchedModels))
|
2012-10-04 07:06:28 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasVariant(ArrayRef<PredTransition> Transitions,
|
|
|
|
CodeGenSchedModels &SchedModels) {
|
2018-04-26 20:56:26 +08:00
|
|
|
for (const PredTransition &PTI : Transitions) {
|
|
|
|
for (const SmallVectorImpl<unsigned> &WSI : PTI.WriteSequences)
|
|
|
|
for (unsigned WI : WSI)
|
|
|
|
if (hasAliasedVariants(SchedModels.getSchedWrite(WI), SchedModels))
|
2012-10-04 07:06:28 +08:00
|
|
|
return true;
|
2018-04-26 20:56:26 +08:00
|
|
|
|
|
|
|
for (const SmallVectorImpl<unsigned> &RSI : PTI.ReadSequences)
|
|
|
|
for (unsigned RI : RSI)
|
|
|
|
if (hasAliasedVariants(SchedModels.getSchedRead(RI), SchedModels))
|
2012-10-04 07:06:28 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate IntersectingVariants with any variants or aliased sequences of the
|
|
|
|
// given SchedRW whose processor indices and predicates are not mutually
|
2013-03-30 03:08:31 +08:00
|
|
|
// exclusive with the given transition.
|
2012-10-04 07:06:28 +08:00
|
|
|
void PredTransitions::getIntersectingVariants(
|
|
|
|
const CodeGenSchedRW &SchedRW, unsigned TransIdx,
|
|
|
|
std::vector<TransVariant> &IntersectingVariants) {
|
|
|
|
|
2013-03-30 03:08:31 +08:00
|
|
|
bool GenericRW = false;
|
|
|
|
|
2012-10-04 07:06:28 +08:00
|
|
|
std::vector<TransVariant> Variants;
|
|
|
|
if (SchedRW.HasVariants) {
|
|
|
|
unsigned VarProcIdx = 0;
|
|
|
|
if (SchedRW.TheDef->getValueInit("SchedModel")->isComplete()) {
|
|
|
|
Record *ModelDef = SchedRW.TheDef->getValueAsDef("SchedModel");
|
|
|
|
VarProcIdx = SchedModels.getProcModel(ModelDef).Index;
|
|
|
|
}
|
|
|
|
// Push each variant. Assign TransVecIdx later.
|
|
|
|
const RecVec VarDefs = SchedRW.TheDef->getValueAsListOfDefs("Variants");
|
2017-10-09 01:23:30 +08:00
|
|
|
for (Record *VarDef : VarDefs)
|
2018-04-26 20:56:26 +08:00
|
|
|
Variants.emplace_back(VarDef, SchedRW.Index, VarProcIdx, 0);
|
2013-03-30 03:08:31 +08:00
|
|
|
if (VarProcIdx == 0)
|
|
|
|
GenericRW = true;
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
|
|
|
for (RecIter AI = SchedRW.Aliases.begin(), AE = SchedRW.Aliases.end();
|
|
|
|
AI != AE; ++AI) {
|
|
|
|
// If either the SchedAlias itself or the SchedReadWrite that it aliases
|
|
|
|
// to is defined within a processor model, constrain all variants to
|
|
|
|
// that processor.
|
|
|
|
unsigned AliasProcIdx = 0;
|
|
|
|
if ((*AI)->getValueInit("SchedModel")->isComplete()) {
|
|
|
|
Record *ModelDef = (*AI)->getValueAsDef("SchedModel");
|
|
|
|
AliasProcIdx = SchedModels.getProcModel(ModelDef).Index;
|
|
|
|
}
|
|
|
|
const CodeGenSchedRW &AliasRW =
|
|
|
|
SchedModels.getSchedRW((*AI)->getValueAsDef("AliasRW"));
|
|
|
|
|
|
|
|
if (AliasRW.HasVariants) {
|
|
|
|
const RecVec VarDefs = AliasRW.TheDef->getValueAsListOfDefs("Variants");
|
2017-10-10 23:58:45 +08:00
|
|
|
for (Record *VD : VarDefs)
|
2018-04-26 20:56:26 +08:00
|
|
|
Variants.emplace_back(VD, AliasRW.Index, AliasProcIdx, 0);
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
2018-04-26 20:56:26 +08:00
|
|
|
if (AliasRW.IsSequence)
|
|
|
|
Variants.emplace_back(AliasRW.TheDef, SchedRW.Index, AliasProcIdx, 0);
|
2013-03-30 03:08:31 +08:00
|
|
|
if (AliasProcIdx == 0)
|
|
|
|
GenericRW = true;
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
2017-10-09 01:23:30 +08:00
|
|
|
for (TransVariant &Variant : Variants) {
|
2012-10-04 07:06:28 +08:00
|
|
|
// Don't expand variants if the processor models don't intersect.
|
|
|
|
// A zero processor index means any processor.
|
2013-07-14 12:42:23 +08:00
|
|
|
SmallVectorImpl<unsigned> &ProcIndices = TransVec[TransIdx].ProcIndices;
|
2017-10-09 01:23:30 +08:00
|
|
|
if (ProcIndices[0] && Variant.ProcIdx) {
|
2012-10-04 07:06:28 +08:00
|
|
|
unsigned Cnt = std::count(ProcIndices.begin(), ProcIndices.end(),
|
|
|
|
Variant.ProcIdx);
|
|
|
|
if (!Cnt)
|
|
|
|
continue;
|
|
|
|
if (Cnt > 1) {
|
|
|
|
const CodeGenProcModel &PM =
|
|
|
|
*(SchedModels.procModelBegin() + Variant.ProcIdx);
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(Variant.VarOrSeqDef->getLoc(),
|
|
|
|
"Multiple variants defined for processor " +
|
|
|
|
PM.ModelName +
|
|
|
|
" Ensure only one SchedAlias exists per RW.");
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Variant.VarOrSeqDef->isSubClassOf("SchedVar")) {
|
|
|
|
Record *PredDef = Variant.VarOrSeqDef->getValueAsDef("Predicate");
|
|
|
|
if (mutuallyExclusive(PredDef, TransVec[TransIdx].PredTerm))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (IntersectingVariants.empty()) {
|
|
|
|
// The first variant builds on the existing transition.
|
|
|
|
Variant.TransVecIdx = TransIdx;
|
|
|
|
IntersectingVariants.push_back(Variant);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Push another copy of the current transition for more variants.
|
|
|
|
Variant.TransVecIdx = TransVec.size();
|
|
|
|
IntersectingVariants.push_back(Variant);
|
2013-03-29 08:13:08 +08:00
|
|
|
TransVec.push_back(TransVec[TransIdx]);
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
|
|
|
}
|
2013-03-30 03:08:31 +08:00
|
|
|
if (GenericRW && IntersectingVariants.empty()) {
|
|
|
|
PrintFatalError(SchedRW.TheDef->getLoc(), "No variant of this type has "
|
|
|
|
"a matching predicate on any processor");
|
|
|
|
}
|
2012-10-04 07:06:28 +08:00
|
|
|
}
|
|
|
|
|
2012-09-22 10:24:21 +08:00
|
|
|
// Push the Reads/Writes selected by this variant onto the PredTransition
|
|
|
|
// specified by VInfo.
|
|
|
|
void PredTransitions::
|
|
|
|
pushVariant(const TransVariant &VInfo, bool IsRead) {
|
|
|
|
PredTransition &Trans = TransVec[VInfo.TransVecIdx];
|
|
|
|
|
|
|
|
// If this operand transition is reached through a processor-specific alias,
|
|
|
|
// then the whole transition is specific to this processor.
|
|
|
|
if (VInfo.ProcIdx != 0)
|
|
|
|
Trans.ProcIndices.assign(1, VInfo.ProcIdx);
|
|
|
|
|
2012-09-15 08:19:59 +08:00
|
|
|
IdxVec SelectedRWs;
|
2012-10-04 07:06:28 +08:00
|
|
|
if (VInfo.VarOrSeqDef->isSubClassOf("SchedVar")) {
|
|
|
|
Record *PredDef = VInfo.VarOrSeqDef->getValueAsDef("Predicate");
|
2018-04-26 20:56:26 +08:00
|
|
|
Trans.PredTerm.emplace_back(IsRead, VInfo.RWIdx,PredDef);
|
2012-10-04 07:06:28 +08:00
|
|
|
RecVec SelectedDefs = VInfo.VarOrSeqDef->getValueAsListOfDefs("Selected");
|
|
|
|
SchedModels.findRWs(SelectedDefs, SelectedRWs, IsRead);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(VInfo.VarOrSeqDef->isSubClassOf("WriteSequence") &&
|
|
|
|
"variant must be a SchedVariant or aliased WriteSequence");
|
|
|
|
SelectedRWs.push_back(SchedModels.getSchedRWIdx(VInfo.VarOrSeqDef, IsRead));
|
|
|
|
}
|
2012-09-15 08:19:59 +08:00
|
|
|
|
2012-09-22 10:24:21 +08:00
|
|
|
const CodeGenSchedRW &SchedRW = SchedModels.getSchedRW(VInfo.RWIdx, IsRead);
|
2012-09-15 08:19:59 +08:00
|
|
|
|
2016-12-01 01:48:10 +08:00
|
|
|
SmallVectorImpl<SmallVector<unsigned,4>> &RWSequences = IsRead
|
2012-09-15 08:19:59 +08:00
|
|
|
? Trans.ReadSequences : Trans.WriteSequences;
|
|
|
|
if (SchedRW.IsVariadic) {
|
|
|
|
unsigned OperIdx = RWSequences.size()-1;
|
|
|
|
// Make N-1 copies of this transition's last sequence.
|
2018-04-26 20:56:26 +08:00
|
|
|
RWSequences.insert(RWSequences.end(), SelectedRWs.size() - 1,
|
|
|
|
RWSequences[OperIdx]);
|
2012-09-15 08:19:59 +08:00
|
|
|
// Push each of the N elements of the SelectedRWs onto a copy of the last
|
|
|
|
// sequence (split the current operand into N operands).
|
|
|
|
// Note that write sequences should be expanded within this loop--the entire
|
|
|
|
// sequence belongs to a single operand.
|
|
|
|
for (IdxIter RWI = SelectedRWs.begin(), RWE = SelectedRWs.end();
|
|
|
|
RWI != RWE; ++RWI, ++OperIdx) {
|
|
|
|
IdxVec ExpandedRWs;
|
|
|
|
if (IsRead)
|
|
|
|
ExpandedRWs.push_back(*RWI);
|
|
|
|
else
|
|
|
|
SchedModels.expandRWSequence(*RWI, ExpandedRWs, IsRead);
|
|
|
|
RWSequences[OperIdx].insert(RWSequences[OperIdx].end(),
|
|
|
|
ExpandedRWs.begin(), ExpandedRWs.end());
|
|
|
|
}
|
|
|
|
assert(OperIdx == RWSequences.size() && "missed a sequence");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Push this transition's expanded sequence onto this transition's last
|
|
|
|
// sequence (add to the current operand's sequence).
|
|
|
|
SmallVectorImpl<unsigned> &Seq = RWSequences.back();
|
|
|
|
IdxVec ExpandedRWs;
|
|
|
|
for (IdxIter RWI = SelectedRWs.begin(), RWE = SelectedRWs.end();
|
|
|
|
RWI != RWE; ++RWI) {
|
|
|
|
if (IsRead)
|
|
|
|
ExpandedRWs.push_back(*RWI);
|
|
|
|
else
|
|
|
|
SchedModels.expandRWSequence(*RWI, ExpandedRWs, IsRead);
|
|
|
|
}
|
|
|
|
Seq.insert(Seq.end(), ExpandedRWs.begin(), ExpandedRWs.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RWSeq is a sequence of all Reads or all Writes for the next read or write
|
|
|
|
// operand. StartIdx is an index into TransVec where partial results
|
2012-09-22 10:24:21 +08:00
|
|
|
// starts. RWSeq must be applied to all transitions between StartIdx and the end
|
2012-09-15 08:19:59 +08:00
|
|
|
// of TransVec.
|
|
|
|
void PredTransitions::substituteVariantOperand(
|
|
|
|
const SmallVectorImpl<unsigned> &RWSeq, bool IsRead, unsigned StartIdx) {
|
|
|
|
|
|
|
|
// Visit each original RW within the current sequence.
|
|
|
|
for (SmallVectorImpl<unsigned>::const_iterator
|
|
|
|
RWI = RWSeq.begin(), RWE = RWSeq.end(); RWI != RWE; ++RWI) {
|
|
|
|
const CodeGenSchedRW &SchedRW = SchedModels.getSchedRW(*RWI, IsRead);
|
|
|
|
// Push this RW on all partial PredTransitions or distribute variants.
|
|
|
|
// New PredTransitions may be pushed within this loop which should not be
|
|
|
|
// revisited (TransEnd must be loop invariant).
|
|
|
|
for (unsigned TransIdx = StartIdx, TransEnd = TransVec.size();
|
|
|
|
TransIdx != TransEnd; ++TransIdx) {
|
|
|
|
// In the common case, push RW onto the current operand's sequence.
|
2012-09-22 10:24:21 +08:00
|
|
|
if (!hasAliasedVariants(SchedRW, SchedModels)) {
|
2012-09-15 08:19:59 +08:00
|
|
|
if (IsRead)
|
|
|
|
TransVec[TransIdx].ReadSequences.back().push_back(*RWI);
|
|
|
|
else
|
|
|
|
TransVec[TransIdx].WriteSequences.back().push_back(*RWI);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Distribute this partial PredTransition across intersecting variants.
|
2012-10-04 07:06:28 +08:00
|
|
|
// This will push a copies of TransVec[TransIdx] on the back of TransVec.
|
2012-09-22 10:24:21 +08:00
|
|
|
std::vector<TransVariant> IntersectingVariants;
|
2012-10-04 07:06:28 +08:00
|
|
|
getIntersectingVariants(SchedRW, TransIdx, IntersectingVariants);
|
2012-09-15 08:19:59 +08:00
|
|
|
// Now expand each variant on top of its copy of the transition.
|
2012-09-22 10:24:21 +08:00
|
|
|
for (std::vector<TransVariant>::const_iterator
|
2012-09-15 08:19:59 +08:00
|
|
|
IVI = IntersectingVariants.begin(),
|
|
|
|
IVE = IntersectingVariants.end();
|
2012-09-22 10:24:21 +08:00
|
|
|
IVI != IVE; ++IVI) {
|
|
|
|
pushVariant(*IVI, IsRead);
|
|
|
|
}
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For each variant of a Read/Write in Trans, substitute the sequence of
|
|
|
|
// Read/Writes guarded by the variant. This is exponential in the number of
|
|
|
|
// variant Read/Writes, but in practice detection of mutually exclusive
|
|
|
|
// predicates should result in linear growth in the total number variants.
|
|
|
|
//
|
|
|
|
// This is one step in a breadth-first search of nested variants.
|
|
|
|
void PredTransitions::substituteVariants(const PredTransition &Trans) {
|
|
|
|
// Build up a set of partial results starting at the back of
|
|
|
|
// PredTransitions. Remember the first new transition.
|
|
|
|
unsigned StartIdx = TransVec.size();
|
2018-03-22 14:15:10 +08:00
|
|
|
TransVec.emplace_back();
|
2012-09-15 08:19:59 +08:00
|
|
|
TransVec.back().PredTerm = Trans.PredTerm;
|
2012-09-22 10:24:21 +08:00
|
|
|
TransVec.back().ProcIndices = Trans.ProcIndices;
|
2012-09-15 08:19:59 +08:00
|
|
|
|
|
|
|
// Visit each original write sequence.
|
2016-12-01 01:48:10 +08:00
|
|
|
for (SmallVectorImpl<SmallVector<unsigned,4>>::const_iterator
|
2012-09-15 08:19:59 +08:00
|
|
|
WSI = Trans.WriteSequences.begin(), WSE = Trans.WriteSequences.end();
|
|
|
|
WSI != WSE; ++WSI) {
|
|
|
|
// Push a new (empty) write sequence onto all partial Transitions.
|
|
|
|
for (std::vector<PredTransition>::iterator I =
|
|
|
|
TransVec.begin() + StartIdx, E = TransVec.end(); I != E; ++I) {
|
2018-03-22 14:15:10 +08:00
|
|
|
I->WriteSequences.emplace_back();
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
substituteVariantOperand(*WSI, /*IsRead=*/false, StartIdx);
|
|
|
|
}
|
|
|
|
// Visit each original read sequence.
|
2016-12-01 01:48:10 +08:00
|
|
|
for (SmallVectorImpl<SmallVector<unsigned,4>>::const_iterator
|
2012-09-15 08:19:59 +08:00
|
|
|
RSI = Trans.ReadSequences.begin(), RSE = Trans.ReadSequences.end();
|
|
|
|
RSI != RSE; ++RSI) {
|
|
|
|
// Push a new (empty) read sequence onto all partial Transitions.
|
|
|
|
for (std::vector<PredTransition>::iterator I =
|
|
|
|
TransVec.begin() + StartIdx, E = TransVec.end(); I != E; ++I) {
|
2018-03-22 14:15:10 +08:00
|
|
|
I->ReadSequences.emplace_back();
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
substituteVariantOperand(*RSI, /*IsRead=*/true, StartIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new SchedClass for each variant found by inferFromRW. Pass
|
|
|
|
static void inferFromTransitions(ArrayRef<PredTransition> LastTransitions,
|
2012-09-22 10:24:21 +08:00
|
|
|
unsigned FromClassIdx,
|
2012-09-15 08:19:59 +08:00
|
|
|
CodeGenSchedModels &SchedModels) {
|
|
|
|
// For each PredTransition, create a new CodeGenSchedTransition, which usually
|
|
|
|
// requires creating a new SchedClass.
|
|
|
|
for (ArrayRef<PredTransition>::iterator
|
|
|
|
I = LastTransitions.begin(), E = LastTransitions.end(); I != E; ++I) {
|
|
|
|
IdxVec OperWritesVariant;
|
2018-03-21 04:24:12 +08:00
|
|
|
transform(I->WriteSequences, std::back_inserter(OperWritesVariant),
|
|
|
|
[&SchedModels](ArrayRef<unsigned> WS) {
|
|
|
|
return SchedModels.findOrInsertRW(WS, /*IsRead=*/false);
|
|
|
|
});
|
2012-09-15 08:19:59 +08:00
|
|
|
IdxVec OperReadsVariant;
|
2018-03-21 04:24:12 +08:00
|
|
|
transform(I->ReadSequences, std::back_inserter(OperReadsVariant),
|
|
|
|
[&SchedModels](ArrayRef<unsigned> RS) {
|
|
|
|
return SchedModels.findOrInsertRW(RS, /*IsRead=*/true);
|
|
|
|
});
|
2012-09-15 08:19:59 +08:00
|
|
|
CodeGenSchedTransition SCTrans;
|
|
|
|
SCTrans.ToClassIdx =
|
2014-04-15 15:20:03 +08:00
|
|
|
SchedModels.addSchedClass(/*ItinClassDef=*/nullptr, OperWritesVariant,
|
2018-03-25 06:58:03 +08:00
|
|
|
OperReadsVariant, I->ProcIndices);
|
|
|
|
SCTrans.ProcIndices.assign(I->ProcIndices.begin(), I->ProcIndices.end());
|
2012-09-15 08:19:59 +08:00
|
|
|
// The final PredTerm is unique set of predicates guarding the transition.
|
|
|
|
RecVec Preds;
|
2018-03-21 04:24:12 +08:00
|
|
|
transform(I->PredTerm, std::back_inserter(Preds),
|
|
|
|
[](const PredCheck &P) {
|
|
|
|
return P.Predicate;
|
|
|
|
});
|
2018-03-21 04:24:10 +08:00
|
|
|
Preds.erase(std::unique(Preds.begin(), Preds.end()), Preds.end());
|
2018-03-25 06:58:02 +08:00
|
|
|
SCTrans.PredTerm = std::move(Preds);
|
|
|
|
SchedModels.getSchedClass(FromClassIdx)
|
|
|
|
.Transitions.push_back(std::move(SCTrans));
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-22 10:24:21 +08:00
|
|
|
// Create new SchedClasses for the given ReadWrite list. If any of the
|
|
|
|
// ReadWrites refers to a SchedVariant, create a new SchedClass for each variant
|
|
|
|
// of the ReadWrite list, following Aliases if necessary.
|
2015-10-24 20:46:49 +08:00
|
|
|
void CodeGenSchedModels::inferFromRW(ArrayRef<unsigned> OperWrites,
|
|
|
|
ArrayRef<unsigned> OperReads,
|
2012-09-15 08:19:59 +08:00
|
|
|
unsigned FromClassIdx,
|
2015-10-24 20:46:49 +08:00
|
|
|
ArrayRef<unsigned> ProcIndices) {
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "INFER RW proc("; dumpIdxVec(ProcIndices);
|
|
|
|
dbgs() << ") ");
|
2012-09-15 08:19:59 +08:00
|
|
|
|
|
|
|
// Create a seed transition with an empty PredTerm and the expanded sequences
|
|
|
|
// of SchedWrites for the current SchedClass.
|
|
|
|
std::vector<PredTransition> LastTransitions;
|
2018-03-22 14:15:10 +08:00
|
|
|
LastTransitions.emplace_back();
|
2012-09-22 10:24:21 +08:00
|
|
|
LastTransitions.back().ProcIndices.append(ProcIndices.begin(),
|
|
|
|
ProcIndices.end());
|
|
|
|
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned WriteIdx : OperWrites) {
|
2012-09-15 08:19:59 +08:00
|
|
|
IdxVec WriteSeq;
|
2015-10-24 20:46:49 +08:00
|
|
|
expandRWSequence(WriteIdx, WriteSeq, /*IsRead=*/false);
|
2018-03-22 14:15:10 +08:00
|
|
|
LastTransitions[0].WriteSequences.emplace_back();
|
|
|
|
SmallVectorImpl<unsigned> &Seq = LastTransitions[0].WriteSequences.back();
|
2018-03-21 04:24:14 +08:00
|
|
|
Seq.append(WriteSeq.begin(), WriteSeq.end());
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "("; dumpIdxVec(Seq); dbgs() << ") ");
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << " Reads: ");
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned ReadIdx : OperReads) {
|
2012-09-15 08:19:59 +08:00
|
|
|
IdxVec ReadSeq;
|
2015-10-24 20:46:49 +08:00
|
|
|
expandRWSequence(ReadIdx, ReadSeq, /*IsRead=*/true);
|
2018-03-22 14:15:10 +08:00
|
|
|
LastTransitions[0].ReadSequences.emplace_back();
|
|
|
|
SmallVectorImpl<unsigned> &Seq = LastTransitions[0].ReadSequences.back();
|
2018-03-21 04:24:14 +08:00
|
|
|
Seq.append(ReadSeq.begin(), ReadSeq.end());
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "("; dumpIdxVec(Seq); dbgs() << ") ");
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(dbgs() << '\n');
|
2012-09-15 08:19:59 +08:00
|
|
|
|
|
|
|
// Collect all PredTransitions for individual operands.
|
|
|
|
// Iterate until no variant writes remain.
|
|
|
|
while (hasVariant(LastTransitions, *this)) {
|
|
|
|
PredTransitions Transitions(*this);
|
2018-03-21 04:24:16 +08:00
|
|
|
for (const PredTransition &Trans : LastTransitions)
|
|
|
|
Transitions.substituteVariants(Trans);
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(Transitions.dump());
|
2012-09-15 08:19:59 +08:00
|
|
|
LastTransitions.swap(Transitions.TransVec);
|
|
|
|
}
|
|
|
|
// If the first transition has no variants, nothing to do.
|
|
|
|
if (LastTransitions[0].PredTerm.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// WARNING: We are about to mutate the SchedClasses vector. Do not refer to
|
|
|
|
// OperWrites, OperReads, or ProcIndices after calling inferFromTransitions.
|
2012-09-22 10:24:21 +08:00
|
|
|
inferFromTransitions(LastTransitions, FromClassIdx, *this);
|
2012-09-15 08:19:59 +08:00
|
|
|
}
|
|
|
|
|
2013-04-24 07:45:14 +08:00
|
|
|
// Check if any processor resource group contains all resource records in
|
|
|
|
// SubUnits.
|
|
|
|
bool CodeGenSchedModels::hasSuperGroup(RecVec &SubUnits, CodeGenProcModel &PM) {
|
|
|
|
for (unsigned i = 0, e = PM.ProcResourceDefs.size(); i < e; ++i) {
|
|
|
|
if (!PM.ProcResourceDefs[i]->isSubClassOf("ProcResGroup"))
|
|
|
|
continue;
|
|
|
|
RecVec SuperUnits =
|
|
|
|
PM.ProcResourceDefs[i]->getValueAsListOfDefs("Resources");
|
|
|
|
RecIter RI = SubUnits.begin(), RE = SubUnits.end();
|
|
|
|
for ( ; RI != RE; ++RI) {
|
2016-08-12 06:21:41 +08:00
|
|
|
if (!is_contained(SuperUnits, *RI)) {
|
2013-04-24 07:45:14 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (RI == RE)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that overlapping groups have a common supergroup.
|
|
|
|
void CodeGenSchedModels::verifyProcResourceGroups(CodeGenProcModel &PM) {
|
|
|
|
for (unsigned i = 0, e = PM.ProcResourceDefs.size(); i < e; ++i) {
|
|
|
|
if (!PM.ProcResourceDefs[i]->isSubClassOf("ProcResGroup"))
|
|
|
|
continue;
|
|
|
|
RecVec CheckUnits =
|
|
|
|
PM.ProcResourceDefs[i]->getValueAsListOfDefs("Resources");
|
|
|
|
for (unsigned j = i+1; j < e; ++j) {
|
|
|
|
if (!PM.ProcResourceDefs[j]->isSubClassOf("ProcResGroup"))
|
|
|
|
continue;
|
|
|
|
RecVec OtherUnits =
|
|
|
|
PM.ProcResourceDefs[j]->getValueAsListOfDefs("Resources");
|
|
|
|
if (std::find_first_of(CheckUnits.begin(), CheckUnits.end(),
|
|
|
|
OtherUnits.begin(), OtherUnits.end())
|
|
|
|
!= CheckUnits.end()) {
|
|
|
|
// CheckUnits and OtherUnits overlap
|
|
|
|
OtherUnits.insert(OtherUnits.end(), CheckUnits.begin(),
|
|
|
|
CheckUnits.end());
|
|
|
|
if (!hasSuperGroup(OtherUnits, PM)) {
|
|
|
|
PrintFatalError((PM.ProcResourceDefs[i])->getLoc(),
|
|
|
|
"proc resource group overlaps with "
|
|
|
|
+ PM.ProcResourceDefs[j]->getName()
|
|
|
|
+ " but no supergroup contains both.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
// Collect all the RegisterFile definitions available in this target.
|
|
|
|
void CodeGenSchedModels::collectRegisterFiles() {
|
|
|
|
RecVec RegisterFileDefs = Records.getAllDerivedDefinitions("RegisterFile");
|
|
|
|
|
|
|
|
// RegisterFiles is the vector of CodeGenRegisterFile.
|
|
|
|
for (Record *RF : RegisterFileDefs) {
|
|
|
|
// For each register file definition, construct a CodeGenRegisterFile object
|
|
|
|
// and add it to the appropriate scheduling model.
|
|
|
|
CodeGenProcModel &PM = getProcModel(RF->getValueAsDef("SchedModel"));
|
|
|
|
PM.RegisterFiles.emplace_back(CodeGenRegisterFile(RF->getName(),RF));
|
|
|
|
CodeGenRegisterFile &CGRF = PM.RegisterFiles.back();
|
[tblgen][llvm-mca] Add the ability to describe move elimination candidates via tablegen.
This patch adds the ability to identify instructions that are "move elimination
candidates". It also allows scheduling models to describe processor register
files that allow move elimination.
A move elimination candidate is an instruction that can be eliminated at
register renaming stage.
Each subtarget can specify which instructions are move elimination candidates
with the help of tablegen class "IsOptimizableRegisterMove" (see
llvm/Target/TargetInstrPredicate.td).
For example, on X86, BtVer2 allows both GPR and MMX/SSE moves to be eliminated.
The definition of 'IsOptimizableRegisterMove' for BtVer2 looks like this:
```
def : IsOptimizableRegisterMove<[
InstructionEquivalenceClass<[
// GPR variants.
MOV32rr, MOV64rr,
// MMX variants.
MMX_MOVQ64rr,
// SSE variants.
MOVAPSrr, MOVUPSrr,
MOVAPDrr, MOVUPDrr,
MOVDQArr, MOVDQUrr,
// AVX variants.
VMOVAPSrr, VMOVUPSrr,
VMOVAPDrr, VMOVUPDrr,
VMOVDQArr, VMOVDQUrr
], CheckNot<CheckSameRegOperand<0, 1>> >
]>;
```
Definitions of IsOptimizableRegisterMove from processor models of a same
Target are processed by the SubtargetEmitter to auto-generate a target-specific
override for each of the following predicate methods:
```
bool TargetSubtargetInfo::isOptimizableRegisterMove(const MachineInstr *MI)
const;
bool MCInstrAnalysis::isOptimizableRegisterMove(const MCInst &MI, unsigned
CPUID) const;
```
By default, those methods return false (i.e. conservatively assume that there
are no move elimination candidates).
Tablegen class RegisterFile has been extended with the following information:
- The set of register classes that allow move elimination.
- Maxium number of moves that can be eliminated every cycle.
- Whether move elimination is restricted to moves from registers that are
known to be zero.
This patch is structured in three part:
A first part (which is mostly boilerplate) adds the new
'isOptimizableRegisterMove' target hooks, and extends existing register file
descriptors in MC by introducing new fields to describe properties related to
move elimination.
A second part, uses the new tablegen constructs to describe move elimination in
the BtVer2 scheduling model.
A third part, teaches llm-mca how to query the new 'isOptimizableRegisterMove'
hook to mark instructions that are candidates for move elimination. It also
teaches class RegisterFile how to describe constraints on move elimination at
PRF granularity.
llvm-mca tests for btver2 show differences before/after this patch.
Differential Revision: https://reviews.llvm.org/D53134
llvm-svn: 344334
2018-10-12 19:23:04 +08:00
|
|
|
CGRF.MaxMovesEliminatedPerCycle =
|
|
|
|
RF->getValueAsInt("MaxMovesEliminatedPerCycle");
|
|
|
|
CGRF.AllowZeroMoveEliminationOnly =
|
|
|
|
RF->getValueAsBit("AllowZeroMoveEliminationOnly");
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
|
|
|
|
// Now set the number of physical registers as well as the cost of registers
|
|
|
|
// in each register class.
|
|
|
|
CGRF.NumPhysRegs = RF->getValueAsInt("NumPhysRegs");
|
2018-10-11 18:39:03 +08:00
|
|
|
if (!CGRF.NumPhysRegs) {
|
|
|
|
PrintFatalError(RF->getLoc(),
|
|
|
|
"Invalid RegisterFile with zero physical registers");
|
|
|
|
}
|
|
|
|
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
RecVec RegisterClasses = RF->getValueAsListOfDefs("RegClasses");
|
|
|
|
std::vector<int64_t> RegisterCosts = RF->getValueAsListOfInts("RegCosts");
|
[tblgen][llvm-mca] Add the ability to describe move elimination candidates via tablegen.
This patch adds the ability to identify instructions that are "move elimination
candidates". It also allows scheduling models to describe processor register
files that allow move elimination.
A move elimination candidate is an instruction that can be eliminated at
register renaming stage.
Each subtarget can specify which instructions are move elimination candidates
with the help of tablegen class "IsOptimizableRegisterMove" (see
llvm/Target/TargetInstrPredicate.td).
For example, on X86, BtVer2 allows both GPR and MMX/SSE moves to be eliminated.
The definition of 'IsOptimizableRegisterMove' for BtVer2 looks like this:
```
def : IsOptimizableRegisterMove<[
InstructionEquivalenceClass<[
// GPR variants.
MOV32rr, MOV64rr,
// MMX variants.
MMX_MOVQ64rr,
// SSE variants.
MOVAPSrr, MOVUPSrr,
MOVAPDrr, MOVUPDrr,
MOVDQArr, MOVDQUrr,
// AVX variants.
VMOVAPSrr, VMOVUPSrr,
VMOVAPDrr, VMOVUPDrr,
VMOVDQArr, VMOVDQUrr
], CheckNot<CheckSameRegOperand<0, 1>> >
]>;
```
Definitions of IsOptimizableRegisterMove from processor models of a same
Target are processed by the SubtargetEmitter to auto-generate a target-specific
override for each of the following predicate methods:
```
bool TargetSubtargetInfo::isOptimizableRegisterMove(const MachineInstr *MI)
const;
bool MCInstrAnalysis::isOptimizableRegisterMove(const MCInst &MI, unsigned
CPUID) const;
```
By default, those methods return false (i.e. conservatively assume that there
are no move elimination candidates).
Tablegen class RegisterFile has been extended with the following information:
- The set of register classes that allow move elimination.
- Maxium number of moves that can be eliminated every cycle.
- Whether move elimination is restricted to moves from registers that are
known to be zero.
This patch is structured in three part:
A first part (which is mostly boilerplate) adds the new
'isOptimizableRegisterMove' target hooks, and extends existing register file
descriptors in MC by introducing new fields to describe properties related to
move elimination.
A second part, uses the new tablegen constructs to describe move elimination in
the BtVer2 scheduling model.
A third part, teaches llm-mca how to query the new 'isOptimizableRegisterMove'
hook to mark instructions that are candidates for move elimination. It also
teaches class RegisterFile how to describe constraints on move elimination at
PRF granularity.
llvm-mca tests for btver2 show differences before/after this patch.
Differential Revision: https://reviews.llvm.org/D53134
llvm-svn: 344334
2018-10-12 19:23:04 +08:00
|
|
|
ListInit *MoveElimInfo = RF->getValueAsListInit("AllowMoveElimination");
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
for (unsigned I = 0, E = RegisterClasses.size(); I < E; ++I) {
|
|
|
|
int Cost = RegisterCosts.size() > I ? RegisterCosts[I] : 1;
|
[tblgen][llvm-mca] Add the ability to describe move elimination candidates via tablegen.
This patch adds the ability to identify instructions that are "move elimination
candidates". It also allows scheduling models to describe processor register
files that allow move elimination.
A move elimination candidate is an instruction that can be eliminated at
register renaming stage.
Each subtarget can specify which instructions are move elimination candidates
with the help of tablegen class "IsOptimizableRegisterMove" (see
llvm/Target/TargetInstrPredicate.td).
For example, on X86, BtVer2 allows both GPR and MMX/SSE moves to be eliminated.
The definition of 'IsOptimizableRegisterMove' for BtVer2 looks like this:
```
def : IsOptimizableRegisterMove<[
InstructionEquivalenceClass<[
// GPR variants.
MOV32rr, MOV64rr,
// MMX variants.
MMX_MOVQ64rr,
// SSE variants.
MOVAPSrr, MOVUPSrr,
MOVAPDrr, MOVUPDrr,
MOVDQArr, MOVDQUrr,
// AVX variants.
VMOVAPSrr, VMOVUPSrr,
VMOVAPDrr, VMOVUPDrr,
VMOVDQArr, VMOVDQUrr
], CheckNot<CheckSameRegOperand<0, 1>> >
]>;
```
Definitions of IsOptimizableRegisterMove from processor models of a same
Target are processed by the SubtargetEmitter to auto-generate a target-specific
override for each of the following predicate methods:
```
bool TargetSubtargetInfo::isOptimizableRegisterMove(const MachineInstr *MI)
const;
bool MCInstrAnalysis::isOptimizableRegisterMove(const MCInst &MI, unsigned
CPUID) const;
```
By default, those methods return false (i.e. conservatively assume that there
are no move elimination candidates).
Tablegen class RegisterFile has been extended with the following information:
- The set of register classes that allow move elimination.
- Maxium number of moves that can be eliminated every cycle.
- Whether move elimination is restricted to moves from registers that are
known to be zero.
This patch is structured in three part:
A first part (which is mostly boilerplate) adds the new
'isOptimizableRegisterMove' target hooks, and extends existing register file
descriptors in MC by introducing new fields to describe properties related to
move elimination.
A second part, uses the new tablegen constructs to describe move elimination in
the BtVer2 scheduling model.
A third part, teaches llm-mca how to query the new 'isOptimizableRegisterMove'
hook to mark instructions that are candidates for move elimination. It also
teaches class RegisterFile how to describe constraints on move elimination at
PRF granularity.
llvm-mca tests for btver2 show differences before/after this patch.
Differential Revision: https://reviews.llvm.org/D53134
llvm-svn: 344334
2018-10-12 19:23:04 +08:00
|
|
|
|
|
|
|
bool AllowMoveElim = false;
|
|
|
|
if (MoveElimInfo->size() > I) {
|
|
|
|
BitInit *Val = cast<BitInit>(MoveElimInfo->getElement(I));
|
|
|
|
AllowMoveElim = Val->getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
CGRF.Costs.emplace_back(RegisterClasses[I], Cost, AllowMoveElim);
|
[MC][Tablegen] Allow the definition of processor register files in the scheduling model for llvm-mca
This patch allows the description of register files in processor scheduling
models. This addresses PR36662.
A new tablegen class named 'RegisterFile' has been added to TargetSchedule.td.
Targets can optionally describe register files for their processors using that
class. In particular, class RegisterFile allows to specify:
- The total number of physical registers.
- Which target registers are accessible through the register file.
- The cost of allocating a register at register renaming stage.
Example (from this patch - see file X86/X86ScheduleBtVer2.td)
def FpuPRF : RegisterFile<72, [VR64, VR128, VR256], [1, 1, 2]>
Here, FpuPRF describes a register file for MMX/XMM/YMM registers. On Jaguar
(btver2), a YMM register definition consumes 2 physical registers, while MMX/XMM
register definitions only cost 1 physical register.
The syntax allows to specify an empty set of register classes. An empty set of
register classes means: this register file models all the registers specified by
the Target. For each register class, users can specify an optional register
cost. By default, register costs default to 1. A value of 0 for the number of
physical registers means: "this register file has an unbounded number of
physical registers".
This patch is structured in two parts.
* Part 1 - MC/Tablegen *
A first part adds the tablegen definition of RegisterFile, and teaches the
SubtargetEmitter how to emit information related to register files.
Information about register files is accessible through an instance of
MCExtraProcessorInfo.
The idea behind this design is to logically partition the processor description
which is only used by external tools (like llvm-mca) from the processor
information used by the llvm machine schedulers.
I think that this design would make easier for targets to get rid of the extra
processor information if they don't want it.
* Part 2 - llvm-mca related *
The second part of this patch is related to changes to llvm-mca.
The main differences are:
1) class RegisterFile now needs to take into account the "cost of a register"
when allocating physical registers at register renaming stage.
2) Point 1. triggered a minor refactoring which lef to the removal of the
"maximum 32 register files" restriction.
3) The BackendStatistics view has been updated so that we can print out extra
details related to each register file implemented by the processor.
The effect of point 3. is also visible in tests register-files-[1..5].s.
Differential Revision: https://reviews.llvm.org/D44980
llvm-svn: 329067
2018-04-03 21:36:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
// Collect and sort WriteRes, ReadAdvance, and ProcResources.
|
|
|
|
void CodeGenSchedModels::collectProcResources() {
|
2016-06-21 11:24:03 +08:00
|
|
|
ProcResourceDefs = Records.getAllDerivedDefinitions("ProcResourceUnits");
|
|
|
|
ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup");
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
// Add any subtarget-specific SchedReadWrites that are directly associated
|
|
|
|
// with processor resources. Refer to the parent SchedClass's ProcIndices to
|
|
|
|
// determine which processors they apply to.
|
2018-04-26 20:56:26 +08:00
|
|
|
for (const CodeGenSchedClass &SC :
|
|
|
|
make_range(schedClassBegin(), schedClassEnd())) {
|
|
|
|
if (SC.ItinClassDef) {
|
|
|
|
collectItinProcResources(SC.ItinClassDef);
|
|
|
|
continue;
|
2013-02-01 11:19:54 +08:00
|
|
|
}
|
2018-04-26 20:56:26 +08:00
|
|
|
|
|
|
|
// This class may have a default ReadWrite list which can be overriden by
|
|
|
|
// InstRW definitions.
|
|
|
|
for (Record *RW : SC.InstRWs) {
|
|
|
|
Record *RWModelDef = RW->getValueAsDef("SchedModel");
|
|
|
|
unsigned PIdx = getProcModel(RWModelDef).Index;
|
|
|
|
IdxVec Writes, Reads;
|
|
|
|
findRWs(RW->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);
|
|
|
|
collectRWResources(Writes, Reads, PIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
collectRWResources(SC.Writes, SC.Reads, SC.ProcIndices);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
// Add resources separately defined by each subtarget.
|
|
|
|
RecVec WRDefs = Records.getAllDerivedDefinitions("WriteRes");
|
2017-10-11 17:33:23 +08:00
|
|
|
for (Record *WR : WRDefs) {
|
|
|
|
Record *ModelDef = WR->getValueAsDef("SchedModel");
|
|
|
|
addWriteRes(WR, getProcModel(ModelDef).Index);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2014-03-13 11:49:20 +08:00
|
|
|
RecVec SWRDefs = Records.getAllDerivedDefinitions("SchedWriteRes");
|
2017-10-11 17:33:23 +08:00
|
|
|
for (Record *SWR : SWRDefs) {
|
|
|
|
Record *ModelDef = SWR->getValueAsDef("SchedModel");
|
|
|
|
addWriteRes(SWR, getProcModel(ModelDef).Index);
|
2014-03-13 11:49:20 +08:00
|
|
|
}
|
2012-09-15 08:20:02 +08:00
|
|
|
RecVec RADefs = Records.getAllDerivedDefinitions("ReadAdvance");
|
2017-10-11 17:33:23 +08:00
|
|
|
for (Record *RA : RADefs) {
|
|
|
|
Record *ModelDef = RA->getValueAsDef("SchedModel");
|
|
|
|
addReadAdvance(RA, getProcModel(ModelDef).Index);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2014-03-13 11:49:20 +08:00
|
|
|
RecVec SRADefs = Records.getAllDerivedDefinitions("SchedReadAdvance");
|
2017-10-11 17:33:23 +08:00
|
|
|
for (Record *SRA : SRADefs) {
|
|
|
|
if (SRA->getValueInit("SchedModel")->isComplete()) {
|
|
|
|
Record *ModelDef = SRA->getValueAsDef("SchedModel");
|
|
|
|
addReadAdvance(SRA, getProcModel(ModelDef).Index);
|
2014-03-13 11:49:20 +08:00
|
|
|
}
|
|
|
|
}
|
2013-06-15 12:50:06 +08:00
|
|
|
// Add ProcResGroups that are defined within this processor model, which may
|
|
|
|
// not be directly referenced but may directly specify a buffer size.
|
|
|
|
RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup");
|
2017-10-10 00:21:25 +08:00
|
|
|
for (Record *PRG : ProcResGroups) {
|
2017-10-05 21:27:43 +08:00
|
|
|
if (!PRG->getValueInit("SchedModel")->isComplete())
|
2013-06-15 12:50:06 +08:00
|
|
|
continue;
|
2017-10-05 21:27:43 +08:00
|
|
|
CodeGenProcModel &PM = getProcModel(PRG->getValueAsDef("SchedModel"));
|
|
|
|
if (!is_contained(PM.ProcResourceDefs, PRG))
|
|
|
|
PM.ProcResourceDefs.push_back(PRG);
|
2013-06-15 12:50:06 +08:00
|
|
|
}
|
2018-02-05 20:23:51 +08:00
|
|
|
// Add ProcResourceUnits unconditionally.
|
|
|
|
for (Record *PRU : Records.getAllDerivedDefinitions("ProcResourceUnits")) {
|
|
|
|
if (!PRU->getValueInit("SchedModel")->isComplete())
|
|
|
|
continue;
|
|
|
|
CodeGenProcModel &PM = getProcModel(PRU->getValueAsDef("SchedModel"));
|
|
|
|
if (!is_contained(PM.ProcResourceDefs, PRU))
|
|
|
|
PM.ProcResourceDefs.push_back(PRU);
|
|
|
|
}
|
2012-09-15 08:20:02 +08:00
|
|
|
// Finalize each ProcModel by sorting the record arrays.
|
2014-12-09 16:05:51 +08:00
|
|
|
for (CodeGenProcModel &PM : ProcModels) {
|
2018-10-01 06:31:29 +08:00
|
|
|
llvm::sort(PM.WriteResDefs, LessRecord());
|
|
|
|
llvm::sort(PM.ReadAdvanceDefs, LessRecord());
|
|
|
|
llvm::sort(PM.ProcResourceDefs, LessRecord());
|
2018-05-14 20:53:11 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
PM.dump();
|
|
|
|
dbgs() << "WriteResDefs: "; for (RecIter RI = PM.WriteResDefs.begin(),
|
|
|
|
RE = PM.WriteResDefs.end();
|
|
|
|
RI != RE; ++RI) {
|
|
|
|
if ((*RI)->isSubClassOf("WriteRes"))
|
|
|
|
dbgs() << (*RI)->getValueAsDef("WriteType")->getName() << " ";
|
|
|
|
else
|
|
|
|
dbgs() << (*RI)->getName() << " ";
|
|
|
|
} dbgs() << "\nReadAdvanceDefs: ";
|
|
|
|
for (RecIter RI = PM.ReadAdvanceDefs.begin(),
|
|
|
|
RE = PM.ReadAdvanceDefs.end();
|
|
|
|
RI != RE; ++RI) {
|
|
|
|
if ((*RI)->isSubClassOf("ReadAdvance"))
|
|
|
|
dbgs() << (*RI)->getValueAsDef("ReadType")->getName() << " ";
|
|
|
|
else
|
|
|
|
dbgs() << (*RI)->getName() << " ";
|
|
|
|
} dbgs()
|
|
|
|
<< "\nProcResourceDefs: ";
|
|
|
|
for (RecIter RI = PM.ProcResourceDefs.begin(),
|
|
|
|
RE = PM.ProcResourceDefs.end();
|
|
|
|
RI != RE; ++RI) { dbgs() << (*RI)->getName() << " "; } dbgs()
|
|
|
|
<< '\n');
|
2013-04-24 07:45:14 +08:00
|
|
|
verifyProcResourceGroups(PM);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2016-06-21 11:24:03 +08:00
|
|
|
|
|
|
|
ProcResourceDefs.clear();
|
|
|
|
ProcResGroups.clear();
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
|
2016-03-02 04:03:21 +08:00
|
|
|
void CodeGenSchedModels::checkCompleteness() {
|
|
|
|
bool Complete = true;
|
|
|
|
bool HadCompleteModel = false;
|
|
|
|
for (const CodeGenProcModel &ProcModel : procModels()) {
|
2018-04-05 21:11:36 +08:00
|
|
|
const bool HasItineraries = ProcModel.hasItineraries();
|
2016-03-02 04:03:21 +08:00
|
|
|
if (!ProcModel.ModelDef->getValueAsBit("CompleteModel"))
|
|
|
|
continue;
|
|
|
|
for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) {
|
|
|
|
if (Inst->hasNoSchedulingInfo)
|
|
|
|
continue;
|
2016-06-24 16:43:27 +08:00
|
|
|
if (ProcModel.isUnsupported(*Inst))
|
|
|
|
continue;
|
2016-03-02 04:03:21 +08:00
|
|
|
unsigned SCIdx = getSchedClassIdx(*Inst);
|
|
|
|
if (!SCIdx) {
|
|
|
|
if (Inst->TheDef->isValueUnset("SchedRW") && !HadCompleteModel) {
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintError(Inst->TheDef->getLoc(),
|
|
|
|
"No schedule information for instruction '" +
|
|
|
|
Inst->TheDef->getName() + "'");
|
2016-03-02 04:03:21 +08:00
|
|
|
Complete = false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const CodeGenSchedClass &SC = getSchedClass(SCIdx);
|
|
|
|
if (!SC.Writes.empty())
|
|
|
|
continue;
|
2018-04-05 21:11:36 +08:00
|
|
|
if (HasItineraries && SC.ItinClassDef != nullptr &&
|
2016-11-01 02:59:52 +08:00
|
|
|
SC.ItinClassDef->getName() != "NoItinerary")
|
2016-03-03 08:04:59 +08:00
|
|
|
continue;
|
2016-03-02 04:03:21 +08:00
|
|
|
|
|
|
|
const RecVec &InstRWs = SC.InstRWs;
|
2016-08-12 08:18:03 +08:00
|
|
|
auto I = find_if(InstRWs, [&ProcModel](const Record *R) {
|
|
|
|
return R->getValueAsDef("SchedModel") == ProcModel.ModelDef;
|
|
|
|
});
|
2016-03-02 04:03:21 +08:00
|
|
|
if (I == InstRWs.end()) {
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintError(Inst->TheDef->getLoc(), "'" + ProcModel.ModelName +
|
|
|
|
"' lacks information for '" +
|
|
|
|
Inst->TheDef->getName() + "'");
|
2016-03-02 04:03:21 +08:00
|
|
|
Complete = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HadCompleteModel = true;
|
|
|
|
}
|
2016-03-02 05:36:12 +08:00
|
|
|
if (!Complete) {
|
|
|
|
errs() << "\n\nIncomplete schedule models found.\n"
|
|
|
|
<< "- Consider setting 'CompleteModel = 0' while developing new models.\n"
|
|
|
|
<< "- Pseudo instructions can be marked with 'hasNoSchedulingInfo = 1'.\n"
|
|
|
|
<< "- Instructions should usually have Sched<[...]> as a superclass, "
|
2016-06-24 16:43:27 +08:00
|
|
|
"you may temporarily use an empty list.\n"
|
|
|
|
<< "- Instructions related to unsupported features can be excluded with "
|
|
|
|
"list<Predicate> UnsupportedFeatures = [HasA,..,HasY]; in the "
|
|
|
|
"processor model.\n\n";
|
2016-03-02 04:03:21 +08:00
|
|
|
PrintFatalError("Incomplete schedule model");
|
2016-03-02 05:36:12 +08:00
|
|
|
}
|
2016-03-02 04:03:21 +08:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
// Collect itinerary class resources for each processor.
|
|
|
|
void CodeGenSchedModels::collectItinProcResources(Record *ItinClassDef) {
|
|
|
|
for (unsigned PIdx = 0, PEnd = ProcModels.size(); PIdx != PEnd; ++PIdx) {
|
|
|
|
const CodeGenProcModel &PM = ProcModels[PIdx];
|
|
|
|
// For all ItinRW entries.
|
|
|
|
bool HasMatch = false;
|
|
|
|
for (RecIter II = PM.ItinRWDefs.begin(), IE = PM.ItinRWDefs.end();
|
|
|
|
II != IE; ++II) {
|
|
|
|
RecVec Matched = (*II)->getValueAsListOfDefs("MatchedItinClasses");
|
|
|
|
if (!std::count(Matched.begin(), Matched.end(), ItinClassDef))
|
|
|
|
continue;
|
|
|
|
if (HasMatch)
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError((*II)->getLoc(), "Duplicate itinerary class "
|
|
|
|
+ ItinClassDef->getName()
|
|
|
|
+ " in ItinResources for " + PM.ModelName);
|
2012-09-15 08:20:02 +08:00
|
|
|
HasMatch = true;
|
|
|
|
IdxVec Writes, Reads;
|
|
|
|
findRWs((*II)->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);
|
2018-03-25 05:57:35 +08:00
|
|
|
collectRWResources(Writes, Reads, PIdx);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-10 13:43:13 +08:00
|
|
|
void CodeGenSchedModels::collectRWResources(unsigned RWIdx, bool IsRead,
|
2015-10-24 20:46:49 +08:00
|
|
|
ArrayRef<unsigned> ProcIndices) {
|
2012-10-10 13:43:13 +08:00
|
|
|
const CodeGenSchedRW &SchedRW = getSchedRW(RWIdx, IsRead);
|
|
|
|
if (SchedRW.TheDef) {
|
|
|
|
if (!IsRead && SchedRW.TheDef->isSubClassOf("SchedWriteRes")) {
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned Idx : ProcIndices)
|
|
|
|
addWriteRes(SchedRW.TheDef, Idx);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2012-10-10 13:43:13 +08:00
|
|
|
else if (IsRead && SchedRW.TheDef->isSubClassOf("SchedReadAdvance")) {
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned Idx : ProcIndices)
|
|
|
|
addReadAdvance(SchedRW.TheDef, Idx);
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2012-10-10 13:43:13 +08:00
|
|
|
}
|
|
|
|
for (RecIter AI = SchedRW.Aliases.begin(), AE = SchedRW.Aliases.end();
|
|
|
|
AI != AE; ++AI) {
|
|
|
|
IdxVec AliasProcIndices;
|
|
|
|
if ((*AI)->getValueInit("SchedModel")->isComplete()) {
|
|
|
|
AliasProcIndices.push_back(
|
|
|
|
getProcModel((*AI)->getValueAsDef("SchedModel")).Index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
AliasProcIndices = ProcIndices;
|
|
|
|
const CodeGenSchedRW &AliasRW = getSchedRW((*AI)->getValueAsDef("AliasRW"));
|
|
|
|
assert(AliasRW.IsRead == IsRead && "cannot alias reads to writes");
|
|
|
|
|
|
|
|
IdxVec ExpandedRWs;
|
|
|
|
expandRWSequence(AliasRW.Index, ExpandedRWs, IsRead);
|
|
|
|
for (IdxIter SI = ExpandedRWs.begin(), SE = ExpandedRWs.end();
|
|
|
|
SI != SE; ++SI) {
|
|
|
|
collectRWResources(*SI, IsRead, AliasProcIndices);
|
2012-09-22 10:24:21 +08:00
|
|
|
}
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-10 13:43:13 +08:00
|
|
|
// Collect resources for a set of read/write types and processor indices.
|
2015-10-24 20:46:49 +08:00
|
|
|
void CodeGenSchedModels::collectRWResources(ArrayRef<unsigned> Writes,
|
|
|
|
ArrayRef<unsigned> Reads,
|
|
|
|
ArrayRef<unsigned> ProcIndices) {
|
|
|
|
for (unsigned Idx : Writes)
|
|
|
|
collectRWResources(Idx, /*IsRead=*/false, ProcIndices);
|
2012-10-10 13:43:13 +08:00
|
|
|
|
2015-10-24 20:46:49 +08:00
|
|
|
for (unsigned Idx : Reads)
|
|
|
|
collectRWResources(Idx, /*IsRead=*/true, ProcIndices);
|
2012-10-10 13:43:13 +08:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
// Find the processor's resource units for this kind of resource.
|
|
|
|
Record *CodeGenSchedModels::findProcResUnits(Record *ProcResKind,
|
2017-11-22 05:33:52 +08:00
|
|
|
const CodeGenProcModel &PM,
|
|
|
|
ArrayRef<SMLoc> Loc) const {
|
2012-09-15 08:20:02 +08:00
|
|
|
if (ProcResKind->isSubClassOf("ProcResourceUnits"))
|
|
|
|
return ProcResKind;
|
|
|
|
|
2014-04-15 15:20:03 +08:00
|
|
|
Record *ProcUnitDef = nullptr;
|
2016-06-21 11:24:03 +08:00
|
|
|
assert(!ProcResourceDefs.empty());
|
|
|
|
assert(!ProcResGroups.empty());
|
2012-09-15 08:20:02 +08:00
|
|
|
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ProcResDef : ProcResourceDefs) {
|
|
|
|
if (ProcResDef->getValueAsDef("Kind") == ProcResKind
|
|
|
|
&& ProcResDef->getValueAsDef("SchedModel") == PM.ModelDef) {
|
2012-09-15 08:20:02 +08:00
|
|
|
if (ProcUnitDef) {
|
2017-11-22 05:33:52 +08:00
|
|
|
PrintFatalError(Loc,
|
2012-10-26 04:33:17 +08:00
|
|
|
"Multiple ProcessorResourceUnits associated with "
|
|
|
|
+ ProcResKind->getName());
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
2017-09-13 18:31:10 +08:00
|
|
|
ProcUnitDef = ProcResDef;
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
}
|
2017-09-13 18:31:10 +08:00
|
|
|
for (Record *ProcResGroup : ProcResGroups) {
|
|
|
|
if (ProcResGroup == ProcResKind
|
|
|
|
&& ProcResGroup->getValueAsDef("SchedModel") == PM.ModelDef) {
|
2013-03-15 05:21:50 +08:00
|
|
|
if (ProcUnitDef) {
|
2017-11-22 05:33:52 +08:00
|
|
|
PrintFatalError(Loc,
|
2013-03-15 05:21:50 +08:00
|
|
|
"Multiple ProcessorResourceUnits associated with "
|
|
|
|
+ ProcResKind->getName());
|
|
|
|
}
|
2017-09-13 18:31:10 +08:00
|
|
|
ProcUnitDef = ProcResGroup;
|
2013-03-15 05:21:50 +08:00
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:20:02 +08:00
|
|
|
if (!ProcUnitDef) {
|
2017-11-22 05:33:52 +08:00
|
|
|
PrintFatalError(Loc,
|
2012-10-26 04:33:17 +08:00
|
|
|
"No ProcessorResources associated with "
|
|
|
|
+ ProcResKind->getName());
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
return ProcUnitDef;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iteratively add a resource and its super resources.
|
|
|
|
void CodeGenSchedModels::addProcResource(Record *ProcResKind,
|
2017-11-22 05:33:52 +08:00
|
|
|
CodeGenProcModel &PM,
|
|
|
|
ArrayRef<SMLoc> Loc) {
|
2016-12-01 01:48:10 +08:00
|
|
|
while (true) {
|
2017-11-22 05:33:52 +08:00
|
|
|
Record *ProcResUnits = findProcResUnits(ProcResKind, PM, Loc);
|
2012-09-15 08:20:02 +08:00
|
|
|
|
|
|
|
// See if this ProcResource is already associated with this processor.
|
2016-08-12 11:55:06 +08:00
|
|
|
if (is_contained(PM.ProcResourceDefs, ProcResUnits))
|
2012-09-15 08:20:02 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
PM.ProcResourceDefs.push_back(ProcResUnits);
|
2013-03-15 05:21:50 +08:00
|
|
|
if (ProcResUnits->isSubClassOf("ProcResGroup"))
|
|
|
|
return;
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
if (!ProcResUnits->getValueInit("Super")->isComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ProcResKind = ProcResUnits->getValueAsDef("Super");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add resources for a SchedWrite to this processor if they don't exist.
|
|
|
|
void CodeGenSchedModels::addWriteRes(Record *ProcWriteResDef, unsigned PIdx) {
|
2012-09-22 10:24:21 +08:00
|
|
|
assert(PIdx && "don't add resources to an invalid Processor model");
|
|
|
|
|
2012-09-15 08:20:02 +08:00
|
|
|
RecVec &WRDefs = ProcModels[PIdx].WriteResDefs;
|
2016-08-12 11:55:06 +08:00
|
|
|
if (is_contained(WRDefs, ProcWriteResDef))
|
2012-09-15 08:20:02 +08:00
|
|
|
return;
|
|
|
|
WRDefs.push_back(ProcWriteResDef);
|
|
|
|
|
|
|
|
// Visit ProcResourceKinds referenced by the newly discovered WriteRes.
|
|
|
|
RecVec ProcResDefs = ProcWriteResDef->getValueAsListOfDefs("ProcResources");
|
|
|
|
for (RecIter WritePRI = ProcResDefs.begin(), WritePRE = ProcResDefs.end();
|
|
|
|
WritePRI != WritePRE; ++WritePRI) {
|
2017-11-22 05:33:52 +08:00
|
|
|
addProcResource(*WritePRI, ProcModels[PIdx], ProcWriteResDef->getLoc());
|
2012-09-15 08:20:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add resources for a ReadAdvance to this processor if they don't exist.
|
|
|
|
void CodeGenSchedModels::addReadAdvance(Record *ProcReadAdvanceDef,
|
|
|
|
unsigned PIdx) {
|
|
|
|
RecVec &RADefs = ProcModels[PIdx].ReadAdvanceDefs;
|
2016-08-12 11:55:06 +08:00
|
|
|
if (is_contained(RADefs, ProcReadAdvanceDef))
|
2012-09-15 08:20:02 +08:00
|
|
|
return;
|
|
|
|
RADefs.push_back(ProcReadAdvanceDef);
|
|
|
|
}
|
|
|
|
|
2012-09-18 06:18:43 +08:00
|
|
|
unsigned CodeGenProcModel::getProcResourceIdx(Record *PRDef) const {
|
2016-08-12 06:21:41 +08:00
|
|
|
RecIter PRPos = find(ProcResourceDefs, PRDef);
|
2012-09-18 06:18:43 +08:00
|
|
|
if (PRPos == ProcResourceDefs.end())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(PRDef->getLoc(), "ProcResource def is not included in "
|
|
|
|
"the ProcResources list for " + ModelName);
|
2012-09-18 06:18:43 +08:00
|
|
|
// Idx=0 is reserved for invalid.
|
2012-11-03 04:57:36 +08:00
|
|
|
return 1 + (PRPos - ProcResourceDefs.begin());
|
2012-09-18 06:18:43 +08:00
|
|
|
}
|
|
|
|
|
2016-06-24 16:43:27 +08:00
|
|
|
bool CodeGenProcModel::isUnsupported(const CodeGenInstruction &Inst) const {
|
|
|
|
for (const Record *TheDef : UnsupportedFeaturesDefs) {
|
|
|
|
for (const Record *PredDef : Inst.TheDef->getValueAsListOfDefs("Predicates")) {
|
|
|
|
if (TheDef->getName() == PredDef->getName())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-07 12:00:00 +08:00
|
|
|
#ifndef NDEBUG
|
2012-09-15 08:19:57 +08:00
|
|
|
void CodeGenProcModel::dump() const {
|
|
|
|
dbgs() << Index << ": " << ModelName << " "
|
|
|
|
<< (ModelDef ? ModelDef->getName() : "inferred") << " "
|
|
|
|
<< (ItinsDef ? ItinsDef->getName() : "no itinerary") << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeGenSchedRW::dump() const {
|
|
|
|
dbgs() << Name << (IsVariadic ? " (V) " : " ");
|
|
|
|
if (IsSequence) {
|
|
|
|
dbgs() << "(";
|
|
|
|
dumpIdxVec(Sequence);
|
|
|
|
dbgs() << ")";
|
2012-07-07 12:00:00 +08:00
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
|
|
|
|
void CodeGenSchedClass::dump(const CodeGenSchedModels* SchedModels) const {
|
2013-03-17 02:58:55 +08:00
|
|
|
dbgs() << "SCHEDCLASS " << Index << ":" << Name << '\n'
|
2012-09-15 08:19:57 +08:00
|
|
|
<< " Writes: ";
|
|
|
|
for (unsigned i = 0, N = Writes.size(); i < N; ++i) {
|
|
|
|
SchedModels->getSchedWrite(Writes[i]).dump();
|
|
|
|
if (i < N-1) {
|
|
|
|
dbgs() << '\n';
|
|
|
|
dbgs().indent(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dbgs() << "\n Reads: ";
|
|
|
|
for (unsigned i = 0, N = Reads.size(); i < N; ++i) {
|
|
|
|
SchedModels->getSchedRead(Reads[i]).dump();
|
|
|
|
if (i < N-1) {
|
|
|
|
dbgs() << '\n';
|
|
|
|
dbgs().indent(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dbgs() << "\n ProcIdx: "; dumpIdxVec(ProcIndices); dbgs() << '\n';
|
2013-03-27 05:36:39 +08:00
|
|
|
if (!Transitions.empty()) {
|
|
|
|
dbgs() << "\n Transitions for Proc ";
|
2017-09-13 18:31:10 +08:00
|
|
|
for (const CodeGenSchedTransition &Transition : Transitions) {
|
|
|
|
dumpIdxVec(Transition.ProcIndices);
|
2013-03-27 05:36:39 +08:00
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
}
|
2012-09-15 08:19:59 +08:00
|
|
|
|
|
|
|
void PredTransitions::dump() const {
|
|
|
|
dbgs() << "Expanded Variants:\n";
|
|
|
|
for (std::vector<PredTransition>::const_iterator
|
|
|
|
TI = TransVec.begin(), TE = TransVec.end(); TI != TE; ++TI) {
|
|
|
|
dbgs() << "{";
|
|
|
|
for (SmallVectorImpl<PredCheck>::const_iterator
|
|
|
|
PCI = TI->PredTerm.begin(), PCE = TI->PredTerm.end();
|
|
|
|
PCI != PCE; ++PCI) {
|
|
|
|
if (PCI != TI->PredTerm.begin())
|
|
|
|
dbgs() << ", ";
|
|
|
|
dbgs() << SchedModels.getSchedRW(PCI->RWIdx, PCI->IsRead).Name
|
|
|
|
<< ":" << PCI->Predicate->getName();
|
|
|
|
}
|
|
|
|
dbgs() << "},\n => {";
|
2016-12-01 01:48:10 +08:00
|
|
|
for (SmallVectorImpl<SmallVector<unsigned,4>>::const_iterator
|
2012-09-15 08:19:59 +08:00
|
|
|
WSI = TI->WriteSequences.begin(), WSE = TI->WriteSequences.end();
|
|
|
|
WSI != WSE; ++WSI) {
|
|
|
|
dbgs() << "(";
|
|
|
|
for (SmallVectorImpl<unsigned>::const_iterator
|
|
|
|
WI = WSI->begin(), WE = WSI->end(); WI != WE; ++WI) {
|
|
|
|
if (WI != WSI->begin())
|
|
|
|
dbgs() << ", ";
|
|
|
|
dbgs() << SchedModels.getSchedWrite(*WI).Name;
|
|
|
|
}
|
|
|
|
dbgs() << "),";
|
|
|
|
}
|
|
|
|
dbgs() << "}\n";
|
|
|
|
}
|
|
|
|
}
|
2012-09-15 08:19:57 +08:00
|
|
|
#endif // NDEBUG
|