Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 pmbaty 1
//===- StandardInstrumentations.h ------------------------------*- 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
/// \file
9
///
10
/// This header defines a class that provides bookkeeping for all standard
11
/// (i.e in-tree) pass instrumentations.
12
///
13
//===----------------------------------------------------------------------===//
14
 
15
#ifndef LLVM_PASSES_STANDARDINSTRUMENTATIONS_H
16
#define LLVM_PASSES_STANDARDINSTRUMENTATIONS_H
17
 
18
#include "llvm/ADT/STLExtras.h"
19
#include "llvm/ADT/SmallVector.h"
20
#include "llvm/ADT/StringRef.h"
21
#include "llvm/IR/BasicBlock.h"
22
#include "llvm/IR/OptBisect.h"
23
#include "llvm/IR/PassTimingInfo.h"
24
#include "llvm/IR/ValueHandle.h"
25
#include "llvm/Support/CommandLine.h"
26
#include "llvm/Support/TimeProfiler.h"
27
#include "llvm/Transforms/IPO/SampleProfileProbe.h"
28
 
29
#include <string>
30
#include <utility>
31
 
32
namespace llvm {
33
 
34
class Module;
35
class Function;
36
class PassInstrumentationCallbacks;
37
 
38
/// Instrumentation to print IR before/after passes.
39
///
40
/// Needs state to be able to print module after pass that invalidates IR unit
41
/// (typically Loop or SCC).
42
class PrintIRInstrumentation {
43
public:
44
  ~PrintIRInstrumentation();
45
 
46
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
47
 
48
private:
49
  void printBeforePass(StringRef PassID, Any IR);
50
  void printAfterPass(StringRef PassID, Any IR);
51
  void printAfterPassInvalidated(StringRef PassID);
52
 
53
  bool shouldPrintBeforePass(StringRef PassID);
54
  bool shouldPrintAfterPass(StringRef PassID);
55
 
56
  using PrintModuleDesc = std::tuple<const Module *, std::string, StringRef>;
57
 
58
  void pushModuleDesc(StringRef PassID, Any IR);
59
  PrintModuleDesc popModuleDesc(StringRef PassID);
60
 
61
  PassInstrumentationCallbacks *PIC;
62
  /// Stack of Module description, enough to print the module after a given
63
  /// pass.
64
  SmallVector<PrintModuleDesc, 2> ModuleDescStack;
65
};
66
 
67
class OptNoneInstrumentation {
68
public:
69
  OptNoneInstrumentation(bool DebugLogging) : DebugLogging(DebugLogging) {}
70
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
71
 
72
private:
73
  bool DebugLogging;
74
  bool shouldRun(StringRef PassID, Any IR);
75
};
76
 
77
class OptPassGateInstrumentation {
78
  LLVMContext &Context;
79
  bool HasWrittenIR = false;
80
public:
81
  OptPassGateInstrumentation(LLVMContext &Context) : Context(Context) {}
82
  bool shouldRun(StringRef PassName, Any IR);
83
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
84
};
85
 
86
struct PrintPassOptions {
87
  /// Print adaptors and pass managers.
88
  bool Verbose = false;
89
  /// Don't print information for analyses.
90
  bool SkipAnalyses = false;
91
  /// Indent based on hierarchy.
92
  bool Indent = false;
93
};
94
 
95
// Debug logging for transformation and analysis passes.
96
class PrintPassInstrumentation {
97
  raw_ostream &print();
98
 
99
public:
100
  PrintPassInstrumentation(bool Enabled, PrintPassOptions Opts)
101
      : Enabled(Enabled), Opts(Opts) {}
102
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
103
 
104
private:
105
  bool Enabled;
106
  PrintPassOptions Opts;
107
  int Indent = 0;
108
};
109
 
110
class PreservedCFGCheckerInstrumentation {
111
public:
112
  // Keeps sticky poisoned flag for the given basic block once it has been
113
  // deleted or RAUWed.
114
  struct BBGuard final : public CallbackVH {
115
    BBGuard(const BasicBlock *BB) : CallbackVH(BB) {}
116
    void deleted() override { CallbackVH::deleted(); }
117
    void allUsesReplacedWith(Value *) override { CallbackVH::deleted(); }
118
    bool isPoisoned() const { return !getValPtr(); }
119
  };
120
 
121
  // CFG is a map BB -> {(Succ, Multiplicity)}, where BB is a non-leaf basic
122
  // block, {(Succ, Multiplicity)} set of all pairs of the block's successors
123
  // and the multiplicity of the edge (BB->Succ). As the mapped sets are
124
  // unordered the order of successors is not tracked by the CFG. In other words
125
  // this allows basic block successors to be swapped by a pass without
126
  // reporting a CFG change. CFG can be guarded by basic block tracking pointers
127
  // in the Graph (BBGuard). That is if any of the block is deleted or RAUWed
128
  // then the CFG is treated poisoned and no block pointer of the Graph is used.
129
  struct CFG {
130
    std::optional<DenseMap<intptr_t, BBGuard>> BBGuards;
131
    DenseMap<const BasicBlock *, DenseMap<const BasicBlock *, unsigned>> Graph;
132
 
133
    CFG(const Function *F, bool TrackBBLifetime);
134
 
135
    bool operator==(const CFG &G) const {
136
      return !isPoisoned() && !G.isPoisoned() && Graph == G.Graph;
137
    }
138
 
139
    bool isPoisoned() const {
140
      return BBGuards && llvm::any_of(*BBGuards, [](const auto &BB) {
141
               return BB.second.isPoisoned();
142
             });
143
    }
144
 
145
    static void printDiff(raw_ostream &out, const CFG &Before,
146
                          const CFG &After);
147
    bool invalidate(Function &F, const PreservedAnalyses &PA,
148
                    FunctionAnalysisManager::Invalidator &);
149
  };
150
 
151
#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
152
  SmallVector<StringRef, 8> PassStack;
153
#endif
154
 
155
  static cl::opt<bool> VerifyPreservedCFG;
156
  void registerCallbacks(PassInstrumentationCallbacks &PIC,
157
                         FunctionAnalysisManager &FAM);
158
};
159
 
160
// Base class for classes that report changes to the IR.
161
// It presents an interface for such classes and provides calls
162
// on various events as the new pass manager transforms the IR.
163
// It also provides filtering of information based on hidden options
164
// specifying which functions are interesting.
165
// Calls are made for the following events/queries:
166
// 1.  The initial IR processed.
167
// 2.  To get the representation of the IR (of type \p T).
168
// 3.  When a pass does not change the IR.
169
// 4.  When a pass changes the IR (given both before and after representations
170
//         of type \p T).
171
// 5.  When an IR is invalidated.
172
// 6.  When a pass is run on an IR that is not interesting (based on options).
173
// 7.  When a pass is ignored (pass manager or adapter pass).
174
// 8.  To compare two IR representations (of type \p T).
175
template <typename IRUnitT> class ChangeReporter {
176
protected:
177
  ChangeReporter(bool RunInVerboseMode) : VerboseMode(RunInVerboseMode) {}
178
 
179
public:
180
  virtual ~ChangeReporter();
181
 
182
  // Determine if this pass/IR is interesting and if so, save the IR
183
  // otherwise it is left on the stack without data.
184
  void saveIRBeforePass(Any IR, StringRef PassID, StringRef PassName);
185
  // Compare the IR from before the pass after the pass.
186
  void handleIRAfterPass(Any IR, StringRef PassID, StringRef PassName);
187
  // Handle the situation where a pass is invalidated.
188
  void handleInvalidatedPass(StringRef PassID);
189
 
190
protected:
191
  // Register required callbacks.
192
  void registerRequiredCallbacks(PassInstrumentationCallbacks &PIC);
193
 
194
  // Called on the first IR processed.
195
  virtual void handleInitialIR(Any IR) = 0;
196
  // Called before and after a pass to get the representation of the IR.
197
  virtual void generateIRRepresentation(Any IR, StringRef PassID,
198
                                        IRUnitT &Output) = 0;
199
  // Called when the pass is not iteresting.
200
  virtual void omitAfter(StringRef PassID, std::string &Name) = 0;
201
  // Called when an interesting IR has changed.
202
  virtual void handleAfter(StringRef PassID, std::string &Name,
203
                           const IRUnitT &Before, const IRUnitT &After,
204
                           Any) = 0;
205
  // Called when an interesting pass is invalidated.
206
  virtual void handleInvalidated(StringRef PassID) = 0;
207
  // Called when the IR or pass is not interesting.
208
  virtual void handleFiltered(StringRef PassID, std::string &Name) = 0;
209
  // Called when an ignored pass is encountered.
210
  virtual void handleIgnored(StringRef PassID, std::string &Name) = 0;
211
 
212
  // Stack of IRs before passes.
213
  std::vector<IRUnitT> BeforeStack;
214
  // Is this the first IR seen?
215
  bool InitialIR = true;
216
 
217
  // Run in verbose mode, printing everything?
218
  const bool VerboseMode;
219
};
220
 
221
// An abstract template base class that handles printing banners and
222
// reporting when things have not changed or are filtered out.
223
template <typename IRUnitT>
224
class TextChangeReporter : public ChangeReporter<IRUnitT> {
225
protected:
226
  TextChangeReporter(bool Verbose);
227
 
228
  // Print a module dump of the first IR that is changed.
229
  void handleInitialIR(Any IR) override;
230
  // Report that the IR was omitted because it did not change.
231
  void omitAfter(StringRef PassID, std::string &Name) override;
232
  // Report that the pass was invalidated.
233
  void handleInvalidated(StringRef PassID) override;
234
  // Report that the IR was filtered out.
235
  void handleFiltered(StringRef PassID, std::string &Name) override;
236
  // Report that the pass was ignored.
237
  void handleIgnored(StringRef PassID, std::string &Name) override;
238
  // Make substitutions in \p S suitable for reporting changes
239
  // after the pass and then print it.
240
 
241
  raw_ostream &Out;
242
};
243
 
244
// A change printer based on the string representation of the IR as created
245
// by unwrapAndPrint.  The string representation is stored in a std::string
246
// to preserve it as the IR changes in each pass.  Note that the banner is
247
// included in this representation but it is massaged before reporting.
248
class IRChangedPrinter : public TextChangeReporter<std::string> {
249
public:
250
  IRChangedPrinter(bool VerboseMode)
251
      : TextChangeReporter<std::string>(VerboseMode) {}
252
  ~IRChangedPrinter() override;
253
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
254
 
255
protected:
256
  // Called before and after a pass to get the representation of the IR.
257
  void generateIRRepresentation(Any IR, StringRef PassID,
258
                                std::string &Output) override;
259
  // Called when an interesting IR has changed.
260
  void handleAfter(StringRef PassID, std::string &Name,
261
                   const std::string &Before, const std::string &After,
262
                   Any) override;
263
};
264
 
265
class IRChangedTester : public IRChangedPrinter {
266
public:
267
  IRChangedTester() : IRChangedPrinter(true) {}
268
  ~IRChangedTester() override;
269
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
270
 
271
protected:
272
  void handleIR(const std::string &IR, StringRef PassID);
273
 
274
  // Check initial IR
275
  void handleInitialIR(Any IR) override;
276
  // Do nothing.
277
  void omitAfter(StringRef PassID, std::string &Name) override;
278
  // Do nothing.
279
  void handleInvalidated(StringRef PassID) override;
280
  // Do nothing.
281
  void handleFiltered(StringRef PassID, std::string &Name) override;
282
  // Do nothing.
283
  void handleIgnored(StringRef PassID, std::string &Name) override;
284
 
285
  // Call test as interesting IR has changed.
286
  void handleAfter(StringRef PassID, std::string &Name,
287
                   const std::string &Before, const std::string &After,
288
                   Any) override;
289
};
290
 
291
// Information that needs to be saved for a basic block in order to compare
292
// before and after the pass to determine if it was changed by a pass.
293
template <typename T> class BlockDataT {
294
public:
295
  BlockDataT(const BasicBlock &B) : Label(B.getName().str()), Data(B) {
296
    raw_string_ostream SS(Body);
297
    B.print(SS, nullptr, true, true);
298
  }
299
 
300
  bool operator==(const BlockDataT &That) const { return Body == That.Body; }
301
  bool operator!=(const BlockDataT &That) const { return Body != That.Body; }
302
 
303
  // Return the label of the represented basic block.
304
  StringRef getLabel() const { return Label; }
305
  // Return the string representation of the basic block.
306
  StringRef getBody() const { return Body; }
307
 
308
  // Return the associated data
309
  const T &getData() const { return Data; }
310
 
311
protected:
312
  std::string Label;
313
  std::string Body;
314
 
315
  // Extra data associated with a basic block
316
  T Data;
317
};
318
 
319
template <typename T> class OrderedChangedData {
320
public:
321
  // Return the names in the order they were saved
322
  std::vector<std::string> &getOrder() { return Order; }
323
  const std::vector<std::string> &getOrder() const { return Order; }
324
 
325
  // Return a map of names to saved representations
326
  StringMap<T> &getData() { return Data; }
327
  const StringMap<T> &getData() const { return Data; }
328
 
329
  bool operator==(const OrderedChangedData<T> &That) const {
330
    return Data == That.getData();
331
  }
332
 
333
  // Call the lambda \p HandlePair on each corresponding pair of data from
334
  // \p Before and \p After.  The order is based on the order in \p After
335
  // with ones that are only in \p Before interspersed based on where they
336
  // occur in \p Before.  This is used to present the output in an order
337
  // based on how the data is ordered in LLVM.
338
  static void report(const OrderedChangedData &Before,
339
                     const OrderedChangedData &After,
340
                     function_ref<void(const T *, const T *)> HandlePair);
341
 
342
protected:
343
  std::vector<std::string> Order;
344
  StringMap<T> Data;
345
};
346
 
347
// Do not need extra information for patch-style change reporter.
348
class EmptyData {
349
public:
350
  EmptyData(const BasicBlock &) {}
351
};
352
 
353
// The data saved for comparing functions.
354
template <typename T>
355
class FuncDataT : public OrderedChangedData<BlockDataT<T>> {
356
public:
357
  FuncDataT(std::string S) : EntryBlockName(S) {}
358
 
359
  // Return the name of the entry block
360
  std::string getEntryBlockName() const { return EntryBlockName; }
361
 
362
protected:
363
  std::string EntryBlockName;
364
};
365
 
366
// The data saved for comparing IRs.
367
template <typename T>
368
class IRDataT : public OrderedChangedData<FuncDataT<T>> {};
369
 
370
// Abstract template base class for a class that compares two IRs.  The
371
// class is created with the 2 IRs to compare and then compare is called.
372
// The static function analyzeIR is used to build up the IR representation.
373
template <typename T> class IRComparer {
374
public:
375
  IRComparer(const IRDataT<T> &Before, const IRDataT<T> &After)
376
      : Before(Before), After(After) {}
377
 
378
  // Compare the 2 IRs. \p handleFunctionCompare is called to handle the
379
  // compare of a function. When \p InModule is set,
380
  // this function is being handled as part of comparing a module.
381
  void compare(
382
      bool CompareModule,
383
      std::function<void(bool InModule, unsigned Minor,
384
                         const FuncDataT<T> &Before, const FuncDataT<T> &After)>
385
          CompareFunc);
386
 
387
  // Analyze \p IR and build the IR representation in \p Data.
388
  static void analyzeIR(Any IR, IRDataT<T> &Data);
389
 
390
protected:
391
  // Generate the data for \p F into \p Data.
392
  static bool generateFunctionData(IRDataT<T> &Data, const Function &F);
393
 
394
  const IRDataT<T> &Before;
395
  const IRDataT<T> &After;
396
};
397
 
398
// A change printer that prints out in-line differences in the basic
399
// blocks.  It uses an InlineComparer to do the comparison so it shows
400
// the differences prefixed with '-' and '+' for code that is removed
401
// and added, respectively.  Changes to the IR that do not affect basic
402
// blocks are not reported as having changed the IR.  The option
403
// -print-module-scope does not affect this change reporter.
404
class InLineChangePrinter : public TextChangeReporter<IRDataT<EmptyData>> {
405
public:
406
  InLineChangePrinter(bool VerboseMode, bool ColourMode)
407
      : TextChangeReporter<IRDataT<EmptyData>>(VerboseMode),
408
        UseColour(ColourMode) {}
409
  ~InLineChangePrinter() override;
410
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
411
 
412
protected:
413
  // Create a representation of the IR.
414
  void generateIRRepresentation(Any IR, StringRef PassID,
415
                                IRDataT<EmptyData> &Output) override;
416
 
417
  // Called when an interesting IR has changed.
418
  void handleAfter(StringRef PassID, std::string &Name,
419
                   const IRDataT<EmptyData> &Before,
420
                   const IRDataT<EmptyData> &After, Any) override;
421
 
422
  void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID,
423
                             StringRef Divider, bool InModule, unsigned Minor,
424
                             const FuncDataT<EmptyData> &Before,
425
                             const FuncDataT<EmptyData> &After);
426
 
427
  bool UseColour;
428
};
429
 
430
class VerifyInstrumentation {
431
  bool DebugLogging;
432
 
433
public:
434
  VerifyInstrumentation(bool DebugLogging) : DebugLogging(DebugLogging) {}
435
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
436
};
437
 
438
/// This class implements --time-trace functionality for new pass manager.
439
/// It provides the pass-instrumentation callbacks that measure the pass
440
/// execution time. They collect time tracing info by TimeProfiler.
441
class TimeProfilingPassesHandler {
442
public:
443
  TimeProfilingPassesHandler();
444
  // We intend this to be unique per-compilation, thus no copies.
445
  TimeProfilingPassesHandler(const TimeProfilingPassesHandler &) = delete;
446
  void operator=(const TimeProfilingPassesHandler &) = delete;
447
 
448
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
449
 
450
private:
451
  // Implementation of pass instrumentation callbacks.
452
  void runBeforePass(StringRef PassID, Any IR);
453
  void runAfterPass();
454
};
455
 
456
// Class that holds transitions between basic blocks.  The transitions
457
// are contained in a map of values to names of basic blocks.
458
class DCData {
459
public:
460
  // Fill the map with the transitions from basic block \p B.
461
  DCData(const BasicBlock &B);
462
 
463
  // Return an iterator to the names of the successor blocks.
464
  StringMap<std::string>::const_iterator begin() const {
465
    return Successors.begin();
466
  }
467
  StringMap<std::string>::const_iterator end() const {
468
    return Successors.end();
469
  }
470
 
471
  // Return the label of the basic block reached on a transition on \p S.
472
  StringRef getSuccessorLabel(StringRef S) const {
473
    assert(Successors.count(S) == 1 && "Expected to find successor.");
474
    return Successors.find(S)->getValue();
475
  }
476
 
477
protected:
478
  // Add a transition to \p Succ on \p Label
479
  void addSuccessorLabel(StringRef Succ, StringRef Label) {
480
    std::pair<std::string, std::string> SS{Succ.str(), Label.str()};
481
    Successors.insert(SS);
482
  }
483
 
484
  StringMap<std::string> Successors;
485
};
486
 
487
// A change reporter that builds a website with links to pdf files showing
488
// dot control flow graphs with changed instructions shown in colour.
489
class DotCfgChangeReporter : public ChangeReporter<IRDataT<DCData>> {
490
public:
491
  DotCfgChangeReporter(bool Verbose);
492
  ~DotCfgChangeReporter() override;
493
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
494
 
495
protected:
496
  // Initialize the HTML file and output the header.
497
  bool initializeHTML();
498
 
499
  // Called on the first IR processed.
500
  void handleInitialIR(Any IR) override;
501
  // Called before and after a pass to get the representation of the IR.
502
  void generateIRRepresentation(Any IR, StringRef PassID,
503
                                IRDataT<DCData> &Output) override;
504
  // Called when the pass is not iteresting.
505
  void omitAfter(StringRef PassID, std::string &Name) override;
506
  // Called when an interesting IR has changed.
507
  void handleAfter(StringRef PassID, std::string &Name,
508
                   const IRDataT<DCData> &Before, const IRDataT<DCData> &After,
509
                   Any) override;
510
  // Called when an interesting pass is invalidated.
511
  void handleInvalidated(StringRef PassID) override;
512
  // Called when the IR or pass is not interesting.
513
  void handleFiltered(StringRef PassID, std::string &Name) override;
514
  // Called when an ignored pass is encountered.
515
  void handleIgnored(StringRef PassID, std::string &Name) override;
516
 
517
  // Generate the pdf file into \p Dir / \p PDFFileName using \p DotFile as
518
  // input and return the html <a> tag with \Text as the content.
519
  static std::string genHTML(StringRef Text, StringRef DotFile,
520
                             StringRef PDFFileName);
521
 
522
  void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID,
523
                             StringRef Divider, bool InModule, unsigned Minor,
524
                             const FuncDataT<DCData> &Before,
525
                             const FuncDataT<DCData> &After);
526
 
527
  unsigned N = 0;
528
  std::unique_ptr<raw_fd_ostream> HTML;
529
};
530
 
531
// Print IR on crash.
532
class PrintCrashIRInstrumentation {
533
public:
534
  PrintCrashIRInstrumentation()
535
      : SavedIR("*** Dump of IR Before Last Pass Unknown ***") {}
536
  ~PrintCrashIRInstrumentation();
537
  void registerCallbacks(PassInstrumentationCallbacks &PIC);
538
  void reportCrashIR();
539
 
540
protected:
541
  std::string SavedIR;
542
 
543
private:
544
  // The crash reporter that will report on a crash.
545
  static PrintCrashIRInstrumentation *CrashReporter;
546
  // Crash handler registered when print-on-crash is specified.
547
  static void SignalHandler(void *);
548
};
549
 
550
/// This class provides an interface to register all the standard pass
551
/// instrumentations and manages their state (if any).
552
class StandardInstrumentations {
553
  PrintIRInstrumentation PrintIR;
554
  PrintPassInstrumentation PrintPass;
555
  TimePassesHandler TimePasses;
556
  TimeProfilingPassesHandler TimeProfilingPasses;
557
  OptNoneInstrumentation OptNone;
558
  OptPassGateInstrumentation OptPassGate;
559
  PreservedCFGCheckerInstrumentation PreservedCFGChecker;
560
  IRChangedPrinter PrintChangedIR;
561
  PseudoProbeVerifier PseudoProbeVerification;
562
  InLineChangePrinter PrintChangedDiff;
563
  DotCfgChangeReporter WebsiteChangeReporter;
564
  PrintCrashIRInstrumentation PrintCrashIR;
565
  IRChangedTester ChangeTester;
566
  VerifyInstrumentation Verify;
567
 
568
  bool VerifyEach;
569
 
570
public:
571
  StandardInstrumentations(LLVMContext &Context, bool DebugLogging,
572
                           bool VerifyEach = false,
573
                           PrintPassOptions PrintPassOpts = PrintPassOptions());
574
 
575
  // Register all the standard instrumentation callbacks. If \p FAM is nullptr
576
  // then PreservedCFGChecker is not enabled.
577
  void registerCallbacks(PassInstrumentationCallbacks &PIC,
578
                         FunctionAnalysisManager *FAM = nullptr);
579
 
580
  TimePassesHandler &getTimePasses() { return TimePasses; }
581
};
582
 
583
extern template class ChangeReporter<std::string>;
584
extern template class TextChangeReporter<std::string>;
585
 
586
extern template class BlockDataT<EmptyData>;
587
extern template class FuncDataT<EmptyData>;
588
extern template class IRDataT<EmptyData>;
589
extern template class ChangeReporter<IRDataT<EmptyData>>;
590
extern template class TextChangeReporter<IRDataT<EmptyData>>;
591
extern template class IRComparer<EmptyData>;
592
 
593
} // namespace llvm
594
 
595
#endif