CIRCT  20.0.0git
GeneratorCallout.cpp
Go to the documentation of this file.
1 //===- GeneratorCallout.cpp - Generator Callout Pass ----------------------===//
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 // Call arbitrary programs and pass them the attributes attached to external
10 // modules.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "circt/Dialect/HW/HWOps.h"
15 #include "circt/Dialect/SV/SVOps.h"
17 #include "mlir/IR/Builders.h"
18 #include "mlir/Pass/Pass.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/Process.h"
22 
23 namespace circt {
24 namespace sv {
25 #define GEN_PASS_DEF_HWGENERATORCALLOUTPASS
26 #include "circt/Dialect/SV/SVPasses.h.inc"
27 } // namespace sv
28 } // namespace circt
29 
30 using namespace circt;
31 using namespace sv;
32 using namespace hw;
33 
34 //===----------------------------------------------------------------------===//
35 // GeneratorCalloutPass
36 //===----------------------------------------------------------------------===//
37 
38 namespace {
39 
40 struct HWGeneratorCalloutPass
41  : public circt::sv::impl::HWGeneratorCalloutPassBase<
42  HWGeneratorCalloutPass> {
43  void runOnOperation() override;
44 
45  void processGenerator(HWModuleGeneratedOp generatedModuleOp,
46  StringRef generatorExe,
47  ArrayRef<StringRef> extraGeneratorArgs);
48 };
49 } // end anonymous namespace
50 
51 void HWGeneratorCalloutPass::runOnOperation() {
52  ModuleOp root = getOperation();
53  SmallVector<StringRef> genOptions;
54  StringRef extraGeneratorArgs(genExecArgs);
55  extraGeneratorArgs.split(genOptions, ';');
56 
57  SmallString<32> execName = llvm::sys::path::filename(genExecutable);
58  SmallString<32> execPath = llvm::sys::path::parent_path(genExecutable);
59 
60  auto generatorExe = llvm::sys::findProgramByName(execName, {execPath});
61  // If program not found, search it in $PATH.
62  if (!generatorExe)
63  generatorExe = llvm::sys::findProgramByName(execName);
64  // If cannot find the executable, then nothing to do, return.
65  if (!generatorExe) {
66  root.emitError("cannot find executable '" + execName + "' in path '" +
67  execPath + "'");
68  return;
69  }
70  for (auto &op : llvm::make_early_inc_range(root.getBody()->getOperations())) {
71  if (auto generator = dyn_cast<HWModuleGeneratedOp>(op))
72  processGenerator(generator, *generatorExe, extraGeneratorArgs);
73  }
74 }
75 
76 void HWGeneratorCalloutPass::processGenerator(
77  HWModuleGeneratedOp generatedModuleOp, StringRef generatorExe,
78  ArrayRef<StringRef> extraGeneratorArgs) {
79  // Get the corresponding schema associated with this generated op.
80  auto genSchema =
81  dyn_cast<HWGeneratorSchemaOp>(generatedModuleOp.getGeneratorKindOp());
82  if (!genSchema)
83  return;
84 
85  // Ignore the generator op if the schema does not match the user specified
86  // schema name from command line "-schema-name"
87  if (genSchema.getDescriptor().str() != schemaName)
88  return;
89 
90  SmallVector<std::string> generatorArgs;
91  // First argument should be the executable name.
92  generatorArgs.push_back(generatorExe.str());
93  for (auto o : extraGeneratorArgs)
94  generatorArgs.push_back(o.str());
95 
96  auto moduleName =
97  generatedModuleOp.getVerilogModuleNameAttr().getValue().str();
98  // The moduleName option is not present in the schema, so add it
99  // explicitly.
100  generatorArgs.push_back("--moduleName");
101  generatorArgs.push_back(moduleName);
102  // Iterate over all the attributes in the schema.
103  // Assumption: All the options required by the generator program must be
104  // present in the schema.
105  for (auto attr : genSchema.getRequiredAttrs()) {
106  auto sAttr = cast<StringAttr>(attr);
107  // Get the port name from schema.
108  StringRef portName = sAttr.getValue();
109  generatorArgs.push_back("--" + portName.str());
110  // Get the value for the corresponding port name.
111  auto v = generatedModuleOp->getAttr(portName);
112  if (auto intV = dyn_cast<IntegerAttr>(v))
113  generatorArgs.push_back(std::to_string(intV.getValue().getZExtValue()));
114  else if (auto strV = dyn_cast<StringAttr>(v))
115  generatorArgs.push_back(strV.getValue().str());
116  else {
117  generatedModuleOp.emitError(
118  "portname attribute " + portName +
119  " value specified on the rtl.module.generated operation is not "
120  "handled, "
121  "only integer and string types supported.");
122  return;
123  }
124  }
125  SmallVector<StringRef> generatorArgStrRef;
126  for (const std::string &a : generatorArgs)
127  generatorArgStrRef.push_back(a);
128 
129  std::string errMsg;
130  SmallString<32> genExecOutFileName;
131  auto errCode = llvm::sys::fs::getPotentiallyUniqueTempFileName(
132  "generatorCalloutTemp", StringRef(""), genExecOutFileName);
133  // Default error code is 0.
134  std::error_code ok;
135  if (errCode != ok) {
136  generatedModuleOp.emitError("cannot generate a unique temporary file name");
137  return;
138  }
139  std::optional<StringRef> redirects[] = {
140  std::nullopt, StringRef(genExecOutFileName), std::nullopt};
141  int result = llvm::sys::ExecuteAndWait(
142  generatorExe, generatorArgStrRef, /*Env=*/std::nullopt,
143  /*Redirects=*/redirects,
144  /*SecondsToWait=*/0, /*MemoryLimit=*/0, &errMsg);
145 
146  if (result != 0) {
147  generatedModuleOp.emitError("execution of '" + generatorExe + "' failed");
148  return;
149  }
150 
151  auto bufferRead = llvm::MemoryBuffer::getFile(genExecOutFileName);
152  if (!bufferRead || !*bufferRead) {
153  generatedModuleOp.emitError("execution of '" + generatorExe +
154  "' did not produce any output file named '" +
155  genExecOutFileName + "'");
156  return;
157  }
158 
159  // Only extract the first line from the output.
160  auto fileContent = (*bufferRead)->getBuffer().split('\n').first.str();
161  OpBuilder builder(generatedModuleOp);
162  auto extMod = builder.create<hw::HWModuleExternOp>(
163  generatedModuleOp.getLoc(), generatedModuleOp.getVerilogModuleNameAttr(),
164  generatedModuleOp.getPortList());
165  // Attach an attribute to which file the definition of the external
166  // module exists in.
167  extMod->setAttr("filenames", builder.getStringAttr(fileContent));
168  generatedModuleOp.erase();
169 }
170 
172  return std::make_unique<HWGeneratorCalloutPass>();
173 }
std::unique_ptr< mlir::Pass > createHWGeneratorCalloutPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1
Definition: sv.py:1