Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion py/dead_dml_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def traverse_ast(ast):
for stmt in stmts:
yield from traverse_ast(stmt)
elif ast.kind == 'object':
(_, _, _, stmts) = ast.args
(_, _, _, _, stmts) = ast.args
for stmt in stmts:
yield from traverse_ast(stmt)
elif ast.kind == 'method':
Expand Down
71 changes: 48 additions & 23 deletions py/dml/dmlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ def toplevel_else_if(t):

@prod_dml12
def object_anonymous_bank(t):
'object : BANK object_spec'
t[0] = ast.object_(site(t), None, 'bank', [], t[2])
'object : maybe_extension BANK object_spec'
t[0] = ast.object_(site(t), None, 'bank', [], None, t[3])

@prod
def array_list_empty(t):
Expand All @@ -312,9 +312,9 @@ def array_list(t):

@prod
def object_regarray(t):
'object : REGISTER objident array_list sizespec offsetspec maybe_istemplate object_spec'
t[0] = ast.object_(site(t), t[2], 'register', t[3],
t[4] + t[5] + t[6] + t[7])
'object : maybe_extension REGISTER objident array_list sizespec offsetspec maybe_istemplate object_spec'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really want to allow in register x size 4 @ 4711 { } ? all the special syntax is a clear declaration marker to me.

I suggest in KIND NAME [INDICES] { ... } as the only allowed form, analogous to the grammar for in each xyz { ... }

BTW: should we forbid the [_ < ...] index forms outside extensions when the provisional is active?

Copy link
Contributor Author

@lwaern-intel lwaern-intel Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really want to allow in register x size 4 @ 4711 { } ?

I reference this in the JIRA issue. I intentionally made this feature as consistent as possible, and the questions of what we should or should not allow a separate issue -- because the answers are not obvious, (in particular, although I previously agreed with forbidding [_ < ...] as a decl, upon reevaluation it's less obvious to me that such a decl would be nonsensical.)

In other words: i don't care when it comes to this PR. To answer these questions, we need deeper discussion and analysis, and this PR has no need for that since this is an unstable provisional. Besides, the consistent design is a virtue in its own right, and answers about it vs. any ad-hoc restrictions we make could perhaps be enlightened by Gustav's use of it.

t[0] = ast.object_(site(t), t[3], 'register', t[4], t[1],
t[5] + t[6] + t[7] + t[8])

@prod
def bitrangespec(t):
Expand All @@ -328,8 +328,8 @@ def bitrangespec_empty(t):

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

def endian_translate_bit(expr, width, bitorder):
if bitorder == 'be':
Expand Down Expand Up @@ -368,10 +368,10 @@ def bitrange_2(t):

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

@prod_dml12
def field_array_size_no(t):
Expand All @@ -397,8 +397,8 @@ def field_array_size(t):

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

@prod_dml14
def data(t):
Expand Down Expand Up @@ -472,20 +472,20 @@ def saved_decl_many_init(t):

@prod
def object3(t):
'''object : CONNECT objident array_list maybe_istemplate object_spec
| INTERFACE objident array_list maybe_istemplate object_spec
| ATTRIBUTE objident array_list maybe_istemplate object_spec
| BANK objident array_list maybe_istemplate object_spec
| EVENT objident array_list maybe_istemplate object_spec
| GROUP objident array_list maybe_istemplate object_spec
| PORT objident array_list maybe_istemplate object_spec
| IMPLEMENT objident array_list maybe_istemplate object_spec'''
t[0] = ast.object_(site(t), t[2], t[1], t[3], t[4] + t[5])
'''object : maybe_extension CONNECT objident array_list maybe_istemplate object_spec
| maybe_extension INTERFACE objident array_list maybe_istemplate object_spec
| maybe_extension ATTRIBUTE objident array_list maybe_istemplate object_spec
| maybe_extension BANK objident array_list maybe_istemplate object_spec
| maybe_extension EVENT objident array_list maybe_istemplate object_spec
| maybe_extension GROUP objident array_list maybe_istemplate object_spec
| maybe_extension PORT objident array_list maybe_istemplate object_spec
| maybe_extension IMPLEMENT objident array_list maybe_istemplate object_spec'''
t[0] = ast.object_(site(t), t[3], t[2], t[4], t[1], t[5] + t[6])

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

@prod_dml12
def maybe_extern_yes(t):
Expand Down Expand Up @@ -884,6 +884,31 @@ def object_desc_none(t):
'object_desc :'
t[0] = []

@prod_dml14
def maybe_extension_yes(t):
'maybe_extension : IN'
if not site(t).provisional_enabled(
provisional.explicit_object_extensions):
report(ESYNTAX(site(t), 'in', None))
t[0] = None
else:
t[0] = True

@prod_dml14
def maybe_extension_no(t):
'maybe_extension : '
enabled = (provisional.explicit_object_extensions
in t.parser.file_info.provisional)
t[0] = False if enabled else None

# Note that `maybe_extension` is used even in DML 1.2 exclusive rules, which
# may seem redundant, but no! Removing those uses would lead to shift/reduce
# conflicts.
@prod_dml12
def maybe_extension(t):
'maybe_extension : '
t[0] = None

@prod
def object_spec_none(t):
'object_spec : object_desc SEMI'
Expand Down
35 changes: 35 additions & 0 deletions py/dml/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,41 @@ def log(self):
DMLError.log(self)
self.print_site_message(self.other_site, "existing declaration")


class EEXTENSION(DMLError):
"""When the `explict_object_extensions` provisional feature is enabled,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link

any object definition made via `in` syntax is considered an extension such
that there must be some other non-extension declaration of the object, or
DMLC will reject the extension.
To declare and define a new object not already declared, omit the `in`
syntax.
"""
fmt = ("object '%s' not declared elsewhere."
" To declare and define a new object, omit 'in'.")

class EMULTIOBJDECL(DMLError):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled a bit with naming of these error tags, but ultimately it doesn't matter much.

"""When the `explicit_object_extensions` provisional feature is enabled,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link

any object declaration not made using `in` syntax is considered a
declaration of a novel object &mdash; because of that, DMLC will reject
it if there already is another non-`in` declaration across files enabling
`explicit_object_extensions`.
"""
fmt = ("object '%s' already declared."
" To extend upon the definition of an object, use 'in %s'")
def __init__(self, site, other_site, objtype, name):
super().__init__(site, name, f'{objtype} {name} ...')
self.other_site = other_site

def log(self):
from . import provisional
DMLError.log(self)
self.print_site_message(self.other_site, "existing declaration")
prov_site = self.site.provisional_enabled(
provisional.explicit_object_extensions)
self.print_site_message(
prov_site,
"enabled by the explicit_object_extensions provisional feature")

class EVARPARAM(DMLError):
"""
The value assigned to the parameter is not a well-defined constant.
Expand Down
28 changes: 28 additions & 0 deletions py/dml/provisional.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,34 @@ class simics_util_vect(ProvisionalFeature):
stable = True
dml12 = True


@feature
class explicit_object_extensions(ProvisionalFeature):
'''<a id="explicit_object_extensions"/>
This feature extends the DML syntax for object declarations to distinguish
Copy link
Contributor

@mandolaerik mandolaerik Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline after <a, otherwise markdown is messed up in the first paragraph.

Copy link
Contributor Author

@lwaern-intel lwaern-intel Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait-- you mean

<a
 id="explicit_object_extensions"/>

? That's messed up, man...

Copy link
Contributor Author

@lwaern-intel lwaern-intel Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait i can't see that reflected for explicit_param_decls. Does that mean its docs are messed up?

Copy link
Contributor Author

@lwaern-intel lwaern-intel Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... in fact I can't see a <a-newline pattern being used anywhere?

Copy link
Contributor

@mandolaerik mandolaerik Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, I meant to write:

<a id="foo"/>

blah `blah`

instead of:

<a id="foo"/>
blah `blah`

The first one is rendered like this:

blah blah

The second one is rendered like this:

blah `blah`

and don't ask me why everything is underlined now

between an intent to introduce a new object to the model structure, and an
intent to extend the definition of an existing object.

The following form is introduced to mark the intent to extend an object:
<pre>
in <em>object-type</em> <em>name</em> <em>...</em> { <em>...</em> }
</pre>
E.g.
<pre>
in bank some_bank { ... }
</pre>

If this form is used while there is no other non-extension declaration of
the named object, then DMLC will signal an error because the definition
was not intended to introduce the object to the model structure.

DMLC will also signal an error if there is more than one non-extension
declaration of the object among the files enabling
`explicit_object_extensions`.
'''
short = "Require `in` syntax for additional declarations of an object"
stable = False

def parse_provisional(
provs: list[("Site", str)]) -> dict[ProvisionalFeature, "Site"]:
ret = {}
Expand Down
45 changes: 35 additions & 10 deletions py/dml/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,9 @@ def wrap_sites(spec, issite, tname):
else:
raise ICE(issite, 'unknown node type %r %r' % (asttype, stmt))
composite_wrapped = [
(objtype, name, arrayinfo,
(objtype, name, arrayinfo, is_extension,
ObjectSpec(*wrap_sites(spec, issite, tname)))
for (objtype, name, arrayinfo, spec) in composite]
for (objtype, name, arrayinfo, is_extension, spec) in composite]
blocks.append((preconds, shallow_wrapped, composite_wrapped))

return (TemplateSite(spec.site, issite, tname), spec.rank,
Expand Down Expand Up @@ -833,8 +833,8 @@ def sort_method_implementations(implementations, obj_specs):
return traits.sort_method_implementations(implementations)

def merge_subobj_defs(def1, def2, parent):
(objtype, name, arrayinfo, obj_specs1) = def1
(objtype2, name2, arrayinfo2, obj_specs2) = def2
(objtype, name, arrayinfo, extension_status1, obj_specs1) = def1
(objtype2, name2, arrayinfo2, extension_status2, obj_specs2) = def2
assert name == name2

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

# extension status:
# None -> dual extension and decl
# True -> extension
# False -> explicit decl
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ternary rep is pretty damn ugly. @mandolaerik does this deserve the use of enum (which'd be a fresh DMLC dependency?) Or is there a third approach perhaps I haven't thought of?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current rep is fine, but I also wouldn't object to enum. The question it poses to me is rather whether it makes sense to handle subobj merge in this incremental pairwise fashion -- I think this logic would be much easier to express if we just merged all object defs at once.

IIRC the pairwise thing is there for a historical reason: before dml 1.4, templates would consist of pre-merged subobj defs, so a template used many times did not have to merge defs again.

It would probably make sense to refactor all of merge_subobj_defs, but no need to do within this PR.

if extension_status1 is extension_status2 is False:
# Blame the one with higher rank if possible. Otherwise, blame the
# new def.
(decl_site_1, decl_site_2) = (
(site1, site2) if (
obj_specs2[0].rank in obj_specs1[0].rank.inferior)
else (site2, site1))
report(EMULTIOBJDECL(decl_site_1, decl_site_2, objtype, name))
# One is explicit decl: merged is explicit decl
elif extension_status1 is False or extension_status2 is False:
extension_status1 = False
# If either is dual, make dual
elif extension_status1 is None or extension_status2 is None:
extension_status1 = None
# Otherwise, both are explicit extensions. Keep explicit extension.

if len(arrayinfo) != len(arrayinfo2):
raise EAINCOMP(site1, site2, name,
"mixing declarations with different number "
Expand All @@ -870,7 +890,8 @@ def merge_subobj_defs(def1, def2, parent):
merged_arrayinfo.append((idxvar1, len1))


return (objtype, name, merged_arrayinfo, obj_specs1 + obj_specs2)
return (objtype, name, merged_arrayinfo, extension_status1,
obj_specs1 + obj_specs2)

def method_is_std(node, methname):
"""
Expand Down Expand Up @@ -1708,13 +1729,14 @@ def mkobj2(obj, obj_specs, params, each_stmts):

for (stmts, obj_spec) in composite_subobjs:
for s in stmts:
(objtype, ident, arrayinfo, subobj_spec) = s
(objtype, ident, arrayinfo, is_extension, subobj_spec) = s

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

subobj_def = (objtype, ident, arrayinfo, [subobj_spec])
subobj_def = (objtype, ident, arrayinfo, is_extension,
[subobj_spec])
if ident in subobj_defs:
subobj_defs[ident] = merge_subobj_defs(subobj_defs[ident],
subobj_def, obj)
Expand All @@ -1724,7 +1746,10 @@ def mkobj2(obj, obj_specs, params, each_stmts):
symbols[ident] = subobj_spec.site
subobj_defs[ident] = subobj_def

for (_, _, arrayinfo, specs) in subobj_defs.values():
for (_, ident, arrayinfo, extension_status, specs) in subobj_defs.values():
if extension_status is True:
for spec in specs:
report(EEXTENSION(spec.site, ident))
for (i, (idx, dimsize_ast)) in enumerate(arrayinfo):
if dimsize_ast is None:
idxref = (f" (with index variable '{idx.args[0]}')"
Expand Down Expand Up @@ -1814,7 +1839,7 @@ def mkobj2(obj, obj_specs, params, each_stmts):
# the whole register.
if obj.objtype == 'register' and not any(
objtype == 'field'
for (objtype, _, _, _) in list(subobj_defs.values())):
for (objtype, _, _, _, _) in list(subobj_defs.values())):
# The implicit field instantiates the built-in field
# template and does nothing else.
subobjs.append(mkobj(
Expand Down Expand Up @@ -1856,7 +1881,7 @@ def mkobj2(obj, obj_specs, params, each_stmts):
subobj_name_defs = {}

for name in sorted(subobj_defs, key=lambda name: name or ''):
(objtype, ident, arrayinfo, subobj_specs) = subobj_defs[name]
(objtype, ident, arrayinfo, _, subobj_specs) = subobj_defs[name]
if (not obj.accepts_child_type(objtype)
# HACK: disallow non-toplevel banks in DML 1.2, see SIMICS-19009
or (dml.globals.dml_version == (1, 2)
Expand Down
9 changes: 5 additions & 4 deletions py/dml/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def defined_symbols(self):
symbols[sub.args[0]] = (sub.kind, sub.site)
else:
assert sub.kind == 'error'
for (_, name, _, specs) in composite:
for (_, name, _, _, specs) in composite:
symbols[name] = ('subobj', specs.site)
return symbols

Expand Down Expand Up @@ -299,11 +299,12 @@ def obj_from_asts(site, stmts):
block = []
for decl_ast in composite:
assert decl_ast.kind == 'object'
(name, objtype, indices, sub_stmts) = decl_ast.args
(name, objtype, indices, is_extension,
sub_stmts) = decl_ast.args
spec = obj_from_asts(
decl_ast.site, sub_stmts + [ast.is_(
decl_ast.site, [(decl_ast.site, objtype)])])
block.append((objtype, name, indices, spec))
block.append((objtype, name, indices, is_extension, spec))
blocks.append((preconds, shallow, block))
return ObjectSpec(site, rank, is_stmts, in_eachs, params, blocks)
return obj_from_asts(site, stmts)
Expand Down Expand Up @@ -331,7 +332,7 @@ def rank_structure(asts):
while queue:
(spec, conditional) = queue.pop()
if spec.kind == 'object':
(_, objtype, _, stmts) = spec.args
(_, objtype, _, _, stmts) = spec.args
inferior[objtype] = spec
queue.extend((stmt, conditional) for stmt in stmts)
elif spec.kind == 'is':
Expand Down
37 changes: 37 additions & 0 deletions test/1.4/provisional/T_explicit_object_extensions.dml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
© 2025 Intel Corporation
SPDX-License-Identifier: MPL-2.0
*/

dml 1.4;

provisional explicit_object_extensions;

device test;

import "explicit_object_extensions_disabled.dml";

template t {
group g1 {
group child;
}
in group g2 {
group child;
}
}

template u {
in group g1 {
in group child {
session int dummy;
}
}
group g2 {
in group child {
session int dummy;
}
}
}

group common_1;
in group common_2;
Loading