CIRCT 20.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 emitTest(rtg::TestOp test, bool emitHeaderFooter = false) {
108 if (emitHeaderFooter)
109 os << "# Begin of " << test.getSymName() << "\n\n";
110
111 for (auto &op : *test.getBody()) {
112 if (op.hasTrait<OpTrait::ConstantLike>()) {
113 SmallVector<OpFoldResult> results;
114 if (failed(op.fold(results)))
115 return failure();
116
117 for (auto [val, res] : llvm::zip(op.getResults(), results)) {
118 auto attr = res.dyn_cast<Attribute>();
119 if (!attr)
120 return failure();
121
122 state[val] = attr;
123 }
124
125 continue;
126 }
127
128 auto res = TypeSwitch<Operation *, LogicalResult>(&op)
129 .Case<InstructionOpInterface, LabelDeclOp, LabelOp>(
130 [&](auto op) { return emit(op); })
131 .Default([](auto op) {
132 return op->emitError("emitter unknown RTG operation");
133 });
134
135 if (failed(res))
136 return failure();
137 }
138
139 state.clear();
140
141 if (emitHeaderFooter)
142 os << "\n# End of " << test.getSymName() << "\n\n";
143
144 return success();
145 }
146
147private:
148 /// Output Stream.
149 llvm::raw_ostream &os;
150
151 /// Instructions to emit in binary.
152 const DenseSet<StringAttr> &unsupportedInstr;
153
154 /// Evaluated values.
155 DenseMap<Value, Attribute> state;
156};
157
158} // namespace
159
160static void
162 const std::string &unsupportedInstructionsFile,
163 DenseSet<StringAttr> &unsupportedInstrs) {
164 if (!unsupportedInstructionsFile.empty()) {
165 std::ifstream input(unsupportedInstructionsFile);
166 std::string token;
167 while (std::getline(input, token, ',')) {
168 auto trimmed = StringRef(token).trim();
169 if (!trimmed.empty())
170 unsupportedInstrs.insert(StringAttr::get(ctxt, trimmed));
171 }
172 }
173}
174
175static std::unique_ptr<llvm::ToolOutputFile>
176createOutputFile(StringRef filename, StringRef dirname,
177 function_ref<InFlightDiagnostic()> emitError) {
178 // Determine the output path from the output directory and filename.
179 SmallString<128> outputFilename(dirname);
180 appendPossiblyAbsolutePath(outputFilename, filename);
181 auto outputDir = llvm::sys::path::parent_path(outputFilename);
182
183 // Create the output directory if needed.
184 std::error_code error = llvm::sys::fs::create_directories(outputDir);
185 if (error) {
186 emitError() << "cannot create output directory \"" << outputDir
187 << "\": " << error.message();
188 return {};
189 }
190
191 // Open the output file.
192 std::string errorMessage;
193 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
194 if (!output)
195 emitError() << errorMessage;
196 return output;
197}
198
199//===----------------------------------------------------------------------===//
200// EmitRTGISAAssemblyPass
201//===----------------------------------------------------------------------===//
202
203namespace {
204struct EmitRTGISAAssemblyPass
205 : public rtg::impl::EmitRTGISAAssemblyPassBase<EmitRTGISAAssemblyPass> {
206 using Base::Base;
207
208 void runOnOperation() override;
209 /// Emit each 'rtg.test' into a separate file using the test's name as the
210 /// filename.
211 LogicalResult emitSplit(const DenseSet<StringAttr> &unsupportedInstr);
212 /// Emit all tests into a single file (or print them to stderr if no file path
213 /// is given).
214 LogicalResult emit(const DenseSet<StringAttr> &unsupportedInstr);
215};
216} // namespace
217
218void EmitRTGISAAssemblyPass::runOnOperation() {
219 if ((!path.hasValue() || path.empty()) && splitOutput) {
220 getOperation().emitError("'split-output' option only valid in combination "
221 "with a valid 'path' argument");
222 return signalPassFailure();
223 }
224
225 DenseSet<StringAttr> unsupportedInstr;
226 for (const auto &instr : unsupportedInstructions)
227 unsupportedInstr.insert(StringAttr::get(&getContext(), instr));
229 &getContext(), unsupportedInstructionsFile.getValue(), unsupportedInstr);
230
231 if (splitOutput) {
232 if (failed(emitSplit(unsupportedInstr)))
233 return signalPassFailure();
234
235 return;
236 }
237
238 if (failed(emit(unsupportedInstr)))
239 return signalPassFailure();
240}
241
242LogicalResult
243EmitRTGISAAssemblyPass::emit(const DenseSet<StringAttr> &unsupportedInstr) {
244 std::unique_ptr<llvm::ToolOutputFile> file;
245 bool emitToFile = path.hasValue() && !path.empty();
246 if (emitToFile) {
247 file = createOutputFile(path, std::string(),
248 [&]() { return getOperation().emitError(); });
249 if (!file)
250 return failure();
251
252 file->keep();
253 }
254
255 Emitter emitter(emitToFile ? file->os() : llvm::errs(), unsupportedInstr);
256 for (auto test : getOperation().getOps<TestOp>())
257 if (failed(emitter.emitTest(test, true)))
258 return failure();
259
260 return success();
261}
262
263LogicalResult EmitRTGISAAssemblyPass::emitSplit(
264 const DenseSet<StringAttr> &unsupportedInstr) {
265 auto tests = getOperation().getOps<TestOp>();
266 return failableParallelForEach(
267 &getContext(), tests.begin(), tests.end(), [&](rtg::TestOp test) {
268 auto res = createOutputFile(test.getSymName().str() + ".s", path,
269 [&]() { return test.emitError(); });
270 if (!res)
271 return failure();
272
273 res->keep();
274 return Emitter(res->os(), unsupportedInstr).emitTest(test);
275 });
276}
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