Franck Pommereau

generate a single output file

...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
6 # $Id: cx86.py,v 1.3 2004/06/02 21:05:23 varmaa Exp $ 6 # $Id: cx86.py,v 1.3 2004/06/02 21:05:23 varmaa Exp $
7 # --------------------------------------------------------------- 7 # ---------------------------------------------------------------
8 8
9 +import io
10 +
9 from . import cparse 11 from . import cparse
10 from .cvisitors import Visitor 12 from .cvisitors import Visitor
11 13
...@@ -394,28 +396,25 @@ class CodeGenVisitor(Visitor): ...@@ -394,28 +396,25 @@ class CodeGenVisitor(Visitor):
394 """Visitor that generates x86 assembly code for the abstract 396 """Visitor that generates x86 assembly code for the abstract
395 syntax tree.""" 397 syntax tree."""
396 398
397 - def __init__(self, file, show_comments=0): 399 + # The current label number we're on, for generating
400 + # jump labels in the assembly code (e.g., 'LO', 'L1', etc).
401 + __label = 0
402 +
403 + # Current label number for generating string literal labels.
404 + __str_literal_label = 0
405 +
406 + def __init__(self, path):
398 """Constructor. 'file' is the file object to output the 407 """Constructor. 'file' is the file object to output the
399 - resulting code to. If 'show_comments' is true, then annotated 408 + resulting code to."""
400 - comments are produced for the generated assembly code."""
401 409
402 Visitor.__init__(self) 410 Visitor.__init__(self)
403 411
404 - # The current label number we're on, for generating 412 + # path of compiled C source
405 - # jump labels in the assembly code (e.g., 'LO', 'L1', etc). 413 + self.path = path
406 - self.__label = 0
407 -
408 - # Current label number for generating string literal labels.
409 - self.__str_literal_label = 0
410 414
411 # Current assembly code for string literals. 415 # Current assembly code for string literals.
412 - self.__str_literal_str = "" 416 + self.str_literal_str = io.StringIO()
413 - 417 + self.str_literal_dict = {}
414 - # Whether we should show comments or not.
415 - self.show_comments = show_comments
416 -
417 - # The file we're outputting the generated code to.
418 - self.file = file
419 418
420 # A hashtable of binary operators and the assembly 419 # A hashtable of binary operators and the assembly
421 # instructions corresponding to them. Certain instructions 420 # instructions corresponding to them. Certain instructions
...@@ -454,7 +453,7 @@ class CodeGenVisitor(Visitor): ...@@ -454,7 +453,7 @@ class CodeGenVisitor(Visitor):
454 """Generate a new jump label and return it.""" 453 """Generate a new jump label and return it."""
455 454
456 label = ".L%d" % self.__label 455 label = ".L%d" % self.__label
457 - self.__label += 1 456 + self.__class__.__label += 1
458 return label 457 return label
459 458
460 def o(self, str, comment=None): 459 def o(self, str, comment=None):
...@@ -462,22 +461,20 @@ class CodeGenVisitor(Visitor): ...@@ -462,22 +461,20 @@ class CodeGenVisitor(Visitor):
462 with an optional annotated comment (if comments are 461 with an optional annotated comment (if comments are
463 enabled).""" 462 enabled)."""
464 463
465 - if self.show_comments and comment != None: 464 + if comment != None:
466 comment = "# %s" % comment 465 comment = "# %s" % comment
467 - self.curr_str += "%-35s %s\n" % (str, comment) 466 + self.curr_str.write("%-35s %s\n" % (str, comment))
468 else: 467 else:
469 if str == "": 468 if str == "":
470 return 469 return
471 - self.curr_str += str + "\n" 470 + self.curr_str.write(str + "\n")
472 471
473 def c(self, str, indent_amt=2): 472 def c(self, str, indent_amt=2):
474 - """Output a single-line comment to the output file, if 473 + "Output a single-line comment to the output file."
475 - comments are enabled."""
476 474
477 indent = " " * indent_amt 475 indent = " " * indent_amt
478 476
479 - if self.show_comments: 477 + self.o("\n%s# %s\n" % (indent, str))
480 - self.o("\n%s# %s\n" % (indent, str))
481 478
482 def vNodeList(self, node): 479 def vNodeList(self, node):
483 self._visitList(node.nodes) 480 self._visitList(node.nodes)
...@@ -513,36 +510,43 @@ class CodeGenVisitor(Visitor): ...@@ -513,36 +510,43 @@ class CodeGenVisitor(Visitor):
513 """Generate and return a list of global variable 510 """Generate and return a list of global variable
514 definitions.""" 511 definitions."""
515 512
516 - globals_str = ".global_vars:\n" 513 + globals_str = io.StringIO()
517 for symbol in node.symtab.entries.values(): 514 for symbol in node.symtab.entries.values():
518 symbol.compile_loc = self.symbol_prepend + symbol.name 515 symbol.compile_loc = self.symbol_prepend + symbol.name
519 if not symbol.type.is_function() and not symbol.extern: 516 if not symbol.type.is_function() and not symbol.extern:
520 - globals_str += " .comm %s,%d\n" % \ 517 + globals_str.write(" .comm %s,%d\n"
521 - (symbol.compile_loc, \ 518 + % (symbol.compile_loc,
522 - self._calc_var_size(symbol.type)*WEIRD_MULTIPLIER) 519 + self._calc_var_size(symbol.type)*WEIRD_MULTIPLIER))
523 return globals_str 520 return globals_str
524 521
525 def vTranslationUnit(self, node): 522 def vTranslationUnit(self, node):
526 """Outputs the entire assembly source file.""" 523 """Outputs the entire assembly source file."""
527 524
528 - self.curr_str = "" 525 + self.curr_str = io.StringIO()
529 - self.o("# Generated by c.py")
530 - self.o("# Atul Varma (Spring 2004)\n")
531 - self.o(" .text")
532 526
533 - globals_str = self._generate_global_variable_definitions(node) 527 + self.globals_str = self._generate_global_variable_definitions(node)
534 528
535 # Generate the main code. 529 # Generate the main code.
536 self._visitList(node.nodes) 530 self._visitList(node.nodes)
537 531
538 - # Append global variable definitions. 532 + @classmethod
539 - self.o(globals_str) 533 + def concat (cls, outfile, chunks) :
540 - 534 + outfile.write("# Generated by cct\n"
541 - # Append string literal definitions. 535 + "# Franck Pommereau (2018)\n"
542 - self.o(self.__str_literal_str) 536 + "# Adapted from Atul Varma's c.py (Spring 2004)\n\n")
543 - 537 + #
544 - # Output the entire file. 538 + outfile.write(".text\n\n")
545 - self.file.write(self.curr_str) 539 + for c in chunks :
540 + outfile.write("# code from file %r\n" % c.path)
541 + outfile.write(c.curr_str.getvalue())
542 + #
543 + outfile.write(".global_vars:\n\n")
544 + for c in chunks :
545 + outfile.write("\n# globals from file %r\n\n" % c.path)
546 + outfile.write(c.globals_str.getvalue())
547 + for c in chunks :
548 + outfile.write("\n# string literals from file %r\n\n" % c.path)
549 + outfile.write(c.str_literal_str.getvalue())
546 550
547 def _calc_var_size(self, type): 551 def _calc_var_size(self, type):
548 """Calculate and return the size of the given type, in 552 """Calculate and return the size of the given type, in
...@@ -674,7 +678,7 @@ class CodeGenVisitor(Visitor): ...@@ -674,7 +678,7 @@ class CodeGenVisitor(Visitor):
674 # insert it into our code later on. 678 # insert it into our code later on.
675 679
676 old_str = self.curr_str 680 old_str = self.curr_str
677 - self.curr_str = "" 681 + self.curr_str = io.StringIO()
678 682
679 node.body.accept(self) 683 node.body.accept(self)
680 684
...@@ -692,7 +696,7 @@ class CodeGenVisitor(Visitor): ...@@ -692,7 +696,7 @@ class CodeGenVisitor(Visitor):
692 self.stack.save_callee_saves() 696 self.stack.save_callee_saves()
693 697
694 # Add the previously-generated assembly code for the function. 698 # Add the previously-generated assembly code for the function.
695 - self.curr_str += function_str 699 + self.curr_str.write(function_str.getvalue())
696 700
697 self.o("%s:" % self.curr_func_end_label) 701 self.o("%s:" % self.curr_func_end_label)
698 702
...@@ -816,10 +820,14 @@ class CodeGenVisitor(Visitor): ...@@ -816,10 +820,14 @@ class CodeGenVisitor(Visitor):
816 generate (but do not yet emit) the assembly for it, and return 820 generate (but do not yet emit) the assembly for it, and return
817 the name of the new label.""" 821 the name of the new label."""
818 822
823 + if str in self.str_literal_dict :
824 + return self.str_literal_dict[str]
825 +
819 label_str = "LC%d" % self.__str_literal_label 826 label_str = "LC%d" % self.__str_literal_label
827 + self.str_literal_dict[str] = label_str
820 str = str.replace('\n', '\\12') 828 str = str.replace('\n', '\\12')
821 - self.__str_literal_str += """%s:\n .ascii "%s\\0"\n""" % (label_str, str) 829 + self.str_literal_str.write("""%s:\n .ascii "%s\\0"\n""" % (label_str, str))
822 - self.__str_literal_label += 1 830 + self.__class__.__str_literal_label += 1
823 return label_str 831 return label_str
824 832
825 def vStringLiteral(self, node): 833 def vStringLiteral(self, node):
......
...@@ -20,8 +20,9 @@ class Compiler (object) : ...@@ -20,8 +20,9 @@ class Compiler (object) :
20 """This object encapsulates the front-end for the compiler and 20 """This object encapsulates the front-end for the compiler and
21 serves as a facade interface to the 'meat' of the compiler 21 serves as a facade interface to the 'meat' of the compiler
22 underneath.""" 22 underneath."""
23 - def __init__ (self, arch) : 23 + def __init__ (self, arch, path) :
24 self.arch = arch 24 self.arch = arch
25 + self.path = path
25 self.total_errors = 0 26 self.total_errors = 0
26 self.total_warnings = 0 27 self.total_warnings = 0
27 def _parse (self) : 28 def _parse (self) :
...@@ -34,37 +35,39 @@ class Compiler (object) : ...@@ -34,37 +35,39 @@ class Compiler (object) :
34 self.total_warnings += visitor.warnings 35 self.total_warnings += visitor.warnings
35 if visitor.has_errors(): 36 if visitor.has_errors():
36 raise CompileError() 37 raise CompileError()
37 - def _do_compile (self, outfile, ast_file) : 38 + def _do_compile (self, ast_file) :
38 """Compiles the code to the given file object. Enabling 39 """Compiles the code to the given file object. Enabling
39 show_ast prints out the abstract syntax tree.""" 40 show_ast prints out the abstract syntax tree."""
40 self._parse() 41 self._parse()
41 self._compile_phase(cvisitors.SymtabVisitor()) 42 self._compile_phase(cvisitors.SymtabVisitor())
42 self._compile_phase(cvisitors.TypeCheckVisitor()) 43 self._compile_phase(cvisitors.TypeCheckVisitor())
43 self._compile_phase(cvisitors.FlowControlVisitor()) 44 self._compile_phase(cvisitors.FlowControlVisitor())
44 - self._compile_phase(self.arch.CodeGenVisitor(outfile)) 45 + comp = self.arch.CodeGenVisitor(self.path)
46 + self._compile_phase(comp)
45 if ast_file is not None: 47 if ast_file is not None:
46 self._compile_phase(cvisitors.ASTPrinterVisitor(ast_file)) 48 self._compile_phase(cvisitors.ASTPrinterVisitor(ast_file))
49 + return comp
47 def _print_stats (self) : 50 def _print_stats (self) :
48 "Prints the total number of errors/warnings from compilation." 51 "Prints the total number of errors/warnings from compilation."
49 if self.total_errors : 52 if self.total_errors :
50 print("%d errors" % self.total_errors) 53 print("%d errors" % self.total_errors)
51 if self.total_warnings : 54 if self.total_warnings :
52 print("%s warnings" % self.total_warnings) 55 print("%s warnings" % self.total_warnings)
53 - def compile (self, code, outfile, show_ast) : 56 + def compile (self, code, show_ast) :
54 "Compiles the given code string to the given file object." 57 "Compiles the given code string to the given file object."
55 self.code = code 58 self.code = code
56 try: 59 try:
57 - self._do_compile(outfile, show_ast) 60 + chunk = self._do_compile(show_ast)
58 except cparse.ParseError: 61 except cparse.ParseError:
59 print("Errors encountered, bailing.") 62 print("Errors encountered, bailing.")
60 - return 1 63 + return
61 except CompileError: 64 except CompileError:
62 self._print_stats() 65 self._print_stats()
63 print("Errors encountered, bailing.") 66 print("Errors encountered, bailing.")
64 - return 1 67 + return
65 self._print_stats() 68 self._print_stats()
66 print("Compile successful.") 69 print("Compile successful.")
67 - return 0 70 + return chunk
68 71
69 def main (args=None) : 72 def main (args=None) :
70 parser = argparse.ArgumentParser(prog="cct", 73 parser = argparse.ArgumentParser(prog="cct",
...@@ -83,6 +86,7 @@ def main (args=None) : ...@@ -83,6 +86,7 @@ def main (args=None) :
83 parser.add_argument("source", nargs="+", metavar="PATH", 86 parser.add_argument("source", nargs="+", metavar="PATH",
84 help="C source files(s) to compile") 87 help="C source files(s) to compile")
85 args = parser.parse_args(args) 88 args = parser.parse_args(args)
89 + chunks = []
86 for src in args.source : 90 for src in args.source :
87 print("Compiling %r" % src) 91 print("Compiling %r" % src)
88 if args.ast : 92 if args.ast :
...@@ -92,8 +96,10 @@ def main (args=None) : ...@@ -92,8 +96,10 @@ def main (args=None) :
92 else : 96 else :
93 ast_file = None 97 ast_file = None
94 code = "\n".join(open(src).readlines()) 98 code = "\n".join(open(src).readlines())
95 - retval = Compiler(ARCH[args.arch]).compile(code, args.o, ast_file) 99 + ret = Compiler(ARCH[args.arch], src).compile(code, ast_file)
96 if ast_file is not None : 100 if ast_file is not None :
97 ast_file.close() 101 ast_file.close()
98 - if retval != 0 : 102 + if ret is None :
99 - sys.exit(retval) 103 + sys.exit(1)
104 + chunks.append(ret)
105 + ARCH[args.arch].CodeGenVisitor.concat(args.o, chunks)
......
...@@ -149,5 +149,11 @@ int main(int argc, char **argv) { ...@@ -149,5 +149,11 @@ int main(int argc, char **argv) {
149 printf("array-built string is: %s\n", c); 149 printf("array-built string is: %s\n", c);
150 free(c); 150 free(c);
151 } 151 }
152 +
153 + /* multiple defined strings */
154 +
155 + printf("this string is defined twice\n");
156 + printf("this string is defined twice\n");
157 +
152 return 0; 158 return 0;
153 } 159 }
......
...@@ -11,16 +11,8 @@ ...@@ -11,16 +11,8 @@
11 /* Test global variable. */ 11 /* Test global variable. */
12 int stuff_count; 12 int stuff_count;
13 13
14 -/* Test of static function definition, to make sure it
15 - doesn't conflict with fib() defined in foo.c. */
16 -static int fib()
17 -{
18 - return stuff_count += 1;
19 -}
20 -
21 /* Increment global variable. */ 14 /* Increment global variable. */
22 int increment_stuff_count() 15 int increment_stuff_count()
23 { 16 {
24 - fib(); 17 + return stuff_count += 1;
25 - return 0;
26 } 18 }
......