CIRCT 21.0.0git
Loading...
Searching...
No Matches
EmitRTGISAAssemblyPass.cpp
Go to the documentation of this file.
1//===- EmitRTGISAAssemblyPass.cpp - RTG Assembly Emitter ------------------===//
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// This is the main ISA Assembly emitter implementation for the RTG dialect.
10//
11//===----------------------------------------------------------------------===//
12
16#include "circt/Support/Path.h"
17#include "mlir/IR/Threading.h"
18#include "mlir/Support/FileUtilities.h"
19#include "llvm/ADT/SmallString.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/TypeSwitch.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/Path.h"
24#include "llvm/Support/ToolOutputFile.h"
25#include "llvm/Support/raw_ostream.h"
26#include <fstream>
27
28namespace circt {
29namespace rtg {
30#define GEN_PASS_DEF_EMITRTGISAASSEMBLYPASS
31#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
32} // namespace rtg
33} // namespace circt
34
35using namespace circt;
36using namespace rtg;
37
38#define DEBUG_TYPE "emit-rtg-isa-assembly"
39
40namespace {
41
42class Emitter {
43public:
44 Emitter(llvm::raw_ostream &os, const DenseSet<StringAttr> &unsupportedInstr)
45 : os(os), unsupportedInstr(unsupportedInstr) {}
46
47 LogicalResult emit(InstructionOpInterface instr) {
48 os << llvm::indent(4);
49 bool useBinary =
50 unsupportedInstr.contains(instr->getName().getIdentifier());
51
52 // TODO: we cannot just assume that double-slash is the way to do a line
53 // comment
54 if (useBinary)
55 os << "# ";
56
57 SmallVector<Attribute> operands;
58 for (auto operand : instr->getOperands()) {
59 if (isa<LabelType>(operand.getType()) && useBinary)
60 return instr->emitError("labels cannot be emitted as binary");
61
62 auto attr = state.lookup(operand);
63 if (!attr)
64 return failure();
65
66 operands.push_back(attr);
67 }
68
69 instr.printInstructionAssembly(os, operands);
70 os << "\n";
71
72 if (!useBinary)
73 return success();
74
75 os << llvm::indent(4);
76 // TODO: don't hardcode '.word'
77 os << ".word 0x";
78 instr.printInstructionBinary(os, operands);
79 os << "\n";
80
81 return success();
82 }
83
84 LogicalResult emit(LabelDeclOp op) {
85 if (!op.getArgs().empty())
86 return op->emitError(
87 "label arguments must be elaborated before emission");
88
89 state[op.getLabel()] = op.getFormatStringAttr();
90 return success();
91 }
92
93 LogicalResult emit(LabelOp op) {
94 auto labelStr = cast<StringAttr>(state[op.getLabel()]).getValue();
95 if (op.getVisibility() == LabelVisibility::external) {
96 os << ".extern " << labelStr << "\n";
97 return success();
98 }
99
100 if (op.getVisibility() == LabelVisibility::global)
101 os << ".global " << labelStr << "\n";
102
103 os << labelStr << ":\n";
104 return success();
105 }
106
107 LogicalResult emit(CommentOp op) {
108 os << llvm::indent(4) << "# " << op.getComment() << "\n";
109 return success();
110 }
111
112 LogicalResult emitTest(rtg::TestOp test, bool emitHeaderFooter = false) {
113 if (emitHeaderFooter)
114 os << "# Begin of " << test.getSymName() << "\n\n";
115
116 for (auto &op : *test.getBody()) {
117 if (op.hasTrait<OpTrait::ConstantLike>()) {
118 SmallVector<OpFoldResult> results;
119 if (failed(op.fold(results)))
120 return failure();
121
122 for (auto [val, res] : llvm::zip(op.getResults(), results)) {
123 auto attr = res.dyn_cast<Attribute>();
124 if (!attr)
125 return failure();
126
127 state[val] = attr;
128 }
129
130 continue;
131 }
132
133 auto res =
134 TypeSwitch<Operation *, LogicalResult>(&op)
135 .Case<InstructionOpInterface, LabelDeclOp, LabelOp, CommentOp>(
136 [&](auto op) { return emit(op); })
137 .Default([](auto op) {
138 return op->emitError("emitter unknown RTG operation");
139 });
140
141 if (failed(res))
142 return failure();
143 }
144
145 state.clear();
146
147 if (emitHeaderFooter)
148 os << "\n# End of " << test.getSymName() << "\n\n";
149
150 return success();
151 }
152
153private:
154 /// Output Stream.
155 llvm::raw_ostream &os;
156
157 /// Instructions to emit in binary.
158 const DenseSet<StringAttr> &unsupportedInstr;
159
160 /// Evaluated values.
161 DenseMap<Value, Attribute> state;
162};
163
164} // namespace
165
166static void
168 const std::string &unsupportedInstructionsFile,
169 DenseSet<StringAttr> &unsupportedInstrs) {
170 if (!unsupportedInstructionsFile.empty()) {
171 std::ifstream input(unsupportedInstructionsFile);
172 std::string token;
173 while (std::getline(input, token, ',')) {
174 auto trimmed = StringRef(token).trim();
175 if (!trimmed.empty())
176 unsupportedInstrs.insert(StringAttr::get(ctxt, trimmed));
177 }
178 }
179}
180
181static std::unique_ptr<llvm::ToolOutputFile>
182createOutputFile(StringRef filename, StringRef dirname,
183 function_ref<InFlightDiagnostic()> emitError) {
184 // Determine the output path from the output directory and filename.
185 SmallString<128> outputFilename(dirname);
186 appendPossiblyAbsolutePath(outputFilename, filename);
187 auto outputDir = llvm::sys::path::parent_path(outputFilename);
188
189 // Create the output directory if needed.
190 std::error_code error = llvm::sys::fs::create_directories(outputDir);
191 if (error) {
192 emitError() << "cannot create output directory \"" << outputDir
193 << "\": " << error.message();
194 return {};
195 }
196
197 // Open the output file.
198 std::string errorMessage;
199 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
200 if (!output)
201 emitError() << errorMessage;
202 return output;
203}
204
205//===----------------------------------------------------------------------===//
206// EmitRTGISAAssemblyPass
207//===----------------------------------------------------------------------===//
208
209namespace {
210struct EmitRTGISAAssemblyPass
211 : public rtg::impl::EmitRTGISAAssemblyPassBase<EmitRTGISAAssemblyPass> {
212 using Base::Base;
213
214 void runOnOperation() override;
215 /// Emit each 'rtg.test' into a separate file using the test's name as the
216 /// filename.
217 LogicalResult emitSplit(const DenseSet<StringAttr> &unsupportedInstr);
218 /// Emit all tests into a single file (or print them to stderr if no file path
219 /// is given).
220 LogicalResult emit(const DenseSet<StringAttr> &unsupportedInstr);
221};
222} // namespace
223
224void EmitRTGISAAssemblyPass::runOnOperation() {
225 if ((!path.hasValue() || path.empty()) && splitOutput) {
226 getOperation().emitError("'split-output' option only valid in combination "
227 "with a valid 'path' argument");
228 return signalPassFailure();
229 }
230
231 DenseSet<StringAttr> unsupportedInstr;
232 for (const auto &instr : unsupportedInstructions)
233 unsupportedInstr.insert(StringAttr::get(&getContext(), instr));
235 &getContext(), unsupportedInstructionsFile.getValue(), unsupportedInstr);
236
237 if (splitOutput) {
238 if (failed(emitSplit(unsupportedInstr)))
239 return signalPassFailure();
240
241 return;
242 }
243
244 if (failed(emit(unsupportedInstr)))
245 return signalPassFailure();
246}
247
248LogicalResult
249EmitRTGISAAssemblyPass::emit(const DenseSet<StringAttr> &unsupportedInstr) {
250 std::unique_ptr<llvm::ToolOutputFile> file;
251 bool emitToFile = path.hasValue() && !path.empty() && path != "-";
252 if (emitToFile) {
253 file = createOutputFile(path, std::string(),
254 [&]() { return getOperation().emitError(); });
255 if (!file)
256 return failure();
257
258 file->keep();
259 }
260
261 Emitter emitter(emitToFile ? file->os()
262 : (path == "-" ? llvm::outs() : llvm::errs()),
263 unsupportedInstr);
264 for (auto test : getOperation().getOps<TestOp>())
265 if (failed(emitter.emitTest(test, true)))
266 return failure();
267
268 return success();
269}
270
271LogicalResult EmitRTGISAAssemblyPass::emitSplit(
272 const DenseSet<StringAttr> &unsupportedInstr) {
273 auto tests = getOperation().getOps<TestOp>();
274 return failableParallelForEach(
275 &getContext(), tests.begin(), tests.end(), [&](rtg::TestOp test) {
276 auto res = createOutputFile(test.getSymName().str() + ".s", path,
277 [&]() { return test.emitError(); });
278 if (!res)
279 return failure();
280
281 res->keep();
282 return Emitter(res->os(), unsupportedInstr).emitTest(test);
283 });
284}
static void parseUnsupportedInstructionsFile(MLIRContext *ctxt, const std::string &unsupportedInstructionsFile, DenseSet< StringAttr > &unsupportedInstrs)
static std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
Definition Path.cpp:23
Definition emit.py:1
Definition rtg.py:1