OpenASIP 2.2
Loading...
Searching...
No Matches
RISCVTDGen.cc
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 Tampere University.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18/**
19 * @file RISCVTDGen.cc
20 *
21 * Implementation of RISCVTDGen class.
22 *
23 * @author Kari Hepola 2024 (kari.hepola@tuni.fi)
24 * @note rating: red
25 */
26
27#include "RISCVTDGen.hh"
28
29#include "BEMGenerator.hh"
30#include "BinaryEncoding.hh"
31#include "Exception.hh"
32#include "InstructionFormat.hh"
33#include "Machine.hh"
34#include "MapTools.hh"
35#include "Operation.hh"
36#include "OperationPool.hh"
40#include "RISCVFields.hh"
41#include "RISCVTools.hh"
42#include "TCEString.hh"
43#include "Application.hh"
44#include "tce_config.h"
45#include <iostream>
46#include <regex>
47#include <sstream>
48#include <iomanip>
49
50#define DEBUG_RISCV_TDGEN 0
51
53 : TDGen(mach, false), bem_(NULL) {
54 bem_ = BEMGenerator(mach).generate();
55 assert(bem_ != NULL);
58}
59
60/**
61 * OpenASIP converts hex numbers to unsigned by default. The converted
62 * number might not fit into i32. This function transforms
63 * the given instruction pattern to use hex encoding.
64 *
65 * @param pattern The instruction pattern.
66 * @return The instruction pattern in hex format.
67 */
68std::string
69RISCVTDGen::decimalsToHex(const std::string& pattern) const {
70 std::regex patternRegex(R"((i32\s)(-?\d+))");
71 std::smatch match;
72 std::string modifiedPattern = pattern;
73
74 auto start = modifiedPattern.cbegin();
75 while (std::regex_search(
76 start, modifiedPattern.cend(), match, patternRegex)) {
77 try {
78 std::string numStr = match[2].str();
79 std::string hexStr;
80
81 if (numStr[0] == '-') {
82 int num = std::stoi(numStr);
83 hexStr = TCEString::intToHexString(num);
84 } else {
85 unsigned long num = std::stoul(numStr);
87 }
88
89 // Calculate the actual start position in the modified string
90 size_t matchPos = match.position(2) + std::distance(
91 modifiedPattern.cbegin(), start);
92 modifiedPattern.replace(matchPos, match.length(2), hexStr);
93 // Move start past the replaced part
94 start = modifiedPattern.cbegin() + matchPos + hexStr.length();
95 } catch (const std::invalid_argument& e) {
96 std::cerr << "Invalid argument: " << e.what() << std::endl;
97 break;
98 } catch (const std::out_of_range& e) {
99 std::cerr << "Out of range error: " << e.what() << std::endl;
100 break;
101 }
102 }
103
104 return modifiedPattern;
105}
106
107
108void
110 customOps_.clear();
111 const std::vector<std::string> formatsToSearch = {
116 };
117 for (const std::string& fName : formatsToSearch) {
118 InstructionFormat* format = bem_->instructionFormat(fName);
119 if (format == NULL) {
120 continue;
121 }
122 for (int i = 0; i < format->operationCount(); i++) {
123 const std::string op = format->operationAtIndex(i);
125 customOps_.insert({op, format->encoding(op)});
126 }
127 }
128 }
129}
130
131// TODO: make this mapping better so that it works for any numIns
132
133std::string
134RISCVTDGen::getFormatType(const std::string& opName) const {
135 OperationPool opPool;
136 Operation& op = opPool.operation(opName.c_str());
137 const unsigned numIns = op.numberOfInputs();
138 const unsigned numOuts = op.numberOfOutputs();
139 if (numIns == 3 && numOuts == 1) {
140 return "OARVR3R";
141 } else if (numIns == 2 && numOuts == 1) {
142 return "OARVR2R";
143 } else if (numIns == 1 && numOuts == 1) {
144 return "OARVR1R";
145 } else if (numIns == 1 && numOuts == 0) {
146 return "OARVR1";
147 } else {
148 std::cerr << "Error: cannot find format type for operation "
149 << op.name() << " with numIns=" << numIns << " numOuts="
150 << numOuts << std::endl;
151 std::cerr << "Legal configurations: " << std::endl;
152 std::cerr << " numIns=3, numOuts=1" << std::endl;
153 std::cerr << " numIns=2, numOuts=1" << std::endl;
154 std::cerr << " numIns=1, numOuts=1" << std::endl;
155 std::cerr << " numIns=1, numOuts=0" << std::endl;
156 assert(false);
157 }
158 return "";
159}
160
161void
163 const std::string& name,
164 const int encoding) const {
165 const TCEString opName = TCEString(name);
166 const std::string f3 = RISCVTools::getFunc3Str(encoding);
167 std::string f7 = RISCVTools::getFunc7Str(encoding);
168 const std::string fType = getFormatType(name);
169 assert(fType != "");
170 std::string opc = "OPC_CUSTOM_0";
171 if (fType == "OARVR3R") {
172 std::string opc = "OPC_CUSTOM_1";
173 // rs3 takes 5 bits from f7
174 f7 = RISCVTools::getFunc2Str(encoding);
175 } else {
176 f7 = RISCVTools::getFunc7Str(encoding);
177 }
178 o << "def " << "OA_" + opName.upper() << " : " << fType << "<" << f7
179 <<", " << f3 << ", " << opc << ", \"" << "oa_" + name << "\">;";
180 o << "\n";
181}
182
183void
186 for (auto op : customOps_) {
187 writeInstructionDeclaration(o, op.first, op.second);
188 }
189}
190
191/**
192 * OpenASIP uses different reg declarations and pattern structure than the
193 * upstream RISC-V BE. This method transforms the TDGen generated patterns
194 * to the RISC-V one.
195 *
196 * @param pattern The pattern generated by TDGen
197 * @return The RISC-V target pattern
198 */
199std::string
201 const unsigned numIns) const {
202 assert(numIns == 3 || numIns == 2 || numIns == 1);
203 // Remove the output specifier
204 size_t pos = pattern.find(',');
205 if (pos != std::string::npos) {
206 pattern = pattern.substr(pos + 2);
207 }
208 // Replace register specifiers
209 TCEString patTCEStr = TCEString(pattern);
210 patTCEStr.replaceString("R32IRegs:$op1", "(XLenVT GPR:$rs1)");
211 if (numIns == 3) {
212 patTCEStr.replaceString("R32IRegs:$op2", "(XLenVT GPR:$rs2)");
213 patTCEStr.replaceString("R32IRegs:$op3", "(XLenVT GPR:$rs3)");
214 patTCEStr.replaceString("R32IRegs:$op4", "(XLenVT GPR:$rd)");
215 } else if (numIns == 2) {
216 patTCEStr.replaceString("R32IRegs:$op2", "(XLenVT GPR:$rs2)");
217 patTCEStr.replaceString("R32IRegs:$op3", "(XLenVT GPR:$rd)");
218 } else {
219 patTCEStr.replaceString("R32IRegs:$op2", "(XLenVT GPR:$rd)");
220 }
221 patTCEStr.replaceString("\n", "");
222 patTCEStr = decimalsToHex(patTCEStr);
223 return patTCEStr;
224}
225
226void
228 const unsigned numIns = op.numberOfInputs();
229 const unsigned numOuts = op.numberOfOutputs();
230 if (!operationCanBeMatched(op) || numIns > 3 || numIns < 1 ||
231 numOuts != 1) {
232 std::cout << "Skipping pattern for " << op.name() << std::endl;
233 return;
234 }
235 std::string pattern;
236 const std::string operandTypes =
237 mach_.is64bit() ? std::string(numIns + numOuts, OT_REG_LONG)
238 : std::string(numIns + numOuts, OT_REG_INT);
239
240 std::vector<OperationDAG*> dags;
241 OperationDAG* trivialDag = NULL;
243
245 op.dagCount() == 0) {
246 trivialDag = createTrivialDAG(op);
247 dags.push_back(trivialDag);
248 } else {
249 const std::vector<OperationDAG*> matchableDAGs =
251 dags.insert(dags.end(), matchableDAGs.begin(), matchableDAGs.end());
252 }
253 for (const OperationDAG* dag : dags) {
254 pattern = operationPattern(op, *dag, operandTypes);
255 o << "def : Pat<(XLenVT ";
256 o << transformTCEPattern(pattern, numIns);
257 o << ", (" << "OA_" + op.name().upper();
258 if (numIns == 3) {
259 o << " GPR:$rs1, GPR:$rs2, GPR:$rs3)>;\n";
260 } else if (numIns == 2) {
261 o << " GPR:$rs1, GPR:$rs2)>;\n";
262 } else {
263 o << " GPR:$rs1)>;\n";
264 }
265 }
266 if (trivialDag != NULL) {
267 delete trivialDag;
268 }
269}
270
271void
273 OperationPool opPool;
274 // o << "// ---- Instruction pattern defs start ----\n\n";
275 for (auto customOp : customOps_) {
276 const std::string opName = customOp.first;
277 Operation& op = opPool.operation(opName.c_str());
279 }
280 // o << "// ---- Instruction pattern defs end ----\n\n";
281}
282
283void
285 std::ostringstream tdDeclarationBuffer;
286 std::ostringstream tdPatternBuffer;
287 writeInstructionDeclarations(tdDeclarationBuffer);
288 declarationStr_ = tdDeclarationBuffer.str();
289 writePatternDefinitions(tdPatternBuffer);
290 patternStr_ = tdPatternBuffer.str();
291
292#ifdef DEBUG_RISCV_TDGEN
293 std::cout << declarationStr_;
294 std::cout << patternStr_;
295#endif
296}
297
298void
299RISCVTDGen::generateBackend(const std::string& path) const {
300 std::ofstream customInstrInfoTD;
301 customInstrInfoTD.open((path + "/RISCVInstrInfoOpenASIP.td").c_str());
302 customInstrInfoTD << declarationStr_;
303 customInstrInfoTD << patternStr_;
304 customInstrInfoTD.close();
305}
306
307std::string
311
312void
313RISCVTDGen::dumpClassDefinitions(std::ostream& o) const {
314 o << "/*\n";
315 o << " * Generated by OpenASIP\n";
316 /* These are not needed since the target contents would be different
317 between versions if necessary. */
318 o << " * OpenASIP version: " << Application::TCEVersionString() << "\n";
319 o << " * LLVM version: " << LLVM_VERSION << "\n";
320 o << " */\n";
321 o << "\n";
322 o << "class OARVInstR1<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode,\n";
323 o << " dag outs, dag ins, string opcodestr, string argstr>\n";
324 o << " : RVInst<outs, ins, opcodestr, argstr, [], InstFormatOther> {\n";
325 o << " bits<5> rs1;\n";
326 o << "\n";
327 o << " let Inst{31-25} = funct7;\n";
328 o << " let Inst{24-20} = 0;\n";
329 o << " let Inst{19-15} = rs1;\n";
330 o << " let Inst{14-12} = funct3;\n";
331 o << " let Inst{11-7} = 0;\n";
332 o << " let Inst{6-0} = opcode.Value;\n";
333 o << "}\n";
334 o << "\n";
335 o << "class OARVInstR1R<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode,\n";
336 o << " dag outs, dag ins, string opcodestr, string argstr>\n";
337 o << " : RVInst<outs, ins, opcodestr, argstr, [], InstFormatOther> {\n";
338 o << " bits<5> rs1;\n";
339 o << " bits<5> rd;\n";
340 o << "\n";
341 o << " let Inst{31-25} = funct7;\n";
342 o << " let Inst{24-20} = 0;\n";
343 o << " let Inst{19-15} = rs1;\n";
344 o << " let Inst{14-12} = funct3;\n";
345 o << " let Inst{11-7} = rd;\n";
346 o << " let Inst{6-0} = opcode.Value;\n";
347 o << "}\n";
348 o << "\n";
349 o << "let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in\n";
350 o << "class OARVR2R<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, string\n";
351 o << " opcodestr, bit Commutable = 0>\n";
352 o << " : RVInstR<funct7, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1,\n";
353 o << " GPR:$rs2), opcodestr, \"$rd, $rs1, $rs2\"> {\n";
354 o << " let isCommutable = Commutable;\n";
355 o << "}\n";
356 o << "\n";
357 o << "let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in\n";
358 o << "class OARVR3R<bits<2> funct2, bits<3> funct3, RISCVOpcode opcode, string\n";
359 o << " opcodestr, bit Commutable = 0>\n";
360 o << " : RVInstR4<funct2, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1,\n";
361 o << " GPR:$rs2, GPR:$rs3), opcodestr, \"$rd, $rs1, $rs2, $rs3\"> {\n";
362 o << " let isCommutable = Commutable;\n";
363 o << "}\n";
364 o << "\n";
365 o << "let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in\n";
366 o << "class OARVR1R<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, string\n";
367 o << " opcodestr>\n";
368 o << " : OARVInstR1R<funct7, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1),\n";
369 o << " opcodestr, \"$rd, $rs1\"> {\n";
370 o << "}\n";
371 o << "\n";
372 o << "let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in\n";
373 o << "class OARVR1<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, string\n";
374 o << " opcodestr>\n";
375 o << " : OARVInstR1<funct7, funct3, opcode, (outs), (ins GPR:$rs1),\n";
376 o << " opcodestr, \"$rs1\"> {\n";
377 o << "}\n";
378}
#define assert(condition)
find Finds info of the inner loops in the false
static std::string TCEVersionString()
BinaryEncoding * generate()
InstructionFormat & instructionFormat(int index) const
std::string operationAtIndex(const int index) const
int encoding(const std::string &op) const
static bool containsKey(const MapType &aMap, const KeyType &aKey)
Operation & operation(const char *name)
virtual TCEString name() const
Definition Operation.cc:93
virtual int numberOfInputs() const
Definition Operation.cc:192
virtual int dagCount() const
Definition Operation.cc:134
virtual int numberOfOutputs() const
Definition Operation.cc:202
std::string getFormatType(const std::string &opName) const
virtual std::string generateBackend() const
std::string patternStr_
Definition RISCVTDGen.hh:72
void writeInstructionDeclarations(std::ostream &o) const
std::string transformTCEPattern(std::string pattern, const unsigned numIns) const
void dumpClassDefinitions(std::ostream &) const
std::string decimalsToHex(const std::string &pattern) const
Definition RISCVTDGen.cc:69
void writePatternDefinition(std::ostream &o, Operation &op)
RISCVTDGen(const TTAMachine::Machine &mach)
Definition RISCVTDGen.cc:52
void writeInstructionDeclaration(std::ostream &o, const std::string &name, const int encoding) const
void findCustomOps()
std::map< std::string, int > customOps_
Definition RISCVTDGen.hh:70
void writePatternDefinitions(std::ostream &o)
std::string declarationStr_
Definition RISCVTDGen.hh:71
virtual void initializeBackendContents()
BinaryEncoding * bem_
Definition RISCVTDGen.hh:69
static std::string getFunc2Str(const int encoding)
static std::string getFunc3Str(const int encoding)
static std::string getFunc7Str(const int encoding)
TCEString upper() const
Definition TCEString.cc:86
TCEString & replaceString(const std::string &old, const std::string &newString)
Definition TCEString.cc:94
static std::string intToHexString(int num)
Definition TCEString.cc:288
static std::string unsignedToHexString(unsigned num)
Definition TCEString.cc:295
Definition TDGen.hh:77
static const char OT_REG_LONG
Definition TDGen.hh:499
const std::vector< OperationDAG * > getMatchableOperationDAGs(const Operation &op)
Definition TDGen.cc:5391
OperationDAG * createTrivialDAG(Operation &op)
Definition TDGen.cc:6093
const TTAMachine::Machine & mach_
Definition TDGen.hh:463
virtual TCEString llvmOperationPattern(const Operation &op, char operandType=' ') const
Definition TDGen.cc:4895
virtual char operandChar(Operand &operand)
Definition TDGen.cc:4755
bool operationCanBeMatched(const Operation &op, std::set< std::string > *recursionCycleCheck=NULL, bool recursionHasStore=false)
Definition TDGen.cc:5281
std::string operationPattern(const Operation &op, const OperationDAG &dag, const std::string &operandTypes)
Definition TDGen.cc:5421
static const char OT_REG_INT
Definition TDGen.hh:498
bool is64bit() const
Definition Machine.hh:260
const std::string RISCV_R1R_TYPE_NAME
const std::string RISCV_R3R_TYPE_NAME
const std::string RISCV_R_TYPE_NAME
const std::string RISCV_R1_TYPE_NAME
const std::map< std::string, int > RISCVRTypeOperations
Definition RISCVFields.hh:8