1 | //===- LocationSnapshot.cpp - Location Snapshot Utilities -----------------===// |
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 | #include "mlir/Transforms/LocationSnapshot.h" |
10 | |
11 | #include "mlir/IR/AsmState.h" |
12 | #include "mlir/IR/Builders.h" |
13 | #include "mlir/IR/OperationSupport.h" |
14 | #include "mlir/Pass/Pass.h" |
15 | #include "mlir/Support/FileUtilities.h" |
16 | #include "llvm/Support/FileSystem.h" |
17 | #include "llvm/Support/ToolOutputFile.h" |
18 | #include <optional> |
19 | |
20 | namespace mlir { |
21 | #define GEN_PASS_DEF_LOCATIONSNAPSHOT |
22 | #include "mlir/Transforms/Passes.h.inc" |
23 | } // namespace mlir |
24 | |
25 | using namespace mlir; |
26 | |
27 | /// This function generates new locations from the given IR by snapshotting the |
28 | /// IR to the given stream, and using the printed locations within that stream. |
29 | /// If a 'tag' is non-empty, the generated locations are represented as a |
30 | /// NameLoc with the given tag as the name, and then fused with the existing |
31 | /// locations. Otherwise, the existing locations are replaced. |
32 | static void generateLocationsFromIR(raw_ostream &os, StringRef fileName, |
33 | Operation *op, const OpPrintingFlags &flags, |
34 | StringRef tag) { |
35 | // Print the IR to the stream, and collect the raw line+column information. |
36 | AsmState::LocationMap opToLineCol; |
37 | AsmState state(op, flags, &opToLineCol); |
38 | op->print(os, state); |
39 | |
40 | Builder builder(op->getContext()); |
41 | std::optional<StringAttr> tagIdentifier; |
42 | if (!tag.empty()) |
43 | tagIdentifier = builder.getStringAttr(tag); |
44 | |
45 | // Walk and generate new locations for each of the operations. |
46 | StringAttr file = builder.getStringAttr(fileName); |
47 | op->walk([&](Operation *opIt) { |
48 | // Check to see if this operation has a mapped location. Some operations may |
49 | // be elided from the printed form, e.g. the body terminators of some region |
50 | // operations. |
51 | auto it = opToLineCol.find(opIt); |
52 | if (it == opToLineCol.end()) |
53 | return; |
54 | const std::pair<unsigned, unsigned> &lineCol = it->second; |
55 | auto newLoc = FileLineColLoc::get(file, lineCol.first, lineCol.second); |
56 | |
57 | // If we don't have a tag, set the location directly |
58 | if (!tagIdentifier) { |
59 | opIt->setLoc(newLoc); |
60 | return; |
61 | } |
62 | |
63 | // Otherwise, build a fused location with the existing op loc. |
64 | opIt->setLoc(builder.getFusedLoc( |
65 | {opIt->getLoc(), NameLoc::get(*tagIdentifier, newLoc)})); |
66 | }); |
67 | } |
68 | |
69 | /// This function generates new locations from the given IR by snapshotting the |
70 | /// IR to the given file, and using the printed locations within that file. If |
71 | /// `filename` is empty, a temporary file is generated instead. |
72 | static LogicalResult generateLocationsFromIR(StringRef fileName, Operation *op, |
73 | OpPrintingFlags flags, |
74 | StringRef tag) { |
75 | // If a filename wasn't provided, then generate one. |
76 | SmallString<32> filepath(fileName); |
77 | if (filepath.empty()) { |
78 | if (std::error_code error = llvm::sys::fs::createTemporaryFile( |
79 | Prefix: "mlir_snapshot" , Suffix: "tmp.mlir" , ResultPath&: filepath)) { |
80 | return op->emitError() |
81 | << "failed to generate temporary file for location snapshot: " |
82 | << error.message(); |
83 | } |
84 | } |
85 | |
86 | // Open the output file for emission. |
87 | std::string error; |
88 | std::unique_ptr<llvm::ToolOutputFile> outputFile = |
89 | openOutputFile(outputFilename: filepath, errorMessage: &error); |
90 | if (!outputFile) |
91 | return op->emitError() << error; |
92 | |
93 | // Generate the intermediate locations. |
94 | generateLocationsFromIR(os&: outputFile->os(), fileName: filepath, op, flags, tag); |
95 | outputFile->keep(); |
96 | return success(); |
97 | } |
98 | |
99 | /// This function generates new locations from the given IR by snapshotting the |
100 | /// IR to the given stream, and using the printed locations within that stream. |
101 | /// The generated locations replace the current operation locations. |
102 | void mlir::generateLocationsFromIR(raw_ostream &os, StringRef fileName, |
103 | Operation *op, OpPrintingFlags flags) { |
104 | ::generateLocationsFromIR(os, fileName, op, flags, /*tag=*/StringRef()); |
105 | } |
106 | /// This function generates new locations from the given IR by snapshotting the |
107 | /// IR to the given file, and using the printed locations within that file. If |
108 | /// `filename` is empty, a temporary file is generated instead. |
109 | LogicalResult mlir::generateLocationsFromIR(StringRef fileName, Operation *op, |
110 | OpPrintingFlags flags) { |
111 | return ::generateLocationsFromIR(fileName, op, flags, /*tag=*/StringRef()); |
112 | } |
113 | |
114 | /// This function generates new locations from the given IR by snapshotting the |
115 | /// IR to the given stream, and using the printed locations within that stream. |
116 | /// The generated locations are represented as a NameLoc with the given tag as |
117 | /// the name, and then fused with the existing locations. |
118 | void mlir::generateLocationsFromIR(raw_ostream &os, StringRef fileName, |
119 | StringRef tag, Operation *op, |
120 | OpPrintingFlags flags) { |
121 | ::generateLocationsFromIR(os, fileName, op, flags, tag); |
122 | } |
123 | /// This function generates new locations from the given IR by snapshotting the |
124 | /// IR to the given file, and using the printed locations within that file. If |
125 | /// `filename` is empty, a temporary file is generated instead. |
126 | LogicalResult mlir::generateLocationsFromIR(StringRef fileName, StringRef tag, |
127 | Operation *op, |
128 | OpPrintingFlags flags) { |
129 | return ::generateLocationsFromIR(fileName, op, flags, tag); |
130 | } |
131 | |
132 | namespace { |
133 | struct LocationSnapshotPass |
134 | : public impl::LocationSnapshotBase<LocationSnapshotPass> { |
135 | using impl::LocationSnapshotBase<LocationSnapshotPass>::LocationSnapshotBase; |
136 | |
137 | void runOnOperation() override { |
138 | Operation *op = getOperation(); |
139 | if (failed(generateLocationsFromIR(fileName, op, getFlags(), tag))) |
140 | return signalPassFailure(); |
141 | } |
142 | |
143 | private: |
144 | /// build the flags from the command line arguments to the pass |
145 | OpPrintingFlags getFlags() { |
146 | OpPrintingFlags flags; |
147 | flags.enableDebugInfo(enable: enableDebugInfo, prettyForm: printPrettyDebugInfo); |
148 | flags.printGenericOpForm(enable: printGenericOpForm); |
149 | if (useLocalScope) |
150 | flags.useLocalScope(); |
151 | return flags; |
152 | } |
153 | }; |
154 | } // namespace |
155 | |