Skip to content
Draft
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
84 changes: 70 additions & 14 deletions compiler/back_end/cpp/header_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,16 @@ def _offset_storage_adapter(buffer_type, alignment, static_offset):
)


def _bytes_to_bits_convertor(buffer_type, byte_order, size):
def _bytes_to_bits_convertor(buffer_type, byte_order, size, bit_numbering="Lsb0"):
assert byte_order, "byte_order should not be empty."
return "{}::BitBlock</**/{}::{}ByteOrderer<typename {}>, {}>".format(
_SUPPORT_NAMESPACE, _SUPPORT_NAMESPACE, byte_order, buffer_type, size
)
if bit_numbering == "Msb0":
return "{}::BitBlockMsb0</**/{}::{}ByteOrderer<typename {}>, {}>".format(
_SUPPORT_NAMESPACE, _SUPPORT_NAMESPACE, byte_order, buffer_type, size
)
else:
return "{}::BitBlock</**/{}::{}ByteOrderer<typename {}>, {}>".format(
_SUPPORT_NAMESPACE, _SUPPORT_NAMESPACE, byte_order, buffer_type, size
)


def _get_fully_qualified_namespace(name, ir):
Expand All @@ -365,15 +370,22 @@ def _get_fully_qualified_name(name, ir):


def _get_adapted_cpp_buffer_type_for_field(
type_definition, size_in_bits, buffer_type, byte_order, parent_addressable_unit
type_definition,
size_in_bits,
buffer_type,
byte_order,
parent_addressable_unit,
bit_numbering="Lsb0",
):
"""Returns the adapted C++ type information needed to construct a view."""
if (
parent_addressable_unit == ir_data.AddressableUnit.BYTE
and type_definition.addressable_unit == ir_data.AddressableUnit.BIT
):
assert byte_order
return _bytes_to_bits_convertor(buffer_type, byte_order, size_in_bits)
return _bytes_to_bits_convertor(
buffer_type, byte_order, size_in_bits, bit_numbering
)
else:
assert (
parent_addressable_unit == type_definition.addressable_unit
Expand All @@ -391,6 +403,7 @@ def _get_cpp_view_type_for_type_definition(
byte_order,
parent_addressable_unit,
validator,
bit_numbering="Lsb0",
):
"""Returns the C++ type information needed to construct a view.

Expand All @@ -410,13 +423,20 @@ def _get_cpp_view_type_for_type_definition(
parent_addressable_unit: The addressable_unit_size of the structure
containing this structure.
validator: The name of the validator type to be injected into the view.
bit_numbering: The bit numbering scheme for the type, either "Lsb0"
(default) or "Msb0".

Returns:
A tuple of: the C++ view type and a (possibly-empty) list of the C++ types
of Emboss parameters which must be passed to the view's constructor.
"""
adapted_buffer_type = _get_adapted_cpp_buffer_type_for_field(
type_definition, size, buffer_type, byte_order, parent_addressable_unit
type_definition,
size,
buffer_type,
byte_order,
parent_addressable_unit,
bit_numbering,
)
if type_definition.has_field("external"):
# Externals do not (yet) support runtime parameters.
Expand Down Expand Up @@ -469,7 +489,14 @@ def _get_cpp_view_type_for_type_definition(


def _get_cpp_view_type_for_physical_type(
type_ir, size, byte_order, ir, buffer_type, parent_addressable_unit, validator
type_ir,
size,
byte_order,
ir,
buffer_type,
parent_addressable_unit,
validator,
bit_numbering="Lsb0",
):
"""Returns the C++ type information needed to construct a field's view.

Expand All @@ -488,6 +515,8 @@ def _get_cpp_view_type_for_physical_type(
parent_addressable_unit: The addressable_unit_size of the structure
containing this type.
validator: The name of the validator type to be injected into the view.
bit_numbering: The bit numbering scheme for the type, either "Lsb0"
(default) or "Msb0".

Returns:
A tuple of: the C++ type for a view of the given Emboss Type and a list of
Expand All @@ -514,6 +543,7 @@ def _get_cpp_view_type_for_physical_type(
_offset_storage_adapter(buffer_type, element_size, 0),
parent_addressable_unit,
validator,
bit_numbering,
)
)
return (
Expand All @@ -539,6 +569,14 @@ def _get_cpp_view_type_for_physical_type(
referenced_type = ir_util.find_object(reference, ir)
if parent_addressable_unit > referenced_type.addressable_unit:
assert byte_order, repr(type_ir)
# Get bit_numbering from the referenced type (for bits types)
bit_numbering_attr = ir_util.get_attribute(
referenced_type.attribute, "bit_numbering"
)
if bit_numbering_attr:
referenced_bit_numbering = bit_numbering_attr.string_constant.text
else:
referenced_bit_numbering = bit_numbering # Use passed value as default
reader, parameter_types = _get_cpp_view_type_for_type_definition(
referenced_type,
size,
Expand All @@ -547,6 +585,7 @@ def _get_cpp_view_type_for_physical_type(
byte_order,
parent_addressable_unit,
validator,
referenced_bit_numbering,
)
return reader, parameter_types, list(type_ir.atomic_type.runtime_parameter)

Expand Down Expand Up @@ -885,7 +924,7 @@ def _alignment_of_location(location):


def _get_cpp_type_reader_of_field(
field_ir, ir, buffer_type, validator, parent_addressable_unit
field_ir, ir, buffer_type, validator, parent_addressable_unit, bit_numbering="Lsb0"
):
"""Returns the C++ view type for a field."""
field_size = None
Expand All @@ -911,19 +950,24 @@ def _get_cpp_type_reader_of_field(
_offset_storage_adapter(buffer_type, field_alignment, field_offset),
parent_addressable_unit,
validator,
bit_numbering,
)


def _generate_structure_field_methods(
enclosing_type_name, field_ir, ir, parent_addressable_unit
enclosing_type_name,
field_ir,
ir,
parent_addressable_unit,
bit_numbering="Lsb0",
):
if ir_util.field_is_virtual(field_ir):
return _generate_structure_virtual_field_methods(
enclosing_type_name, field_ir, ir
)
else:
return _generate_structure_physical_field_methods(
enclosing_type_name, field_ir, ir, parent_addressable_unit
enclosing_type_name, field_ir, ir, parent_addressable_unit, bit_numbering
)


Expand Down Expand Up @@ -1108,7 +1152,11 @@ def _generate_validator_type_for(enclosing_type_name, field_ir, ir):


def _generate_structure_physical_field_methods(
enclosing_type_name, field_ir, ir, parent_addressable_unit
enclosing_type_name,
field_ir,
ir,
parent_addressable_unit,
bit_numbering="Lsb0",
):
"""Generates C++ code for methods for a single physical field.

Expand All @@ -1118,6 +1166,7 @@ def _generate_structure_physical_field_methods(
ir: The full IR for the module.
parent_addressable_unit: The addressable unit (BIT or BYTE) of the enclosing
structure.
bit_numbering: The bit numbering scheme, either "Lsb0" (default) or "Msb0".

Returns:
A tuple of (declarations, definitions). The declarations can be inserted
Expand All @@ -1131,7 +1180,8 @@ def _generate_structure_physical_field_methods(

type_reader, unused_parameter_types, parameter_expressions = (
_get_cpp_type_reader_of_field(
field_ir, ir, "Storage", validator_type, parent_addressable_unit
field_ir, ir, "Storage", validator_type, parent_addressable_unit,
bit_numbering
)
)

Expand Down Expand Up @@ -1347,6 +1397,12 @@ def _generate_structure_definition(type_ir, ir, config: Config):
_generate_subtype_definitions(type_ir, ir, config)
)
type_name = type_ir.name.name.text
# Get bit_numbering attribute from the type, defaulting to "Lsb0"
bit_numbering_attr = ir_util.get_attribute(type_ir.attribute, "bit_numbering")
if bit_numbering_attr:
bit_numbering = bit_numbering_attr.string_constant.text
else:
bit_numbering = "Lsb0"
field_helper_type_definitions = []
field_method_declarations = []
field_method_definitions = []
Expand Down Expand Up @@ -1424,7 +1480,7 @@ def _generate_structure_definition(type_ir, ir, config: Config):
for field_index in type_ir.structure.fields_in_dependency_order:
field = type_ir.structure.field[field_index]
helper_types, declaration, definition = _generate_structure_field_methods(
type_name, field, ir, type_ir.addressable_unit
type_name, field, ir, type_ir.addressable_unit, bit_numbering
)
field_helper_type_definitions.append(helper_types)
field_method_definitions.append(definition)
Expand Down
4 changes: 4 additions & 0 deletions compiler/front_end/attribute_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
{"BigEndian", "LittleEndian", "Null"}
)
_VALID_TEXT_OUTPUT = attribute_util.string_from_list({"Emit", "Skip"})
_VALID_BIT_NUMBERING = attribute_util.string_from_list({"Lsb0", "Msb0"})


def _valid_back_ends(attr, module_source_file):
Expand All @@ -67,6 +68,7 @@ def _valid_back_ends(attr, module_source_file):
# Attributes must be the same type no matter where they occur.
_ATTRIBUTE_TYPES = {
attributes.ADDRESSABLE_UNIT_SIZE: attribute_util.INTEGER_CONSTANT,
attributes.BIT_NUMBERING: _VALID_BIT_NUMBERING,
attributes.BYTE_ORDER: _VALID_BYTE_ORDER,
attributes.ENUM_MAXIMUM_BITS: attribute_util.INTEGER_CONSTANT,
attributes.FIXED_SIZE: attribute_util.INTEGER_CONSTANT,
Expand All @@ -79,10 +81,12 @@ def _valid_back_ends(attr, module_source_file):
}

_MODULE_ATTRIBUTES = {
(attributes.BIT_NUMBERING, True),
(attributes.BYTE_ORDER, True),
(attributes.BACK_ENDS, False),
}
_BITS_ATTRIBUTES = {
(attributes.BIT_NUMBERING, False),
(attributes.FIXED_SIZE, False),
(attributes.REQUIRES, False),
}
Expand Down
69 changes: 69 additions & 0 deletions compiler/front_end/attribute_checker_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,75 @@ def test_rejects_unknown_enum_value_attribute(self):
error.filter_errors(attribute_checker.normalize_and_verify(ir)),
)

def test_accepts_msb0_bit_numbering_on_bits(self):
ir = _make_ir_from_emb(
'[$default byte_order: "BigEndian"]\n'
"bits Foo:\n"
' [bit_numbering: "Msb0"]\n'
" 0 [+1] Flag high_bit\n"
)
self.assertEqual([], attribute_checker.normalize_and_verify(ir))

def test_accepts_lsb0_bit_numbering_on_bits(self):
ir = _make_ir_from_emb(
'[$default byte_order: "BigEndian"]\n'
"bits Foo:\n"
' [bit_numbering: "Lsb0"]\n'
" 7 [+1] Flag high_bit\n"
)
self.assertEqual([], attribute_checker.normalize_and_verify(ir))

def test_accepts_default_bit_numbering_on_module(self):
ir = _make_ir_from_emb(
'[$default bit_numbering: "Msb0"]\n'
'[$default byte_order: "BigEndian"]\n'
"bits Foo:\n"
" 0 [+1] Flag high_bit\n"
)
self.assertEqual([], attribute_checker.normalize_and_verify(ir))

def test_rejects_unknown_bit_numbering(self):
ir = _make_ir_from_emb(
'[$default byte_order: "BigEndian"]\n'
"bits Foo:\n"
' [bit_numbering: "BadValue"]\n'
" 0 [+1] Flag high_bit\n"
)
bit_numbering_attr = ir.module[0].type[0].attribute[0]
self.assertEqual(
[
[
error.error(
"m.emb",
bit_numbering_attr.value.source_location,
"Attribute 'bit_numbering' must be 'Lsb0' or 'Msb0'.",
)
]
],
attribute_checker.normalize_and_verify(ir),
)

def test_rejects_bit_numbering_on_struct(self):
ir = _make_ir_from_emb(
'[$default byte_order: "BigEndian"]\n'
"struct Foo:\n"
' [bit_numbering: "Msb0"]\n'
" 0 [+1] UInt bar\n"
)
bit_numbering_attr = ir.module[0].type[0].attribute[0]
self.assertEqual(
[
[
error.error(
"m.emb",
bit_numbering_attr.name.source_location,
"Unknown attribute 'bit_numbering' on struct 'Foo'.",
)
]
],
attribute_checker.normalize_and_verify(ir),
)


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions compiler/front_end/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# Attribute names which may be used by other parts of the front end.
ADDRESSABLE_UNIT_SIZE = "addressable_unit_size"
BYTE_ORDER = "byte_order"
BIT_NUMBERING = "bit_numbering"
RANGE = "range"
FIXED_SIZE = "fixed_size_in_bits"
IS_INTEGER = "is_integer"
Expand Down
Loading