1 | //===- verify-uselistorder.cpp - The LLVM Modular Optimizer ---------------===// |
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 | // Verify that use-list order can be serialized correctly. After reading the |
10 | // provided IR, this tool shuffles the use-lists and then writes and reads to a |
11 | // separate Module whose use-list orders are compared to the original. |
12 | // |
13 | // The shuffles are deterministic, but guarantee that use-lists will change. |
14 | // The algorithm per iteration is as follows: |
15 | // |
16 | // 1. Seed the random number generator. The seed is different for each |
17 | // shuffle. Shuffle 0 uses default+0, shuffle 1 uses default+1, and so on. |
18 | // |
19 | // 2. Visit every Value in a deterministic order. |
20 | // |
21 | // 3. Assign a random number to each Use in the Value's use-list in order. |
22 | // |
23 | // 4. If the numbers are already in order, reassign numbers until they aren't. |
24 | // |
25 | // 5. Sort the use-list using Value::sortUseList(), which is a stable sort. |
26 | // |
27 | //===----------------------------------------------------------------------===// |
28 | |
29 | #include "llvm/ADT/DenseMap.h" |
30 | #include "llvm/ADT/DenseSet.h" |
31 | #include "llvm/AsmParser/Parser.h" |
32 | #include "llvm/Bitcode/BitcodeReader.h" |
33 | #include "llvm/Bitcode/BitcodeWriter.h" |
34 | #include "llvm/IR/LLVMContext.h" |
35 | #include "llvm/IR/Module.h" |
36 | #include "llvm/IR/UseListOrder.h" |
37 | #include "llvm/IR/Verifier.h" |
38 | #include "llvm/IRReader/IRReader.h" |
39 | #include "llvm/Support/CommandLine.h" |
40 | #include "llvm/Support/Debug.h" |
41 | #include "llvm/Support/ErrorHandling.h" |
42 | #include "llvm/Support/FileSystem.h" |
43 | #include "llvm/Support/FileUtilities.h" |
44 | #include "llvm/Support/InitLLVM.h" |
45 | #include "llvm/Support/MemoryBuffer.h" |
46 | #include "llvm/Support/SourceMgr.h" |
47 | #include "llvm/Support/SystemUtils.h" |
48 | #include "llvm/Support/raw_ostream.h" |
49 | #include <random> |
50 | #include <vector> |
51 | |
52 | using namespace llvm; |
53 | |
54 | #define DEBUG_TYPE "uselistorder" |
55 | |
56 | static cl::OptionCategory Cat("verify-uselistorder Options" ); |
57 | |
58 | static cl::opt<std::string> InputFilename(cl::Positional, |
59 | cl::desc("<input bitcode file>" ), |
60 | cl::init(Val: "-" ), |
61 | cl::value_desc("filename" )); |
62 | |
63 | static cl::opt<bool> SaveTemps("save-temps" , cl::desc("Save temp files" ), |
64 | cl::cat(Cat)); |
65 | |
66 | static cl::opt<unsigned> |
67 | NumShuffles("num-shuffles" , |
68 | cl::desc("Number of times to shuffle and verify use-lists" ), |
69 | cl::init(Val: 1), cl::cat(Cat)); |
70 | |
71 | namespace { |
72 | |
73 | struct TempFile { |
74 | std::string Filename; |
75 | FileRemover Remover; |
76 | bool init(const std::string &Ext); |
77 | bool writeBitcode(const Module &M) const; |
78 | bool writeAssembly(const Module &M) const; |
79 | std::unique_ptr<Module> readBitcode(LLVMContext &Context) const; |
80 | std::unique_ptr<Module> readAssembly(LLVMContext &Context) const; |
81 | }; |
82 | |
83 | struct ValueMapping { |
84 | DenseMap<const Value *, unsigned> IDs; |
85 | std::vector<const Value *> Values; |
86 | |
87 | /// Construct a value mapping for module. |
88 | /// |
89 | /// Creates mapping from every value in \c M to an ID. This mapping includes |
90 | /// un-referencable values. |
91 | /// |
92 | /// Every \a Value that gets serialized in some way should be represented |
93 | /// here. The order needs to be deterministic, but it's unnecessary to match |
94 | /// the value-ids in the bitcode writer. |
95 | /// |
96 | /// All constants that are referenced by other values are included in the |
97 | /// mapping, but others -- which wouldn't be serialized -- are not. |
98 | ValueMapping(const Module &M); |
99 | |
100 | /// Map a value. |
101 | /// |
102 | /// Maps a value. If it's a constant, maps all of its operands first. |
103 | void map(const Value *V); |
104 | unsigned lookup(const Value *V) const { return IDs.lookup(Val: V); } |
105 | }; |
106 | |
107 | } // end namespace |
108 | |
109 | bool TempFile::init(const std::string &Ext) { |
110 | SmallVector<char, 64> Vector; |
111 | LLVM_DEBUG(dbgs() << " - create-temp-file\n" ); |
112 | if (auto EC = sys::fs::createTemporaryFile(Prefix: "uselistorder" , Suffix: Ext, ResultPath&: Vector)) { |
113 | errs() << "verify-uselistorder: error: " << EC.message() << "\n" ; |
114 | return true; |
115 | } |
116 | assert(!Vector.empty()); |
117 | |
118 | Filename.assign(first: Vector.data(), last: Vector.data() + Vector.size()); |
119 | Remover.setFile(filename: Filename, deleteIt: !SaveTemps); |
120 | if (SaveTemps) |
121 | outs() << " - filename = " << Filename << "\n" ; |
122 | return false; |
123 | } |
124 | |
125 | bool TempFile::writeBitcode(const Module &M) const { |
126 | LLVM_DEBUG(dbgs() << " - write bitcode\n" ); |
127 | std::error_code EC; |
128 | raw_fd_ostream OS(Filename, EC, sys::fs::OF_None); |
129 | if (EC) { |
130 | errs() << "verify-uselistorder: error: " << EC.message() << "\n" ; |
131 | return true; |
132 | } |
133 | |
134 | WriteBitcodeToFile(M, Out&: OS, /* ShouldPreserveUseListOrder */ true); |
135 | return false; |
136 | } |
137 | |
138 | bool TempFile::writeAssembly(const Module &M) const { |
139 | LLVM_DEBUG(dbgs() << " - write assembly\n" ); |
140 | std::error_code EC; |
141 | raw_fd_ostream OS(Filename, EC, sys::fs::OF_TextWithCRLF); |
142 | if (EC) { |
143 | errs() << "verify-uselistorder: error: " << EC.message() << "\n" ; |
144 | return true; |
145 | } |
146 | |
147 | M.print(OS, AAW: nullptr, /* ShouldPreserveUseListOrder */ true); |
148 | return false; |
149 | } |
150 | |
151 | std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { |
152 | LLVM_DEBUG(dbgs() << " - read bitcode\n" ); |
153 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOr = |
154 | MemoryBuffer::getFile(Filename); |
155 | if (!BufferOr) { |
156 | errs() << "verify-uselistorder: error: " << BufferOr.getError().message() |
157 | << "\n" ; |
158 | return nullptr; |
159 | } |
160 | |
161 | MemoryBuffer *Buffer = BufferOr.get().get(); |
162 | Expected<std::unique_ptr<Module>> ModuleOr = |
163 | parseBitcodeFile(Buffer: Buffer->getMemBufferRef(), Context); |
164 | if (!ModuleOr) { |
165 | logAllUnhandledErrors(E: ModuleOr.takeError(), OS&: errs(), |
166 | ErrorBanner: "verify-uselistorder: error: " ); |
167 | return nullptr; |
168 | } |
169 | |
170 | return std::move(ModuleOr.get()); |
171 | } |
172 | |
173 | std::unique_ptr<Module> TempFile::readAssembly(LLVMContext &Context) const { |
174 | LLVM_DEBUG(dbgs() << " - read assembly\n" ); |
175 | SMDiagnostic Err; |
176 | std::unique_ptr<Module> M = parseAssemblyFile(Filename, Err, Context); |
177 | if (!M) |
178 | Err.print(ProgName: "verify-uselistorder" , S&: errs()); |
179 | return M; |
180 | } |
181 | |
182 | ValueMapping::ValueMapping(const Module &M) { |
183 | // Every value should be mapped, including things like void instructions and |
184 | // basic blocks that are kept out of the ValueEnumerator. |
185 | // |
186 | // The current mapping order makes it easier to debug the tables. It happens |
187 | // to be similar to the ID mapping when writing ValueEnumerator, but they |
188 | // aren't (and needn't be) in sync. |
189 | |
190 | // Globals. |
191 | for (const GlobalVariable &G : M.globals()) |
192 | map(V: &G); |
193 | for (const GlobalAlias &A : M.aliases()) |
194 | map(V: &A); |
195 | for (const GlobalIFunc &IF : M.ifuncs()) |
196 | map(V: &IF); |
197 | for (const Function &F : M) |
198 | map(V: &F); |
199 | |
200 | // Constants used by globals. |
201 | for (const GlobalVariable &G : M.globals()) |
202 | if (G.hasInitializer()) |
203 | map(V: G.getInitializer()); |
204 | for (const GlobalAlias &A : M.aliases()) |
205 | map(V: A.getAliasee()); |
206 | for (const GlobalIFunc &IF : M.ifuncs()) |
207 | map(V: IF.getResolver()); |
208 | for (const Function &F : M) |
209 | for (Value *Op : F.operands()) |
210 | map(V: Op); |
211 | |
212 | // Function bodies. |
213 | for (const Function &F : M) { |
214 | for (const Argument &A : F.args()) |
215 | map(V: &A); |
216 | for (const BasicBlock &BB : F) |
217 | map(V: &BB); |
218 | for (const BasicBlock &BB : F) |
219 | for (const Instruction &I : BB) |
220 | map(V: &I); |
221 | |
222 | // Constants used by instructions. |
223 | for (const BasicBlock &BB : F) { |
224 | for (const Instruction &I : BB) { |
225 | for (const DbgVariableRecord &DVR : |
226 | filterDbgVars(R: I.getDbgRecordRange())) { |
227 | for (Value *Op : DVR.location_ops()) |
228 | map(V: Op); |
229 | if (DVR.isDbgAssign()) |
230 | map(V: DVR.getAddress()); |
231 | } |
232 | for (const Value *Op : I.operands()) { |
233 | // Look through a metadata wrapper. |
234 | if (const auto *MAV = dyn_cast<MetadataAsValue>(Val: Op)) |
235 | if (const auto *VAM = dyn_cast<ValueAsMetadata>(Val: MAV->getMetadata())) |
236 | Op = VAM->getValue(); |
237 | |
238 | if ((isa<Constant>(Val: Op) && !isa<GlobalValue>(Val: *Op)) || |
239 | isa<InlineAsm>(Val: Op)) |
240 | map(V: Op); |
241 | } |
242 | } |
243 | } |
244 | } |
245 | } |
246 | |
247 | void ValueMapping::map(const Value *V) { |
248 | if (!V->hasUseList()) |
249 | return; |
250 | |
251 | if (IDs.lookup(Val: V)) |
252 | return; |
253 | |
254 | if (auto *C = dyn_cast<Constant>(Val: V)) |
255 | if (!isa<GlobalValue>(Val: C)) |
256 | for (const Value *Op : C->operands()) |
257 | map(V: Op); |
258 | |
259 | Values.push_back(x: V); |
260 | IDs[V] = Values.size(); |
261 | } |
262 | |
263 | #ifndef NDEBUG |
264 | static void dumpMapping(const ValueMapping &VM) { |
265 | dbgs() << "value-mapping (size = " << VM.Values.size() << "):\n" ; |
266 | for (unsigned I = 0, E = VM.Values.size(); I != E; ++I) { |
267 | dbgs() << " - id = " << I << ", value = " ; |
268 | VM.Values[I]->dump(); |
269 | } |
270 | } |
271 | |
272 | static void debugValue(const ValueMapping &M, unsigned I, StringRef Desc) { |
273 | const Value *V = M.Values[I]; |
274 | dbgs() << " - " << Desc << " value = " ; |
275 | V->dump(); |
276 | for (const Use &U : V->uses()) { |
277 | dbgs() << " => use: op = " << U.getOperandNo() |
278 | << ", user-id = " << M.IDs.lookup(U.getUser()) << ", user = " ; |
279 | U.getUser()->dump(); |
280 | } |
281 | } |
282 | |
283 | static void debugUserMismatch(const ValueMapping &L, const ValueMapping &R, |
284 | unsigned I) { |
285 | dbgs() << " - fail: user mismatch: ID = " << I << "\n" ; |
286 | debugValue(L, I, "LHS" ); |
287 | debugValue(R, I, "RHS" ); |
288 | |
289 | dbgs() << "\nlhs-" ; |
290 | dumpMapping(L); |
291 | dbgs() << "\nrhs-" ; |
292 | dumpMapping(R); |
293 | } |
294 | |
295 | static void debugSizeMismatch(const ValueMapping &L, const ValueMapping &R) { |
296 | dbgs() << " - fail: map size: " << L.Values.size() |
297 | << " != " << R.Values.size() << "\n" ; |
298 | dbgs() << "\nlhs-" ; |
299 | dumpMapping(L); |
300 | dbgs() << "\nrhs-" ; |
301 | dumpMapping(R); |
302 | } |
303 | #endif |
304 | |
305 | static bool matches(const ValueMapping &LM, const ValueMapping &RM) { |
306 | LLVM_DEBUG(dbgs() << "compare value maps\n" ); |
307 | if (LM.Values.size() != RM.Values.size()) { |
308 | LLVM_DEBUG(debugSizeMismatch(LM, RM)); |
309 | return false; |
310 | } |
311 | |
312 | // This mapping doesn't include dangling constant users, since those don't |
313 | // get serialized. However, checking if users are constant and calling |
314 | // isConstantUsed() on every one is very expensive. Instead, just check if |
315 | // the user is mapped. |
316 | auto skipUnmappedUsers = |
317 | [&](Value::const_use_iterator &U, Value::const_use_iterator E, |
318 | const ValueMapping &M) { |
319 | while (U != E && !M.lookup(V: U->getUser())) |
320 | ++U; |
321 | }; |
322 | |
323 | // Iterate through all values, and check that both mappings have the same |
324 | // users. |
325 | for (unsigned I = 0, E = LM.Values.size(); I != E; ++I) { |
326 | const Value *L = LM.Values[I]; |
327 | const Value *R = RM.Values[I]; |
328 | auto LU = L->use_begin(), LE = L->use_end(); |
329 | auto RU = R->use_begin(), RE = R->use_end(); |
330 | skipUnmappedUsers(LU, LE, LM); |
331 | skipUnmappedUsers(RU, RE, RM); |
332 | |
333 | while (LU != LE) { |
334 | if (RU == RE) { |
335 | LLVM_DEBUG(debugUserMismatch(LM, RM, I)); |
336 | return false; |
337 | } |
338 | if (LM.lookup(V: LU->getUser()) != RM.lookup(V: RU->getUser())) { |
339 | LLVM_DEBUG(debugUserMismatch(LM, RM, I)); |
340 | return false; |
341 | } |
342 | if (LU->getOperandNo() != RU->getOperandNo()) { |
343 | LLVM_DEBUG(debugUserMismatch(LM, RM, I)); |
344 | return false; |
345 | } |
346 | skipUnmappedUsers(++LU, LE, LM); |
347 | skipUnmappedUsers(++RU, RE, RM); |
348 | } |
349 | if (RU != RE) { |
350 | LLVM_DEBUG(debugUserMismatch(LM, RM, I)); |
351 | return false; |
352 | } |
353 | } |
354 | |
355 | return true; |
356 | } |
357 | |
358 | static void verifyAfterRoundTrip(const Module &M, |
359 | std::unique_ptr<Module> OtherM) { |
360 | if (!OtherM) |
361 | report_fatal_error(reason: "parsing failed" ); |
362 | if (verifyModule(M: *OtherM, OS: &errs())) |
363 | report_fatal_error(reason: "verification failed" ); |
364 | if (!matches(LM: ValueMapping(M), RM: ValueMapping(*OtherM))) |
365 | report_fatal_error(reason: "use-list order changed" ); |
366 | } |
367 | |
368 | static void verifyBitcodeUseListOrder(const Module &M) { |
369 | TempFile F; |
370 | if (F.init(Ext: "bc" )) |
371 | report_fatal_error(reason: "failed to initialize bitcode file" ); |
372 | |
373 | if (F.writeBitcode(M)) |
374 | report_fatal_error(reason: "failed to write bitcode" ); |
375 | |
376 | LLVMContext Context; |
377 | verifyAfterRoundTrip(M, OtherM: F.readBitcode(Context)); |
378 | } |
379 | |
380 | static void verifyAssemblyUseListOrder(const Module &M) { |
381 | TempFile F; |
382 | if (F.init(Ext: "ll" )) |
383 | report_fatal_error(reason: "failed to initialize assembly file" ); |
384 | |
385 | if (F.writeAssembly(M)) |
386 | report_fatal_error(reason: "failed to write assembly" ); |
387 | |
388 | LLVMContext Context; |
389 | verifyAfterRoundTrip(M, OtherM: F.readAssembly(Context)); |
390 | } |
391 | |
392 | static void verifyUseListOrder(const Module &M) { |
393 | outs() << "verify bitcode\n" ; |
394 | verifyBitcodeUseListOrder(M); |
395 | outs() << "verify assembly\n" ; |
396 | verifyAssemblyUseListOrder(M); |
397 | } |
398 | |
399 | static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen, |
400 | DenseSet<Value *> &Seen) { |
401 | if (!V->hasUseList()) |
402 | return; |
403 | |
404 | if (!Seen.insert(V).second) |
405 | return; |
406 | |
407 | if (auto *C = dyn_cast<Constant>(Val: V)) |
408 | if (!isa<GlobalValue>(Val: C)) |
409 | for (Value *Op : C->operands()) |
410 | shuffleValueUseLists(V: Op, Gen, Seen); |
411 | |
412 | if (V->use_empty() || std::next(x: V->use_begin()) == V->use_end()) |
413 | // Nothing to shuffle for 0 or 1 users. |
414 | return; |
415 | |
416 | // Generate random numbers between 10 and 99, which will line up nicely in |
417 | // debug output. We're not worried about collisions here. |
418 | LLVM_DEBUG(dbgs() << "V = " ; V->dump()); |
419 | std::uniform_int_distribution<short> Dist(10, 99); |
420 | SmallDenseMap<const Use *, short, 16> Order; |
421 | auto compareUses = |
422 | [&Order](const Use &L, const Use &R) { return Order[&L] < Order[&R]; }; |
423 | do { |
424 | for (const Use &U : V->uses()) { |
425 | auto I = Dist(Gen); |
426 | Order[&U] = I; |
427 | LLVM_DEBUG(dbgs() << " - order: " << I << ", op = " << U.getOperandNo() |
428 | << ", U = " ; |
429 | U.getUser()->dump()); |
430 | } |
431 | } while (llvm::is_sorted(Range: V->uses(), C: compareUses)); |
432 | |
433 | LLVM_DEBUG(dbgs() << " => shuffle\n" ); |
434 | V->sortUseList(Cmp: compareUses); |
435 | |
436 | LLVM_DEBUG({ |
437 | for (const Use &U : V->uses()) { |
438 | dbgs() << " - order: " << Order.lookup(&U) |
439 | << ", op = " << U.getOperandNo() << ", U = " ; |
440 | U.getUser()->dump(); |
441 | } |
442 | }); |
443 | } |
444 | |
445 | static void reverseValueUseLists(Value *V, DenseSet<Value *> &Seen) { |
446 | if (!V->hasUseList()) |
447 | return; |
448 | |
449 | if (!Seen.insert(V).second) |
450 | return; |
451 | |
452 | if (auto *C = dyn_cast<Constant>(Val: V)) |
453 | if (!isa<GlobalValue>(Val: C)) |
454 | for (Value *Op : C->operands()) |
455 | reverseValueUseLists(V: Op, Seen); |
456 | |
457 | if (V->use_empty() || std::next(x: V->use_begin()) == V->use_end()) |
458 | // Nothing to shuffle for 0 or 1 users. |
459 | return; |
460 | |
461 | LLVM_DEBUG({ |
462 | dbgs() << "V = " ; |
463 | V->dump(); |
464 | for (const Use &U : V->uses()) { |
465 | dbgs() << " - order: op = " << U.getOperandNo() << ", U = " ; |
466 | U.getUser()->dump(); |
467 | } |
468 | dbgs() << " => reverse\n" ; |
469 | }); |
470 | |
471 | V->reverseUseList(); |
472 | |
473 | LLVM_DEBUG({ |
474 | for (const Use &U : V->uses()) { |
475 | dbgs() << " - order: op = " << U.getOperandNo() << ", U = " ; |
476 | U.getUser()->dump(); |
477 | } |
478 | }); |
479 | } |
480 | |
481 | template <class Changer> |
482 | static void changeUseLists(Module &M, Changer changeValueUseList) { |
483 | // Visit every value that would be serialized to an IR file. |
484 | // |
485 | // Globals. |
486 | for (GlobalVariable &G : M.globals()) |
487 | changeValueUseList(&G); |
488 | for (GlobalAlias &A : M.aliases()) |
489 | changeValueUseList(&A); |
490 | for (GlobalIFunc &IF : M.ifuncs()) |
491 | changeValueUseList(&IF); |
492 | for (Function &F : M) |
493 | changeValueUseList(&F); |
494 | |
495 | // Constants used by globals. |
496 | for (GlobalVariable &G : M.globals()) |
497 | if (G.hasInitializer()) |
498 | changeValueUseList(G.getInitializer()); |
499 | for (GlobalAlias &A : M.aliases()) |
500 | changeValueUseList(A.getAliasee()); |
501 | for (GlobalIFunc &IF : M.ifuncs()) |
502 | changeValueUseList(IF.getResolver()); |
503 | for (Function &F : M) |
504 | for (Value *Op : F.operands()) |
505 | changeValueUseList(Op); |
506 | |
507 | // Function bodies. |
508 | for (Function &F : M) { |
509 | for (Argument &A : F.args()) |
510 | changeValueUseList(&A); |
511 | for (BasicBlock &BB : F) |
512 | changeValueUseList(&BB); |
513 | for (BasicBlock &BB : F) |
514 | for (Instruction &I : BB) |
515 | changeValueUseList(&I); |
516 | |
517 | // Constants used by instructions. |
518 | for (BasicBlock &BB : F) |
519 | for (Instruction &I : BB) |
520 | for (Value *Op : I.operands()) { |
521 | // Look through a metadata wrapper. |
522 | if (auto *MAV = dyn_cast<MetadataAsValue>(Val: Op)) |
523 | if (auto *VAM = dyn_cast<ValueAsMetadata>(Val: MAV->getMetadata())) |
524 | Op = VAM->getValue(); |
525 | if ((isa<Constant>(Val: Op) && !isa<GlobalValue>(Val: *Op)) || |
526 | isa<InlineAsm>(Val: Op)) |
527 | changeValueUseList(Op); |
528 | } |
529 | } |
530 | |
531 | if (verifyModule(M, OS: &errs())) |
532 | report_fatal_error(reason: "verification failed" ); |
533 | } |
534 | |
535 | static void shuffleUseLists(Module &M, unsigned SeedOffset) { |
536 | std::minstd_rand0 Gen(std::minstd_rand0::default_seed + SeedOffset); |
537 | DenseSet<Value *> Seen; |
538 | changeUseLists(M, changeValueUseList: [&](Value *V) { shuffleValueUseLists(V, Gen, Seen); }); |
539 | LLVM_DEBUG(dbgs() << "\n" ); |
540 | } |
541 | |
542 | static void reverseUseLists(Module &M) { |
543 | DenseSet<Value *> Seen; |
544 | changeUseLists(M, changeValueUseList: [&](Value *V) { reverseValueUseLists(V, Seen); }); |
545 | LLVM_DEBUG(dbgs() << "\n" ); |
546 | } |
547 | |
548 | int main(int argc, char **argv) { |
549 | InitLLVM X(argc, argv); |
550 | |
551 | // Enable debug stream buffering. |
552 | EnableDebugBuffering = true; |
553 | |
554 | cl::HideUnrelatedOptions(Category&: Cat); |
555 | cl::ParseCommandLineOptions(argc, argv, |
556 | Overview: "llvm tool to verify use-list order\n" ); |
557 | |
558 | LLVMContext Context; |
559 | SMDiagnostic Err; |
560 | |
561 | // Load the input module... |
562 | std::unique_ptr<Module> M = parseIRFile(Filename: InputFilename, Err, Context); |
563 | |
564 | if (!M) { |
565 | Err.print(ProgName: argv[0], S&: errs()); |
566 | return 1; |
567 | } |
568 | if (verifyModule(M: *M, OS: &errs())) { |
569 | errs() << argv[0] << ": " << InputFilename |
570 | << ": error: input module is broken!\n" ; |
571 | return 1; |
572 | } |
573 | |
574 | // Verify the use lists now and after reversing them. |
575 | outs() << "*** verify-uselistorder ***\n" ; |
576 | verifyUseListOrder(M: *M); |
577 | outs() << "reverse\n" ; |
578 | reverseUseLists(M&: *M); |
579 | verifyUseListOrder(M: *M); |
580 | |
581 | for (unsigned I = 0, E = NumShuffles; I != E; ++I) { |
582 | outs() << "\n" ; |
583 | |
584 | // Shuffle with a different (deterministic) seed each time. |
585 | outs() << "shuffle (" << I + 1 << " of " << E << ")\n" ; |
586 | shuffleUseLists(M&: *M, SeedOffset: I); |
587 | |
588 | // Verify again before and after reversing. |
589 | verifyUseListOrder(M: *M); |
590 | outs() << "reverse\n" ; |
591 | reverseUseLists(M&: *M); |
592 | verifyUseListOrder(M: *M); |
593 | } |
594 | |
595 | return 0; |
596 | } |
597 | |