Skip to content

Commit bcd632e

Browse files
committed
explicit_object_extensions provisional -- SIMICS-23313
1 parent 5eb5bd4 commit bcd632e

File tree

6 files changed

+152
-38
lines changed

6 files changed

+152
-38
lines changed

py/dead_dml_methods.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def traverse_ast(ast):
7676
for stmt in stmts:
7777
yield from traverse_ast(stmt)
7878
elif ast.kind == 'object':
79-
(_, _, _, stmts) = ast.args
79+
(_, _, _, _, stmts) = ast.args
8080
for stmt in stmts:
8181
yield from traverse_ast(stmt)
8282
elif ast.kind == 'method':

py/dml/dmlparse.py

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ def toplevel_else_if(t):
297297

298298
@prod_dml12
299299
def object_anonymous_bank(t):
300-
'object : BANK object_spec'
301-
t[0] = ast.object_(site(t), None, 'bank', [], t[2])
300+
'object : maybe_extension BANK object_spec'
301+
t[0] = ast.object_(site(t), None, 'bank', [], None, t[3])
302302

303303
@prod
304304
def array_list_empty(t):
@@ -312,9 +312,9 @@ def array_list(t):
312312

313313
@prod
314314
def object_regarray(t):
315-
'object : REGISTER objident array_list sizespec offsetspec maybe_istemplate object_spec'
316-
t[0] = ast.object_(site(t), t[2], 'register', t[3],
317-
t[4] + t[5] + t[6] + t[7])
315+
'object : maybe_extension REGISTER objident array_list sizespec offsetspec maybe_istemplate object_spec'
316+
t[0] = ast.object_(site(t), t[3], 'register', t[4], t[1],
317+
t[5] + t[6] + t[7] + t[8])
318318

319319
@prod
320320
def bitrangespec(t):
@@ -328,8 +328,8 @@ def bitrangespec_empty(t):
328328

329329
@prod_dml14
330330
def object_field(t):
331-
'object : FIELD objident array_list bitrangespec maybe_istemplate object_spec'
332-
t[0] = ast.object_(site(t), t[2], 'field', t[3], t[4] + t[5] + t[6])
331+
'object : maybe_extension FIELD objident array_list bitrangespec maybe_istemplate object_spec'
332+
t[0] = ast.object_(site(t), t[3], 'field', t[4], t[1], t[5] + t[6] + t[7])
333333

334334
def endian_translate_bit(expr, width, bitorder):
335335
if bitorder == 'be':
@@ -368,10 +368,10 @@ def bitrange_2(t):
368368

369369
@prod_dml12
370370
def object_field_1(t):
371-
'object : FIELD objident bitrange maybe_istemplate object_spec'
371+
'object : maybe_extension FIELD objident bitrange maybe_istemplate object_spec'
372372
if logging.show_porting:
373-
report(PFIELDRANGE(site(t, 3)))
374-
t[0] = ast.object_(site(t), t[2], 'field', [], t[3] + t[4] + t[5])
373+
report(PFIELDRANGE(site(t, 4)))
374+
t[0] = ast.object_(site(t), t[3], 'field', [], None, t[4] + t[5] + t[6])
375375

376376
@prod_dml12
377377
def field_array_size_no(t):
@@ -397,8 +397,8 @@ def field_array_size(t):
397397

398398
@prod_dml12
399399
def object_field_2(t):
400-
'object : FIELD objident fieldarraysize bitrangespec maybe_istemplate object_spec'
401-
t[0] = ast.object_(site(t), t[2], 'field', t[3], t[4] + t[5] + t[6])
400+
'object : maybe_extension FIELD objident fieldarraysize bitrangespec maybe_istemplate object_spec'
401+
t[0] = ast.object_(site(t), t[3], 'field', t[4], None, t[5] + t[6] + t[7])
402402

403403
@prod_dml14
404404
def data(t):
@@ -472,20 +472,20 @@ def saved_decl_many_init(t):
472472

473473
@prod
474474
def object3(t):
475-
'''object : CONNECT objident array_list maybe_istemplate object_spec
476-
| INTERFACE objident array_list maybe_istemplate object_spec
477-
| ATTRIBUTE objident array_list maybe_istemplate object_spec
478-
| BANK objident array_list maybe_istemplate object_spec
479-
| EVENT objident array_list maybe_istemplate object_spec
480-
| GROUP objident array_list maybe_istemplate object_spec
481-
| PORT objident array_list maybe_istemplate object_spec
482-
| IMPLEMENT objident array_list maybe_istemplate object_spec'''
483-
t[0] = ast.object_(site(t), t[2], t[1], t[3], t[4] + t[5])
475+
'''object : maybe_extension CONNECT objident array_list maybe_istemplate object_spec
476+
| maybe_extension INTERFACE objident array_list maybe_istemplate object_spec
477+
| maybe_extension ATTRIBUTE objident array_list maybe_istemplate object_spec
478+
| maybe_extension BANK objident array_list maybe_istemplate object_spec
479+
| maybe_extension EVENT objident array_list maybe_istemplate object_spec
480+
| maybe_extension GROUP objident array_list maybe_istemplate object_spec
481+
| maybe_extension PORT objident array_list maybe_istemplate object_spec
482+
| maybe_extension IMPLEMENT objident array_list maybe_istemplate object_spec'''
483+
t[0] = ast.object_(site(t), t[3], t[2], t[4], t[1], t[5] + t[6])
484484

485485
@prod_dml14
486486
def object_subdevice(t):
487-
'''object : SUBDEVICE objident array_list maybe_istemplate object_spec'''
488-
t[0] = ast.object_(site(t), t[2], t[1], t[3], t[4] + t[5])
487+
'''object : maybe_extension SUBDEVICE objident array_list maybe_istemplate object_spec'''
488+
t[0] = ast.object_(site(t), t[3], t[2], t[4], t[1], t[5] + t[6])
489489

490490
@prod_dml12
491491
def maybe_extern_yes(t):
@@ -884,6 +884,31 @@ def object_desc_none(t):
884884
'object_desc :'
885885
t[0] = []
886886

887+
@prod_dml14
888+
def maybe_extension_yes(t):
889+
'maybe_extension : IN'
890+
if not site(t).provisional_enabled(
891+
provisional.explicit_object_extensions):
892+
report(ESYNTAX(site(t), 'in', None))
893+
t[0] = None
894+
else:
895+
t[0] = True
896+
897+
@prod_dml14
898+
def maybe_extension_no(t):
899+
'maybe_extension : '
900+
enabled = (provisional.explicit_object_extensions
901+
in t.parser.file_info.provisional)
902+
t[0] = False if enabled else None
903+
904+
# Note that `maybe_extension` is used even in DML 1.2 exclusive rules, which
905+
# may seem redundant, but no! Removing those uses would lead to shift/reduce
906+
# conflicts.
907+
@prod_dml12
908+
def maybe_extension(t):
909+
'maybe_extension : '
910+
t[0] = None
911+
887912
@prod
888913
def object_spec_none(t):
889914
'object_spec : object_desc SEMI'

py/dml/messages.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,41 @@ def log(self):
11691169
DMLError.log(self)
11701170
self.print_site_message(self.other_site, "existing declaration")
11711171

1172+
1173+
class EEXTENSION(DMLError):
1174+
"""When the `explict_object_extensions` provisional feature is enabled,
1175+
any object definition made via `in` syntax is considered an extension such
1176+
that there must be some other non-extension declaration of the object, or
1177+
DMLC will reject the extension.
1178+
To declare and define a new object not already declared, omit the `in`
1179+
syntax.
1180+
"""
1181+
fmt = ("object '%s' not declared elsewhere."
1182+
" To declare and define a new object, omit 'in'.")
1183+
1184+
class EMULTIOBJDECL(DMLError):
1185+
"""When the `explicit_object_extensions` provisional feature is enabled,
1186+
any object declaration not made using `in` syntax is considered a
1187+
declaration of a novel object — because of that, DMLC will reject
1188+
it if there already is another non-`in` declaration across files enabling
1189+
`explicit_object_extensions`.
1190+
"""
1191+
fmt = ("object '%s' already declared."
1192+
" To extend upon the definition of an object, use 'in %s'")
1193+
def __init__(self, site, other_site, objtype, name):
1194+
super().__init__(site, name, f'{objtype} {name} ...')
1195+
self.other_site = other_site
1196+
1197+
def log(self):
1198+
from . import provisional
1199+
DMLError.log(self)
1200+
self.print_site_message(self.other_site, "existing declaration")
1201+
prov_site = self.site.provisional_enabled(
1202+
provisional.explicit_object_extensions)
1203+
self.print_site_message(
1204+
prov_site,
1205+
"enabled by the explicit_object_extensions provisional feature")
1206+
11721207
class EVARPARAM(DMLError):
11731208
"""
11741209
The value assigned to the parameter is not a well-defined constant.

py/dml/provisional.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,34 @@ class simics_util_vect(ProvisionalFeature):
135135
stable = True
136136
dml12 = True
137137

138+
139+
@feature
140+
class explicit_object_extensions(ProvisionalFeature):
141+
'''<a id="explicit_object_extensions"/>
142+
This feature extends the DML syntax for object declarations to distinguish
143+
between an intent to introduce a new object to the model structure, and an
144+
intent to extend the definition of an existing object.
145+
146+
The following form is introduced to mark the intent to extend an object:
147+
<pre>
148+
in <em>object-type</em> <em>name</em> <em>...</em> { <em>...</em> }
149+
</pre>
150+
E.g.
151+
<pre>
152+
in bank some_bank { ... }
153+
</pre>
154+
155+
If this form is used while there is no other non-extension declaration of
156+
the named object, then DMLC will signal an error because the definition
157+
was not intended to introduce the object to the model structure.
158+
159+
DMLC will also signal an error if there is more than one non-extension
160+
declaration of the object among the files enabling
161+
`explicit_object_extensions`.
162+
'''
163+
short = "Require `in` syntax for additional declarations of an object"
164+
stable = False
165+
138166
def parse_provisional(
139167
provs: list[("Site", str)]) -> dict[ProvisionalFeature, "Site"]:
140168
ret = {}

py/dml/structure.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,9 @@ def wrap_sites(spec, issite, tname):
503503
else:
504504
raise ICE(issite, 'unknown node type %r %r' % (asttype, stmt))
505505
composite_wrapped = [
506-
(objtype, name, arrayinfo,
506+
(objtype, name, arrayinfo, is_extension,
507507
ObjectSpec(*wrap_sites(spec, issite, tname)))
508-
for (objtype, name, arrayinfo, spec) in composite]
508+
for (objtype, name, arrayinfo, is_extension, spec) in composite]
509509
blocks.append((preconds, shallow_wrapped, composite_wrapped))
510510

511511
return (TemplateSite(spec.site, issite, tname), spec.rank,
@@ -833,8 +833,8 @@ def sort_method_implementations(implementations, obj_specs):
833833
return traits.sort_method_implementations(implementations)
834834

835835
def merge_subobj_defs(def1, def2, parent):
836-
(objtype, name, arrayinfo, obj_specs1) = def1
837-
(objtype2, name2, arrayinfo2, obj_specs2) = def2
836+
(objtype, name, arrayinfo, extension_status1, obj_specs1) = def1
837+
(objtype2, name2, arrayinfo2, extension_status2, obj_specs2) = def2
838838
assert name == name2
839839

840840
site1 = obj_specs1[0].site
@@ -844,6 +844,26 @@ def merge_subobj_defs(def1, def2, parent):
844844
report(ENAMECOLL(site1, site2, name))
845845
return def1
846846

847+
# extension status:
848+
# None -> dual extension and decl
849+
# True -> extension
850+
# False -> explicit decl
851+
if extension_status1 is extension_status2 is False:
852+
# Blame the one with higher rank if possible. Otherwise, blame the
853+
# new def.
854+
(decl_site_1, decl_site_2) = (
855+
(site1, site2) if (
856+
obj_specs2[0].rank in obj_specs1[0].rank.inferior)
857+
else (site2, site1))
858+
report(EMULTIOBJDECL(decl_site_1, decl_site_2, objtype, name))
859+
# One is explicit decl: merged is explicit decl
860+
elif extension_status1 is False or extension_status2 is False:
861+
extension_status1 = False
862+
# If either is dual, make dual
863+
elif extension_status1 is None or extension_status2 is None:
864+
extension_status1 = None
865+
# Otherwise, both are explicit extensions. Keep explicit extension.
866+
847867
if len(arrayinfo) != len(arrayinfo2):
848868
raise EAINCOMP(site1, site2, name,
849869
"mixing declarations with different number "
@@ -870,7 +890,8 @@ def merge_subobj_defs(def1, def2, parent):
870890
merged_arrayinfo.append((idxvar1, len1))
871891

872892

873-
return (objtype, name, merged_arrayinfo, obj_specs1 + obj_specs2)
893+
return (objtype, name, merged_arrayinfo, extension_status1,
894+
obj_specs1 + obj_specs2)
874895

875896
def method_is_std(node, methname):
876897
"""
@@ -1708,13 +1729,14 @@ def mkobj2(obj, obj_specs, params, each_stmts):
17081729

17091730
for (stmts, obj_spec) in composite_subobjs:
17101731
for s in stmts:
1711-
(objtype, ident, arrayinfo, subobj_spec) = s
1732+
(objtype, ident, arrayinfo, is_extension, subobj_spec) = s
17121733

17131734
if ident is None:
17141735
assert (dml.globals.dml_version == (1, 2)
17151736
and objtype in {'bank', 'field'})
17161737

1717-
subobj_def = (objtype, ident, arrayinfo, [subobj_spec])
1738+
subobj_def = (objtype, ident, arrayinfo, is_extension,
1739+
[subobj_spec])
17181740
if ident in subobj_defs:
17191741
subobj_defs[ident] = merge_subobj_defs(subobj_defs[ident],
17201742
subobj_def, obj)
@@ -1724,7 +1746,10 @@ def mkobj2(obj, obj_specs, params, each_stmts):
17241746
symbols[ident] = subobj_spec.site
17251747
subobj_defs[ident] = subobj_def
17261748

1727-
for (_, _, arrayinfo, specs) in subobj_defs.values():
1749+
for (_, ident, arrayinfo, extension_status, specs) in subobj_defs.values():
1750+
if extension_status is True:
1751+
for spec in specs:
1752+
report(EEXTENSION(spec.site, ident))
17281753
for (i, (idx, dimsize_ast)) in enumerate(arrayinfo):
17291754
if dimsize_ast is None:
17301755
idxref = (f" (with index variable '{idx.args[0]}')"
@@ -1814,7 +1839,7 @@ def mkobj2(obj, obj_specs, params, each_stmts):
18141839
# the whole register.
18151840
if obj.objtype == 'register' and not any(
18161841
objtype == 'field'
1817-
for (objtype, _, _, _) in list(subobj_defs.values())):
1842+
for (objtype, _, _, _, _) in list(subobj_defs.values())):
18181843
# The implicit field instantiates the built-in field
18191844
# template and does nothing else.
18201845
subobjs.append(mkobj(
@@ -1856,7 +1881,7 @@ def mkobj2(obj, obj_specs, params, each_stmts):
18561881
subobj_name_defs = {}
18571882

18581883
for name in sorted(subobj_defs, key=lambda name: name or ''):
1859-
(objtype, ident, arrayinfo, subobj_specs) = subobj_defs[name]
1884+
(objtype, ident, arrayinfo, _, subobj_specs) = subobj_defs[name]
18601885
if (not obj.accepts_child_type(objtype)
18611886
# HACK: disallow non-toplevel banks in DML 1.2, see SIMICS-19009
18621887
or (dml.globals.dml_version == (1, 2)

py/dml/template.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def defined_symbols(self):
123123
symbols[sub.args[0]] = (sub.kind, sub.site)
124124
else:
125125
assert sub.kind == 'error'
126-
for (_, name, _, specs) in composite:
126+
for (_, name, _, _, specs) in composite:
127127
symbols[name] = ('subobj', specs.site)
128128
return symbols
129129

@@ -299,11 +299,12 @@ def obj_from_asts(site, stmts):
299299
block = []
300300
for decl_ast in composite:
301301
assert decl_ast.kind == 'object'
302-
(name, objtype, indices, sub_stmts) = decl_ast.args
302+
(name, objtype, indices, is_extension,
303+
sub_stmts) = decl_ast.args
303304
spec = obj_from_asts(
304305
decl_ast.site, sub_stmts + [ast.is_(
305306
decl_ast.site, [(decl_ast.site, objtype)])])
306-
block.append((objtype, name, indices, spec))
307+
block.append((objtype, name, indices, is_extension, spec))
307308
blocks.append((preconds, shallow, block))
308309
return ObjectSpec(site, rank, is_stmts, in_eachs, params, blocks)
309310
return obj_from_asts(site, stmts)
@@ -331,7 +332,7 @@ def rank_structure(asts):
331332
while queue:
332333
(spec, conditional) = queue.pop()
333334
if spec.kind == 'object':
334-
(_, objtype, _, stmts) = spec.args
335+
(_, objtype, _, _, stmts) = spec.args
335336
inferior[objtype] = spec
336337
queue.extend((stmt, conditional) for stmt in stmts)
337338
elif spec.kind == 'is':

0 commit comments

Comments
 (0)