blob: 5f67b396ad959c42ae12275a2f8ef0209042bd9f [file] [log] [blame]
Clement Courbetd939f6d2018-09-13 07:40:53 +00001//===-- SnippetGenerator.h --------------------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Clement Courbetd939f6d2018-09-13 07:40:53 +00006//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// Defines the abstract SnippetGenerator class for generating code that allows
11/// measuring a certain property of instructions (e.g. latency).
12///
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
16#define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
17
18#include "Assembler.h"
19#include "BenchmarkCode.h"
Guillaume Chatelet7f8d3102018-09-26 11:57:24 +000020#include "CodeTemplate.h"
Clement Courbetd939f6d2018-09-13 07:40:53 +000021#include "LlvmState.h"
22#include "MCInstrDescView.h"
23#include "RegisterAliasing.h"
24#include "llvm/MC/MCInst.h"
25#include "llvm/Support/Error.h"
26#include <cstdlib>
27#include <memory>
28#include <vector>
29
Fangrui Song32401af2018-10-22 17:10:47 +000030namespace llvm {
Clement Courbetd939f6d2018-09-13 07:40:53 +000031namespace exegesis {
32
Guillaume Chateletfcbb6f32018-10-17 11:37:28 +000033std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT);
Guillaume Chatelet296a8622018-10-15 09:09:19 +000034
35// Generates code templates that has a self-dependency.
Clement Courbet50cdd562019-10-09 11:58:42 +000036Expected<std::vector<CodeTemplate>>
Roman Lebedev6030fe02020-02-12 20:54:39 +030037generateSelfAliasingCodeTemplates(InstructionTemplate Variant);
Guillaume Chatelet296a8622018-10-15 09:09:19 +000038
39// Generates code templates without assignment constraints.
Clement Courbet50cdd562019-10-09 11:58:42 +000040Expected<std::vector<CodeTemplate>>
Roman Lebedev6030fe02020-02-12 20:54:39 +030041generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant,
42 StringRef Msg);
Guillaume Chatelet296a8622018-10-15 09:09:19 +000043
Clement Courbetd939f6d2018-09-13 07:40:53 +000044// A class representing failures that happened during Benchmark, they are used
45// to report informations to the user.
Clement Courbet50cdd562019-10-09 11:58:42 +000046class SnippetGeneratorFailure : public StringError {
Clement Courbetd939f6d2018-09-13 07:40:53 +000047public:
Clement Courbet50cdd562019-10-09 11:58:42 +000048 SnippetGeneratorFailure(const Twine &S);
Clement Courbetd939f6d2018-09-13 07:40:53 +000049};
50
51// Common code for all benchmark modes.
52class SnippetGenerator {
53public:
Clement Courbet2cd0f282019-10-08 14:30:24 +000054 struct Options {
55 unsigned MaxConfigsPerOpcode = 1;
56 };
57
58 explicit SnippetGenerator(const LLVMState &State, const Options &Opts);
Clement Courbetd939f6d2018-09-13 07:40:53 +000059
60 virtual ~SnippetGenerator();
61
62 // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
Roman Lebedev6030fe02020-02-12 20:54:39 +030063 Error generateConfigurations(const InstructionTemplate &Variant,
64 std::vector<BenchmarkCode> &Benchmarks,
65 const BitVector &ExtraForbiddenRegs) const;
Clement Courbetd939f6d2018-09-13 07:40:53 +000066
67 // Given a snippet, computes which registers the setup code needs to define.
Guillaume Chateletc96a97b2018-09-20 12:22:18 +000068 std::vector<RegisterValue> computeRegisterInitialValues(
Guillaume Chatelet70ac0192018-09-27 09:23:04 +000069 const std::vector<InstructionTemplate> &Snippet) const;
Clement Courbetd939f6d2018-09-13 07:40:53 +000070
71protected:
72 const LLVMState &State;
Clement Courbet2cd0f282019-10-08 14:30:24 +000073 const Options Opts;
Clement Courbetd939f6d2018-09-13 07:40:53 +000074
Clement Courbetd939f6d2018-09-13 07:40:53 +000075private:
76 // API to be implemented by subclasses.
Clement Courbet50cdd562019-10-09 11:58:42 +000077 virtual Expected<std::vector<CodeTemplate>>
Roman Lebedev6030fe02020-02-12 20:54:39 +030078 generateCodeTemplates(InstructionTemplate Variant,
Clement Courbet8ef97e12019-09-27 08:04:10 +000079 const BitVector &ForbiddenRegisters) const = 0;
Clement Courbetd939f6d2018-09-13 07:40:53 +000080};
81
Guillaume Chatelet415b2fb2018-10-01 12:19:10 +000082// A global Random Number Generator to randomize configurations.
83// FIXME: Move random number generation into an object and make it seedable for
84// unit tests.
85std::mt19937 &randomGenerator();
86
Roman Lebedeva8223582019-04-08 10:11:00 +000087// Picks a random unsigned integer from 0 to Max (inclusive).
88size_t randomIndex(size_t Max);
89
Guillaume Chatelet415b2fb2018-10-01 12:19:10 +000090// Picks a random bit among the bits set in Vector and returns its index.
91// Precondition: Vector must have at least one bit set.
Clement Courbet50cdd562019-10-09 11:58:42 +000092size_t randomBit(const BitVector &Vector);
Guillaume Chatelet415b2fb2018-10-01 12:19:10 +000093
94// Picks a random configuration, then selects a random def and a random use from
95// it and finally set the selected values in the provided InstructionInstances.
96void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
97 InstructionTemplate &DefIB, InstructionTemplate &UseIB);
98
99// Assigns a Random Value to all Variables in IT that are still Invalid.
100// Do not use any of the registers in `ForbiddenRegs`.
Clement Courbet04fd2042020-01-22 15:49:10 +0100101Error randomizeUnsetVariables(const LLVMState &State,
102 const BitVector &ForbiddenRegs,
103 InstructionTemplate &IT);
Guillaume Chatelet415b2fb2018-10-01 12:19:10 +0000104
Roman Lebedev6030fe02020-02-12 20:54:39 +0300105// Combination generator.
106//
107// Example: given input {{0, 1}, {2}, {3, 4}} it will produce the following
108// combinations: {0, 2, 3}, {0, 2, 4}, {1, 2, 3}, {1, 2, 4}.
109//
110// It is important to think of input as vector-of-vectors, where the
111// outer vector is the variable space, and inner vector is choice space.
112// The number of choices for each variable can be different.
113//
114// As for implementation, it is useful to think of this as a weird number,
115// where each digit (==variable) may have different base (==number of choices).
116// Thus modelling of 'produce next combination' is exactly analogous to the
117// incrementing of an number - increment lowest digit (pick next choice for the
118// variable), and if it wrapped to the beginning then increment next digit.
119template <typename choice_type, typename choices_storage_type,
120 int variable_smallsize>
121class CombinationGenerator {
122 template <typename T> struct WrappingIterator {
123 using value_type = T;
124
125 const ArrayRef<value_type> Range;
126 typename decltype(Range)::const_iterator Position;
127
128 // Rewind the tape, placing the position to again point at the beginning.
129 void rewind() { Position = Range.begin(); }
130
131 // Advance position forward, possibly wrapping to the beginning.
132 // Returns whether the wrap happened.
133 bool operator++() {
134 ++Position;
135 bool Wrapped = Position == Range.end();
136 if (Wrapped)
137 rewind();
138 return Wrapped;
139 }
140
141 // Get the value at which we are currently pointing.
142 operator const value_type &() const { return *Position; }
143
144 WrappingIterator(ArrayRef<value_type> Range_) : Range(Range_) {
145 assert(!Range.empty() && "The range must not be empty.");
146 rewind();
147 }
Roman Lebedev6030fe02020-02-12 20:54:39 +0300148 };
149
150 const ArrayRef<choices_storage_type> VariablesChoices;
Roman Lebedev6030fe02020-02-12 20:54:39 +0300151
Roman Lebedev687bbf82020-02-12 23:30:22 +0300152 void performGeneration(
153 const function_ref<bool(ArrayRef<choice_type>)> Callback) const {
Roman Lebedev6030fe02020-02-12 20:54:39 +0300154 SmallVector<WrappingIterator<choice_type>, variable_smallsize>
155 VariablesState;
156
Roman Lebedevbaf98372020-02-15 00:47:13 +0300157 // 'increment' of the the whole VariablesState is defined identically to the
158 // increment of a number: starting from the least significant element,
159 // increment it, and if it wrapped, then propagate that carry by also
160 // incrementing next (more significant) element.
161 auto IncrementState =
162 [](MutableArrayRef<WrappingIterator<choice_type>> VariablesState)
163 -> bool {
164 for (WrappingIterator<choice_type> &Variable :
165 llvm::reverse(VariablesState)) {
166 bool Wrapped = ++Variable;
167 if (!Wrapped)
168 return false; // There you go, next combination is ready.
169 // We have carry - increment more significant variable next..
170 }
171 return true; // MSB variable wrapped, no more unique combinations.
172 };
173
Roman Lebedev6030fe02020-02-12 20:54:39 +0300174 // Initialize the per-variable state to refer to the possible choices for
175 // that variable.
176 VariablesState.reserve(VariablesChoices.size());
Craig Topper1fe6e6f2020-02-14 13:41:50 -0800177 for (ArrayRef<choice_type> VC : VariablesChoices)
178 VariablesState.emplace_back(VC);
Roman Lebedev6030fe02020-02-12 20:54:39 +0300179
180 // Temporary buffer to store each combination before performing Callback.
181 SmallVector<choice_type, variable_smallsize> CurrentCombination;
182 CurrentCombination.resize(VariablesState.size());
183
184 while (true) {
185 // Gather the currently-selected variable choices into a vector.
186 for (auto I : llvm::zip(VariablesState, CurrentCombination))
187 std::get<1>(I) = std::get<0>(I);
188 // And pass the new combination into callback, as intended.
189 if (/*Abort=*/Callback(CurrentCombination))
190 return;
Roman Lebedevbaf98372020-02-15 00:47:13 +0300191 // And tick the state to next combination, which will be unique.
192 if (IncrementState(VariablesState))
193 return; // All combinations produced.
Roman Lebedev6030fe02020-02-12 20:54:39 +0300194 }
195 };
196
197public:
Roman Lebedev687bbf82020-02-12 23:30:22 +0300198 CombinationGenerator(ArrayRef<choices_storage_type> VariablesChoices_)
199 : VariablesChoices(VariablesChoices_) {
Roman Lebedev6030fe02020-02-12 20:54:39 +0300200#ifndef NDEBUG
201 assert(!VariablesChoices.empty() && "There should be some variables.");
202 llvm::for_each(VariablesChoices, [](ArrayRef<choice_type> VariableChoices) {
203 assert(!VariableChoices.empty() &&
204 "There must always be some choice, at least a placeholder one.");
205 });
206#endif
207 }
208
209 // How many combinations can we produce, max?
210 // This is at most how many times the callback will be called.
211 size_t numCombinations() const {
212 size_t NumVariants = 1;
213 for (ArrayRef<choice_type> VariableChoices : VariablesChoices)
214 NumVariants *= VariableChoices.size();
215 assert(NumVariants >= 1 &&
216 "We should always end up producing at least one combination");
217 return NumVariants;
218 }
219
220 // Actually perform exhaustive combination generation.
221 // Each result will be passed into the callback.
Roman Lebedev687bbf82020-02-12 23:30:22 +0300222 void generate(const function_ref<bool(ArrayRef<choice_type>)> Callback) {
223 performGeneration(Callback);
224 }
Roman Lebedev6030fe02020-02-12 20:54:39 +0300225};
226
Clement Courbetd939f6d2018-09-13 07:40:53 +0000227} // namespace exegesis
Fangrui Song32401af2018-10-22 17:10:47 +0000228} // namespace llvm
Clement Courbetd939f6d2018-09-13 07:40:53 +0000229
230#endif // LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H