arch-power: Add doubleword modulo instructions
[gem5.git] / src / arch / micro_asm.py
1 # Copyright (c) 2003-2005 The Regents of The University of Michigan
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 import os
28 import sys
29 import re
30 import traceback
31 # get type names
32 from types import *
33
34 from ply import lex
35 from ply import yacc
36
37 ##########################################################################
38 #
39 # Base classes for use outside of the assembler
40 #
41 ##########################################################################
42
43 class Micro_Container(object):
44 def __init__(self, name):
45 self.microops = []
46 self.name = name
47 self.directives = {}
48 self.micro_classes = {}
49 self.labels = {}
50
51 def add_microop(self, mnemonic, microop):
52 self.microops.append(microop)
53
54 def __str__(self):
55 string = "%s:\n" % self.name
56 for microop in self.microops:
57 string += " %s\n" % microop
58 return string
59
60 class Combinational_Macroop(Micro_Container):
61 pass
62
63 class Rom_Macroop(object):
64 def __init__(self, name, target):
65 self.name = name
66 self.target = target
67
68 def __str__(self):
69 return "%s: %s\n" % (self.name, self.target)
70
71 class Rom(Micro_Container):
72 def __init__(self, name):
73 super(Rom, self).__init__(name)
74 self.externs = {}
75
76 ##########################################################################
77 #
78 # Support classes
79 #
80 ##########################################################################
81
82 class Label(object):
83 def __init__(self):
84 self.extern = False
85 self.name = ""
86
87 class Block(object):
88 def __init__(self):
89 self.statements = []
90
91 class Statement(object):
92 def __init__(self):
93 self.is_microop = False
94 self.is_directive = False
95 self.params = ""
96
97 class Microop(Statement):
98 def __init__(self):
99 super(Microop, self).__init__()
100 self.mnemonic = ""
101 self.labels = []
102 self.is_microop = True
103
104 class Directive(Statement):
105 def __init__(self):
106 super(Directive, self).__init__()
107 self.name = ""
108 self.is_directive = True
109
110 ##########################################################################
111 #
112 # Functions that handle common tasks
113 #
114 ##########################################################################
115
116 def print_error(message):
117 print()
118 print("*** %s" % message)
119 print()
120
121 def handle_statement(parser, container, statement):
122 if statement.is_microop:
123 if statement.mnemonic not in parser.microops.keys():
124 raise Exception("Unrecognized mnemonic: {}".format(
125 statement.mnemonic))
126 parser.symbols["__microopClassFromInsideTheAssembler"] = \
127 parser.microops[statement.mnemonic]
128 try:
129 microop = eval('__microopClassFromInsideTheAssembler(%s)' %
130 statement.params, {}, parser.symbols)
131 except:
132 print_error("Error creating microop object with mnemonic %s." % \
133 statement.mnemonic)
134 raise
135 try:
136 for label in statement.labels:
137 container.labels[label.text] = microop
138 if label.is_extern:
139 container.externs[label.text] = microop
140 container.add_microop(statement.mnemonic, microop)
141 except:
142 print_error("Error adding microop.")
143 raise
144 elif statement.is_directive:
145 if statement.name not in container.directives.keys():
146 raise Exception("Unrecognized directive: {}".format(
147 statement.name))
148 parser.symbols["__directiveFunctionFromInsideTheAssembler"] = \
149 container.directives[statement.name]
150 try:
151 eval('__directiveFunctionFromInsideTheAssembler(%s)' %
152 statement.params, {}, parser.symbols)
153 except:
154 print_error("Error executing directive.")
155 print(container.directives)
156 raise
157 else:
158 raise Exception("Didn't recognize the type of statement {}".format(
159 statement))
160
161 ##########################################################################
162 #
163 # Lexer specification
164 #
165 ##########################################################################
166
167 # Error handler. Just call exit. Output formatted to work under
168 # Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
169 # prints a Python stack backtrace too (can be handy when trying to
170 # debug the parser itself).
171 def error(lineno, string, print_traceback = False):
172 # Print a Python stack backtrace if requested.
173 if (print_traceback):
174 traceback.print_exc()
175 if lineno != 0:
176 line_str = "%d:" % lineno
177 else:
178 line_str = ""
179 sys.exit("%s %s" % (line_str, string))
180
181 reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
182
183 tokens = reserved + (
184 # identifier
185 'ID',
186 # arguments for microops and directives
187 'PARAMS',
188
189 'LPAREN', 'RPAREN',
190 'LBRACE', 'RBRACE',
191 'COLON', 'SEMI', 'DOT',
192 'NEWLINE'
193 )
194
195 # New lines are ignored at the top level, but they end statements in the
196 # assembler
197 states = (
198 ('asm', 'exclusive'),
199 ('params', 'exclusive'),
200 )
201
202 reserved_map = { }
203 for r in reserved:
204 reserved_map[r.lower()] = r
205
206 # Ignore comments
207 def t_ANY_COMMENT(t):
208 r'\#[^\n]*(?=\n)'
209
210 def t_ANY_MULTILINECOMMENT(t):
211 r'/\*([^/]|((?<!\*)/))*\*/'
212
213 # A colon marks the end of a label. It should follow an ID which will
214 # put the lexer in the "params" state. Seeing the colon will put it back
215 # in the "asm" state since it knows it saw a label and not a mnemonic.
216 def t_params_COLON(t):
217 r':'
218 t.lexer.begin('asm')
219 return t
220
221 # Parameters are a string of text which don't contain an unescaped statement
222 # statement terminator, ie a newline or semi colon.
223 def t_params_PARAMS(t):
224 r'([^\n;\\]|(\\[\n;\\]))+'
225 t.lineno += t.value.count('\n')
226 unescapeParamsRE = re.compile(r'(\\[\n;\\])')
227 def unescapeParams(mo):
228 val = mo.group(0)
229 return val[1]
230 t.value = unescapeParamsRE.sub(unescapeParams, t.value)
231 t.lexer.begin('asm')
232 return t
233
234 # An "ID" in the micro assembler is either a label, directive, or mnemonic
235 # If it's either a directive or a mnemonic, it will be optionally followed by
236 # parameters. If it's a label, the following colon will make the lexer stop
237 # looking for parameters.
238 def t_asm_ID(t):
239 r'[A-Za-z_]\w*'
240 t.type = reserved_map.get(t.value, 'ID')
241 # If the ID is really "extern", we shouldn't start looking for parameters
242 # yet. The real ID, the label itself, is coming up.
243 if t.type != 'EXTERN':
244 t.lexer.begin('params')
245 return t
246
247 # If there is a label and you're -not- in the assembler (which would be caught
248 # above), don't start looking for parameters.
249 def t_ANY_ID(t):
250 r'[A-Za-z_]\w*'
251 t.type = reserved_map.get(t.value, 'ID')
252 return t
253
254 # Braces enter and exit micro assembly
255 def t_INITIAL_LBRACE(t):
256 r'\{'
257 t.lexer.begin('asm')
258 return t
259
260 def t_asm_RBRACE(t):
261 r'\}'
262 t.lexer.begin('INITIAL')
263 return t
264
265 # At the top level, keep track of newlines only for line counting.
266 def t_INITIAL_NEWLINE(t):
267 r'\n+'
268 t.lineno += t.value.count('\n')
269
270 # In the micro assembler, do line counting but also return a token. The
271 # token is needed by the parser to detect the end of a statement.
272 def t_asm_NEWLINE(t):
273 r'\n+'
274 t.lineno += t.value.count('\n')
275 return t
276
277 # A newline or semi colon when looking for params signals that the statement
278 # is over and the lexer should go back to looking for regular assembly.
279 def t_params_NEWLINE(t):
280 r'\n+'
281 t.lineno += t.value.count('\n')
282 t.lexer.begin('asm')
283 return t
284
285 def t_params_SEMI(t):
286 r';'
287 t.lexer.begin('asm')
288 return t
289
290 # Basic regular expressions to pick out simple tokens
291 t_ANY_LPAREN = r'\('
292 t_ANY_RPAREN = r'\)'
293 t_ANY_SEMI = r';'
294 t_ANY_DOT = r'\.'
295
296 t_ANY_ignore = ' \t\x0c'
297
298 def t_ANY_error(t):
299 error(t.lineno, "illegal character '%s'" % t.value[0])
300 t.skip(1)
301
302 ##########################################################################
303 #
304 # Parser specification
305 #
306 ##########################################################################
307
308 # Start symbol for a file which may have more than one macroop or rom
309 # specification.
310 def p_file(t):
311 'file : opt_rom_or_macros'
312
313 def p_opt_rom_or_macros_0(t):
314 'opt_rom_or_macros : '
315
316 def p_opt_rom_or_macros_1(t):
317 'opt_rom_or_macros : rom_or_macros'
318
319 def p_rom_or_macros_0(t):
320 'rom_or_macros : rom_or_macro'
321
322 def p_rom_or_macros_1(t):
323 'rom_or_macros : rom_or_macros rom_or_macro'
324
325 def p_rom_or_macro_0(t):
326 '''rom_or_macro : rom_block
327 | macroop_def'''
328
329 # Defines a section of microcode that should go in the current ROM
330 def p_rom_block(t):
331 'rom_block : DEF ROM block SEMI'
332 if not t.parser.rom:
333 print_error("Rom block found, but no Rom object specified.")
334 raise TypeError("Rom block found, but no Rom object was specified.")
335 for statement in t[3].statements:
336 handle_statement(t.parser, t.parser.rom, statement)
337 t[0] = t.parser.rom
338
339 # Defines a macroop that jumps to an external label in the ROM
340 def p_macroop_def_0(t):
341 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
342 if not t.parser.rom_macroop_type:
343 print_error("ROM based macroop found, but no ROM macroop " +
344 "class was specified.")
345 raise TypeError("ROM based macroop found, but no ROM macroop " +
346 "class was specified.")
347 macroop = t.parser.rom_macroop_type(t[3], t[5])
348 t.parser.macroops[t[3]] = macroop
349
350
351 # Defines a macroop that is combinationally generated
352 def p_macroop_def_1(t):
353 'macroop_def : DEF MACROOP ID block SEMI'
354 try:
355 curop = t.parser.macro_type(t[3])
356 except TypeError:
357 print_error("Error creating macroop object.")
358 raise
359 for statement in t[4].statements:
360 handle_statement(t.parser, curop, statement)
361 t.parser.macroops[t[3]] = curop
362
363 # A block of statements
364 def p_block(t):
365 'block : LBRACE statements RBRACE'
366 block = Block()
367 block.statements = t[2]
368 t[0] = block
369
370 def p_statements_0(t):
371 'statements : statement'
372 if t[1]:
373 t[0] = [t[1]]
374 else:
375 t[0] = []
376
377 def p_statements_1(t):
378 'statements : statements statement'
379 if t[2]:
380 t[1].append(t[2])
381 t[0] = t[1]
382
383 def p_statement(t):
384 'statement : content_of_statement end_of_statement'
385 t[0] = t[1]
386
387 # A statement can be a microop or an assembler directive
388 def p_content_of_statement_0(t):
389 '''content_of_statement : microop
390 | directive'''
391 t[0] = t[1]
392
393 # Ignore empty statements
394 def p_content_of_statement_1(t):
395 'content_of_statement : '
396 pass
397
398 # Statements are ended by newlines or a semi colon
399 def p_end_of_statement(t):
400 '''end_of_statement : NEWLINE
401 | SEMI'''
402 pass
403
404 # Different flavors of microop to avoid shift/reduce errors
405 def p_microop_0(t):
406 'microop : labels ID'
407 microop = Microop()
408 microop.labels = t[1]
409 microop.mnemonic = t[2]
410 t[0] = microop
411
412 def p_microop_1(t):
413 'microop : ID'
414 microop = Microop()
415 microop.mnemonic = t[1]
416 t[0] = microop
417
418 def p_microop_2(t):
419 'microop : labels ID PARAMS'
420 microop = Microop()
421 microop.labels = t[1]
422 microop.mnemonic = t[2]
423 microop.params = t[3]
424 t[0] = microop
425
426 def p_microop_3(t):
427 'microop : ID PARAMS'
428 microop = Microop()
429 microop.mnemonic = t[1]
430 microop.params = t[2]
431 t[0] = microop
432
433 # Labels in the microcode
434 def p_labels_0(t):
435 'labels : label'
436 t[0] = [t[1]]
437
438 def p_labels_1(t):
439 'labels : labels label'
440 t[1].append(t[2])
441 t[0] = t[1]
442
443 # labels on lines by themselves are attached to the following instruction.
444 def p_labels_2(t):
445 'labels : labels NEWLINE'
446 t[0] = t[1]
447
448 def p_label_0(t):
449 'label : ID COLON'
450 label = Label()
451 label.is_extern = False
452 label.text = t[1]
453 t[0] = label
454
455 def p_label_1(t):
456 'label : EXTERN ID COLON'
457 label = Label()
458 label.is_extern = True
459 label.text = t[2]
460 t[0] = label
461
462 # Directives for the macroop
463 def p_directive_0(t):
464 'directive : DOT ID'
465 directive = Directive()
466 directive.name = t[2]
467 t[0] = directive
468
469 def p_directive_1(t):
470 'directive : DOT ID PARAMS'
471 directive = Directive()
472 directive.name = t[2]
473 directive.params = t[3]
474 t[0] = directive
475
476 # Parse error handler. Note that the argument here is the offending
477 # *token*, not a grammar symbol (hence the need to use t.value)
478 def p_error(t):
479 if t:
480 error(t.lineno, "syntax error at '%s'" % t.value)
481 else:
482 error(0, "unknown syntax error", True)
483
484 class MicroAssembler(object):
485
486 def __init__(self, macro_type, microops,
487 rom = None, rom_macroop_type = None):
488 self.lexer = lex.lex()
489 self.parser = yacc.yacc()
490 self.parser.macro_type = macro_type
491 self.parser.macroops = {}
492 self.parser.microops = microops
493 self.parser.rom = rom
494 self.parser.rom_macroop_type = rom_macroop_type
495 self.parser.symbols = {}
496 self.symbols = self.parser.symbols
497
498 def assemble(self, asm):
499 self.parser.parse(asm, lexer=self.lexer)
500 macroops = self.parser.macroops
501 self.parser.macroops = {}
502 return macroops