1abe3e5baSRiver Riddle //===- LocationSnapshot.cpp - Location Snapshot Utilities -----------------===//
2abe3e5baSRiver Riddle //
3abe3e5baSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4abe3e5baSRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
5abe3e5baSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6abe3e5baSRiver Riddle //
7abe3e5baSRiver Riddle //===----------------------------------------------------------------------===//
8abe3e5baSRiver Riddle 
9abe3e5baSRiver Riddle #include "mlir/Transforms/LocationSnapshot.h"
101834ad4aSRiver Riddle #include "PassDetail.h"
11abe3e5baSRiver Riddle #include "mlir/IR/AsmState.h"
12abe3e5baSRiver Riddle #include "mlir/IR/Builders.h"
13abe3e5baSRiver Riddle #include "mlir/Support/FileUtilities.h"
14abe3e5baSRiver Riddle #include "llvm/Support/FileSystem.h"
15abe3e5baSRiver Riddle #include "llvm/Support/ToolOutputFile.h"
16abe3e5baSRiver Riddle 
17abe3e5baSRiver Riddle using namespace mlir;
18abe3e5baSRiver Riddle 
19abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
20abe3e5baSRiver Riddle /// IR to the given stream, and using the printed locations within that stream.
21abe3e5baSRiver Riddle /// If a 'tag' is non-empty, the generated locations are represented as a
22abe3e5baSRiver Riddle /// NameLoc with the given tag as the name, and then fused with the existing
23abe3e5baSRiver Riddle /// locations. Otherwise, the existing locations are replaced.
generateLocationsFromIR(raw_ostream & os,StringRef fileName,Operation * op,const OpPrintingFlags & flags,StringRef tag)24abe3e5baSRiver Riddle static void generateLocationsFromIR(raw_ostream &os, StringRef fileName,
252c0f1798SItai Zukerman                                     Operation *op, const OpPrintingFlags &flags,
26abe3e5baSRiver Riddle                                     StringRef tag) {
27abe3e5baSRiver Riddle   // Print the IR to the stream, and collect the raw line+column information.
28abe3e5baSRiver Riddle   AsmState::LocationMap opToLineCol;
292c0f1798SItai Zukerman   AsmState state(op, flags, &opToLineCol);
30*988a3ba0SSergei Grechanik   op->print(os, state);
31abe3e5baSRiver Riddle 
32abe3e5baSRiver Riddle   Builder builder(op->getContext());
33195730a6SRiver Riddle   Optional<StringAttr> tagIdentifier;
34abe3e5baSRiver Riddle   if (!tag.empty())
35195730a6SRiver Riddle     tagIdentifier = builder.getStringAttr(tag);
36abe3e5baSRiver Riddle 
37abe3e5baSRiver Riddle   // Walk and generate new locations for each of the operations.
38195730a6SRiver Riddle   StringAttr file = builder.getStringAttr(fileName);
39abe3e5baSRiver Riddle   op->walk([&](Operation *opIt) {
40abe3e5baSRiver Riddle     // Check to see if this operation has a mapped location. Some operations may
41abe3e5baSRiver Riddle     // be elided from the printed form, e.g. the body terminators of some region
42abe3e5baSRiver Riddle     // operations.
43abe3e5baSRiver Riddle     auto it = opToLineCol.find(opIt);
44abe3e5baSRiver Riddle     if (it == opToLineCol.end())
45abe3e5baSRiver Riddle       return;
46abe3e5baSRiver Riddle     const std::pair<unsigned, unsigned> &lineCol = it->second;
47e6260ad0SRiver Riddle     auto newLoc = FileLineColLoc::get(file, lineCol.first, lineCol.second);
48abe3e5baSRiver Riddle 
49abe3e5baSRiver Riddle     // If we don't have a tag, set the location directly
50abe3e5baSRiver Riddle     if (!tagIdentifier) {
51abe3e5baSRiver Riddle       opIt->setLoc(newLoc);
52abe3e5baSRiver Riddle       return;
53abe3e5baSRiver Riddle     }
54abe3e5baSRiver Riddle 
55abe3e5baSRiver Riddle     // Otherwise, build a fused location with the existing op loc.
56abe3e5baSRiver Riddle     opIt->setLoc(builder.getFusedLoc(
57abe3e5baSRiver Riddle         {opIt->getLoc(), NameLoc::get(*tagIdentifier, newLoc)}));
58abe3e5baSRiver Riddle   });
59abe3e5baSRiver Riddle }
60abe3e5baSRiver Riddle 
61abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
62abe3e5baSRiver Riddle /// IR to the given file, and using the printed locations within that file. If
63abe3e5baSRiver Riddle /// `filename` is empty, a temporary file is generated instead.
generateLocationsFromIR(StringRef fileName,Operation * op,OpPrintingFlags flags,StringRef tag)64abe3e5baSRiver Riddle static LogicalResult generateLocationsFromIR(StringRef fileName, Operation *op,
65abe3e5baSRiver Riddle                                              OpPrintingFlags flags,
66abe3e5baSRiver Riddle                                              StringRef tag) {
67abe3e5baSRiver Riddle   // If a filename wasn't provided, then generate one.
68abe3e5baSRiver Riddle   SmallString<32> filepath(fileName);
69abe3e5baSRiver Riddle   if (filepath.empty()) {
70abe3e5baSRiver Riddle     if (std::error_code error = llvm::sys::fs::createTemporaryFile(
71abe3e5baSRiver Riddle             "mlir_snapshot", "tmp.mlir", filepath)) {
72abe3e5baSRiver Riddle       return op->emitError()
73abe3e5baSRiver Riddle              << "failed to generate temporary file for location snapshot: "
74abe3e5baSRiver Riddle              << error.message();
75abe3e5baSRiver Riddle     }
76abe3e5baSRiver Riddle   }
77abe3e5baSRiver Riddle 
78abe3e5baSRiver Riddle   // Open the output file for emission.
79abe3e5baSRiver Riddle   std::string error;
80abe3e5baSRiver Riddle   std::unique_ptr<llvm::ToolOutputFile> outputFile =
81abe3e5baSRiver Riddle       openOutputFile(filepath, &error);
82abe3e5baSRiver Riddle   if (!outputFile)
83abe3e5baSRiver Riddle     return op->emitError() << error;
84abe3e5baSRiver Riddle 
85abe3e5baSRiver Riddle   // Generate the intermediate locations.
86abe3e5baSRiver Riddle   generateLocationsFromIR(outputFile->os(), filepath, op, flags, tag);
87abe3e5baSRiver Riddle   outputFile->keep();
88abe3e5baSRiver Riddle   return success();
89abe3e5baSRiver Riddle }
90abe3e5baSRiver Riddle 
91abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
92abe3e5baSRiver Riddle /// IR to the given stream, and using the printed locations within that stream.
93abe3e5baSRiver Riddle /// The generated locations replace the current operation locations.
generateLocationsFromIR(raw_ostream & os,StringRef fileName,Operation * op,OpPrintingFlags flags)94abe3e5baSRiver Riddle void mlir::generateLocationsFromIR(raw_ostream &os, StringRef fileName,
95abe3e5baSRiver Riddle                                    Operation *op, OpPrintingFlags flags) {
96abe3e5baSRiver Riddle   ::generateLocationsFromIR(os, fileName, op, flags, /*tag=*/StringRef());
97abe3e5baSRiver Riddle }
98abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
99abe3e5baSRiver Riddle /// IR to the given file, and using the printed locations within that file. If
100abe3e5baSRiver Riddle /// `filename` is empty, a temporary file is generated instead.
generateLocationsFromIR(StringRef fileName,Operation * op,OpPrintingFlags flags)101abe3e5baSRiver Riddle LogicalResult mlir::generateLocationsFromIR(StringRef fileName, Operation *op,
102abe3e5baSRiver Riddle                                             OpPrintingFlags flags) {
103abe3e5baSRiver Riddle   return ::generateLocationsFromIR(fileName, op, flags, /*tag=*/StringRef());
104abe3e5baSRiver Riddle }
105abe3e5baSRiver Riddle 
106abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
107abe3e5baSRiver Riddle /// IR to the given stream, and using the printed locations within that stream.
108abe3e5baSRiver Riddle /// The generated locations are represented as a NameLoc with the given tag as
109abe3e5baSRiver Riddle /// the name, and then fused with the existing locations.
generateLocationsFromIR(raw_ostream & os,StringRef fileName,StringRef tag,Operation * op,OpPrintingFlags flags)110abe3e5baSRiver Riddle void mlir::generateLocationsFromIR(raw_ostream &os, StringRef fileName,
111abe3e5baSRiver Riddle                                    StringRef tag, Operation *op,
112abe3e5baSRiver Riddle                                    OpPrintingFlags flags) {
113abe3e5baSRiver Riddle   ::generateLocationsFromIR(os, fileName, op, flags, tag);
114abe3e5baSRiver Riddle }
115abe3e5baSRiver Riddle /// This function generates new locations from the given IR by snapshotting the
116abe3e5baSRiver Riddle /// IR to the given file, and using the printed locations within that file. If
117abe3e5baSRiver Riddle /// `filename` is empty, a temporary file is generated instead.
generateLocationsFromIR(StringRef fileName,StringRef tag,Operation * op,OpPrintingFlags flags)118abe3e5baSRiver Riddle LogicalResult mlir::generateLocationsFromIR(StringRef fileName, StringRef tag,
119abe3e5baSRiver Riddle                                             Operation *op,
120abe3e5baSRiver Riddle                                             OpPrintingFlags flags) {
121abe3e5baSRiver Riddle   return ::generateLocationsFromIR(fileName, op, flags, tag);
122abe3e5baSRiver Riddle }
123abe3e5baSRiver Riddle 
124abe3e5baSRiver Riddle namespace {
12580aca1eaSRiver Riddle struct LocationSnapshotPass
1261834ad4aSRiver Riddle     : public LocationSnapshotBase<LocationSnapshotPass> {
127abe3e5baSRiver Riddle   LocationSnapshotPass() = default;
LocationSnapshotPass__anon232a03400211::LocationSnapshotPass128abe3e5baSRiver Riddle   LocationSnapshotPass(OpPrintingFlags flags, StringRef fileName, StringRef tag)
129abe3e5baSRiver Riddle       : flags(flags) {
130abe3e5baSRiver Riddle     this->fileName = fileName.str();
131abe3e5baSRiver Riddle     this->tag = tag.str();
132abe3e5baSRiver Riddle   }
133abe3e5baSRiver Riddle 
runOnOperation__anon232a03400211::LocationSnapshotPass134abe3e5baSRiver Riddle   void runOnOperation() override {
135abe3e5baSRiver Riddle     Operation *op = getOperation();
136abe3e5baSRiver Riddle     if (failed(generateLocationsFromIR(fileName, op, OpPrintingFlags(), tag)))
137abe3e5baSRiver Riddle       return signalPassFailure();
138abe3e5baSRiver Riddle   }
139abe3e5baSRiver Riddle 
140abe3e5baSRiver Riddle   /// The printing flags to use when creating the snapshot.
141abe3e5baSRiver Riddle   OpPrintingFlags flags;
142abe3e5baSRiver Riddle };
143be0a7e9fSMehdi Amini } // namespace
144abe3e5baSRiver Riddle 
createLocationSnapshotPass(OpPrintingFlags flags,StringRef fileName,StringRef tag)145abe3e5baSRiver Riddle std::unique_ptr<Pass> mlir::createLocationSnapshotPass(OpPrintingFlags flags,
146abe3e5baSRiver Riddle                                                        StringRef fileName,
147abe3e5baSRiver Riddle                                                        StringRef tag) {
148abe3e5baSRiver Riddle   return std::make_unique<LocationSnapshotPass>(flags, fileName, tag);
149abe3e5baSRiver Riddle }
createLocationSnapshotPass()1508155e41aSRiver Riddle std::unique_ptr<Pass> mlir::createLocationSnapshotPass() {
1518155e41aSRiver Riddle   return std::make_unique<LocationSnapshotPass>();
1528155e41aSRiver Riddle }
153