MiniCC.py 8.59 KB
Newer Older
Matthieu Moy's avatar
Matthieu Moy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#! /usr/bin/env python3
"""
Code generation lab, main file. Code Generation with Smart IRs.
Usage:
    python3 MiniCC.py <filename>
    python3 MiniCC.py --help
"""
import traceback
from MiniCLexer import MiniCLexer
from MiniCParser import MiniCParser
from TP04.MiniCCodeGen3AVisitor import MiniCCodeGen3AVisitor
from TP03.MiniCTypingVisitor import MiniCTypingVisitor, MiniCTypeError
from Errors import MiniCUnsupportedError, MiniCInternalError, AllocationError
from TP04.SimpleAllocations import (
    NaiveAllocator, AllInMemAllocator
)
try:  # Common part for TP05 and TP05a
    from TP05.CFG import CFG  # type: ignore[import]
except ModuleNotFoundError:
    pass
try:  # Common part for TP05 and TP05b
    from TP05.SmartAllocation import SmartAllocator  # type: ignore[import]
except ModuleNotFoundError:
    pass
try:  # Liveness for TP05 (M1IF08)
    from TP05.LivenessDataFlow import LivenessDataFlow  # type: ignore[import]
except ModuleNotFoundError:
    pass
try:  # SSA for TP05a (CAP)
    from TP05.SSA import (enter_ssa, exit_ssa)  # type: ignore[import]
except ModuleNotFoundError:
    pass
try:  # Liveness for TP05b (CAP)
    from TP05.LivenessSSA import LivenessSSA  # type: ignore[import]
except ModuleNotFoundError:
    pass
37
38
39
40
try:  # Optim for TP05c (CAP)
    from TP05c.OptimSSA import OptimSSA  # type: ignore[import]
except ModuleNotFoundError:
    pass
Matthieu Moy's avatar
Matthieu Moy committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

import argparse

from antlr4 import FileStream, CommonTokenStream
from antlr4.error.ErrorListener import ErrorListener

import os
import sys


class CountErrorListener(ErrorListener):
    """Count number of errors.

    Parser provides getNumberOfSyntaxErrors(), but the Lexer
    apparently doesn't provide an easy way to know if an error occurred
    after the fact. Do the counting ourserves with a listener.
    """

    def __init__(self):
        super(CountErrorListener, self).__init__()
        self.count = 0

    def syntaxError(self, recognizer, offending_symbol, line, column, msg, e):
        self.count += 1


def main(inputname, reg_alloc, enable_ssa=False,
         typecheck=True, typecheck_only=False, stdout=False, output_name=None, debug=False,
69
         debug_graphs=False, ssa_graphs=False, ssa_optims=False):
Matthieu Moy's avatar
Matthieu Moy committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    (basename, rest) = os.path.splitext(inputname)
    if not typecheck_only:
        if stdout:
            output_name = None
            print("Code will be generated on standard output")
        elif output_name is None:
            output_name = basename + ".s"
            print("Code will be generated in file " + output_name)

    input_s = FileStream(inputname, encoding='utf-8')
    lexer = MiniCLexer(input_s)
    counter = CountErrorListener()
    lexer._listeners.append(counter)
    stream = CommonTokenStream(lexer)
    parser = MiniCParser(stream)
    parser._listeners.append(counter)
    tree = parser.prog()
    if counter.count > 0:
        exit(3)  # Syntax or lexicography errors occurred, don't try to go further.
    if typecheck:
        typing_visitor = MiniCTypingVisitor()
        try:
            typing_visitor.visit(tree)
        except MiniCTypeError as e:
            print(e.args[0])
            exit(2)

    if typecheck_only:
        if debug:
            print("Not running code generation because of --typecheck-only.")
        return

    # Codegen 3@ CFG Visitor, first argument is debug mode
    visitor3 = MiniCCodeGen3AVisitor(debug, parser)

    # dump generated code on stdout or file.
    with open(output_name, 'w') if output_name else sys.stdout as output:
        visitor3.visit(tree)
        for function in visitor3.get_functions():
            # Allocation part
            cfg = CFG(function)
            if debug_graphs:
                s = "{}.{}.dot".format(basename, cfg._name)
                print("Output", s)
                cfg.print_dot(s)
            if enable_ssa:
                DF = enter_ssa(cfg, basename, debug, ssa_graphs)
                if ssa_graphs:
                    s = "{}.{}.ssa.dot".format(basename, cfg._name)
                    print("Output", s)
                    cfg.print_dot(s, DF, True)
121
122
123
124
125
126
                if ssa_optims:
                    OptimSSA(cfg, debug=debug)
                    if ssa_graphs:
                        s = "{}.{}.optimssa.dot".format(basename, cfg._name)
                        print("Output", s)
                        cfg.print_dot(s, view=True)
Matthieu Moy's avatar
Matthieu Moy committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
            allocator = None
            if reg_alloc == "naive":
                allocator = NaiveAllocator(cfg)
                comment = "naive allocation"
            elif reg_alloc == "all_in_mem":
                allocator = AllInMemAllocator(cfg)
                comment = "all-in-memory allocation"
            elif reg_alloc == "smart":
                liveness = None
                if enable_ssa:
                    try:
                        liveness = LivenessSSA(cfg, debug=debug)
                    except NameError:
                        form = "CFG in SSA form"
                        raise ValueError("Invalid dataflow form: \
liveness file not found for {}.".format(form))
                else:
                    try:
                        liveness = LivenessDataFlow(cfg, debug=debug)
                    except NameError:
                        form = "CFG not in SSA form"
                        raise ValueError("Invalid dataflow form: \
liveness file not found for {}.".format(form))
                allocator = SmartAllocator(cfg, basename, liveness,
                                           debug, debug_graphs)
                comment = "smart allocation with graph coloring"
            elif reg_alloc == "none":
                comment = "non executable 3-Address instructions"
            else:
                raise ValueError("Invalid allocation strategy:" + reg_alloc)
            if allocator:
                allocator.prepare()
            if enable_ssa:
                exit_ssa(cfg)
                comment += " with SSA"
            if allocator:
                allocator.rewriteCode(cfg)
            if enable_ssa and ssa_graphs:
                s = "{}.{}.exitssa.dot".format(basename, cfg._name)
                print("Output", s)
                cfg.print_dot(s, view=True)
            cfg.print_code(output, comment=comment)
            if debug:
                visitor3.printSymbolTable()


# command line management
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Generate code for .c file')

    parser.add_argument('filename', type=str,
                        help='Source file.')
    parser.add_argument('--reg-alloc', type=str,
                        choices=['none', 'naive', 'all_in_mem', 'smart'],
                        help='Allocation to perform')
    parser.add_argument('--ssa', action='store_true',
                        default=False,
                        help='Enable SSA form')
185
186
187
    parser.add_argument('--ssa-optim', action='store_true',
                        default=False,
                        help='Enable SSA optimizations')
Matthieu Moy's avatar
Matthieu Moy committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
    parser.add_argument('--stdout', action='store_true',
                        help='Generate code to stdout')
    parser.add_argument('--debug', action='store_true',
                        default=False,
                        help='Emit verbose debug output')
    parser.add_argument('--graphs', action='store_true',
                        default=False,
                        help='Display graphs (CFG, conflict graph).')
    parser.add_argument('--ssa-graphs', action='store_true',
                        default=False,
                        help='Display SSA graphs (DT, DF).')
    parser.add_argument('--disable-typecheck', action='store_true',
                        default=False,
                        help="Don't run the typechecker before generating code")
202
    parser.add_argument('--disable-codegen', action='store_true',
Matthieu Moy's avatar
Matthieu Moy committed
203
204
205
206
207
208
209
                        default=False,
                        help="Run only the typechecker, don't try generating code.")
    parser.add_argument('--output', type=str,
                        help='Generate code to outfile')

    args = parser.parse_args()

210
    if args.reg_alloc is None and not args.disable_codegen:
Matthieu Moy's avatar
Matthieu Moy committed
211
212
        print("error: the following arguments is required: --reg-alloc")
        exit(1)
213
214
215
    if not args.ssa and args.ssa_optim:
        print("error: SSA is needed for optimizations")
        exit(1)
Matthieu Moy's avatar
Matthieu Moy committed
216
217
218

    try:
        main(args.filename, args.reg_alloc, args.ssa,
219
             not args.disable_typecheck, args.disable_codegen,
Matthieu Moy's avatar
Matthieu Moy committed
220
             args.stdout, args.output, args.debug,
221
             args.graphs, args.ssa_graphs, args.ssa_optim)
Matthieu Moy's avatar
Matthieu Moy committed
222
223
224
225
226
227
    except MiniCUnsupportedError as e:
        print(e)
        exit(5)
    except (MiniCInternalError, AllocationError):
        traceback.print_exc()
        exit(4)