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