CIRCT  20.0.0git
AssignOutputDirs.cpp
Go to the documentation of this file.
1 //===- AssignOutputDirs.cpp - Assign Output Directories ---------*- 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 
16 #include "circt/Support/Debug.h"
17 #include "llvm/ADT/DenseMap.h"
18 #include "llvm/ADT/PostOrderIterator.h"
19 #include "llvm/ADT/SmallPtrSet.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/Path.h"
22 
23 #define DEBUG_TYPE "firrtl-assign-output-dirs"
24 
25 namespace circt {
26 namespace firrtl {
27 #define GEN_PASS_DEF_ASSIGNOUTPUTDIRS
28 #include "circt/Dialect/FIRRTL/Passes.h.inc"
29 } // namespace firrtl
30 } // namespace circt
31 
32 using namespace circt;
33 using namespace firrtl;
34 namespace path = llvm::sys::path;
35 namespace fs = llvm::sys::fs;
36 
37 using hw::OutputFileAttr;
38 
39 // If moduleOutputDir is a relative path, convert it to an absolute path, by
40 // interpreting moduleOutputDir as relative to the outputDir.
41 static void makeAbsolute(StringRef outputDir,
42  SmallString<64> &moduleOutputDir) {
43  auto sep = llvm::sys::path::get_separator();
44  if (!moduleOutputDir.empty())
45  assert(moduleOutputDir.ends_with(sep));
46  fs::make_absolute(outputDir, moduleOutputDir);
47  path::remove_dots(moduleOutputDir, true);
48  moduleOutputDir += sep;
49 }
50 
51 // If outputDir is a prefix of moduleOutputDir, then make moduleOutputDir
52 // relative to outputDir. Otherwise, leave moduleOutputDir as absolute.
53 static void tryMakeRelative(StringRef outputDir,
54  SmallString<64> &moduleOutputDir) {
55  if (moduleOutputDir.starts_with(outputDir))
56  moduleOutputDir.erase(moduleOutputDir.begin(),
57  moduleOutputDir.begin() + outputDir.size());
58 }
59 
60 static void makeCommonPrefix(StringRef outputDir, SmallString<64> &a,
61  OutputFileAttr attr) {
62  if (attr) {
63  SmallString<64> b(attr.getDirectory());
64  makeAbsolute(outputDir, b);
65  makeCommonPrefix(a, b);
66  } else {
67  makeCommonPrefix(a, outputDir);
68  }
69 }
70 
71 static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op) {
72  return op->getAttrOfType<hw::OutputFileAttr>("output_file");
73 }
74 
75 namespace {
76 struct AssignOutputDirsPass
77  : public circt::firrtl::impl::AssignOutputDirsBase<AssignOutputDirsPass> {
78  AssignOutputDirsPass(StringRef outputDir) {
79  if (!outputDir.empty())
80  outputDirOption = std::string(outputDir);
81  }
82 
83  void runOnOperation() override;
84 };
85 } // namespace
86 
87 void AssignOutputDirsPass::runOnOperation() {
88  LLVM_DEBUG(debugPassHeader(this) << "\n");
89  SmallString<64> outputDir(outputDirOption);
90  if (fs::make_absolute(outputDir)) {
91  emitError(mlir::UnknownLoc::get(&getContext()),
92  "failed to convert the output directory to an absolute path");
93  signalPassFailure();
94  return;
95  }
96  path::remove_dots(outputDir, true);
97  auto sep = path::get_separator();
98  if (!outputDir.ends_with(sep))
99  outputDir.append(sep);
100 
101  bool changed = false;
102 
103  LLVM_DEBUG(llvm::dbgs() << "Updating modules:\n");
104  DenseSet<InstanceGraphNode *> visited;
105  for (auto *root : getAnalysis<InstanceGraph>()) {
106  for (auto *node : llvm::inverse_post_order_ext(root, visited)) {
107  FModuleLike moduleLike =
108  dyn_cast<FModuleLike>(node->getModule().getOperation());
109  if (!moduleLike || !isa<FModuleOp, FExtModuleOp>(moduleLike))
110  continue;
111  if (moduleLike->getAttrOfType<hw::OutputFileAttr>("output_file") ||
112  moduleLike.isPublic())
113  continue;
114 
115  // Get the output directory of the first parent, and then fold the current
116  // output directory with the LCA of all other discovered output
117  // directories.
118  SmallString<64> moduleOutputDir;
119  auto i = node->usesBegin();
120  auto e = node->usesEnd();
121  for (; i != e; ++i) {
122  auto parent = (*i)->getParent()->getModule();
123  auto file = getOutputFile(parent);
124  if (file) {
125  moduleOutputDir = file.getDirectory();
126  makeAbsolute(outputDir, moduleOutputDir);
127  } else {
128  moduleOutputDir = outputDir;
129  }
130  ++i;
131  break;
132  }
133  for (; i != e; ++i) {
134  auto parent = (*i)->getParent()->getModule();
135  makeCommonPrefix(outputDir, moduleOutputDir, getOutputFile(parent));
136  }
137 
138  tryMakeRelative(outputDir, moduleOutputDir);
139  if (!moduleOutputDir.empty()) {
140  auto f =
141  hw::OutputFileAttr::getAsDirectory(&getContext(), moduleOutputDir);
142  moduleLike->setAttr("output_file", f);
143  changed = true;
144  LLVM_DEBUG({
145  llvm::dbgs() << " - name: " << moduleLike.getName() << "\n"
146  << " directory: " << f.getFilename() << "\n";
147  });
148  }
149  }
150  }
151 
152  if (!changed)
153  markAllAnalysesPreserved();
154  LLVM_DEBUG(debugFooter() << "\n");
155 }
156 
157 std::unique_ptr<mlir::Pass>
158 circt::firrtl::createAssignOutputDirsPass(StringRef outputDir) {
159  return std::make_unique<AssignOutputDirsPass>(outputDir);
160 }
static void makeAbsolute(StringRef outputDir, SmallString< 64 > &moduleOutputDir)
static void tryMakeRelative(StringRef outputDir, SmallString< 64 > &moduleOutputDir)
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
assert(baseType &&"element must be base type")
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
std::unique_ptr< mlir::Pass > createAssignOutputDirsPass(mlir::StringRef outputDir="")
void makeCommonPrefix(SmallString< 64 > &a, StringRef b)
Truncate a to the common prefix of a and b.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition: Debug.cpp:31
llvm::raw_ostream & debugFooter(int width=80)
Write a boilerplate footer to the debug stream to indicate that a pass has ended.
Definition: Debug.cpp:35