From 8bbd748e6eee855df3aae039f36de564ddffa430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Hedstr=C3=B6m?= Date: Fri, 26 Nov 2021 13:43:50 +0100 Subject: [PATCH 01/13] Marsupilami draft --- generate_parsetabs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generate_parsetabs.py b/generate_parsetabs.py index 38c93826e..78a215b82 100644 --- a/generate_parsetabs.py +++ b/generate_parsetabs.py @@ -1,6 +1,8 @@ # © 2021 Intel Corporation # SPDX-License-Identifier: MPL-2.0 +# Hubba hubba zut zut + import os import sys From d4ef7f04ad197a0422d49661cee469b6353991d8 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 27 Jan 2022 18:51:04 +0100 Subject: [PATCH 02/13] Use indices from traitref, instead of inferring from pointer arithmetic --- py/dml/c_backend.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 180f6443f..b661bf697 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -2013,17 +2013,8 @@ def generate_trait_trampoline(method, vtable_trait): [_, (tname, ttype)] = implicit_inargs site = method.site obj = method.parent - path = obj.traits.ancestry_paths[vtable_trait][0] if obj.dimensions: - if path[0] is vtable_trait: - downcast = tname - else: - downcast = 'DOWNCAST(%s, %s, %s)' % ( - tname, cident(path[0].name), - '.'.join(cident(t.name) for t in path[1:])) - out('int _flat_index = ((struct _%s *)%s.trait) - &%s%s;\n' % ( - cident(path[0].name), downcast, obj.traits.vtable_cname(path[0]), - '[0]' * obj.dimensions)) + out(f'int _flat_index = {tname}.id.encoded_index;\n') indices = [ mkLit(site, '((_flat_index / %d) %% %d)' % ( reduce(operator.mul, obj.dimsizes[dim + 1:], 1), From 9e2ded1e0ee1e633ddd926ac763151fbc63908fc Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 1 Feb 2022 15:10:36 +0100 Subject: [PATCH 03/13] Cover nontrivial indexing of session variables --- test/1.4/structure/T_trait.dml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/1.4/structure/T_trait.dml b/test/1.4/structure/T_trait.dml index 2030ba2f6..2e9eb2446 100644 --- a/test/1.4/structure/T_trait.dml +++ b/test/1.4/structure/T_trait.dml @@ -53,26 +53,27 @@ port p[i < 2] { } method test() throws { - local child c = cast(p[0], child); + local child c = cast(p[1], child); local base pp; local base *ppp = &pp; *ppp = cast(cast(c, base), base); pp.m(0); c.m(0); c.o(0); - expect(ppp->o(3) == 12, "ppp->o(3) == 12"); - expect(p[1].o(3) == 16, "p[1].o(3) == 16");; + expect(ppp->o(3) == 16, "ppp->o(3) == 16"); + expect(p[0].o(3) == 12, "p[0].o(3) == 12");; // The parameters value is given as a float value 14.2; the parameter's type // causes this to be truncated to an integer when referenced // dynamically as a trait parameter. expect(cast(p[1], child).param == 14, "cast(p[1], child).param == 14"); expect(p[1].param == 14.2, "p[1].param == 14.2"); - p[0].d = 4; - p[0].e = 5; + p[1].d = 4; + p[1].e = 5; + p[0].d = 6; expect(ppp->d == 4, "ppp->d == 4"); expect(ppp->e == 5, "ppp->e == 5"); - expect(p[1].traitref_param.d == 4, "p[1].traitref_param.d == 4"); - expect(p[1].traitref_param.e == 5, "p[1].traitref_param.e == 5"); + expect(p[1].traitref_param.d == 6, "p[1].traitref_param.d == 6"); + expect(p[0].traitref_param.e == 5, "p[0].traitref_param.e == 5"); expect(p[1].param == 14.2, "p[1].param == 14.2"); p[1].hello(); } From 73306b1b99fae743c3ff45a670b826bb487389f9 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 1 Feb 2022 18:26:47 +0100 Subject: [PATCH 04/13] Rearrange device struct: propagate array dimensions to leaf members --- include/simics/dmllib.h | 23 +++++-------- py/dml/c_backend.py | 71 +++++++++++++++-------------------------- py/dml/crep.py | 42 ++++++++++++++---------- py/dml/ctree.py | 16 +++++----- 4 files changed, 66 insertions(+), 86 deletions(-) diff --git a/include/simics/dmllib.h b/include/simics/dmllib.h index 725aeb6d7..835367513 100644 --- a/include/simics/dmllib.h +++ b/include/simics/dmllib.h @@ -807,7 +807,6 @@ typedef struct { get_attr_t get; set_attr_t set; uint32 array_size; - uint32 array_delta; } _port_array_attr_t; static attr_value_t _getattr_from_portobj_array(lang_void *ptr, conf_object_t *obj, @@ -815,12 +814,11 @@ _getattr_from_portobj_array(lang_void *ptr, conf_object_t *obj, { ASSERT(SIM_attr_is_nil(*idx)); _port_array_attr_t *port = (_port_array_attr_t *)ptr; - uintptr_t port_obj_ptr_base = (uintptr_t)obj - + port->port_obj_base_offset; + conf_object_t **port_obj_ptr_base = (conf_object_t **)( + (uintptr_t)obj + port->port_obj_base_offset); attr_value_t vals = SIM_alloc_attr_list(port->array_size); for (uint32 i = 0; i < port->array_size; i++) { - conf_object_t *port_obj = *(conf_object_t **)( - port_obj_ptr_base + (uint64)i * port->array_delta); + conf_object_t *port_obj = port_obj_ptr_base[i]; attr_value_t val = port->get(NULL, port_obj, NULL); if (SIM_attr_is_invalid(val)) { SIM_attr_free(&vals); @@ -836,11 +834,10 @@ _setattr_from_portobj_array(lang_void *ptr, conf_object_t *obj, { ASSERT(SIM_attr_is_nil(*idx)); _port_array_attr_t *port = (_port_array_attr_t *)ptr; - uintptr_t port_obj_ptr_base = (uintptr_t)obj - + port->port_obj_base_offset; + conf_object_t **port_obj_ptr_base = (conf_object_t **)( + (uintptr_t)obj + port->port_obj_base_offset); for (uint32 i = 0; i < port->array_size; i++) { - conf_object_t *port_obj = *(conf_object_t **)( - port_obj_ptr_base + (uint64)i * port->array_delta); + conf_object_t *port_obj = port_obj_ptr_base[i]; attr_value_t val = SIM_attr_list_item(*vals, i); set_error_t err = port->set(NULL, port_obj, &val, NULL); if (err != Sim_Set_Ok) { @@ -850,11 +847,10 @@ _setattr_from_portobj_array(lang_void *ptr, conf_object_t *obj, return Sim_Set_Ok; } // port_obj_offset is the offset within the device struct of the first pointer -// to a port object. Pointers to remaining port object in the array are located -// array_delta bytes apart. +// to a port object. Remaining port objects are stored consecutively in memory. UNUSED static void _register_port_array_attr(conf_class_t *devcls, conf_class_t *portcls, - ptrdiff_t port_obj_offset, ptrdiff_t array_delta, + ptrdiff_t port_obj_offset, uint32 array_size, bool is_bank, const char *portname, const char *attrname, get_attr_t getter, set_attr_t setter, @@ -867,9 +863,6 @@ _register_port_array_attr(conf_class_t *devcls, conf_class_t *portcls, data->get = getter; data->set = setter; data->array_size = array_size; - data->array_delta = array_delta; - // storing ptrdiff in uint32, overflow will not happen in practise - ASSERT(data->array_delta == array_delta); char name[strlen(portname) + strlen(attrname) + 2]; sprintf(name, "%s_%s", portname, attrname); strbuf_t proxy_desc = sb_newf("Proxy attribute for %s.%s[].%s", diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index b661bf697..afdd70f93 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -117,7 +117,7 @@ def print_device_substruct(node): if the node needs no storage.''' def arraywrap(node, typ): - for arraylen in reversed(node.arraylens()): + for arraylen in reversed(node.dimsizes): typ = TArray(typ, mkIntegerLiteral(node.site, arraylen)) return typ @@ -133,7 +133,7 @@ def composite_ctype(node, unfiltered_members, label=None): for s in crep.ancestor_cnames(node)) structtype = TStruct(members, label) structtype.print_struct_definition() - return arraywrap(node, structtype) + return structtype if node.objtype == 'device': members = [("obj", conf_object_t)] @@ -149,7 +149,7 @@ def composite_ctype(node, unfiltered_members, label=None): elif ((node.objtype == 'session' or node.objtype == 'saved') or (dml.globals.dml_version == (1, 2) and node.objtype == 'interface')): - return crep.node_storage_type(node, node.site) + return arraywrap(node, crep.node_storage_type(node, node.site)) elif (node.objtype in ('register', 'field') and dml.globals.dml_version == (1, 2)): @@ -162,7 +162,8 @@ def composite_ctype(node, unfiltered_members, label=None): @apply def members(): if allocate: - yield ('__DMLfield', crep.node_storage_type(node)) + yield ('__DMLfield', + arraywrap(node, crep.node_storage_type(node))) for sub in node.get_components(): if not sub.ident: # implicit field: Don't add anything, storage @@ -179,7 +180,7 @@ def members(): else: # really a _port_object_t* rather than conf_object_t*, but # there is no DML type for the former - obj = [("_obj", TPtr(conf_object_t))] + obj = [("_obj", arraywrap(node, TPtr(conf_object_t)))] return composite_ctype(node, obj + [(crep.cname(sub), print_device_substruct(sub)) @@ -546,10 +547,10 @@ def generate_attribute_common(initcode, node, port, dimsizes, prefix, if port.dimensions == 0: register_attribute(node.site, None, "%s_%s" % (port.name, attrname)) initcode.out( - '_register_port_attr(class, %s, offsetof(%s, %s._obj), %s,' + '_register_port_attr(class, %s, offsetof(%s, %s), %s,' % (port_class_ident(port), crep.structtype(dml.globals.device), - crep.cref_node(port, ()), + crep.cref_portobj(port, ()), 'true' if port.objtype == "bank" else 'false') + ' "%s", "%s", %s, %s, %s, "%s", %s);\n' % (port.name, attrname, getter, setter, @@ -557,17 +558,16 @@ def generate_attribute_common(initcode, node, port, dimsizes, prefix, elif port.dimensions == 1: # Generate an accessor attribute for legacy reasons register_attribute(node.site, None, "%s_%s" % (port.name, attrname)) - member = crep.cref_node( + member = crep.cref_portobj( port, (mkLit(port.site, '0', TInt(32, False)),)) (dimsize,) = port.dimsizes initcode.out( - '_register_port_array_attr(class, %s, offsetof(%s, %s._obj),' + '_register_port_array_attr(class, %s, offsetof(%s, %s),' % (port_class_ident(port), crep.structtype(dml.globals.device), member) - + ' sizeof(((%s*)0)->%s), %d, %s, "%s", "%s", %s, %s, %s, "%s",' - % (crep.structtype(dml.globals.device), member, dimsize, - 'true' if port.objtype == "bank" else 'false', + + ' %d, %s, "%s", "%s", %s, %s, %s, "%s",' + % (dimsize, 'true' if port.objtype == "bank" else 'false', port.name, attrname, getter, setter, attr_flag, attr_type) + ' %s);\n' % (doc.read(),)) else: @@ -1491,16 +1491,16 @@ def generate_init_port_objs(device): index_array = "%s%s" % ( index_enumeration.read(), ''.join('[_i%d]' % (i,) for i in range(port.dimensions))) - out('_dev->%s._obj = _init_port_object(&_dev->obj' - % (crep.cref_node(port, loop_indices),) + out('_dev->%s = _init_port_object(&_dev->obj' + % (crep.cref_portobj(port, loop_indices),) + ', sb_str(&portname), %d, %s);\n' % (port.dimensions, index_array)) out('sb_free(&portname);\n') for _ in range(port.dimensions): out('\n}', preindent=-1) else: - out('_dev->%s._obj = _init_port_object(' - % (crep.cref_node(port, ()),) + out('_dev->%s = _init_port_object(' + % (crep.cref_portobj(port, ()),) + '&_dev->obj, "%s%s", 0, NULL);\n' % (port_prefix, port.logname_anonymized())) out('}\n', preindent=-1) @@ -1984,7 +1984,7 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): assert session_node.objtype in ('session', 'saved') args.append('offsetof(%s, %s)' % ( crep.structtype(dml.globals.device), - crep.cref_node(session_node, indices))) + crep.cref_session(session_node, indices))) # initialize vtable instance vtable_arg = '&%s%s' % (node.traits.vtable_cname(trait), index_str) init_calls.append( @@ -2069,7 +2069,7 @@ def calculate_saved_userdata(node, dimsizes, attr_name, sym_spec = None): # saved variable loop_vars = (mkIntegerConstant(decl_site, 0, False),) * node.dimensions var_ref = mkNodeRef(node.site, node, loop_vars) - device_member = crep.cref_node(node, loop_vars) + device_member = crep.cref_session(node, loop_vars) else: assert False @@ -2077,38 +2077,17 @@ def calculate_saved_userdata(node, dimsizes, attr_name, sym_spec = None): crep.structtype(dml.globals.device), device_member) - attr_type = serialize.map_dmltype_to_attrtype(decl_site, var_ref.ctype()) c_type = var_ref.ctype() + attr_type = serialize.map_dmltype_to_attrtype(decl_site, c_type) for dimension in reversed(dimsizes): attr_type = "[%s{%d}]" % (attr_type, dimension) - # For variables that are spread out throughout the device struct, build - # up the correct strides through their dimensions - if node.objtype == 'saved': - curr_node = node.parent - dimension_strides = [] - while curr_node.objtype != 'device': - # if the current node is not an array, we skip it - if curr_node.local_dimensions() == 0: - curr_node = curr_node.parent - continue - loopvars = ((mkIntegerConstant(decl_site, 0, False),) * - curr_node.dimensions) - # If size is constant below this point, we stop - if not loopvars: - break - dimension_strides.append("sizeof(((%s*)0)->%s)" % ( - crep.structtype(dml.globals.device), - crep.cref_node(curr_node, loopvars))) - curr_node = curr_node.parent - dimension_strides = tuple(reversed(dimension_strides)) - else: - # Static variables are easier to access, with a predictable stride - dimension_strides = tuple( - "sizeof(((%s*)0)->%s%s)" % ( - crep.structtype(dml.globals.device), - device_member, "[0]" * (depth + 1)) - for depth in range(node.dimensions)) + dimension_strides = [] + stride = f'sizeof({c_type.declaration("")})' + for dimsize in reversed(node.dimsizes): + dimension_strides.append(stride) + stride += f' * {dimsize}' + dimension_strides = tuple(reversed(dimension_strides)) serialize_name = serialize.lookup_serialize(c_type) deserialize_name = serialize.lookup_deserialize(c_type) diff --git a/py/dml/crep.py b/py/dml/crep.py index d81cd40a8..528c9fe72 100644 --- a/py/dml/crep.py +++ b/py/dml/crep.py @@ -14,7 +14,8 @@ __all__ = ( 'cname', 'cref', - 'cref_node', + 'cref_portobj', + 'cref_session', 'ctype', 'conf_obj', 'cloggroup', @@ -60,31 +61,38 @@ def cref(method_node): # This might actually conflict, but the chances are small. return '__'.join(ancestor_cnames(method_node)[1:]) -def cref_node(node, indices): +def cref_portobj(node, indices): + assert node.objtype in {'port', 'bank'} + components = ['_obj'] + parent = node + while parent.parent: + components.append(cname(parent)) + parent = parent.parent + return ('.'.join(reversed(components)) + + ''.join(f'[{i.read()}]' for i in indices)) + +def cref_session(node, indices): + assert (node.objtype in {'session', 'saved'} + or (dml.globals.dml_version == (1, 2) + and node.objtype in {'field', 'register', 'interface', + 'attribute', 'device'})), ( + node.objtype) assert isinstance(indices, tuple) if node.name is None and node.objtype == 'field': assert dml.globals.dml_version == (1, 2) # implicit field, inherits everything from parent register - return cref_node(node.parent, indices) - if node.objtype == 'device': - return '' - if node.objtype == 'method': - return cref(node) + return cref_session(node.parent, indices) components = [] - if (dml.globals.dml_version == (1, 2) - and node.objtype in ('register', 'field') and not node.simple_storage): + if node.objtype in ('register', 'field') and not node.simple_storage: + assert dml.globals.dml_version == (1, 2) components.append('__DMLfield') parent = node while parent.parent: - remaining_dimensions = len(indices) - parent.local_dimensions() - used_indices = indices[remaining_dimensions:] - indices = indices[:remaining_dimensions] - index = "".join(['[%s]' % i.read() for i in used_indices]) - components.append(cname(parent) + index) + components.append(cname(parent)) parent = parent.parent - assert not indices - return '.'.join(reversed(components)) + return ('.'.join(reversed(components)) + + ''.join(f'[{i.read()}]' for i in indices)) def node_storage_type(node, site = None): "Return the storage type for a node, or None" @@ -157,7 +165,7 @@ def conf_object(node, indices): and node.name is None): return '&_dev->obj' else: - return '_dev->%s._obj' % cref_node(node, indices[:node.dimensions]) + return '_dev->%s' % cref_portobj(node, indices[:node.dimensions]) def cloggroup(name): if dml.globals.compat_dml12: diff --git a/py/dml/ctree.py b/py/dml/ctree.py index e28c3b459..25704e6cc 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -2493,8 +2493,8 @@ def read_iface_struct(iface_noderef): interface struct.''' if dml.globals.dml_version == (1, 2): assert isinstance(iface_noderef, NodeRefWithStorage) - return '_dev->' + crep.cref_node(iface_noderef.node, - iface_noderef.indices) + return '_dev->' + crep.cref_session(iface_noderef.node, + iface_noderef.indices) else: assert isinstance(iface_noderef, PlainNodeRef) struct_name = param_str(iface_noderef.node, '_c_type') @@ -3527,18 +3527,18 @@ def read(self): report(PVAL(dmlparse.end_site(self.site))) node = self.node - expr = crep.cref_node(node, self.indices) if node.objtype == 'method': # enforced by crep.node_storage_type assert dml.globals.dml_version == (1, 2) from . import codegen method = codegen.method_instance(node) codegen.mark_method_referenced(method) - expr = '_DML_M_' + expr - elif expr: - expr = '_dev->' + expr + expr = '_DML_M_' + crep.cref(node) + elif node.objtype == 'device': + assert dml.globals.dml_version == (1, 2) + return '_dev' else: - expr = '_dev' + expr = '_dev->' + crep.cref_session(node, self.indices) return expr def apply(self, args): @@ -3564,7 +3564,7 @@ def __str__(self): assert name return name def read(self): - return '_dev->' + crep.cref_node(self.node, self.indices) + return '_dev->' + crep.cref_session(self.node, self.indices) class PlainNodeRef(NodeRef, NonValue): pass From 3338a4d8040a683b35ae351cad0b2f6eb05ac8e5 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 1 Feb 2022 18:27:31 +0100 Subject: [PATCH 05/13] Rename crep crep_method is still a bad name, but at least consistent with other crep_* --- py/dml/c_backend.py | 16 ++++++++-------- py/dml/codegen.py | 2 +- py/dml/crep.py | 4 ++-- py/dml/ctree.py | 4 ++-- py/dml/structure.py | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index afdd70f93..592334d77 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -759,9 +759,9 @@ def generate_implement_method(device, ifacestruct, meth, indices): mt, '', ifacemethtype.output_type, 'method') if indices is PORTOBJ: - name = '_DML_PIFACE_' + crep.cref(meth) + name = '_DML_PIFACE_' + crep.cref_method(meth) else: - name = '_DML_IFACE_' + crep.cref(meth) + "".join([ + name = '_DML_IFACE_' + crep.cref_method(meth) + "".join([ "__%d" % idx for idx in indices]) wrap_method(meth, name, indices) @@ -1049,7 +1049,7 @@ def event_callbacks(event, indices): if not method: raise ICE(event.site, 'cannot find method %s' % (method_name)) result.append((method, '_DML_EV_%s%s' % ( - crep.cref(method), + crep.cref_method(method), '_'.join(str(i) for i in indices)))) return result @@ -1133,15 +1133,15 @@ def generate_register_tables(device): dims = r.dimsizes or [] getter = r.node.get_component('_get64') setter = r.node.get_component('_set64') - generate_reg_callback(getter, '_DML_MI_%s' % crep.cref(getter), + generate_reg_callback(getter, '_DML_MI_%s' % crep.cref_method(getter), ) - generate_reg_callback(setter, '_DML_MI_%s' % crep.cref(setter)) + generate_reg_callback(setter, '_DML_MI_%s' % crep.cref_method(setter)) name = r.node.logname_anonymized(tuple("" for _ in dims), relative='bank') regs.append((name, len(dims), - '_DML_MI_%s' % crep.cref(getter), - '_DML_MI_%s' % crep.cref(setter))) + '_DML_MI_%s' % crep.cref_method(getter), + '_DML_MI_%s' % crep.cref_method(setter))) mark_method_referenced(method_instance(getter)) mark_method_referenced(method_instance(setter)) regidxs[r] = i @@ -1941,7 +1941,7 @@ def scramble_argname(name): def trait_trampoline_name(method, vtable_trait): return "%s__trampoline_from_%s" % ( - crep.cref(method), vtable_trait.name) + crep.cref_method(method), vtable_trait.name) def flatten_object_subtree(node): '''return a list of all composite subobjects inside node''' diff --git a/py/dml/codegen.py b/py/dml/codegen.py index dfaad3e10..da09718dc 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -2530,7 +2530,7 @@ def get_name(self): return name def get_cname(self): - base = crep.cref(self.method) + base = crep.cref_method(self.method) return '_DML_M_' + base + self.suffix def canonicalize_signature(signature): diff --git a/py/dml/crep.py b/py/dml/crep.py index 528c9fe72..fb3108220 100644 --- a/py/dml/crep.py +++ b/py/dml/crep.py @@ -13,7 +13,7 @@ __all__ = ( 'cname', - 'cref', + 'cref_method', 'cref_portobj', 'cref_session', 'ctype', @@ -56,7 +56,7 @@ def ancestor_cnames(node): node = node.parent return list(reversed(names)) -def cref(method_node): +def cref_method(method_node): assert method_node.objtype == 'method' # This might actually conflict, but the chances are small. return '__'.join(ancestor_cnames(method_node)[1:]) diff --git a/py/dml/ctree.py b/py/dml/ctree.py index 25704e6cc..95a6ead23 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -2238,7 +2238,7 @@ def make_simple(cls, site, rh): and node.name == 'callback' \ and node.parent.objtype == 'event': return AddressOf(site, mkLit( - site, '_DML_EV_'+crep.cref(node), + site, '_DML_EV_'+crep.cref_method(node), TFunction([TPtr(TNamed('conf_object_t')), TPtr(TVoid())], TVoid()))) @@ -3533,7 +3533,7 @@ def read(self): from . import codegen method = codegen.method_instance(node) codegen.mark_method_referenced(method) - expr = '_DML_M_' + crep.cref(node) + expr = '_DML_M_' + crep.cref_method(node) elif node.objtype == 'device': assert dml.globals.dml_version == (1, 2) return '_dev' diff --git a/py/dml/structure.py b/py/dml/structure.py index 8eae773dc..8099f7022 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -1329,7 +1329,7 @@ def process_method_implementations(obj, name, implementations, raise EEXTERN(method.site) func = method_instance(method) mark_method_referenced(func) - mark_method_exported(func, crep.cref(method), msite) + mark_method_exported(func, crep.cref_method(method), msite) break # Export hard_reset and soft_reset from device objects in 1.2 # automatically @@ -1337,7 +1337,7 @@ def process_method_implementations(obj, name, implementations, name in ('hard_reset', 'soft_reset')): func = method_instance(method) mark_method_referenced(func) - mark_method_exported(func, crep.cref(method), obj.site) + mark_method_exported(func, crep.cref_method(method), obj.site) return method From 9416d9c9194d3912b0233bd81177427e40dda89a Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 1 Feb 2022 15:12:02 +0100 Subject: [PATCH 06/13] Use encoded index when accessing session variables This way, the vtable member can be constant across object arrays --- py/dml/c_backend.py | 5 ++++- py/dml/ctree.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 592334d77..3e3c7b619 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1984,7 +1984,10 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): assert session_node.objtype in ('session', 'saved') args.append('offsetof(%s, %s)' % ( crep.structtype(dml.globals.device), - crep.cref_session(session_node, indices))) + crep.cref_session( + session_node, + (mkIntegerConstant(node.site, 0, False),) + * len(indices)))) # initialize vtable instance vtable_arg = '&%s%s' % (node.traits.vtable_cname(trait), index_str) init_calls.append( diff --git a/py/dml/ctree.py b/py/dml/ctree.py index 95a6ead23..d026c7a71 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -3342,9 +3342,9 @@ def ctype(self): return TPtr(self.type_) def read(self): - return '(%s)((uintptr_t)_dev + %s->%s)' % ( + return '((%s)((uintptr_t)_dev + %s->%s) + (%s).id.encoded_index)' % ( TPtr(self.type_).declaration(''), vtable_read(self.traitref), - self.name) + self.name, self.traitref.read()) class TraitMethodRef(NonValue): '''A reference to a bound method in a trait. The expression From 602640c952f7c2192f506211ee78d1075fcc1ad2 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 2 Feb 2022 19:22:14 +0100 Subject: [PATCH 07/13] Represent parameters in object arrays as arrays in vtables This is a first step, uses quadratic memory since vtables are still expanded across arrays (causes violent death in T_largearray) --- include/simics/dmllib.h | 4 ++++ py/dml/c_backend.py | 45 ++++++++++++++++++++++++++++++++++------- py/dml/ctree.py | 9 ++++++--- py/dml/traits.py | 2 +- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/include/simics/dmllib.h b/include/simics/dmllib.h index 835367513..d076ec661 100644 --- a/include/simics/dmllib.h +++ b/include/simics/dmllib.h @@ -183,6 +183,10 @@ DML_eq(uint64 a, uint64 b) ({_traitref_t __tref = traitref; \ ((struct _ ## type *) __tref.trait)->method(dev, __tref);}) \ +#define VTABLE_PARAM(traitref, vtable_type, member) \ + ({_traitref_t __tref = traitref; \ + ((vtable_type *)__tref.trait)->member[__tref.id.encoded_index];}) + #define _raw_load_uint8_be_t UNALIGNED_LOAD_BE8 #define _raw_load_uint16_be_t UNALIGNED_LOAD_BE16 #define _raw_load_uint32_be_t UNALIGNED_LOAD_BE32 diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 3e3c7b619..d64c87829 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1925,7 +1925,7 @@ def scramble_argname(name): out(', %s' % (mtype.declaration(scrambled_name),)) elif member_kind == 'parameter': (site, typ) = vtable_trait.vtable_params[name] - out(', %s' % (typ.declaration(scrambled_name))) + out(', %s' % (TPtr(typ).declaration(scrambled_name))) else: assert member_kind == 'session' out(', uint32 %s' % (scrambled_name)) @@ -1968,6 +1968,9 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): param_overrides = param_values[node] for trait in node.traits.referenced: args = [] + param_decl = [] + param_init = [] + param_indices = ''.join(f'[_i{i}]' for i in range(node.dimensions)) for name in tinit_args(trait): member_kind = trait.member_kind(name) if member_kind == 'method': @@ -1977,7 +1980,17 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): if name in method_overrides else "NULL") elif member_kind == 'parameter': # param_overrides contains C expression strings - args.append(param_overrides[name]) + (value, t) = param_overrides[name] + var = f'_param_{name}' + args.append(f'{var}' + '[0]' * node.dimensions) + array_type = t + for d in reversed(node.dimsizes): + array_type = TArray(array_type, + mkIntegerLiteral(node.site, d)) + param_decl.append(f'''\ +{TPtr(array_type).declaration(var)} = MM_MALLOC(1, typeof(*{var})); +''') + param_init.append(f'(*{var}){param_indices} = {value};') else: assert member_kind == 'session' session_node = node.get_component(name) @@ -1990,9 +2003,26 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): * len(indices)))) # initialize vtable instance vtable_arg = '&%s%s' % (node.traits.vtable_cname(trait), index_str) - init_calls.append( - (dims, '_tinit_%s(%s);\n' - % (trait.name, ', '.join([vtable_arg] + args)))) + init_call = ('_tinit_%s(%s);\n' + % (trait.name, ', '.join([vtable_arg] + args))) + + if param_init: + code = param_decl + close = [] + for (i, d) in enumerate(node.dimsizes): + indent = ' ' * i + code.append( + indent + f'for (unsigned _i{i} = 0; _i{i} < {d}; ++_i{i}) {{') + close.append(indent + '}') + code.extend(' ' * node.dimensions + init + for init in param_init) + code.extend(reversed(close)) + code.append(init_call) + code = ['{'] + [' ' + line for line in code] + ['}'] + else: + assert not param_decl + code = [init_call] + init_calls.append((dims, ''.join(f'{line}\n' for line in code))) for subnode in node.get_components(): if isinstance(subnode, objects.CompositeObject): init_calls.extend(init_trait_vtables_for_node( @@ -2251,11 +2281,12 @@ def trait_param_value(node, param_type_site, param_type): for dim in range(node.dimensions))) if isinstance(expr, NonValue): raise expr.exc() - return source_for_assignment(expr.site, param_type, expr).read() + return (source_for_assignment(expr.site, param_type, expr).read(), + param_type) except DMLError as e: report(e) - return "0" + return ("0", param_type) def resolve_trait_param_values(node): '''Generate code for parameter initialization of all traits diff --git a/py/dml/ctree.py b/py/dml/ctree.py index d026c7a71..7ace0b53c 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -3314,7 +3314,7 @@ def vtable_read(expr): expr.read()) class TraitParameter(Expression): - priority = 160 # SubRef.priority + priority = dml.expr.Apply.priority @auto_init def __init__(self, site, traitref, name, type): pass @@ -3322,8 +3322,11 @@ def __str__(self): return "%s.%s" % (self.traitref, self.name) def read(self): - return ("%s->%s" - % (vtable_read(self.traitref), self.name)) + t = realtype(self.traitref.ctype()) + assert isinstance(t, TTrait) + vtable_type = f'struct _{cident(t.trait.name)}' + return (f'VTABLE_PARAM({self.traitref.read()}, {vtable_type}' + f', {self.name})') class TraitSessionRef(Expression): '''A reference to a trait session variable. diff --git a/py/dml/traits.py b/py/dml/traits.py index 657b0dd09..388a8753d 100644 --- a/py/dml/traits.py +++ b/py/dml/traits.py @@ -686,7 +686,7 @@ def vtable_method_type(self, inp, outp, throws): def vtable(self): for (name, (_, ptype)) in list(self.vtable_params.items()): - yield (name, ptype) + yield (name, TPtr(ptype)) for (name, (_, ptype)) in list(self.vtable_sessions.items()): yield (name, TInt(32, False)) for (name, (_, inp, outp, throws)) in list(self.vtable_methods.items()): From e096d7848a5bc7859faa0b1e8acfcdd371831fc5 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 2 Feb 2022 22:34:27 +0100 Subject: [PATCH 08/13] Share vtable instance across object arrays --- include/simics/dmllib.h | 8 +--- py/dml/c_backend.py | 97 ++++++++++------------------------------- py/dml/codegen.py | 3 +- py/dml/ctree.py | 3 +- 4 files changed, 27 insertions(+), 84 deletions(-) diff --git a/include/simics/dmllib.h b/include/simics/dmllib.h index d076ec661..15efcf977 100644 --- a/include/simics/dmllib.h +++ b/include/simics/dmllib.h @@ -605,14 +605,10 @@ _identity_eq(const _identity_t a, const _identity_t b) { // List of vtables of a specific trait in one specific object typedef struct { - // first trait vtable instance. Instances are stored linearly in a - // possibly multi-dimensional array, with outer index corresponding to - // index of outer DML object. - uintptr_t base; + // trait vtable instance + void *vtable; // total number of elements (product of object's dimsizes) uint64 num; - // offset between two vtable instances; at least sizeof() - uint32 offset; // The unique object id uint32 id; } _vtable_list_t; diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index d64c87829..857e1b6c4 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1698,16 +1698,13 @@ def generate_each_in_table(node, trait, subnodes): # vtables exist, because trait instances are marked referenced # when an 'each in' instance is referenced ancestry_path = sub.traits.ancestry_paths[trait][0] - base = '&%s%s%s' % ( + base = '&%s%s' % ( sub.traits.vtable_cname(ancestry_path[0]), - '[0]' * sub.dimensions, ''.join('.' + cident(t.name) for t in ancestry_path[1:])) num = reduce(operator.mul, sub.dimsizes, 1) uniq = sub.uniq - offset = 'sizeof(struct _%s)' % (cident(ancestry_path[0].name),) - items.append("{(uintptr_t)%s, %d, %s, %d}\n" - % (base, num, offset, uniq)) + items.append("{%s, %d, %d}\n" % (base, num, uniq)) init = '{\n%s}' % (',\n'.join(' %s' % (item,) for item in items),) add_variable_declaration( 'const _vtable_list_t %s[%d]' % (ident, len(subnodes)), init) @@ -1949,21 +1946,8 @@ def flatten_object_subtree(node): 'event', 'port', 'implement', 'attribute', 'connect', 'interface', 'bank', 'group', 'register', 'field') -def init_trait_vtables_for_node(node, param_values, dims, parent_indices): - init_calls = [] - if node.isindexed(): - if all(not o.traits for o in flatten_object_subtree(node)): - return - indices = () - for (arridx, arrlen) in enumerate(node.arraylens()): - idx = "_i%d" % (arridx + node.nonlocal_dimensions()) - dims = dims + (arrlen,) - indices += (mkLit(node.site, idx, TInt(32, True)),) - indices = parent_indices + indices - else: - indices = parent_indices +def init_trait_vtables_for_node(node, param_values): if node.traits: - index_str = ''.join('[%s]' % (i,) for i in indices) method_overrides = node.traits.method_overrides param_overrides = param_values[node] for trait in node.traits.referenced: @@ -2000,9 +1984,9 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): crep.cref_session( session_node, (mkIntegerConstant(node.site, 0, False),) - * len(indices)))) + * node.dimensions))) # initialize vtable instance - vtable_arg = '&%s%s' % (node.traits.vtable_cname(trait), index_str) + vtable_arg = f'&{node.traits.vtable_cname(trait)}' init_call = ('_tinit_%s(%s);\n' % (trait.name, ', '.join([vtable_arg] + args))) @@ -2022,12 +2006,7 @@ def init_trait_vtables_for_node(node, param_values, dims, parent_indices): else: assert not param_decl code = [init_call] - init_calls.append((dims, ''.join(f'{line}\n' for line in code))) - for subnode in node.get_components(): - if isinstance(subnode, objects.CompositeObject): - init_calls.extend(init_trait_vtables_for_node( - subnode, param_values, dims, indices)) - return init_calls + yield ''.join(f'{line}\n' for line in code) def generate_trait_trampoline(method, vtable_trait): implicit_inargs = vtable_trait.implicit_args() @@ -2079,9 +2058,8 @@ def generate_vtable_instances(devnode): if subnode.traits: for trait in subnode.traits.referenced: add_variable_declaration( - 'struct _%s %s%s' - % (cident(trait.name), subnode.traits.vtable_cname(trait), - ''.join('[%d]' % i for i in subnode.dimsizes))) + 'struct _%s %s' + % (cident(trait.name), subnode.traits.vtable_cname(trait))) def calculate_saved_userdata(node, dimsizes, attr_name, sym_spec = None): if node.objtype == 'method': @@ -2224,53 +2202,24 @@ def generate_init_trait_vtables(node, param_values): generate_tinit(trait) for subnode in flatten_object_subtree(node): generate_trait_trampolines(subnode) - init_calls = init_trait_vtables_for_node(node, param_values, (), ()) - by_dims = {} - for (dims, call) in init_calls: - by_dims.setdefault(dims, []).append(call) - all_dims = sorted(by_dims) - fun_count = 0 - call_count = 0 - def finish_current_function(last_dims): - for _ in last_dims: - out('}\n', preindent=-1) + init_blocks = [ + block + for subnode in flatten_object_subtree(node) + for block in init_trait_vtables_for_node(subnode, param_values)] + + # split into chunks of ~20 objects each. + # Tested in a device with ~1500 objects, + # where optimum chunk size seems to be between 10 and 100. + chunks = [init_blocks[n:n+20] for n in range(0, len(init_blocks), 20)] + for (i, blocks) in enumerate(chunks): + out(f'static void _initialize_traits{i}(void) {{\n', postindent=1) + for block in blocks: + out(block) out('}\n', preindent=-1) - - prev_dims = () - for dims in all_dims: - for call in by_dims[dims]: - # split into chunks of ~20 objects each. - # Tested in a device with ~1500 objects, - # where optimum chunk size seems to be between 10 and 100. - if call_count % 20 == 0: - if fun_count != 0: - finish_current_function(prev_dims) - prev_dims = () - out('static void _initialize_traits%d(void) {\n' - % (fun_count,), postindent=1) - fun_count += 1 - common_dims = [] - for (a, b) in zip(dims, prev_dims): - if a == b: - common_dims.append(a) - else: - break - for _ in range(len(common_dims), len(prev_dims)): - out('}\n', preindent=-1) - for i in range(len(common_dims), len(dims)): - idxvar = '_i%d' % (i,) - out('for (int %s = 0; %s < %d; %s++) {\n' - % (idxvar, idxvar, dims[i], idxvar), - postindent=1) - prev_dims = dims - out(call) - call_count += 1 - if fun_count: - finish_current_function(prev_dims) start_function_definition('void _initialize_traits(void)') out('{\n', postindent=1) - for i in range(fun_count): - out('_initialize_traits%d();\n' % (i,)) + for i in range(len(chunks)): + out(f'_initialize_traits{i}();\n') out('}\n', preindent=-1) splitting_point() diff --git a/py/dml/codegen.py b/py/dml/codegen.py index da09718dc..cf4d850af 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -1909,8 +1909,7 @@ def foreach_each_in(site, itername, trait, each_in, ident = each_in_sym.value inner_scope = Symtab(scope) trait_type = TTrait(trait) - trait_ptr = (f'(struct _{cident(trait.name)} *) ' - + '(_list.base + _inner_idx * _list.offset)') + trait_ptr = (f'(struct _{cident(trait.name)} *) _list.vtable') obj_ref = '(_identity_t) { .id = _list.id, .encoded_index = _inner_idx}' inner_scope.add_variable( itername, type=trait_type, diff --git a/py/dml/ctree.py b/py/dml/ctree.py index 7ace0b53c..a39407f52 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -3195,8 +3195,7 @@ def read(self): indices = tuple(mkLit(self.site, '__indices[%d]' % (i,), TInt(32, False)) for i in range(self.node.dimensions)) - structref = (self.node.traits.vtable_cname(self.ancestry_path[0]) - + ''.join('[%s]' % (i.read(),) for i in indices)) + structref = self.node.traits.vtable_cname(self.ancestry_path[0]) pointer = '(&%s)' % ('.'.join([structref] + [ cident(t.name) for t in self.ancestry_path[1:]])) id = ObjIdentity(self.site, self.node, indices).read() From 8f0a58444269a0edb079e448f444fc8ecd6a0674 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 3 Feb 2022 10:53:23 +0100 Subject: [PATCH 09/13] Only allocate params dynamically in multidim objects --- py/dml/c_backend.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 857e1b6c4..80fab7904 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1966,15 +1966,19 @@ def init_trait_vtables_for_node(node, param_values): # param_overrides contains C expression strings (value, t) = param_overrides[name] var = f'_param_{name}' - args.append(f'{var}' + '[0]' * node.dimensions) - array_type = t - for d in reversed(node.dimsizes): - array_type = TArray(array_type, - mkIntegerLiteral(node.site, d)) - param_decl.append(f'''\ -{TPtr(array_type).declaration(var)} = MM_MALLOC(1, typeof(*{var})); -''') - param_init.append(f'(*{var}){param_indices} = {value};') + if node.dimensions: + args.append(f'(void *)((uintptr_t){var})') + array_type = t + for d in reversed(node.dimsizes): + array_type = TArray(array_type, + mkIntegerLiteral(node.site, d)) + param_decl.append(f'''\ + {TPtr(array_type).declaration(var)} = malloc(sizeof(*{var})); + ''') + param_init.append(f'(*{var}){param_indices} = {value};') + else: + args.append('({static %s; %s = %s; &%s;})' + % (t.declaration(var), var, value, var)) else: assert member_kind == 'session' session_node = node.get_component(name) From 99aba37dba2c4ca14c15b1056f33b9e9180693b0 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 3 Feb 2022 10:55:55 +0100 Subject: [PATCH 10/13] Disable optimization for trait init methods --- py/dml/c_backend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 80fab7904..54f9ced15 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1908,7 +1908,7 @@ def scramble_argname(name): tinit_calls.append("_tinit_%s(%s);\n" % ( parent.name, ", ".join(['&_ret->' + cident(parent.name)] + args))) - out('static void\n') + out('static void __attribute__((optimize("O0")))\n') out('_tinit_%s(struct _%s *_ret' % (trait.name, cident(trait.name))) inargs = tinit_args(trait) @@ -2216,7 +2216,8 @@ def generate_init_trait_vtables(node, param_values): # where optimum chunk size seems to be between 10 and 100. chunks = [init_blocks[n:n+20] for n in range(0, len(init_blocks), 20)] for (i, blocks) in enumerate(chunks): - out(f'static void _initialize_traits{i}(void) {{\n', postindent=1) + out('static void __attribute__((optimize("O0")))' + f' _initialize_traits{i}(void) {{\n', postindent=1) for block in blocks: out(block) out('}\n', preindent=-1) From bed46f970481fea5a71896ff0d228d0980535567 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 3 Feb 2022 19:20:36 +0100 Subject: [PATCH 11/13] Capture early when 'each in' is used with implicit static indices --- py/dml/ctree.py | 3 +++ test/1.4/errors/T_EIDXVAR.dml | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/1.4/errors/T_EIDXVAR.dml diff --git a/py/dml/ctree.py b/py/dml/ctree.py index a39407f52..ce0701d09 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -2857,6 +2857,9 @@ def __init__(self, site, trait, node, indices): self.trait = trait self.node = node assert isinstance(indices, tuple) + for index in indices: + if isinstance(index, NonValue): + raise index.exc() self.indices = indices def __str__(self): diff --git a/test/1.4/errors/T_EIDXVAR.dml b/test/1.4/errors/T_EIDXVAR.dml new file mode 100644 index 000000000..076c7c78a --- /dev/null +++ b/test/1.4/errors/T_EIDXVAR.dml @@ -0,0 +1,13 @@ +/* + © 2021-2022 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; +device test; + +group g[i < 2] { + // this is EIDXVAR rather than ENCONST, because the 'each in' expression + // implicitly references an index which is static in this context. + /// ERROR EIDXVAR + #if ((each group in (this)).len == 13) {} +} From b2a153786505f84628852517c32ace2a4d596e40 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 3 Feb 2022 22:30:31 +0100 Subject: [PATCH 12/13] Disallow template references to objects with implicit static indices I failed to create a focused test case; static indices happen at an early stage when it's difficult to get hold of an object reference. --- py/dml/ctree.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py/dml/ctree.py b/py/dml/ctree.py index ce0701d09..f1e27b1c4 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -3168,6 +3168,9 @@ def __init__(self, site, node, trait, indices, if not isinstance(indices, tuple): raise ICE(site, 'bad indices: %r' % (indices,)) assert len(indices) == node.dimensions + for index in indices: + if isinstance(index, NonValue): + raise index.exc() self.node = node self.trait = trait self.indices = indices From be99cf212dd736f3b9375993d1b27c33e708eb10 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 3 Feb 2022 10:53:23 +0100 Subject: [PATCH 13/13] Only array-expand parameters that rely on an index variable --- include/simics/dmllib.h | 19 ++++++++++++++++--- py/dml/c_backend.py | 34 +++++++++++++++++++++------------- test/1.4/structure/T_trait.dml | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/include/simics/dmllib.h b/include/simics/dmllib.h index 15efcf977..0e7dd0726 100644 --- a/include/simics/dmllib.h +++ b/include/simics/dmllib.h @@ -183,9 +183,22 @@ DML_eq(uint64 a, uint64 b) ({_traitref_t __tref = traitref; \ ((struct _ ## type *) __tref.trait)->method(dev, __tref);}) \ -#define VTABLE_PARAM(traitref, vtable_type, member) \ - ({_traitref_t __tref = traitref; \ - ((vtable_type *)__tref.trait)->member[__tref.id.encoded_index];}) +// A parameter is represented in the vtable using a pointer that points to the +// parameter value. A parameter whose value varies across indices is stored as +// an array of all values, indexed by encoded_index. This is marked by setting +// bit 0 of the vtable's pointer to 1. A parameter that stays constant across +// indices is represented as a single value, this is marked by setting bit 0 of +// the pointer to 0. Parameter values are always stored with an alignment of at +// least 2. +#define VTABLE_PARAM(traitref, vtable_type, member) \ + ({_traitref_t __tref = traitref; \ + uintptr_t __member \ + = (uintptr_t)((vtable_type *)__tref.trait)->member; \ + (__member & 1) \ + ? ((typeof(((vtable_type*)NULL)->member))(__member - 1))[ \ + __tref.id.encoded_index] \ + : *(typeof(((vtable_type*)NULL)->member))__member; }) + #define _raw_load_uint8_be_t UNALIGNED_LOAD_BE8 #define _raw_load_uint16_be_t UNALIGNED_LOAD_BE16 diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 54f9ced15..eb4a41581 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1964,10 +1964,10 @@ def init_trait_vtables_for_node(node, param_values): if name in method_overrides else "NULL") elif member_kind == 'parameter': # param_overrides contains C expression strings - (value, t) = param_overrides[name] + (value, t, indexed) = param_overrides[name] var = f'_param_{name}' - if node.dimensions: - args.append(f'(void *)((uintptr_t){var})') + if indexed: + args.append(f'(void *)((uintptr_t){var} + 1)') array_type = t for d in reversed(node.dimsizes): array_type = TArray(array_type, @@ -1977,7 +1977,7 @@ def init_trait_vtables_for_node(node, param_values): ''') param_init.append(f'(*{var}){param_indices} = {value};') else: - args.append('({static %s; %s = %s; &%s;})' + args.append('({static %s __attribute__((aligned(2))); %s = %s; &%s;})' % (t.declaration(var), var, value, var)) else: assert member_kind == 'session' @@ -2230,17 +2230,25 @@ def generate_init_trait_vtables(node, param_values): def trait_param_value(node, param_type_site, param_type): try: - expr = node.get_expr( - tuple(mkLit(node.site, '_i%d' % (dim,), TInt(32, False)) - for dim in range(node.dimensions))) - if isinstance(expr, NonValue): - raise expr.exc() - return (source_for_assignment(expr.site, param_type, expr).read(), - param_type) - + try: + expr = node.get_expr(static_indices(node)) + if isinstance(expr, NonValue): + raise expr.exc() + expr = source_for_assignment(expr.site, param_type, expr) + indexed = False + except EIDXVAR: + indexed = True + expr = node.get_expr( + tuple(mkLit(node.site, '_i%d' % (dim,), TInt(32, False)) + for dim in range(node.dimensions))) + if isinstance(expr, NonValue): + raise expr.exc() + expr = source_for_assignment(expr.site, param_type, expr) + return (expr.read(), + param_type, indexed) except DMLError as e: report(e) - return ("0", param_type) + return ("0", param_type, False) def resolve_trait_param_values(node): '''Generate code for parameter initialization of all traits diff --git a/test/1.4/structure/T_trait.dml b/test/1.4/structure/T_trait.dml index 2e9eb2446..8616f9a4e 100644 --- a/test/1.4/structure/T_trait.dml +++ b/test/1.4/structure/T_trait.dml @@ -52,6 +52,20 @@ port p[i < 2] { } } +// The way param pointers are stored, param values must appear on addresses +// aligned by two; without the alignment logic, references to one of the below +// params will break. +template byte_params { + param aa : uint8; + param ab : uint8; + param ac : uint8; +} +group bytes is byte_params { + param aa = 3; + param ab = 4; + param ac = 5; +} + method test() throws { local child c = cast(p[1], child); local base pp; @@ -76,4 +90,8 @@ method test() throws { expect(p[0].traitref_param.e == 5, "p[0].traitref_param.e == 5"); expect(p[1].param == 14.2, "p[1].param == 14.2"); p[1].hello(); + + assert cast(bytes, byte_params).aa == 3; + assert cast(bytes, byte_params).ab == 4; + assert cast(bytes, byte_params).ac == 5; }