OpenASIP  2.0
tceopgen.cc
Go to the documentation of this file.
1 /*
2  Copyright (c) 2002-2009 Tampere University.
3 
4  This file is part of TTA-Based Codesign Environment (TCE).
5 
6  Permission is hereby granted, free of charge, to any person obtaining a
7  copy of this software and associated documentation files (the "Software"),
8  to deal in the Software without restriction, including without limitation
9  the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  and/or sell copies of the Software, and to permit persons to whom the
11  Software is furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in
14  all copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  DEALINGS IN THE SOFTWARE.
23  */
24 /**
25  * TCE custom op macro header generator for LLVM TCE backend.
26  *
27  * @author Veli-Pekka Jääskeläinen 2007 (vjaaskel-no.spam-cs.tut.fi)
28  * @author Pekka Jääskeläinen 2009 (pjaaskel-no.spam-cs.tut.fi)
29  *
30  * @note rating: red
31  */
32 
33 #include <iostream>
34 #include <fstream>
35 #include <set>
36 #include <assert.h>
37 
38 #include "OperationPool.hh"
39 #include "Operation.hh"
40 #include "Conversion.hh"
41 #include "OperationIndex.hh"
42 #include "Operand.hh"
43 #include "Application.hh"
44 
46 /**
47  * Returns a C type string for the given operand type.
48  */
49 std::string
50 operandTypeCString(const Operand& operand) {
51  switch (operand.type()) {
52  default:
53  case Operand::SINT_WORD:
54  return "int";
55  break;
56  case Operand::UINT_WORD:
57  return "unsigned";
58  break;
60  return "float";
61  break;
63  return "double";
64  break;
65  case Operand::RAW_DATA:
66  return "";
67  break;
69  return "long";
70  break;
72  return "unsigned long";
73  break;
74  }
75  // TODO: it never comes here!
76  // half floats take the "default".
77  return "unsigned";
78 }
79 
80 /**
81  * Produces the _TCE_OPNAME or _TCEFU_OPNAME macro that can be written to the
82  * tceops.h header file from the given operation.
83  */
84 void
86  std::ostream& os, std::string& opName, const Operation& op,
87  mode macroMode, bool legacy) {
88  // Only generate valid RISC-V R-format intrinsics
89  if (macroMode == RISCV) {
90  if (op.numberOfInputs() != 2) {
91  return;
92  }
93  if (op.numberOfOutputs() != 1) {
94  return;
95  }
96  }
97 
98  if (op.numberOfInputs() + op.numberOfOutputs() == 0)
99  return;
100 
101  if (legacy) {
102  os << "#define _TCE";
103  } else {
104  os << "#define _OA";
105  }
106 
107  const std::string outputOperandName =
108  legacy ? "__tce_op_output_" : "__oa_op_output_";
109 
110  switch (macroMode) {
111  case RISCV:
112  os << "_RV_" << opName << "(";
113  break;
114  case FU_ADDRESSABLE:
115  os << "FU_" << opName << "(FU, ";
116  break;
117  case ADDRESSPACE:
118  os << "AS_" << opName << "(AS, ";
119  break;
120  default:
121  os << "_" << opName << "(";
122  }
123 
124  int seenInputs = 0;
125  int seenOutputs = 0;
126  for (int i = 1;
127  i < (op.numberOfInputs() + op.numberOfOutputs() + 1);
128  i++) {
129 
130  const Operand& operand = op.operand(i);
131  if (i > 1)
132  os << ", ";
133  if (operand.isInput()) {
134  seenInputs++;
135  os << "i" << seenInputs;
136  } else {
137  seenOutputs++;
138  os << "o" << seenOutputs;
139  }
140  }
141 
142  os << ") do { ";
143 
144  /* Generate temporary variables to ensure casting to
145  a correct value from the inline assembly statement.
146 
147  Without this, LLVM inline assembly used i8 for the
148  output register type in case a char was used to
149  read the value. This produced strange errors due
150  to the value produced by the inline asm not being
151  masked to the char size (in case the output is really
152  larger than i8 in this case).
153 
154  In this bug (#35), after the char was written to
155  a larger variable, the upper bits (produced by the custom op)
156  were visible which is not expected behavior (writing int
157  to char should zero out the upper bits). Forcing
158  writing to int ensures the inline asm has 32 bit
159  reg for the register thus LLVM produced correct
160  masking/cast operations when writing the value to a smaller
161  variable.
162 
163  Use do {} while(0) block instead of {} to allow using the
164  custom ops as statements which end with ; etc.
165 
166  NOTE: we cannot add "memory" to the clobber list with
167  operations that write to memory because it seems to crash
168  (some versions of) gcc. Thus, they are marked 'volatile'.
169  */
170 
171  for (int out = 1; out < op.numberOfOutputs() + 1; out++) {
172  const Operand& operand = op.output(out - 1);
173  std::string operandTypeString = operandTypeCString(operand);
174  if (operandTypeString != "" && !operand.isVector()) {
175  os << operandTypeCString(operand) << " " << outputOperandName
176  << out << " = (" << operandTypeCString(operand) << ")0; ";
177  }
178  }
179 
180  std::string volatileKeyword = "";
181  if (op.writesMemory() || op.hasSideEffects() ||
182  op.affectsCount() > 0 || op.affectedByCount() > 0) {
183  volatileKeyword = "volatile ";
184  }
185 
186  os << "asm " << volatileKeyword << "(";
187 
188  switch (macroMode) {
189  case RISCV: {
190  os << "\"//" << opName << " ";
191  int iterations = 0;
192  for (iterations = 0; iterations < op.numberOfInputs();
193  iterations++) {
194  os << "\\%" << std::to_string(iterations) << " ";
195  }
196  for (int i = 0; i < op.numberOfOutputs(); i++) {
197  os << "\\%" << std::to_string(iterations + i);
198  }
199  } break;
200  case FU_ADDRESSABLE:
201  os << "FU\".";
202  break;
203  case ADDRESSPACE:
204  os << "\"_AS.\" AS\".";
205  break;
206  default:
207  os << "\"";
208  break;
209  }
210 
211  if (macroMode != RISCV) {
212  os << opName;
213  }
214  os << "\":";
215 
216  for (int out = 1; out < op.numberOfOutputs() + 1; out++) {
217  const Operand& operand = op.output(out - 1);
218 
219  if (out > 1)
220  os << ", ";
221  if (operandTypeCString(operand) != "" && !operand.isVector()) {
222  os << "\"=r\"( " << outputOperandName << out << ")";
223  } else {
224  os << "\"=r\"(o" << out << ")";
225  }
226  }
227  os << ":";
228 
229  for (int in = 1; in < op.numberOfInputs() + 1; in++) {
230  const Operand& operand = op.input(in - 1);
231 
232  if (in > 1)
233  os << ", ";
234  // Only register inputs for RISC-V
235  if (macroMode == RISCV) {
236  if (operandTypeCString(operand) != "") {
237  os << "\"r\"((" << operandTypeCString(operand) << ")(i" << in
238  << "))";
239  } else {
240  os << "\"r\"(i" << in << ")";
241  }
242  } else if (operandTypeCString(operand) != "" && !operand.isVector()) {
243  os << "\"ir\"((" << operandTypeCString(operand)
244  << ")(i" << in << "))";
245  } else {
246  if (operand.isVector()) {
247  os << "\"r\"(i" << in << ")";
248  } else {
249  os << "\"ir\"(i" << in << ")";
250  }
251  }
252  }
253 
254  os << "); ";
255 
256  // write the results from the temps to the output variables
257  for (int out = 1; out < op.numberOfOutputs() + 1; out++) {
258  const Operand& operand = op.output(out - 1);
259  if (operandTypeCString(operand) != "" && !operand.isVector()) {
260  os << "o" << out << " = " << outputOperandName << out << ";";
261  }
262  }
263 
264  os << "} while(0) " << std::endl;
265 }
266 
267 /**
268  * Produces the _TCE_OPNAME and _TCEFU_OPNAME macros that can be
269  * written to the tceops.h header file.
270  */
271 void
272 writeCustomOpMacros(std::ostream& os) {
273 
274  OperationPool pool;
275  OperationIndex& index = pool.index();
276  std::set<std::string> operations;
277 
278  for (int m = 0; m < index.moduleCount(); m++) {
279  OperationModule& mod = index.module(m);
280  try {
281  int opCount = index.operationCount(mod);
282  for (int o = 0; o < opCount; o++) {
283 
284  std::string opName = index.operationName(o, mod);
285  if (operations.count(opName) > 0) {
286  continue;
287  }
288  const Operation& op = pool.operation(opName.c_str());
289  operations.insert(opName);
290  // Write both legacy and new macros
291  writeCustomOpMacro(os, opName, op, NORMAL, true);
292  writeCustomOpMacro(os, opName, op, NORMAL, false);
293  writeCustomOpMacro(os, opName, op, RISCV, true);
294  writeCustomOpMacro(os, opName, op, RISCV, false);
295  writeCustomOpMacro(os, opName, op, FU_ADDRESSABLE, true);
296  writeCustomOpMacro(os, opName, op, FU_ADDRESSABLE, false);
297  if (op.usesMemory()) {
298  writeCustomOpMacro(os, opName, op, ADDRESSPACE, true);
299  writeCustomOpMacro(os, opName, op, ADDRESSPACE, false);
300  }
301  }
302  } catch (const Exception& e) {
304  << "ERROR: " << e.errorMessage() << std::endl;
305  continue;
306  }
307  }
308 }
309 
310 /**
311  * tceopgen main function.
312  *
313  * Generates plugin sourcecode files using TDGen.
314  */
315 int main(int argc, char* argv[]) {
316 
317  if (!(argc == 1 || argc == 3) ||
318  (argc == 3 && Conversion::toString(argv[1]) != std::string("-o"))) {
319 
320  std::cout << "Usage: tceopgen" << std::endl
321  << " -o Output File." << std::endl;
322  return EXIT_FAILURE;
323  }
324 
325  if (argc == 1) {
326  writeCustomOpMacros(std::cout);
327  } else if (argc == 3) {
328  std::ofstream customops;
329  customops.open(argv[2]);
330  if (!customops.good()) {
331  std::cerr << "Error opening '" << argv[2]
332  << "' for writing." << std::endl;
333  return EXIT_FAILURE;
334  }
335  writeCustomOpMacros(customops);
336  customops.close();
337  } else {
338  assert(false);
339  }
340 
341  return EXIT_SUCCESS;
342 }
343 
Operand
Definition: Operand.hh:52
Operation::affectedByCount
virtual int affectedByCount() const
Definition: Operation.cc:416
Operation::writesMemory
virtual bool writesMemory() const
Definition: Operation.cc:252
OperationPool::operation
Operation & operation(const char *name)
Definition: OperationPool.cc:99
mode
mode
Definition: tceopgen.cc:45
Operation::hasSideEffects
virtual bool hasSideEffects() const
Definition: Operation.cc:272
Operation::output
virtual Operand & output(int index) const
Definition: Operation.cc:526
Operand::UINT_WORD
@ UINT_WORD
Definition: Operand.hh:60
OperationPool::index
OperationIndex & index()
Definition: OperationPool.cc:109
Operation::numberOfInputs
virtual int numberOfInputs() const
Definition: Operation.cc:192
writeCustomOpMacro
void writeCustomOpMacro(std::ostream &os, std::string &opName, const Operation &op, mode macroMode, bool legacy)
Definition: tceopgen.cc:85
Conversion::toString
static std::string toString(const T &source)
main
int main(int argc, char *argv[])
Definition: tceopgen.cc:315
writeCustomOpMacros
void writeCustomOpMacros(std::ostream &os)
Definition: tceopgen.cc:272
assert
#define assert(condition)
Definition: Application.hh:86
Operand::isVector
virtual bool isVector() const
Definition: Operand.cc:268
Conversion.hh
Operand::SLONG_WORD
@ SLONG_WORD
Definition: Operand.hh:66
OperationIndex.hh
Application.hh
Operand::FLOAT_WORD
@ FLOAT_WORD
Definition: Operand.hh:61
RISCV
@ RISCV
Definition: tceopgen.cc:45
Operation.hh
ADDRESSPACE
@ ADDRESSPACE
Definition: tceopgen.cc:45
NORMAL
@ NORMAL
Definition: tceopgen.cc:45
Operand::SINT_WORD
@ SINT_WORD
Definition: Operand.hh:59
Exception
Definition: Exception.hh:54
Operand::ULONG_WORD
@ ULONG_WORD
Definition: Operand.hh:67
Operand::RAW_DATA
@ RAW_DATA
Definition: Operand.hh:65
Operation
Definition: Operation.hh:59
Exception::errorMessage
std::string errorMessage() const
Definition: Exception.cc:123
FU_ADDRESSABLE
@ FU_ADDRESSABLE
Definition: tceopgen.cc:45
Operand.hh
Operation::input
virtual Operand & input(int index) const
Definition: Operation.cc:503
Operation::usesMemory
virtual bool usesMemory() const
Definition: Operation.cc:232
Operand::DOUBLE_WORD
@ DOUBLE_WORD
Definition: Operand.hh:62
Operation::operand
virtual Operand & operand(int id) const
Definition: Operation.cc:541
OperationIndex::moduleCount
int moduleCount() const
Application::errorStream
static std::ostream & errorStream()
Definition: Application.cc:171
Operand::type
virtual OperandType type() const
Definition: Operand.cc:165
OperationModule
Definition: OperationModule.hh:46
operandTypeCString
std::string operandTypeCString(const Operand &operand)
Definition: tceopgen.cc:50
OperationIndex
Definition: OperationIndex.hh:58
OperationPool
Definition: OperationPool.hh:52
Operand::isInput
virtual bool isInput() const
Definition: Operand.cc:145
OperationIndex::operationCount
int operationCount(const OperationModule &om)
Definition: OperationIndex.cc:363
OperationPool.hh
Operation::numberOfOutputs
virtual int numberOfOutputs() const
Definition: Operation.cc:202
OperationIndex::module
OperationModule & module(int i)
Operation::affectsCount
virtual int affectsCount() const
Definition: Operation.cc:402
OperationIndex::operationName
std::string operationName(int i, const OperationModule &om)
Definition: OperationIndex.cc:337