Skip to content

Commit

Permalink
[RISCV][llvm-mca] Use LMUL Instruments to provide more accurate repor…
Browse files Browse the repository at this point in the history
…ts on RISCV

On x86 and AArch, SIMD instructions encode all of the scheduling information in the instruction
itself. For example, VADD.I16 q0, q1, q2 is a neon instruction that operates on 16-bit integer
elements stored in 128-bit Q registers, which leads to eight 16-bit lanes in parallel. This kind
of information impacts how the instruction takes to execute and what dependencies this may cause.

On RISCV however, the data that impacts scheduling is encoded in CSR registers such as vtype or
vl, in addition with the instruction itself. But MCA does not track or use the data in these
registers. This patch fixes this problem by introducing Instruments into MCA.

* Replace `CodeRegions` with `AnalysisRegions`
* Add `Instrument` and `InstrumentManager`
* Add `InstrumentRegions`
* Add RISCV Instrument and `InstrumentManager`
* Parse `Instruments` in driver
* Use instruments to override schedule class
* RISCV use lmul instrument to override schedule class
* Fix unit tests to pass empty instruments
* Add -ignore-im clopt to disable this change

A prior version of this patch was commited in. It was reverted in
5e82ee5. 2323a4e reverted
that change because the unit test files caused build errors. This commit adds the original changes
and the fixed test files.

Differential Revision: https://reviews.llvm.org/D137440
  • Loading branch information
michaelmaitland committed Nov 16, 2022
1 parent 4155be3 commit b88b830
Show file tree
Hide file tree
Showing 18 changed files with 970 additions and 130 deletions.
123 changes: 118 additions & 5 deletions llvm/docs/CommandGuide/llvm-mca.rst
Expand Up @@ -227,6 +227,12 @@ option specifies "``-``", then the output will also be sent to standard output.
detect any custom hazards or make any post processing modifications to
instructions.

.. option:: -disable-im

Force usage of the generic InstrumentManager rather than using the target
specific implementation. The generic class creates Instruments that provide
no extra information, and InstrumentManager never overrides the default
schedule class for a given instruction.

EXIT STATUS
-----------
Expand All @@ -238,9 +244,9 @@ USING MARKERS TO ANALYZE SPECIFIC CODE BLOCKS
---------------------------------------------
:program:`llvm-mca` allows for the optional usage of special code comments to
mark regions of the assembly code to be analyzed. A comment starting with
substring ``LLVM-MCA-BEGIN`` marks the beginning of a code region. A comment
starting with substring ``LLVM-MCA-END`` marks the end of a code region. For
example:
substring ``LLVM-MCA-BEGIN`` marks the beginning of an analysis region. A
comment starting with substring ``LLVM-MCA-END`` marks the end of a region.
For example:

.. code-block:: none
Expand All @@ -251,9 +257,9 @@ example:
If no user-defined region is specified, then :program:`llvm-mca` assumes a
default region which contains every instruction in the input file. Every region
is analyzed in isolation, and the final performance report is the union of all
the reports generated for every code region.
the reports generated for every analysis region.

Code regions can have names. For example:
Analysis regions can have names. For example:

.. code-block:: none
Expand Down Expand Up @@ -315,6 +321,91 @@ assembly is equivalent to the assembly generated in the absence of markers.
The `Clang options to emit optimization reports <https://clang.llvm.org/docs/UsersManual.html#options-to-emit-optimization-reports>`_
can also help in detecting missed optimizations.

INSTRUMENT REGIONS
------------------

An InstrumentRegion describes a region of assembly code guarded by
special LLVM-MCA comment directives.

.. code-block:: none
# LLVM-MCA-<INSTRUMENT_TYPE> <data>
... ## asm
where `INSTRUMENT_TYPE` is a type defined by the target and expects
to use `data`.

A comment starting with substring `LLVM-MCA-<INSTRUMENT_TYPE>`
brings data into scope for llvm-mca to use in its analysis for
all following instructions.

If a comment with the same `INSTRUMENT_TYPE` is found later in the
instruction list, then the original InstrumentRegion will be
automatically ended, and a new InstrumentRegion will begin.

If there are comments containing the different `INSTRUMENT_TYPE`,
then both data sets remain available. In contrast with an AnalysisRegion,
an InstrumentRegion does not need a comment to end the region.

Comments that are prefixed with `LLVM-MCA-` but do not correspond to
a valid `INSTRUMENT_TYPE` for the target cause an error, except for
`BEGIN` and `END`, since those correspond to AnalysisRegions. Comments
that do not start with `LLVM-MCA-` are ignored by :program `llvm-mca`.

An instruction (a MCInst) is added to an InstrumentRegion R only
if its location is in range [R.RangeStart, R.RangeEnd].

On RISCV targets, vector instructions have different behaviour depending
on the LMUL. Code can be instrumented with a comment that takes the
following form:

.. code-block:: none
# LLVM-MCA-RISCV-LMUL <M1|M2|M4|M8|MF2|MF4|MF8>
The RISCV InstrumentManager will override the schedule class for vector
instructions to use the scheduling behaviour of its pseudo-instruction
which is LMUL dependent. It makes sense to place RISCV instrument
comments directly after `vset{i}vl{i}` instructions, although
they can be placed anywhere in the program.

Example of program with no call to `vset{i}vl{i}`:

.. code-block:: none
# LLVM-MCA-RISCV-LMUL M2
vadd.vv v2, v2, v2
Example of program with call to `vset{i}vl{i}`:

.. code-block:: none
vsetvli zero, a0, e8, m1, tu, mu
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v2, v2, v2
Example of program with multiple calls to `vset{i}vl{i}`:

.. code-block:: none
vsetvli zero, a0, e8, m1, tu, mu
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v2, v2, v2
vsetvli zero, a0, e8, m8, tu, mu
# LLVM-MCA-RISCV-LMUL M8
vadd.vv v2, v2, v2
Example of program with call to `vsetvl`:

.. code-block:: none
vsetvl rd, rs1, rs2
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v12, v12, v12
vsetvl rd, rs1, rs2
# LLVM-MCA-RISCV-LMUL M4
vadd.vv v12, v12, v12
HOW LLVM-MCA WORKS
------------------

Expand Down Expand Up @@ -1024,6 +1115,28 @@ already have one, refer to an existing implementation to see how to set it
up. The classes are implemented within the target specific backend (for
example `/llvm/lib/Target/AMDGPU/MCA/`) so that they can access backend symbols.

Instrument Manager
""""""""""""""""""""""""""""""""""""
On certain architectures, scheduling information for certain instructions
do not contain all of the information required to identify the most precise
schedule class. For example, data that can have an impact on scheduling can
be stored in CSR registers.

One example of this is on RISCV, where values in registers such as `vtype`
and `vl` change the scheduling behaviour of vector instructions. Since MCA
does not keep track of the values in registers, instrument comments can
be used to specify these values.

InstrumentManager's main function is `getSchedClassID()` which has access
to the MCInst and all of the instruments that are active for that MCInst.
This function can use the instruments to override the schedule class of
the MCInst.

On RISCV, instrument comments containing LMUL information are used
by `getSchedClassID()` to map a vector instruction and the active
LMUL to the scheduling class of the pseudo-instruction that describes
that base instruction and the active LMUL.

Custom Views
""""""""""""""""""""""""""""""""""""
:program:`llvm-mca` comes with several Views such as the Timeline View and
Expand Down
38 changes: 38 additions & 0 deletions llvm/include/llvm/MC/TargetRegistry.h
Expand Up @@ -60,6 +60,7 @@ class TargetOptions;
namespace mca {
class CustomBehaviour;
class InstrPostProcess;
class InstrumentManager;
struct SourceMgr;
} // namespace mca

Expand Down Expand Up @@ -134,6 +135,9 @@ mca::CustomBehaviour *createCustomBehaviour(const MCSubtargetInfo &STI,
mca::InstrPostProcess *createInstrPostProcess(const MCSubtargetInfo &STI,
const MCInstrInfo &MCII);

mca::InstrumentManager *createInstrumentManager(const MCSubtargetInfo &STI,
const MCInstrInfo &MCII);

/// Target - Wrapper for Target specific information.
///
/// For registration purposes, this is a POD type so that targets can be
Expand Down Expand Up @@ -245,6 +249,10 @@ class Target {
mca::InstrPostProcess *(*)(const MCSubtargetInfo &STI,
const MCInstrInfo &MCII);

using InstrumentManagerCtorTy =
mca::InstrumentManager *(*)(const MCSubtargetInfo &STI,
const MCInstrInfo &MCII);

private:
/// Next - The next registered target in the linked list, maintained by the
/// TargetRegistry.
Expand Down Expand Up @@ -354,6 +362,10 @@ class Target {
/// InstrPostProcess, if registered (default = nullptr).
InstrPostProcessCtorTy InstrPostProcessCtorFn = nullptr;

/// InstrumentManagerCtorFn - Construction function for this target's
/// InstrumentManager, if registered (default = nullptr).
InstrumentManagerCtorTy InstrumentManagerCtorFn = nullptr;

public:
Target() = default;

Expand Down Expand Up @@ -706,6 +718,17 @@ class Target {
return nullptr;
}

/// createInstrumentManager - Create a target specific
/// InstrumentManager. This class is used by llvm-mca and requires
/// backend functionality.
mca::InstrumentManager *
createInstrumentManager(const MCSubtargetInfo &STI,
const MCInstrInfo &MCII) const {
if (InstrumentManagerCtorFn)
return InstrumentManagerCtorFn(STI, MCII);
return nullptr;
}

/// @}
};

Expand Down Expand Up @@ -1078,6 +1101,21 @@ struct TargetRegistry {
T.InstrPostProcessCtorFn = Fn;
}

/// RegisterInstrumentManager - Register an InstrumentManager
/// implementation for the given target.
///
/// Clients are responsible for ensuring that registration doesn't occur
/// while another thread is attempting to access the registry. Typically
/// this is done by initializing all targets at program startup.
///
/// @param T - The target being registered.
/// @param Fn - A function to construct an InstrumentManager for the
/// target.
static void RegisterInstrumentManager(Target &T,
Target::InstrumentManagerCtorTy Fn) {
T.InstrumentManagerCtorFn = Fn;
}

/// @}
};

Expand Down
56 changes: 56 additions & 0 deletions llvm/include/llvm/MCA/CustomBehaviour.h
Expand Up @@ -18,6 +18,7 @@
#ifndef LLVM_MCA_CUSTOMBEHAVIOUR_H
#define LLVM_MCA_CUSTOMBEHAVIOUR_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
Expand Down Expand Up @@ -114,6 +115,61 @@ class CustomBehaviour {
getEndViews(llvm::MCInstPrinter &IP, llvm::ArrayRef<llvm::MCInst> Insts);
};

class Instrument {
/// The description of Instrument kind
const StringRef Desc;

/// The instrumentation data
const StringRef Data;

public:
Instrument(StringRef Desc, StringRef Data) : Desc(Desc), Data(Data) {}

Instrument() = default;

virtual ~Instrument() = default;

StringRef getDesc() const { return Desc; }
StringRef getData() const { return Data; }
};

using SharedInstrument = std::shared_ptr<Instrument>;

/// This class allows targets to optionally customize the logic that resolves
/// scheduling class IDs. Targets can use information encoded in Instrument
/// objects to make more informed scheduling decisions.
class InstrumentManager {
protected:
const MCSubtargetInfo &STI;
const MCInstrInfo &MCII;

public:
InstrumentManager(const MCSubtargetInfo &STI, const MCInstrInfo &MCII)
: STI(STI), MCII(MCII) {}

virtual ~InstrumentManager() = default;

/// Returns true if llvm-mca should ignore instruments.
virtual bool shouldIgnoreInstruments() const { return true; }

// Returns true if this supports processing Instrument with
// Instrument.Desc equal to Type
virtual bool supportsInstrumentType(StringRef Type) const { return false; }

/// Allocate an Instrument, and return a shared pointer to it.
virtual SharedInstrument createInstrument(StringRef Desc, StringRef Data);

/// Given an MCInst and a vector of Instrument, a target can
/// return a SchedClassID. This can be used by a subtarget to return a
/// PseudoInstruction SchedClassID instead of the one that belongs to the
/// BaseInstruction This can be useful when a BaseInstruction does not convey
/// the correct scheduling information without additional data. By default,
/// it returns the SchedClassID that belongs to MCI.
virtual unsigned
getSchedClassID(const MCInstrInfo &MCII, const MCInst &MCI,
const SmallVector<SharedInstrument> &IVec) const;
};

} // namespace mca
} // namespace llvm

Expand Down
28 changes: 22 additions & 6 deletions llvm/include/llvm/MCA/InstrBuilder.h
Expand Up @@ -19,6 +19,7 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MCA/CustomBehaviour.h"
#include "llvm/MCA/Instruction.h"
#include "llvm/MCA/Support.h"
#include "llvm/Support/Error.h"
Expand Down Expand Up @@ -62,10 +63,18 @@ class InstrBuilder {
const MCInstrInfo &MCII;
const MCRegisterInfo &MRI;
const MCInstrAnalysis *MCIA;
const InstrumentManager &IM;
SmallVector<uint64_t, 8> ProcResourceMasks;

DenseMap<unsigned short, std::unique_ptr<const InstrDesc>> Descriptors;
DenseMap<const MCInst *, std::unique_ptr<const InstrDesc>> VariantDescriptors;
// Key is the MCI.Opcode and SchedClassID the describe the value InstrDesc
DenseMap<std::pair<unsigned short, unsigned>,
std::unique_ptr<const InstrDesc>>
Descriptors;

// Key is the MCIInst and SchedClassID the describe the value InstrDesc
DenseMap<std::pair<const MCInst *, unsigned>,
std::unique_ptr<const InstrDesc>>
VariantDescriptors;

bool FirstCallInst;
bool FirstReturnInst;
Expand All @@ -74,8 +83,12 @@ class InstrBuilder {
llvm::function_ref<Instruction *(const InstrDesc &)>;
InstRecycleCallback InstRecycleCB;

Expected<const InstrDesc &> createInstrDescImpl(const MCInst &MCI);
Expected<const InstrDesc &> getOrCreateInstrDesc(const MCInst &MCI);
Expected<const InstrDesc &>
createInstrDescImpl(const MCInst &MCI,
const SmallVector<SharedInstrument> &IVec);
Expected<const InstrDesc &>
getOrCreateInstrDesc(const MCInst &MCI,
const SmallVector<SharedInstrument> &IVec);

InstrBuilder(const InstrBuilder &) = delete;
InstrBuilder &operator=(const InstrBuilder &) = delete;
Expand All @@ -86,7 +99,8 @@ class InstrBuilder {

public:
InstrBuilder(const MCSubtargetInfo &STI, const MCInstrInfo &MCII,
const MCRegisterInfo &RI, const MCInstrAnalysis *IA);
const MCRegisterInfo &RI, const MCInstrAnalysis *IA,
const InstrumentManager &IM);

void clear() {
Descriptors.clear();
Expand All @@ -99,7 +113,9 @@ class InstrBuilder {
/// or null if there isn't any.
void setInstRecycleCallback(InstRecycleCallback CB) { InstRecycleCB = CB; }

Expected<std::unique_ptr<Instruction>> createInstruction(const MCInst &MCI);
Expected<std::unique_ptr<Instruction>>
createInstruction(const MCInst &MCI,
const SmallVector<SharedInstrument> &IVec);
};
} // namespace mca
} // namespace llvm
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/MCA/CustomBehaviour.cpp
Expand Up @@ -42,5 +42,16 @@ CustomBehaviour::getEndViews(llvm::MCInstPrinter &IP,
return std::vector<std::unique_ptr<View>>();
}

SharedInstrument InstrumentManager::createInstrument(llvm::StringRef Desc,
llvm::StringRef Data) {
return std::make_shared<Instrument>(Desc, Data);
}

unsigned InstrumentManager::getSchedClassID(
const MCInstrInfo &MCII, const MCInst &MCI,
const llvm::SmallVector<SharedInstrument> &IVec) const {
return MCII.get(MCI.getOpcode()).getSchedClass();
}

} // namespace mca
} // namespace llvm

0 comments on commit b88b830

Please sign in to comment.