Fixed small memory leak in Pass::call()
[yosys.git] / kernel / register.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "register.h"
21 #include "log.h"
22 #include <assert.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26
27 using namespace REGISTER_INTERN;
28 #define MAX_REG_COUNT 1000
29
30 namespace REGISTER_INTERN
31 {
32 bool echo_mode = false;
33 int raw_register_count = 0;
34 bool raw_register_done = false;
35 Pass *raw_register_array[MAX_REG_COUNT];
36
37 std::map<std::string, Frontend*> frontend_register;
38 std::map<std::string, Pass*> pass_register;
39 std::map<std::string, Backend*> backend_register;
40 }
41
42 std::vector<std::string> Frontend::next_args;
43
44 Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help)
45 {
46 assert(!raw_register_done);
47 assert(raw_register_count < MAX_REG_COUNT);
48 raw_register_array[raw_register_count++] = this;
49 }
50
51 void Pass::run_register()
52 {
53 assert(pass_register.count(pass_name) == 0);
54 pass_register[pass_name] = this;
55 }
56
57 void Pass::init_register()
58 {
59 if (raw_register_done)
60 done_register();
61 while (raw_register_count > 0)
62 raw_register_array[--raw_register_count]->run_register();
63 raw_register_done = true;
64 }
65
66 void Pass::done_register()
67 {
68 frontend_register.clear();
69 pass_register.clear();
70 backend_register.clear();
71 raw_register_done = false;
72 }
73
74 Pass::~Pass()
75 {
76 }
77
78 void Pass::help()
79 {
80 log("\n");
81 log("No help message for command `%s'.\n", pass_name.c_str());
82 log("\n");
83 }
84
85 void Pass::cmd_log_args(const std::vector<std::string> &args)
86 {
87 if (args.size() <= 1)
88 return;
89 log("Full command line:");
90 for (size_t i = 0; i < args.size(); i++)
91 log(" %s", args[i].c_str());
92 log("\n");
93 }
94
95 void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
96 {
97 std::string command_text;
98 int error_pos = 0;
99
100 for (size_t i = 0; i < args.size(); i++) {
101 if (i < argidx)
102 error_pos += args[i].size() + 1;
103 command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
104 }
105
106 log("\nSyntax error in command `%s':\n", command_text.c_str());
107 help();
108
109 log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
110 msg.c_str(), command_text.c_str(), error_pos, "");
111 }
112
113 void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
114 {
115 for (; argidx < args.size(); argidx++)
116 {
117 std::string arg = args[argidx];
118
119 if (arg.substr(0, 1) == "-")
120 cmd_error(args, argidx, "Unkown option or option in arguments.");
121
122 if (!select)
123 cmd_error(args, argidx, "Extra argument.");
124
125 handle_extra_select_args(this, args, argidx, args.size(), design);
126 break;
127 }
128 // cmd_log_args(args);
129 }
130
131 void Pass::call(RTLIL::Design *design, std::string command)
132 {
133 std::vector<std::string> args;
134 char *s = strdup(command.c_str()), *sstart = s, *saveptr;
135 s += strspn(s, " \t\r\n");
136 if (*s == 0 || *s == '#') {
137 free(sstart);
138 return;
139 }
140 if (*s == '!') {
141 for (s++; *s == ' ' || *s == '\t'; s++) { }
142 char *p = s + strlen(s) - 1;
143 while (p >= s && (*p == '\r' || *p == '\n'))
144 *(p--) = 0;
145 log_header("Shell command: %s\n", s);
146 int retCode = system(s);
147 if (retCode != 0)
148 log_cmd_error("Shell command returned error code %d.\n", retCode);
149 free(sstart);
150 return;
151 }
152 for (char *p = strtok_r(s, " \t\r\n", &saveptr); p; p = strtok_r(NULL, " \t\r\n", &saveptr)) {
153 std::string str = p;
154 int strsz = str.size();
155 if (str == "#")
156 break;
157 if (strsz > 0 && str[strsz-1] == ';') {
158 int num_semikolon = 0;
159 while (strsz > 0 && str[strsz-1] == ';')
160 strsz--, num_semikolon++;
161 if (strsz > 0)
162 args.push_back(str.substr(0, strsz));
163 call(design, args);
164 args.clear();
165 if (num_semikolon == 2)
166 call(design, "clean");
167 if (num_semikolon == 3)
168 call(design, "clean -purge");
169 } else
170 args.push_back(str);
171 }
172 free(sstart);
173 call(design, args);
174 }
175
176 void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
177 {
178 if (args.size() == 0 || args[0][0] == '#')
179 return;
180
181 if (echo_mode) {
182 log("%s", create_prompt(design, 0));
183 for (size_t i = 0; i < args.size(); i++)
184 log("%s%s", i ? " " : "", args[i].c_str());
185 log("\n");
186 }
187
188 if (pass_register.count(args[0]) == 0)
189 log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
190
191 size_t orig_sel_stack_pos = design->selection_stack.size();
192 pass_register[args[0]]->execute(args, design);
193 while (design->selection_stack.size() > orig_sel_stack_pos)
194 design->selection_stack.pop_back();
195
196 design->check();
197 }
198
199 void Pass::call_newsel(RTLIL::Design *design, std::string command)
200 {
201 std::string backup_selected_active_module = design->selected_active_module;
202 design->selected_active_module.clear();
203 design->selection_stack.push_back(RTLIL::Selection());
204
205 Pass::call(design, command);
206
207 design->selection_stack.pop_back();
208 design->selected_active_module = backup_selected_active_module;
209 }
210
211 void Pass::call_newsel(RTLIL::Design *design, std::vector<std::string> args)
212 {
213 std::string backup_selected_active_module = design->selected_active_module;
214 design->selected_active_module.clear();
215 design->selection_stack.push_back(RTLIL::Selection());
216
217 Pass::call(design, args);
218
219 design->selection_stack.pop_back();
220 design->selected_active_module = backup_selected_active_module;
221 }
222
223 Frontend::Frontend(std::string name, std::string short_help) : Pass("read_"+name, short_help), frontend_name(name)
224 {
225 }
226
227 void Frontend::run_register()
228 {
229 assert(pass_register.count(pass_name) == 0);
230 pass_register[pass_name] = this;
231
232 assert(frontend_register.count(frontend_name) == 0);
233 frontend_register[frontend_name] = this;
234 }
235
236 Frontend::~Frontend()
237 {
238 }
239
240 void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
241 {
242 assert(next_args.empty());
243 do {
244 FILE *f = NULL;
245 next_args.clear();
246 execute(f, std::string(), args, design);
247 args = next_args;
248 fclose(f);
249 } while (!args.empty());
250 }
251
252 void Frontend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
253 {
254 bool called_with_fp = f != NULL;
255
256 next_args.clear();
257 for (; argidx < args.size(); argidx++)
258 {
259 std::string arg = args[argidx];
260
261 if (arg.substr(0, 1) == "-")
262 cmd_error(args, argidx, "Unkown option or option in arguments.");
263 if (f != NULL)
264 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
265
266 filename = arg;
267 f = fopen(filename.c_str(), "r");
268 if (f == NULL)
269 log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
270
271 if (argidx+1 < args.size()) {
272 next_args.insert(next_args.begin(), args.begin(), args.begin()+argidx);
273 next_args.insert(next_args.begin()+argidx, args.begin()+argidx+1, args.end());
274 args.erase(args.begin()+argidx+1, args.end());
275 }
276 break;
277 }
278 if (f == NULL)
279 cmd_error(args, argidx, "No filename given.");
280
281 if (called_with_fp)
282 args.push_back(filename);
283 args[0] = pass_name;
284 // cmd_log_args(args);
285 }
286
287 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
288 {
289 std::vector<std::string> args;
290 char *s = strdup(command.c_str());
291 for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
292 args.push_back(p);
293 free(s);
294 frontend_call(design, f, filename, args);
295 }
296
297 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
298 {
299 if (args.size() == 0)
300 return;
301 if (frontend_register.count(args[0]) == 0)
302 log_cmd_error("No such frontend: %s\n", args[0].c_str());
303
304 if (f != NULL) {
305 frontend_register[args[0]]->execute(f, filename, args, design);
306 } else if (filename == "-") {
307 frontend_register[args[0]]->execute(stdin, "<stdin>", args, design);
308 } else {
309 if (!filename.empty())
310 args.push_back(filename);
311 frontend_register[args[0]]->execute(args, design);
312 }
313
314 design->check();
315 }
316
317 Backend::Backend(std::string name, std::string short_help) : Pass("write_"+name, short_help), backend_name(name)
318 {
319 }
320
321 void Backend::run_register()
322 {
323 assert(pass_register.count(pass_name) == 0);
324 pass_register[pass_name] = this;
325
326 assert(backend_register.count(backend_name) == 0);
327 backend_register[backend_name] = this;
328 }
329
330 Backend::~Backend()
331 {
332 }
333
334 void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
335 {
336 FILE *f = NULL;
337 execute(f, std::string(), args, design);
338 if (f != stdout)
339 fclose(f);
340 }
341
342 void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
343 {
344 bool called_with_fp = f != NULL;
345
346 for (; argidx < args.size(); argidx++)
347 {
348 std::string arg = args[argidx];
349
350 if (arg.substr(0, 1) == "-" && arg != "-")
351 cmd_error(args, argidx, "Unkown option or option in arguments.");
352 if (f != NULL)
353 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
354
355 if (arg == "-") {
356 filename = "<stdout>";
357 f = stdout;
358 continue;
359 }
360
361 filename = arg;
362 f = fopen(filename.c_str(), "w");
363 if (f == NULL)
364 log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
365 }
366
367 if (called_with_fp)
368 args.push_back(filename);
369 args[0] = pass_name;
370 // cmd_log_args(args);
371
372 if (f == NULL) {
373 filename = "<stdout>";
374 f = stdout;
375 }
376 }
377
378 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
379 {
380 std::vector<std::string> args;
381 char *s = strdup(command.c_str());
382 for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
383 args.push_back(p);
384 free(s);
385 backend_call(design, f, filename, args);
386 }
387
388 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
389 {
390 if (args.size() == 0)
391 return;
392 if (backend_register.count(args[0]) == 0)
393 log_cmd_error("No such backend: %s\n", args[0].c_str());
394
395 size_t orig_sel_stack_pos = design->selection_stack.size();
396
397 if (f != NULL) {
398 backend_register[args[0]]->execute(f, filename, args, design);
399 } else if (filename == "-") {
400 backend_register[args[0]]->execute(stdout, "<stdout>", args, design);
401 } else {
402 if (!filename.empty())
403 args.push_back(filename);
404 backend_register[args[0]]->execute(args, design);
405 }
406
407 while (design->selection_stack.size() > orig_sel_stack_pos)
408 design->selection_stack.pop_back();
409
410 design->check();
411 }
412
413 struct HelpPass : public Pass {
414 HelpPass() : Pass("help", "display help messages") { }
415 virtual void help()
416 {
417 log("\n");
418 log(" help ............. list all commands\n");
419 log(" help <command> ... print help message for given command\n");
420 log(" help -all ........ print complete command reference\n");
421 log("\n");
422 }
423 void escape_tex(std::string &tex)
424 {
425 size_t pos = 0;
426 while ((pos = tex.find('_', pos)) != std::string::npos) {
427 tex.replace(pos, 1, "\\_");
428 pos += 2;
429 }
430 }
431 void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
432 {
433 size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n");
434 if (begin != std::string::npos && end != std::string::npos && begin < end)
435 text = text.substr(begin, end-begin+1);
436 std::string cmd_unescaped = cmd;
437 escape_tex(cmd);
438 escape_tex(title);
439 fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str());
440 fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str());
441 fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n");
442 fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str());
443 }
444 void escape_html(std::string &html)
445 {
446 size_t pos = 0;
447 while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
448 switch (html[pos]) {
449 case '<':
450 html.replace(pos, 1, "&lt;");
451 pos += 4;
452 break;
453 case '>':
454 html.replace(pos, 1, "&gt;");
455 pos += 4;
456 break;
457 case '&':
458 html.replace(pos, 1, "&amp;");
459 pos += 5;
460 break;
461 }
462 }
463 void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
464 {
465 FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt");
466 fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str());
467
468 escape_html(cmd);
469 escape_html(title);
470 escape_html(text);
471
472 fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
473
474 fprintf(f, "@cmd_header %s@\n", cmd.c_str());
475 fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str());
476 fprintf(f, "<pre>%s</pre>\n", text.c_str());
477 fprintf(f, "@footer@\n");
478
479 fclose(f);
480 }
481 virtual void execute(std::vector<std::string> args, RTLIL::Design*)
482 {
483 if (args.size() == 1) {
484 log("\n");
485 for (auto &it : REGISTER_INTERN::pass_register)
486 log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
487 log("\n");
488 log("Type 'help <command>' for more information on a command.\n");
489 log("\n");
490 return;
491 }
492
493 if (args.size() == 2) {
494 if (args[1] == "-all") {
495 for (auto &it : REGISTER_INTERN::pass_register) {
496 log("\n\n");
497 log("%s -- %s\n", it.first.c_str(), it.second->short_help.c_str());
498 for (size_t i = 0; i < it.first.size() + it.second->short_help.size() + 6; i++)
499 log("=");
500 log("\n");
501 it.second->help();
502 }
503 }
504 // this option is undocumented as it is for internal use only
505 else if (args[1] == "-write-tex-command-reference-manual") {
506 FILE *f = fopen("command-reference-manual.tex", "wt");
507 fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
508 for (auto &it : REGISTER_INTERN::pass_register) {
509 size_t memsize;
510 char *memptr;
511 FILE *memf = open_memstream(&memptr, &memsize);
512 log_files.push_back(memf);
513 it.second->help();
514 log_files.pop_back();
515 fclose(memf);
516 write_tex(f, it.first, it.second->short_help, memptr);
517 free(memptr);
518 }
519 fclose(f);
520 }
521 // this option is undocumented as it is for internal use only
522 else if (args[1] == "-write-web-command-reference-manual") {
523 FILE *f = fopen("templates/cmd_index.in", "wt");
524 for (auto &it : REGISTER_INTERN::pass_register) {
525 size_t memsize;
526 char *memptr;
527 FILE *memf = open_memstream(&memptr, &memsize);
528 log_files.push_back(memf);
529 it.second->help();
530 log_files.pop_back();
531 fclose(memf);
532 write_html(f, it.first, it.second->short_help, memptr);
533 free(memptr);
534 }
535 fclose(f);
536 }
537 else if (REGISTER_INTERN::pass_register.count(args[1]) == 0)
538 log("No such command: %s\n", args[1].c_str());
539 else
540 REGISTER_INTERN::pass_register.at(args[1])->help();
541 return;
542 }
543
544 help();
545 }
546 } HelpPass;
547
548 struct EchoPass : public Pass {
549 EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
550 virtual void help()
551 {
552 log("\n");
553 log(" echo on\n");
554 log("\n");
555 log("Print all commands to log before executing them.\n");
556 log("\n");
557 log("\n");
558 log(" echo off\n");
559 log("\n");
560 log("Do not print all commands to log before executing them. (default)\n");
561 log("\n");
562 }
563 virtual void execute(std::vector<std::string> args, RTLIL::Design*)
564 {
565 if (args.size() > 2)
566 cmd_error(args, 2, "Unexpected argument.");
567
568 if (args.size() == 2) {
569 if (args[1] == "on")
570 echo_mode = true;
571 else if (args[1] == "off")
572 echo_mode = false;
573 else
574 cmd_error(args, 1, "Unexpected argument.");
575 }
576
577 log("echo %s\n", echo_mode ? "on" : "off");
578 }
579 } EchoPass;
580