Gabe Black has uploaded this change for review. (
https://gem5-review.googlesource.com/c/public/gem5/+/56338 )
Change subject: arch: Add macros to the ucode assembler.
......................................................................
arch: Add macros to the ucode assembler.
This will let us stop using raw python to generate microcode sequences.
This macro mechanism is a little more constrained than the CPP macro
mechanism. Macros must be invoked in place of a microop instantiation,
and must expand to a complete block of microcode. They cannot be used to
substitute arbitrary portions of the microcode, and must fall on clean
boundaries to be legal.
If you want to have shorthand for arguments for microops (for example)
the symbols dict that the microcode assembler already supports can
handle that.
Change-Id: Ib9c761a0dcb224d57c87b9bb8a9de16d7db6ef2c
---
M src/arch/micro_asm_test.py
M src/arch/ucasmlib/assembler.py
M src/arch/ucasmlib/block.py
3 files changed, 161 insertions(+), 26 deletions(-)
diff --git a/src/arch/micro_asm_test.py b/src/arch/micro_asm_test.py
index 45dab1e..b893736 100755
--- a/src/arch/micro_asm_test.py
+++ b/src/arch/micro_asm_test.py
@@ -72,6 +72,20 @@
testAssembly = '''
# Single line comment
+def macro first name="blah"
+{
+ .print "Hello {name}"
+};
+
+def macro middle {
+ first "middle"
+};
+
+def macro last other, also_other
+{
+ .print "other = {other}, also_other = {also_other}"
+};
+
def rom {
goo: bah
extern la: hoop 4*8, "a"
@@ -84,12 +98,18 @@
.tweak
bah
.untweak
+ first
+ first "johnny"
.print "In the midst"
+ middle
+ last "param1", "param2"
bah
dah # single line comment after something
.tweak
};
+undef macro middle;
+
#Extending the rom...
def rom
{
diff --git a/src/arch/ucasmlib/assembler.py b/src/arch/ucasmlib/assembler.py
index ab84ebb..072c87e 100644
--- a/src/arch/ucasmlib/assembler.py
+++ b/src/arch/ucasmlib/assembler.py
@@ -30,6 +30,22 @@
from ply import lex
from ply import yacc
+class Macro:
+ def __init__(self, name, params, body):
+ self.name = name
+ self.params = params
+ self.body = body
+
+ def expand(self, assembler, lineno, args):
+ # Use f-string substitution to expand this macro.
+ try:
+ text = eval(f'(lambda {self.params} :
f{repr(self.body)})({args})',
+ {}, assembler.symbols)
+ except Exception as e:
+ assembler.print_error(lineno, f'Failed to expand macro: {e}')
+ raise
+ return text
+
class MicroAssembler(ParserBase):
######################################################################
#
@@ -37,13 +53,15 @@
#
######################################################################
- reserved = ('DEF', 'MACROOP', 'ROM')
+ reserved = ('DEF', 'UNDEF', 'MACRO', 'MACROOP', 'ROM')
tokens = reserved + (
# identifier
'ID',
# the body of a block
'BODY',
+ # the params of a macro
+ 'PARAMS',
'LPAREN', 'RPAREN',
'LBRACE', 'RBRACE',
@@ -55,21 +73,26 @@
states = (
('asm', 'exclusive'),
('header', 'exclusive'),
+ ('macroheader', 'exclusive'),
+ ('macroparams', 'exclusive'),
+ ('macro', 'exclusive'),
+ ('undef', 'exclusive'),
)
reserved_map = { }
for r in reserved:
reserved_map[r.lower()] = r
- def t_ANY_ID(self, t):
+ def t_macroheader_ID(self, t):
r'[A-Za-z_]\w*'
- t.type = self.reserved_map.get(t.value, 'ID')
- if t.type == 'MACROOP':
- t.lexer.push_state('asm')
- t.lexer.push_state('header')
- elif t.type == 'ROM':
- t.lexer.push_state('asm')
- t.lexer.push_state('header')
+ t.lexer.pop_state()
+ return t
+
+ # Macro params are everything up to the next unescaped {.
+ def t_macroparams_PARAMS(self, t):
+ r'([^{\\]|(\\[.\n]))+'
+ t.lineno += t.value.count('\n')
+ t.value = t.value.strip()
return t
def t_header_ID(self, t):
@@ -77,11 +100,34 @@
return t
# Braces enter and exit blocks.
- def t_header_LBRACE(self, t):
+ def t_header_macroparams_LBRACE(self, t):
r'\{'
t.lexer.pop_state()
return t
+ def t_undef_ID(self, t):
+ r'[A-Za-z_]\w*'
+ t.type = self.reserved_map.get(t.value, 'ID')
+ t.lexer.pop_state()
+ return t
+
+ def t_ANY_ID(self, t):
+ r'[A-Za-z_]\w*'
+ t.type = self.reserved_map.get(t.value, 'ID')
+ if t.type == 'UNDEF':
+ t.lexer.push_state('undef')
+ elif t.type == 'MACRO':
+ t.lexer.push_state('asm')
+ t.lexer.push_state('macroparams')
+ t.lexer.push_state('macroheader')
+ elif t.type == 'MACROOP':
+ t.lexer.push_state('asm')
+ t.lexer.push_state('header')
+ elif t.type == 'ROM':
+ t.lexer.push_state('asm')
+ t.lexer.push_state('header')
+ return t
+
#
# Blocks have lines in them which are terminated with unescaped
newlines,
@@ -97,7 +143,7 @@
# First non-whitespace, non } character.
body_line_re += r'[^\s\}]'
# Non-newline or escaped characters.
- body_line_re += r'([^\n\\]|(\\.))*'
+ body_line_re += r'([^\n\\]|(\\[.\n]))*'
#
# The regular expression to match an entire body.
@@ -158,9 +204,44 @@
# Objects can be of various types.
def p_object(self, t):
- '''object : rom_block
+ '''object : macro_def
+ | macro_undef
+ | rom_block
| macroop_def'''
+ # An optional body for a block.
+ def p_opt_body_0(self, t):
+ 'opt_body : BODY'
+ t[0] = t[1]
+
+ def p_opt_body_1(self, t):
+ 'opt_body :'
+ t[0] = ''
+
+ # An optional set of parameters for a macro.
+ def p_opt_params_0(self, t):
+ 'opt_params : PARAMS'
+ t[0] = t[1]
+
+ def p_opt_params_1(self, t):
+ 'opt_params :'
+ t[0] = ''
+
+ # Defines a macro which is invoked like but expands in place to
arbitrary
+ # text.
+ def p_macro_def(self, t):
+ 'macro_def : DEF MACRO ID opt_params LBRACE opt_body RBRACE SEMI'
+ if t[3] in self.macros:
+ raise ValueError(f'Attempted to redefine macro "{t[3]}".')
+ self.macros[t[3]] = Macro(t[3], t[4], t[6][1])
+
+ # Undefines a macro which was previously defined.
+ def p_macro_undef(self, t):
+ 'macro_undef : UNDEF MACRO ID SEMI'
+ if t[3] not in self.macros:
+ raise ValueError(f'Attempted to undefine unknown
macro "{t[3]}".')
+ del self.macros[t[3]]
+
# Defines a section of microcode that should go in the current ROM.
def p_rom_block(self, t):
'rom_block : DEF ROM block SEMI'
@@ -196,19 +277,19 @@
self.macroops[t[3]] = curop
# A block of statements
- def p_block_0(self, t):
- 'block : LBRACE BODY RBRACE'
- starting_line, text = t[2]
- t[0] = BlockParser().parse(starting_line, text)
-
- def p_block_1(self, t):
- 'block : LBRACE RBRACE'
- t[0] = Block()
+ def p_block(self, t):
+ 'block : LBRACE opt_body RBRACE'
+ if t[2]:
+ starting_line, text = t[2]
+ t[0] = BlockParser(self).parse(starting_line, text)
+ else:
+ t[0] = []
def __init__(self, macro_type, microops, rom=None,
rom_macroop_type=None):
self.lexer = lex.lex(object=self)
self.parser = yacc.yacc(module=self)
self.macro_type = macro_type
+ self.macros = {}
self.macroops = {}
self.microops = microops
self.rom = rom
diff --git a/src/arch/ucasmlib/block.py b/src/arch/ucasmlib/block.py
index 4bf3d38..788e13f 100644
--- a/src/arch/ucasmlib/block.py
+++ b/src/arch/ucasmlib/block.py
@@ -126,7 +126,7 @@
# Parameters are a string of text which don't contain an unescaped
# statement statement terminator, ie a newline or semi colon.
def t_params_PARAMS(self, t):
- r'([^\n;\\]|(\\.))+'
+ r'([^\n;\\]|(\\[.\n]))+'
t.lexer.lineno += t.value.count('\n')
unescapeParamsRE = re.compile(r'(\\[\n;\\])')
def unescapeParams(mo):
@@ -185,7 +185,14 @@
# A block of statements
def p_block(self, t):
'block : opt_statements'
- t[0] = t[1]
+ t[0] = []
+ # Add the statements within a macro and not the macro itself.
+ def insert(item):
+ if isinstance(item, list):
+ list(map(insert, item))
+ else:
+ t[0].append(item)
+ insert(t[1])
# Having statements is optional.
def p_opt_statements_0(self, t):
@@ -203,9 +210,9 @@
def p_statements_1(self, t):
'statements : statements statement'
- if t[2]:
- t[1].append(t[2])
t[0] = t[1]
+ if t[2]:
+ t[0].append(t[2])
# A statement can be of various types.
def p_statement(self, t):
@@ -247,17 +254,24 @@
# A microop with optional parameters.
def p_microop(self, t):
'microop : ID opt_params end_of_statement'
- t[0] = Microop(t[1], t[2])
+ macro = self.assembler.macros.get(t[1], None)
+ if macro:
+ # This is actually a macro and not a microop.
+ t[0] = BlockParser(self.assembler).parse(
+ 0, macro.expand(self.assembler, self.lexer.lineno,
t[2]))
+ else:
+ t[0] = Microop(t[1], t[2])
# Directives for the macroop.
def p_directive(self, t):
'directive : DOT ID opt_params end_of_statement'
t[0] = Directive(t[2], t[3])
- def __init__(self):
+ def __init__(self, assembler):
self.lexer = lex.lex(object=self)
self.parser = yacc.yacc(module=self)
self.statements = []
+ self.assembler = assembler
def parse(self, starting_lineno, text):
self.lexer.lineno = starting_lineno
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/56338
To unsubscribe, or for help writing mail filters, visit
https://gem5-review.googlesource.com/settings
Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Ib9c761a0dcb224d57c87b9bb8a9de16d7db6ef2c
Gerrit-Change-Number: 56338
Gerrit-PatchSet: 1
Gerrit-Owner: Gabe Black <gabe.bl...@gmail.com>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s