LLVM 23.0.0git
WaitingOnGraphOpReplay.h
Go to the documentation of this file.
1//===------ WaitingOnGraphOpReplay.h - Record/replay APIs -------*- C++ -*-===//
2//
3// 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
6//
7//===----------------------------------------------------------------------===//
8//
9// Utilities for capturing and replaying WaitingOnGraph operations.
10//
11//===----------------------------------------------------------------------===//
12#ifndef LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPHOPREPLAY_H
13#define LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPHOPREPLAY_H
14
17#include "llvm/Support/Error.h"
18
19#include <mutex>
20#include <optional>
21#include <variant>
22
23namespace llvm::orc::detail {
24
25/// Records WaitingOnGraph operations to a line-oriented text format on a
26/// raw_ostream. The format is a sequence of operations terminated by "end":
27///
28/// simplify-and-emit <num-supernodes>
29/// sn <index>
30/// defs <num-containers>
31/// container <id> <num-elements>
32/// elements <elem-id>...
33/// deps <num-containers>
34/// ...
35/// fail
36/// failed <num-containers>
37/// container <id> <num-elements>
38/// elements <elem-id>...
39/// end
40///
41/// Container and element ids are integers assigned sequentially by the
42/// recorder. Leading/trailing whitespace on each line is ignored.
43template <typename ContainerIdT, typename ElementIdT>
45 : public detail::WaitingOnGraph<ContainerIdT, ElementIdT>::OpRecorder {
47 using SuperNode = typename WOG::SuperNode;
48 using ContainerId = typename WOG::ContainerId;
49 using ElementId = typename WOG::ElementId;
50 using ContainerElementsMap = typename WOG::ContainerElementsMap;
51 using ElementSet = typename WOG::ElementSet;
52
53public:
55
56 void
57 recordSimplify(const std::vector<std::unique_ptr<SuperNode>> &SNs) override {
58 std::scoped_lock<std::mutex> Lock(M);
59 recordSuperNodes("simplify-and-emit", SNs);
60 OS.flush();
61 }
62
63 void recordFail(const ContainerElementsMap &Failed) override {
64 std::scoped_lock<std::mutex> Lock(M);
65 OS << "fail\n";
66 recordContainerElementsMap(" ", "failed", Failed);
67 OS.flush();
68 }
69
70 void recordEnd() override {
71 std::scoped_lock<std::mutex> Lock(M);
72 OS << "end\n";
73 OS.flush();
74 }
75
76 // Should render the container id as a string.
77 virtual void printContainer(const ContainerId &C) {
78 auto I =
79 ContainerIdMap.insert(std::make_pair(C, ContainerIdMap.size())).first;
80 OS << I->second.Id;
81 }
82
83 // Should render the elements of C as a space-separated list (with a space
84 // before the first element).
85 virtual void printElementsIn(const ContainerId &C,
86 const ElementSet &Elements) {
87 assert(ContainerIdMap.count(C));
88 auto &ElementIdMap = ContainerIdMap[C].ElementIdMap;
89 for (auto &E : Elements) {
90 auto I =
91 ElementIdMap.insert(std::make_pair(E, ElementIdMap.size())).first;
92 OS << " " << I->second;
93 }
94 }
95
96private:
97 struct ContainerIdInfo {
98 ContainerIdInfo() = default;
99 ContainerIdInfo(size_t Id) : Id(Id) {}
100 size_t Id = 0;
101 DenseMap<ElementId, size_t> ElementIdMap;
102 };
104
105 void recordSuperNodes(StringRef OpName,
106 const std::vector<std::unique_ptr<SuperNode>> &SNs) {
107 OS << OpName << " " << SNs.size() << "\n";
108 for (size_t I = 0; I != SNs.size(); ++I) {
109 OS << " sn " << I << "\n";
110 recordContainerElementsMap(" ", "defs", SNs[I]->defs());
111 recordContainerElementsMap(" ", "deps", SNs[I]->deps());
112 }
113 }
114
115 void recordContainerElementsMap(StringRef Indent, StringRef MapName,
116 const ContainerElementsMap &M) {
117 OS << Indent << MapName << " " << M.size() << "\n";
118 for (auto &[Container, Elements] : M) {
119 OS << Indent << " container ";
120 printContainer(Container);
121 OS << " " << Elements.size() << "\n";
122 OS << Indent << " elements ";
123 printElementsIn(Container, Elements);
124 OS << "\n";
125 }
126 }
127
128 std::mutex M;
129 raw_ostream &OS;
130};
131
132template <typename ContainerIdT, typename ElementIdT>
134public:
136 using SuperNode = typename Graph::SuperNode;
138 using ElementId = typename Graph::ElementId;
141
142 /// A simplify-and-emit operation parsed from the input.
144 SimplifyAndEmitOp() = default;
147 SimplifyAndEmitOp(std::vector<std::unique_ptr<SuperNode>> SNs)
148 : SNs(std::move(SNs)) {}
149 std::vector<std::unique_ptr<SuperNode>> SNs;
150 };
151
152 /// A fail operation parsed from the input.
160
161 /// A parsed operation -- either a simplify-and-emit or a fail.
162 using Op = std::variant<SimplifyAndEmitOp, FailOp>;
163
164 /// Replay ops on a given graph.
165 struct Replayer {
167
168 void replay(Op O) {
169 if (auto *SimplifyAndEmit = std::get_if<SimplifyAndEmitOp>(&O))
170 replaySimplifyAndEmit(std::move(SimplifyAndEmit->SNs));
171 else if (auto *Fail = std::get_if<FailOp>(&O))
172 replayFail(std::move(Fail->Failed));
173 }
174
175 void replaySimplifyAndEmit(std::vector<std::unique_ptr<SuperNode>> SNs) {
176 auto SR = Graph::simplify(std::move(SNs));
177 auto ER = G.emit(std::move(SR),
179 {
180 auto I = Failed.find(C);
181 if (I != Failed.end() && I->second.count(E))
182 return ExternalState::Failed;
183 }
184 {
185 auto I = Ready.find(C);
186 if (I != Ready.end() && I->second.count(E))
187 return ExternalState::Ready;
188 }
189 return ExternalState::None;
190 });
191 for (auto &SN : ER.Ready)
192 Ready.merge(SN->defs());
193 for (auto &SN : ER.Failed)
194 Failed.merge(SN->defs());
195 }
196
198 Failed.merge(NewlyFailed);
199
200 auto FailedSNs = G.fail(NewlyFailed);
201 for (auto &SN : FailedSNs)
202 Failed.merge(SN->defs());
203 }
204
208 };
209
210 /// Parser for input buffer.
211 class OpParser {
212 public:
213 using ParseResult = std::pair<std::optional<Op>, StringRef>;
214 virtual ~OpParser() = default;
216
217 protected:
219 parsedSimplifyAndEmit(std::vector<std::unique_ptr<SuperNode>> SNs,
221 return ParseResult(SimplifyAndEmitOp{std::move(SNs)}, Input);
222 }
223
226 return ParseResult(FailOp{std::move(NewlyFailed)}, Input);
227 }
228
230 return ParseResult(std::nullopt, Input);
231 }
232 };
233
234 /// Fallible iterator for iterating over WaitingOnGraph ops.
236 public:
237 /// Default constructed fallible iterator. Serves as end value.
238 OpIterator() = default;
239
240 /// Construct a fallible iterator reading from the given input buffer using
241 /// the given parser.
242 OpIterator(std::shared_ptr<OpParser> P, StringRef Input)
243 : P(std::move(P)), Input(Input), PrevInput(Input) {}
244
246 : P(Other.P), Input(Other.PrevInput), PrevInput(Other.PrevInput) {
247 // We can't just copy Op, we need to re-parse.
248 if (this->P)
249 cantFail(inc());
250 }
251
253 P = Other.P;
254 Input = PrevInput = Other.PrevInput;
255 if (this->P)
256 cantFail(inc());
257 return *this;
258 }
259
260 OpIterator(OpIterator &&) = default;
262
263 /// Move to next record.
265 PrevInput = Input;
266 auto PR = P->parseNext(Input);
267 if (!PR)
268 return PR.takeError();
269 std::tie(CurOp, Input) = std::move(*PR);
270 if (!CurOp) {
271 P = nullptr;
272 Input = "";
273 }
274 return Error::success();
275 }
276
277 // Dereference. Note: Moves op type.
279 assert(CurOp && "Dereferencing end/invalid iterator");
280 return *CurOp;
281 }
282
283 // Dereference. Note: Moves op type.
284 const Op &operator*() const {
285 assert(CurOp && "Dereferencing end/invalid iterator");
286 return *CurOp;
287 }
288
289 /// Compare iterators. End iterators compare equal.
290 friend bool operator==(const OpIterator &LHS, const OpIterator &RHS) {
291 return LHS.P == RHS.P && LHS.Input == RHS.Input;
292 }
293
294 friend bool operator!=(const OpIterator &LHS, const OpIterator &RHS) {
295 return !(LHS == RHS);
296 }
297
298 private:
299 std::shared_ptr<OpParser> P;
300 StringRef Input, PrevInput;
301 std::optional<Op> CurOp;
302 };
303};
304
305/// Returns a fallible iterator range over the operations in the given buffer.
306/// The buffer should contain text in the format produced by
307/// WaitingOnGraphOpStreamRecorder. Parsing errors are reported through Err.
308template <typename ContainerIdT, typename ElementIdT>
310 typename WaitingOnGraphOpReplay<ContainerIdT, ElementIdT>::OpIterator>>
312
314
315 class Parser : public Replay::OpParser {
316 public:
317 using ParseResult = typename Replay::OpParser::ParseResult;
318 using SuperNode = typename Replay::SuperNode;
319 using ContainerElementsMap = typename Replay::ContainerElementsMap;
320
321 /// Parse the next operation from Input into CurrentOp.
322 /// Sets IsEnd on "end" keyword. Returns Error on parse failure.
323 Expected<ParseResult> parseNext(StringRef Input) override {
324 auto Line = getNextLine(Input);
325
326 if (Line.empty())
328 "unexpected end of input (missing 'end')",
330
331 if (Line.consume_front("simplify-and-emit ")) {
332 size_t NumSNs;
333 if (Line.trim().consumeInteger(10, NumSNs))
335 "expected supernode count after 'simplify-and-emit'",
337 auto SNs = parseSuperNodes(Input, NumSNs);
338 if (!SNs)
339 return SNs.takeError();
340 return this->parsedSimplifyAndEmit(std::move(*SNs), Input);
341 } else if (Line.trim() == "fail") {
342 auto FailElems = parseContainerElementsMap("failed", Input);
343 if (!FailElems)
344 return FailElems.takeError();
345 return this->parsedFail(std::move(*FailElems), Input);
346 } else if (Line.trim() == "end")
347 return this->parsedEnd(Input);
348 else
349 return make_error<StringError>("unexpected line: '" + Line + "'",
351 }
352
353 private:
354 static StringRef getNextLine(StringRef &Input) {
355 StringRef Line;
356 // Parse skipping blank lines.
357 do {
358 std::tie(Line, Input) = Input.split('\n');
359 Line = Line.trim();
360 } while (Line.empty() && !Input.empty());
361 return Line;
362 }
363
365 parseSuperNodes(StringRef &Input, size_t NumSNs) {
366 std::vector<std::unique_ptr<SuperNode>> SNs;
367 for (size_t I = 0; I != NumSNs; ++I) {
368 // Parse "sn <index>"
369 StringRef Line = getNextLine(Input);
370 if (!Line.consume_front("sn "))
371 return make_error<StringError>("expected 'sn " + Twine(I) + "'",
373
374 auto Defs = parseContainerElementsMap("defs", Input);
375 if (!Defs)
376 return Defs.takeError();
377 auto Deps = parseContainerElementsMap("deps", Input);
378 if (!Deps)
379 return Deps.takeError();
380
381 SNs.push_back(
382 std::make_unique<SuperNode>(std::move(*Defs), std::move(*Deps)));
383 }
384 return std::move(SNs);
385 }
386
388 parseContainerElementsMap(StringRef ArgName, StringRef &Input) {
389 // Parse "defs <count>"
390 auto Line = getNextLine(Input);
391 if (!Line.consume_front(ArgName))
392 return make_error<StringError>("expected '" + ArgName + " <count>'",
394 size_t NumContainers;
395 if (Line.trim().consumeInteger(10, NumContainers))
396 return make_error<StringError>("expected " + ArgName + " count",
398
399 ContainerElementsMap M;
400 for (size_t I = 0; I != NumContainers; ++I) {
401 Line = getNextLine(Input);
402 if (!Line.consume_front("container "))
403 return make_error<StringError>("expected 'container <id> <count>'",
405
406 size_t Container;
407 Line = Line.trim();
408 if (Line.consumeInteger(10, Container))
409 return make_error<StringError>("expected container id",
411
412 if (M.count(Container))
414 "expected container id to be unique within " + ArgName,
416
417 size_t NumElements;
418 if (Line.trim().consumeInteger(10, NumElements))
419 return make_error<StringError>("expected elements count",
421 if (NumElements == 0)
422 return make_error<StringError>("number of elements for container " +
423 Twine(Container) + " must be > 0",
425
426 Line = getNextLine(Input);
427 if (!Line.consume_front("elements "))
428 return make_error<StringError>("expected 'elements ...'",
430
431 auto &Elements = M[Container];
432 for (size_t J = 0; J != NumElements; ++J) {
433 size_t Elem;
434 Line = Line.trim();
435 if (Line.consumeInteger(10, Elem))
436 return make_error<StringError>("expected element id",
438 if (Elements.count(Elem))
440 "expected element id to be unique within container " +
441 Twine(Container),
443 Elements.insert(Elem);
444 }
445 }
446
447 return std::move(M);
448 }
449 };
450
452 typename Replay::OpIterator Begin(std::make_shared<Parser>(), InputBuffer);
453 typename Replay::OpIterator End;
454 if ((Err = Begin.inc())) // Parse first operation.
455 Begin = End;
456 return make_fallible_range(std::move(Begin), std::move(End), Err);
457}
458
459} // namespace llvm::orc::detail
460
461#endif // LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPHOPREPLAY_H
#define Fail
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define _
#define I(x, y, z)
Definition MD5.cpp:57
#define P(N)
Value * RHS
Value * LHS
The Input class is used to parse a yaml document into in-memory structs and vectors.
Helper for Errors used as out-parameters.
Definition Error.h:1144
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
This class represents success/failure for parsing-like operations that find it important to chain tog...
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
A wrapper class for fallible iterators.
A range adaptor for a pair of iterators.
OpIterator & operator=(OpIterator &&)=default
friend bool operator!=(const OpIterator &LHS, const OpIterator &RHS)
friend bool operator==(const OpIterator &LHS, const OpIterator &RHS)
Compare iterators. End iterators compare equal.
OpIterator(std::shared_ptr< OpParser > P, StringRef Input)
Construct a fallible iterator reading from the given input buffer using the given parser.
OpIterator()=default
Default constructed fallible iterator. Serves as end value.
Expected< ParseResult > parsedEnd(StringRef Input)
Expected< ParseResult > parsedSimplifyAndEmit(std::vector< std::unique_ptr< SuperNode > > SNs, StringRef Input)
std::pair< std::optional< Op >, StringRef > ParseResult
Expected< ParseResult > parsedFail(ContainerElementsMap NewlyFailed, StringRef Input)
virtual Expected< ParseResult > parseNext(StringRef Input)=0
typename Graph::ContainerElementsMap ContainerElementsMap
std::variant< SimplifyAndEmitOp, FailOp > Op
A parsed operation – either a simplify-and-emit or a fail.
WaitingOnGraph< ContainerIdT, ElementIdT > Graph
void recordFail(const ContainerElementsMap &Failed) override
void recordSimplify(const std::vector< std::unique_ptr< SuperNode > > &SNs) override
virtual void printElementsIn(const ContainerId &C, const ElementSet &Elements)
WaitingOnGraph class template.
static SimplifyResult simplify(std::vector< std::unique_ptr< SuperNode > > SNs, OpRecorder *Rec=nullptr)
Preprocess a list of SuperNodes to remove all intra-SN dependencies.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
iterator_range< fallible_iterator< typename WaitingOnGraphOpReplay< ContainerIdT, ElementIdT >::OpIterator > > readWaitingOnGraphOpsFromBuffer(StringRef InputBuffer, Error &Err)
Returns a fallible iterator range over the operations in the given buffer.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
@ Other
Any other memory.
Definition ModRef.h:68
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
iterator_range< fallible_iterator< Underlying > > make_fallible_range(Underlying I, Underlying E, Error &Err)
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1917
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870
void replaySimplifyAndEmit(std::vector< std::unique_ptr< SuperNode > > SNs)
A simplify-and-emit operation parsed from the input.
SimplifyAndEmitOp & operator=(SimplifyAndEmitOp &&)=default
SimplifyAndEmitOp(std::vector< std::unique_ptr< SuperNode > > SNs)