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 for (auto &[Container, Elems] : SN->defs())
193 Ready[Container].insert(Elems.begin(), Elems.end());
194 for (auto &SN : ER.Failed)
195 for (auto &[Container, Elems] : SN->defs())
196 Failed[Container].insert(Elems.begin(), Elems.end());
197 }
198
200 for (auto &[Container, Elems] : NewlyFailed)
201 Failed[Container].insert(Elems.begin(), Elems.end());
202
203 auto FailedSNs = G.fail(NewlyFailed);
204 for (auto &SN : FailedSNs)
205 for (auto &[Container, Elems] : SN->defs())
206 Failed[Container].insert(Elems.begin(), Elems.end());
207 }
208
212 };
213
214 /// Parser for input buffer.
215 class OpParser {
216 public:
217 using ParseResult = std::pair<std::optional<Op>, StringRef>;
218 virtual ~OpParser() = default;
220
221 protected:
223 parsedSimplifyAndEmit(std::vector<std::unique_ptr<SuperNode>> SNs,
225 return ParseResult(SimplifyAndEmitOp{std::move(SNs)}, Input);
226 }
227
230 return ParseResult(FailOp{std::move(NewlyFailed)}, Input);
231 }
232
234 return ParseResult(std::nullopt, Input);
235 }
236 };
237
238 /// Fallible iterator for iterating over WaitingOnGraph ops.
240 public:
241 /// Default constructed fallible iterator. Serves as end value.
242 OpIterator() = default;
243
244 /// Construct a fallible iterator reading from the given input buffer using
245 /// the given parser.
246 OpIterator(std::shared_ptr<OpParser> P, StringRef Input)
247 : P(std::move(P)), Input(Input), PrevInput(Input) {}
248
250 : P(Other.P), Input(Other.PrevInput), PrevInput(Other.PrevInput) {
251 // We can't just copy Op, we need to re-parse.
252 if (this->P)
253 cantFail(inc());
254 }
255
257 P = Other.P;
258 Input = PrevInput = Other.PrevInput;
259 if (this->P)
260 cantFail(inc());
261 return *this;
262 }
263
264 OpIterator(OpIterator &&) = default;
266
267 /// Move to next record.
269 PrevInput = Input;
270 auto PR = P->parseNext(Input);
271 if (!PR)
272 return PR.takeError();
273 std::tie(CurOp, Input) = std::move(*PR);
274 if (!CurOp) {
275 P = nullptr;
276 Input = "";
277 }
278 return Error::success();
279 }
280
281 // Dereference. Note: Moves op type.
283 assert(CurOp && "Dereferencing end/invalid iterator");
284 return *CurOp;
285 }
286
287 // Dereference. Note: Moves op type.
288 const Op &operator*() const {
289 assert(CurOp && "Dereferencing end/invalid iterator");
290 return *CurOp;
291 }
292
293 /// Compare iterators. End iterators compare equal.
294 friend bool operator==(const OpIterator &LHS, const OpIterator &RHS) {
295 return LHS.P == RHS.P && LHS.Input == RHS.Input;
296 }
297
298 friend bool operator!=(const OpIterator &LHS, const OpIterator &RHS) {
299 return !(LHS == RHS);
300 }
301
302 private:
303 std::shared_ptr<OpParser> P;
304 StringRef Input, PrevInput;
305 std::optional<Op> CurOp;
306 };
307};
308
309/// Returns a fallible iterator range over the operations in the given buffer.
310/// The buffer should contain text in the format produced by
311/// WaitingOnGraphOpStreamRecorder. Parsing errors are reported through Err.
312template <typename ContainerIdT, typename ElementIdT>
314 typename WaitingOnGraphOpReplay<ContainerIdT, ElementIdT>::OpIterator>>
316
318
319 class Parser : public Replay::OpParser {
320 public:
321 using ParseResult = typename Replay::OpParser::ParseResult;
322 using SuperNode = typename Replay::SuperNode;
323 using ContainerElementsMap = typename Replay::ContainerElementsMap;
324
325 /// Parse the next operation from Input into CurrentOp.
326 /// Sets IsEnd on "end" keyword. Returns Error on parse failure.
327 Expected<ParseResult> parseNext(StringRef Input) override {
328 auto Line = getNextLine(Input);
329
330 if (Line.empty())
332 "unexpected end of input (missing 'end')",
334
335 if (Line.consume_front("simplify-and-emit ")) {
336 size_t NumSNs;
337 if (Line.trim().consumeInteger(10, NumSNs))
339 "expected supernode count after 'simplify-and-emit'",
341 auto SNs = parseSuperNodes(Input, NumSNs);
342 if (!SNs)
343 return SNs.takeError();
344 return this->parsedSimplifyAndEmit(std::move(*SNs), Input);
345 } else if (Line.trim() == "fail") {
346 auto FailElems = parseContainerElementsMap("failed", Input);
347 if (!FailElems)
348 return FailElems.takeError();
349 return this->parsedFail(std::move(*FailElems), Input);
350 } else if (Line.trim() == "end")
351 return this->parsedEnd(Input);
352 else
353 return make_error<StringError>("unexpected line: '" + Line + "'",
355 }
356
357 private:
358 static StringRef getNextLine(StringRef &Input) {
359 StringRef Line;
360 // Parse skipping blank lines.
361 do {
362 std::tie(Line, Input) = Input.split('\n');
363 Line = Line.trim();
364 } while (Line.empty() && !Input.empty());
365 return Line;
366 }
367
369 parseSuperNodes(StringRef &Input, size_t NumSNs) {
370 std::vector<std::unique_ptr<SuperNode>> SNs;
371 for (size_t I = 0; I != NumSNs; ++I) {
372 // Parse "sn <index>"
373 StringRef Line = getNextLine(Input);
374 if (!Line.consume_front("sn "))
375 return make_error<StringError>("expected 'sn " + Twine(I) + "'",
377
378 auto Defs = parseContainerElementsMap("defs", Input);
379 if (!Defs)
380 return Defs.takeError();
381 auto Deps = parseContainerElementsMap("deps", Input);
382 if (!Deps)
383 return Deps.takeError();
384
385 SNs.push_back(
386 std::make_unique<SuperNode>(std::move(*Defs), std::move(*Deps)));
387 }
388 return std::move(SNs);
389 }
390
392 parseContainerElementsMap(StringRef ArgName, StringRef &Input) {
393 // Parse "defs <count>"
394 auto Line = getNextLine(Input);
395 if (!Line.consume_front(ArgName))
396 return make_error<StringError>("expected '" + ArgName + " <count>'",
398 size_t NumContainers;
399 if (Line.trim().consumeInteger(10, NumContainers))
400 return make_error<StringError>("expected " + ArgName + " count",
402
403 ContainerElementsMap M;
404 for (size_t I = 0; I != NumContainers; ++I) {
405 Line = getNextLine(Input);
406 if (!Line.consume_front("container "))
407 return make_error<StringError>("expected 'container <id> <count>'",
409
410 size_t Container;
411 Line = Line.trim();
412 if (Line.consumeInteger(10, Container))
413 return make_error<StringError>("expected container id",
415
416 if (M.count(Container))
418 "expected container id to be unique within " + ArgName,
420
421 size_t NumElements;
422 if (Line.trim().consumeInteger(10, NumElements))
423 return make_error<StringError>("expected elements count",
425 if (NumElements == 0)
426 return make_error<StringError>("number of elements for container " +
427 Twine(Container) + " must be > 0",
429
430 Line = getNextLine(Input);
431 if (!Line.consume_front("elements "))
432 return make_error<StringError>("expected 'elements ...'",
434
435 auto &Elements = M[Container];
436 for (size_t J = 0; J != NumElements; ++J) {
437 size_t Elem;
438 Line = Line.trim();
439 if (Line.consumeInteger(10, Elem))
440 return make_error<StringError>("expected element id",
442 if (Elements.count(Elem))
444 "expected element id to be unique within container " +
445 Twine(Container),
447 Elements.insert(Elem);
448 }
449 }
450
451 return std::move(M);
452 }
453 };
454
456 typename Replay::OpIterator Begin(std::make_shared<Parser>(), InputBuffer);
457 typename Replay::OpIterator End;
458 if ((Err = Begin.inc())) // Parse first operation.
459 Begin = End;
460 return make_fallible_range(std::move(Begin), std::move(End), Err);
461}
462
463} // namespace llvm::orc::detail
464
465#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)