Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 1 | # Copyright 2019 Google LLC |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
| 15 | """Module which adds and verifies attributes in Emboss IR. |
| 16 | |
| 17 | The main entry point is normalize_and_verify(), which adds attributes and/or |
| 18 | verifies attributes which may have been manually entered. |
| 19 | """ |
| 20 | |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 21 | import re |
| 22 | |
reventlov | 6731fc4 | 2019-10-03 15:23:13 -0700 | [diff] [blame] | 23 | from compiler.front_end import attributes |
| 24 | from compiler.front_end import type_check |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 25 | from compiler.util import attribute_util |
reventlov | 6731fc4 | 2019-10-03 15:23:13 -0700 | [diff] [blame] | 26 | from compiler.util import error |
Eric Rahm | 12c5e84 | 2024-03-22 09:37:24 -0700 | [diff] [blame] | 27 | from compiler.util import ir_data |
Eric Rahm | 3daec5c | 2024-04-15 12:30:02 -0700 | [diff] [blame] | 28 | from compiler.util import ir_data_utils |
reventlov | 6731fc4 | 2019-10-03 15:23:13 -0700 | [diff] [blame] | 29 | from compiler.util import ir_util |
| 30 | from compiler.util import traverse_ir |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 31 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 32 | |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 33 | # Default value for maximum_bits on an `enum`. |
| 34 | _DEFAULT_ENUM_MAXIMUM_BITS = 64 |
| 35 | |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 36 | # Default value for expected_back_ends -- mostly for legacy |
| 37 | _DEFAULT_BACK_ENDS = "cpp" |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 38 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 39 | # Attribute type checkers |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 40 | _VALID_BYTE_ORDER = attribute_util.string_from_list( |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 41 | {"BigEndian", "LittleEndian", "Null"} |
| 42 | ) |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 43 | _VALID_TEXT_OUTPUT = attribute_util.string_from_list({"Emit", "Skip"}) |
| 44 | |
| 45 | |
| 46 | def _valid_back_ends(attr, module_source_file): |
Dmitri Prime | 800bd4a | 2024-09-20 10:19:16 -0700 | [diff] [blame] | 47 | """Checks that `attr` holds a valid list of back end specifiers.""" |
Ben Olmstead | 02a08ab | 2024-09-09 22:12:54 +0000 | [diff] [blame] | 48 | if not re.fullmatch( |
| 49 | r"(?:\s*[a-z][a-z0-9_]*\s*(?:,\s*[a-z][a-z0-9_]*\s*)*,?)?\s*", |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 50 | attr.value.string_constant.text, |
| 51 | ): |
| 52 | return [ |
| 53 | [ |
| 54 | error.error( |
| 55 | module_source_file, |
| 56 | attr.value.source_location, |
| 57 | "Attribute '{name}' must be a comma-delimited list of back end " |
| 58 | 'specifiers (like "cpp, proto")), not "{value}".'.format( |
| 59 | name=attr.name.text, value=attr.value.string_constant.text |
| 60 | ), |
| 61 | ) |
| 62 | ] |
| 63 | ] |
| 64 | return [] |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 65 | |
| 66 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 67 | # Attributes must be the same type no matter where they occur. |
| 68 | _ATTRIBUTE_TYPES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 69 | attributes.ADDRESSABLE_UNIT_SIZE: attribute_util.INTEGER_CONSTANT, |
| 70 | attributes.BYTE_ORDER: _VALID_BYTE_ORDER, |
| 71 | attributes.ENUM_MAXIMUM_BITS: attribute_util.INTEGER_CONSTANT, |
| 72 | attributes.FIXED_SIZE: attribute_util.INTEGER_CONSTANT, |
| 73 | attributes.IS_INTEGER: attribute_util.BOOLEAN_CONSTANT, |
| 74 | attributes.IS_SIGNED: attribute_util.BOOLEAN_CONSTANT, |
| 75 | attributes.REQUIRES: attribute_util.BOOLEAN, |
| 76 | attributes.STATIC_REQUIREMENTS: attribute_util.BOOLEAN, |
| 77 | attributes.TEXT_OUTPUT: _VALID_TEXT_OUTPUT, |
| 78 | attributes.BACK_ENDS: _valid_back_ends, |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | _MODULE_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 82 | (attributes.BYTE_ORDER, True), |
| 83 | (attributes.BACK_ENDS, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 84 | } |
| 85 | _BITS_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 86 | (attributes.FIXED_SIZE, False), |
| 87 | (attributes.REQUIRES, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 88 | } |
| 89 | _STRUCT_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 90 | (attributes.FIXED_SIZE, False), |
| 91 | (attributes.BYTE_ORDER, True), |
| 92 | (attributes.REQUIRES, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 93 | } |
| 94 | _ENUM_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 95 | (attributes.ENUM_MAXIMUM_BITS, False), |
| 96 | (attributes.IS_SIGNED, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 97 | } |
| 98 | _EXTERNAL_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 99 | (attributes.ADDRESSABLE_UNIT_SIZE, False), |
| 100 | (attributes.FIXED_SIZE, False), |
| 101 | (attributes.IS_INTEGER, False), |
| 102 | (attributes.STATIC_REQUIREMENTS, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 103 | } |
| 104 | _STRUCT_PHYSICAL_FIELD_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 105 | (attributes.BYTE_ORDER, False), |
| 106 | (attributes.REQUIRES, False), |
| 107 | (attributes.TEXT_OUTPUT, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 108 | } |
| 109 | _STRUCT_VIRTUAL_FIELD_ATTRIBUTES = { |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 110 | (attributes.REQUIRES, False), |
| 111 | (attributes.TEXT_OUTPUT, False), |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | |
| 115 | def _construct_integer_attribute(name, value, source_location): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 116 | """Constructs an integer Attribute with the given name and value.""" |
| 117 | attr_value = ir_data.AttributeValue( |
| 118 | expression=ir_data.Expression( |
| 119 | constant=ir_data.NumericConstant( |
| 120 | value=str(value), source_location=source_location |
| 121 | ), |
| 122 | type=ir_data.ExpressionType( |
| 123 | integer=ir_data.IntegerType( |
| 124 | modular_value=str(value), |
| 125 | modulus="infinity", |
| 126 | minimum_value=str(value), |
| 127 | maximum_value=str(value), |
| 128 | ) |
| 129 | ), |
| 130 | source_location=source_location, |
| 131 | ), |
| 132 | source_location=source_location, |
| 133 | ) |
| 134 | return ir_data.Attribute( |
| 135 | name=ir_data.Word(text=name, source_location=source_location), |
| 136 | value=attr_value, |
| 137 | source_location=source_location, |
| 138 | ) |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 139 | |
| 140 | |
| 141 | def _construct_boolean_attribute(name, value, source_location): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 142 | """Constructs a boolean Attribute with the given name and value.""" |
| 143 | attr_value = ir_data.AttributeValue( |
| 144 | expression=ir_data.Expression( |
| 145 | boolean_constant=ir_data.BooleanConstant( |
| 146 | value=value, source_location=source_location |
| 147 | ), |
| 148 | type=ir_data.ExpressionType(boolean=ir_data.BooleanType(value=value)), |
| 149 | source_location=source_location, |
| 150 | ), |
| 151 | source_location=source_location, |
| 152 | ) |
| 153 | return ir_data.Attribute( |
| 154 | name=ir_data.Word(text=name, source_location=source_location), |
| 155 | value=attr_value, |
| 156 | source_location=source_location, |
| 157 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 158 | |
| 159 | |
| 160 | def _construct_string_attribute(name, value, source_location): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 161 | """Constructs a string Attribute with the given name and value.""" |
| 162 | attr_value = ir_data.AttributeValue( |
| 163 | string_constant=ir_data.String(text=value, source_location=source_location), |
| 164 | source_location=source_location, |
| 165 | ) |
| 166 | return ir_data.Attribute( |
| 167 | name=ir_data.Word(text=name, source_location=source_location), |
| 168 | value=attr_value, |
| 169 | source_location=source_location, |
| 170 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 171 | |
| 172 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 173 | def _fixed_size_of_struct_or_bits(struct, unit_size): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 174 | """Returns size of struct in bits or None, if struct is not fixed size.""" |
| 175 | size = 0 |
| 176 | for field in struct.field: |
| 177 | if not field.HasField("location"): |
| 178 | # Virtual fields do not contribute to the physical size of the struct. |
| 179 | continue |
| 180 | field_start = ir_util.constant_value(field.location.start) |
| 181 | field_size = ir_util.constant_value(field.location.size) |
| 182 | if field_start is None or field_size is None: |
| 183 | # Technically, start + size could be constant even if start and size are |
| 184 | # not; e.g. if start == x and size == 10 - x, but we don't handle that |
| 185 | # here. |
| 186 | return None |
| 187 | # TODO(bolms): knows_own_size |
| 188 | # TODO(bolms): compute min/max sizes for variable-sized arrays. |
| 189 | field_end = field_start + field_size |
| 190 | if field_end >= size: |
| 191 | size = field_end |
| 192 | return size * unit_size |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 193 | |
| 194 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 195 | def _verify_size_attributes_on_structure( |
| 196 | struct, type_definition, source_file_name, errors |
| 197 | ): |
| 198 | """Verifies size attributes on a struct or bits.""" |
| 199 | fixed_size = _fixed_size_of_struct_or_bits(struct, type_definition.addressable_unit) |
| 200 | fixed_size_attr = ir_util.get_attribute( |
| 201 | type_definition.attribute, attributes.FIXED_SIZE |
| 202 | ) |
| 203 | if not fixed_size_attr: |
| 204 | return |
| 205 | if fixed_size is None: |
| 206 | errors.append( |
| 207 | [ |
| 208 | error.error( |
| 209 | source_file_name, |
| 210 | fixed_size_attr.source_location, |
| 211 | "Struct is marked as fixed size, but contains variable-location " |
| 212 | "fields.", |
| 213 | ) |
| 214 | ] |
| 215 | ) |
| 216 | elif ir_util.constant_value(fixed_size_attr.expression) != fixed_size: |
| 217 | errors.append( |
| 218 | [ |
| 219 | error.error( |
| 220 | source_file_name, |
| 221 | fixed_size_attr.source_location, |
| 222 | "Struct is {} bits, but is marked as {} bits.".format( |
| 223 | fixed_size, ir_util.constant_value(fixed_size_attr.expression) |
| 224 | ), |
| 225 | ) |
| 226 | ] |
| 227 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 228 | |
| 229 | |
| 230 | # TODO(bolms): remove [fixed_size]; it is superseded by $size_in_{bits,bytes} |
| 231 | def _add_missing_size_attributes_on_structure(struct, type_definition): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 232 | """Adds missing size attributes on a struct.""" |
| 233 | fixed_size = _fixed_size_of_struct_or_bits(struct, type_definition.addressable_unit) |
| 234 | if fixed_size is None: |
| 235 | return |
| 236 | fixed_size_attr = ir_util.get_attribute( |
| 237 | type_definition.attribute, attributes.FIXED_SIZE |
| 238 | ) |
| 239 | if not fixed_size_attr: |
| 240 | # TODO(bolms): Use the offset and length of the last field as the |
| 241 | # source_location of the fixed_size attribute? |
| 242 | type_definition.attribute.extend( |
| 243 | [ |
| 244 | _construct_integer_attribute( |
| 245 | attributes.FIXED_SIZE, fixed_size, type_definition.source_location |
| 246 | ) |
| 247 | ] |
| 248 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 249 | |
| 250 | |
| 251 | def _field_needs_byte_order(field, type_definition, ir): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 252 | """Returns true if the given field needs a byte_order attribute.""" |
| 253 | if ir_util.field_is_virtual(field): |
| 254 | # Virtual fields have no physical type, and thus do not need a byte order. |
| 255 | return False |
| 256 | field_type = ir_util.find_object( |
| 257 | ir_util.get_base_type(field.type).atomic_type.reference.canonical_name, ir |
| 258 | ) |
| 259 | assert field_type is not None |
| 260 | assert field_type.addressable_unit != ir_data.AddressableUnit.NONE |
| 261 | return field_type.addressable_unit != type_definition.addressable_unit |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 262 | |
| 263 | |
| 264 | def _field_may_have_null_byte_order(field, type_definition, ir): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 265 | """Returns true if "Null" is a valid byte order for the given field.""" |
| 266 | # If the field is one unit in length, then byte order does not matter. |
| 267 | if ( |
| 268 | ir_util.is_constant(field.location.size) |
| 269 | and ir_util.constant_value(field.location.size) == 1 |
| 270 | ): |
| 271 | return True |
| 272 | unit = type_definition.addressable_unit |
| 273 | # Otherwise, if the field's type is either a one-unit-sized type or an array |
| 274 | # of a one-unit-sized type, then byte order does not matter. |
| 275 | if ( |
| 276 | ir_util.fixed_size_of_type_in_bits(ir_util.get_base_type(field.type), ir) |
| 277 | == unit |
| 278 | ): |
| 279 | return True |
| 280 | # In all other cases, byte order does matter. |
| 281 | return False |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 282 | |
| 283 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 284 | def _add_missing_byte_order_attribute_on_field(field, type_definition, ir, defaults): |
| 285 | """Adds missing byte_order attributes to fields that need them.""" |
| 286 | if _field_needs_byte_order(field, type_definition, ir): |
| 287 | byte_order_attr = ir_util.get_attribute(field.attribute, attributes.BYTE_ORDER) |
| 288 | if byte_order_attr is None: |
| 289 | if attributes.BYTE_ORDER in defaults: |
| 290 | field.attribute.extend([defaults[attributes.BYTE_ORDER]]) |
| 291 | elif _field_may_have_null_byte_order(field, type_definition, ir): |
| 292 | field.attribute.extend( |
| 293 | [ |
| 294 | _construct_string_attribute( |
| 295 | attributes.BYTE_ORDER, "Null", field.source_location |
| 296 | ) |
| 297 | ] |
| 298 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 299 | |
| 300 | |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 301 | def _add_missing_back_ends_to_module(module): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 302 | """Sets the expected_back_ends attribute for a module, if not already set.""" |
| 303 | back_ends_attr = ir_util.get_attribute(module.attribute, attributes.BACK_ENDS) |
| 304 | if back_ends_attr is None: |
| 305 | module.attribute.extend( |
| 306 | [ |
| 307 | _construct_string_attribute( |
| 308 | attributes.BACK_ENDS, _DEFAULT_BACK_ENDS, module.source_location |
| 309 | ) |
| 310 | ] |
| 311 | ) |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 312 | |
| 313 | |
| 314 | def _gather_expected_back_ends(module): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 315 | """Captures the expected_back_ends attribute for `module`.""" |
| 316 | back_ends_attr = ir_util.get_attribute(module.attribute, attributes.BACK_ENDS) |
| 317 | back_ends_str = back_ends_attr.string_constant.text |
| 318 | return {"expected_back_ends": {x.strip() for x in back_ends_str.split(",")} | {""}} |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 319 | |
| 320 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 321 | def _add_addressable_unit_to_external(external, type_definition): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 322 | """Sets the addressable_unit field for an external TypeDefinition.""" |
| 323 | # Strictly speaking, addressable_unit isn't an "attribute," but it's close |
| 324 | # enough that it makes sense to handle it with attributes. |
| 325 | del external # Unused. |
| 326 | size = ir_util.get_integer_attribute( |
| 327 | type_definition.attribute, attributes.ADDRESSABLE_UNIT_SIZE |
| 328 | ) |
| 329 | if size == 1: |
| 330 | type_definition.addressable_unit = ir_data.AddressableUnit.BIT |
| 331 | elif size == 8: |
| 332 | type_definition.addressable_unit = ir_data.AddressableUnit.BYTE |
| 333 | # If the addressable_unit_size is not in (1, 8), it will be caught by |
| 334 | # _verify_addressable_unit_attribute_on_external, below. |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 335 | |
| 336 | |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 337 | def _add_missing_width_and_sign_attributes_on_enum(enum, type_definition): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 338 | """Sets the maximum_bits and is_signed attributes for an enum, if needed.""" |
| 339 | max_bits_attr = ir_util.get_integer_attribute( |
| 340 | type_definition.attribute, attributes.ENUM_MAXIMUM_BITS |
| 341 | ) |
| 342 | if max_bits_attr is None: |
| 343 | type_definition.attribute.extend( |
| 344 | [ |
| 345 | _construct_integer_attribute( |
| 346 | attributes.ENUM_MAXIMUM_BITS, |
| 347 | _DEFAULT_ENUM_MAXIMUM_BITS, |
| 348 | type_definition.source_location, |
| 349 | ) |
| 350 | ] |
| 351 | ) |
| 352 | signed_attr = ir_util.get_boolean_attribute( |
| 353 | type_definition.attribute, attributes.IS_SIGNED |
| 354 | ) |
| 355 | if signed_attr is None: |
| 356 | for value in enum.value: |
| 357 | numeric_value = ir_util.constant_value(value.value) |
| 358 | if numeric_value < 0: |
| 359 | is_signed = True |
| 360 | break |
| 361 | else: |
| 362 | is_signed = False |
| 363 | type_definition.attribute.extend( |
| 364 | [ |
| 365 | _construct_boolean_attribute( |
| 366 | attributes.IS_SIGNED, is_signed, type_definition.source_location |
| 367 | ) |
| 368 | ] |
| 369 | ) |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 370 | |
| 371 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 372 | def _verify_byte_order_attribute_on_field( |
| 373 | field, type_definition, source_file_name, ir, errors |
| 374 | ): |
| 375 | """Verifies the byte_order attribute on the given field.""" |
| 376 | byte_order_attr = ir_util.get_attribute(field.attribute, attributes.BYTE_ORDER) |
| 377 | field_needs_byte_order = _field_needs_byte_order(field, type_definition, ir) |
| 378 | if byte_order_attr and not field_needs_byte_order: |
| 379 | errors.append( |
| 380 | [ |
| 381 | error.error( |
| 382 | source_file_name, |
| 383 | byte_order_attr.source_location, |
| 384 | "Attribute 'byte_order' not allowed on field which is not byte order " |
| 385 | "dependent.", |
| 386 | ) |
| 387 | ] |
| 388 | ) |
| 389 | if not byte_order_attr and field_needs_byte_order: |
| 390 | errors.append( |
| 391 | [ |
| 392 | error.error( |
| 393 | source_file_name, |
| 394 | field.source_location, |
| 395 | "Attribute 'byte_order' required on field which is byte order " |
| 396 | "dependent.", |
| 397 | ) |
| 398 | ] |
| 399 | ) |
| 400 | if ( |
| 401 | byte_order_attr |
| 402 | and byte_order_attr.string_constant.text == "Null" |
| 403 | and not _field_may_have_null_byte_order(field, type_definition, ir) |
| 404 | ): |
| 405 | errors.append( |
| 406 | [ |
| 407 | error.error( |
| 408 | source_file_name, |
| 409 | byte_order_attr.source_location, |
| 410 | "Attribute 'byte_order' may only be 'Null' for one-byte fields.", |
| 411 | ) |
| 412 | ] |
| 413 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 414 | |
| 415 | |
| 416 | def _verify_requires_attribute_on_field(field, source_file_name, ir, errors): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 417 | """Verifies that [requires] is valid on the given field.""" |
| 418 | requires_attr = ir_util.get_attribute(field.attribute, attributes.REQUIRES) |
| 419 | if not requires_attr: |
| 420 | return |
| 421 | if ir_util.field_is_virtual(field): |
| 422 | field_expression_type = field.read_transform.type |
| 423 | else: |
| 424 | if not field.type.HasField("atomic_type"): |
| 425 | errors.append( |
| 426 | [ |
| 427 | error.error( |
| 428 | source_file_name, |
| 429 | requires_attr.source_location, |
| 430 | "Attribute 'requires' is only allowed on integer, " |
| 431 | "enumeration, or boolean fields, not arrays.", |
| 432 | ), |
| 433 | error.note( |
| 434 | source_file_name, field.type.source_location, "Field type." |
| 435 | ), |
| 436 | ] |
| 437 | ) |
| 438 | return |
| 439 | field_type = ir_util.find_object(field.type.atomic_type.reference, ir) |
| 440 | assert field_type, "Field type should be non-None after name resolution." |
| 441 | field_expression_type = type_check.unbounded_expression_type_for_physical_type( |
| 442 | field_type |
| 443 | ) |
Dmitri Prime | bd276c4 | 2024-10-11 12:59:21 -0700 | [diff] [blame^] | 444 | if field_expression_type.which_type not in ( |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 445 | "integer", |
| 446 | "enumeration", |
| 447 | "boolean", |
| 448 | ): |
| 449 | errors.append( |
| 450 | [ |
| 451 | error.error( |
| 452 | source_file_name, |
| 453 | requires_attr.source_location, |
| 454 | "Attribute 'requires' is only allowed on integer, enumeration, or " |
| 455 | "boolean fields.", |
| 456 | ) |
| 457 | ] |
| 458 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 459 | |
| 460 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 461 | def _verify_addressable_unit_attribute_on_external( |
| 462 | external, type_definition, source_file_name, errors |
| 463 | ): |
| 464 | """Verifies the addressable_unit_size attribute on an external.""" |
| 465 | del external # Unused. |
| 466 | addressable_unit_size_attr = ir_util.get_integer_attribute( |
| 467 | type_definition.attribute, attributes.ADDRESSABLE_UNIT_SIZE |
| 468 | ) |
| 469 | if addressable_unit_size_attr is None: |
| 470 | errors.append( |
| 471 | [ |
| 472 | error.error( |
| 473 | source_file_name, |
| 474 | type_definition.source_location, |
| 475 | "Expected '{}' attribute for external type.".format( |
| 476 | attributes.ADDRESSABLE_UNIT_SIZE |
| 477 | ), |
| 478 | ) |
| 479 | ] |
| 480 | ) |
| 481 | elif addressable_unit_size_attr not in (1, 8): |
| 482 | errors.append( |
| 483 | [ |
| 484 | error.error( |
| 485 | source_file_name, |
| 486 | type_definition.source_location, |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 487 | "Only values '1' (bit) and '8' (byte) are allowed for the " |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 488 | "'{}' attribute".format(attributes.ADDRESSABLE_UNIT_SIZE), |
| 489 | ) |
| 490 | ] |
| 491 | ) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 492 | |
| 493 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 494 | def _verify_width_attribute_on_enum(enum, type_definition, source_file_name, errors): |
| 495 | """Verifies the maximum_bits attribute for an enum TypeDefinition.""" |
Ben Olmstead | 1a8f852 | 2024-09-09 20:13:26 +0000 | [diff] [blame] | 496 | del enum # Unused. |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 497 | max_bits_value = ir_util.get_integer_attribute( |
| 498 | type_definition.attribute, attributes.ENUM_MAXIMUM_BITS |
| 499 | ) |
| 500 | # The attribute should already have been defaulted, if not originally present. |
| 501 | assert max_bits_value is not None, "maximum_bits not set" |
| 502 | if max_bits_value > 64 or max_bits_value < 1: |
| 503 | max_bits_attr = ir_util.get_attribute( |
| 504 | type_definition.attribute, attributes.ENUM_MAXIMUM_BITS |
| 505 | ) |
| 506 | errors.append( |
| 507 | [ |
| 508 | error.error( |
| 509 | source_file_name, |
| 510 | max_bits_attr.source_location, |
| 511 | "'maximum_bits' on an 'enum' must be between 1 and 64.", |
| 512 | ) |
| 513 | ] |
| 514 | ) |
reventlov | c50913d | 2022-04-18 15:28:36 -0700 | [diff] [blame] | 515 | |
| 516 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 517 | def _add_missing_attributes_on_ir(ir): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 518 | """Adds missing attributes in a complete IR.""" |
| 519 | traverse_ir.fast_traverse_ir_top_down( |
| 520 | ir, [ir_data.Module], _add_missing_back_ends_to_module |
| 521 | ) |
| 522 | traverse_ir.fast_traverse_ir_top_down( |
| 523 | ir, [ir_data.External], _add_addressable_unit_to_external |
| 524 | ) |
| 525 | traverse_ir.fast_traverse_ir_top_down( |
| 526 | ir, [ir_data.Enum], _add_missing_width_and_sign_attributes_on_enum |
| 527 | ) |
| 528 | traverse_ir.fast_traverse_ir_top_down( |
| 529 | ir, |
| 530 | [ir_data.Structure], |
| 531 | _add_missing_size_attributes_on_structure, |
| 532 | incidental_actions={ |
| 533 | ir_data.Module: attribute_util.gather_default_attributes, |
| 534 | ir_data.TypeDefinition: attribute_util.gather_default_attributes, |
| 535 | ir_data.Field: attribute_util.gather_default_attributes, |
| 536 | }, |
| 537 | parameters={"defaults": {}}, |
| 538 | ) |
| 539 | traverse_ir.fast_traverse_ir_top_down( |
| 540 | ir, |
| 541 | [ir_data.Field], |
| 542 | _add_missing_byte_order_attribute_on_field, |
| 543 | incidental_actions={ |
| 544 | ir_data.Module: attribute_util.gather_default_attributes, |
| 545 | ir_data.TypeDefinition: attribute_util.gather_default_attributes, |
| 546 | ir_data.Field: attribute_util.gather_default_attributes, |
| 547 | }, |
| 548 | parameters={"defaults": {}}, |
| 549 | ) |
| 550 | return [] |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 551 | |
| 552 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 553 | def _verify_field_attributes(field, type_definition, source_file_name, ir, errors): |
| 554 | _verify_byte_order_attribute_on_field( |
| 555 | field, type_definition, source_file_name, ir, errors |
| 556 | ) |
| 557 | _verify_requires_attribute_on_field(field, source_file_name, ir, errors) |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 558 | |
| 559 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 560 | def _verify_back_end_attributes( |
Ben Olmstead | 1a8f852 | 2024-09-09 20:13:26 +0000 | [diff] [blame] | 561 | attribute, expected_back_ends, source_file_name, errors |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 562 | ): |
| 563 | back_end_text = ir_data_utils.reader(attribute).back_end.text |
| 564 | if back_end_text not in expected_back_ends: |
| 565 | expected_back_ends_for_error = expected_back_ends - {""} |
| 566 | errors.append( |
| 567 | [ |
| 568 | error.error( |
| 569 | source_file_name, |
| 570 | attribute.back_end.source_location, |
| 571 | "Back end specifier '{back_end}' does not match any expected back end " |
| 572 | "specifier for this file: '{expected_back_ends}'. Add or update the " |
| 573 | "'[expected_back_ends: \"{new_expected_back_ends}\"]' attribute at the " |
| 574 | "file level if this back end specifier is intentional.".format( |
| 575 | back_end=attribute.back_end.text, |
| 576 | expected_back_ends="', '".join( |
| 577 | sorted(expected_back_ends_for_error) |
| 578 | ), |
| 579 | new_expected_back_ends=", ".join( |
| 580 | sorted(expected_back_ends_for_error | {back_end_text}) |
| 581 | ), |
| 582 | ), |
| 583 | ) |
| 584 | ] |
| 585 | ) |
Dmitri Prime | 5b38126 | 2023-05-01 23:55:07 -0700 | [diff] [blame] | 586 | |
| 587 | |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 588 | def _verify_attributes_on_ir(ir): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 589 | """Verifies attributes in a complete IR.""" |
| 590 | errors = [] |
| 591 | traverse_ir.fast_traverse_ir_top_down( |
| 592 | ir, |
| 593 | [ir_data.Attribute], |
| 594 | _verify_back_end_attributes, |
| 595 | incidental_actions={ |
| 596 | ir_data.Module: _gather_expected_back_ends, |
| 597 | }, |
| 598 | parameters={"errors": errors}, |
| 599 | ) |
| 600 | traverse_ir.fast_traverse_ir_top_down( |
| 601 | ir, |
| 602 | [ir_data.Structure], |
| 603 | _verify_size_attributes_on_structure, |
| 604 | parameters={"errors": errors}, |
| 605 | ) |
| 606 | traverse_ir.fast_traverse_ir_top_down( |
| 607 | ir, |
| 608 | [ir_data.Enum], |
| 609 | _verify_width_attribute_on_enum, |
| 610 | parameters={"errors": errors}, |
| 611 | ) |
| 612 | traverse_ir.fast_traverse_ir_top_down( |
| 613 | ir, |
| 614 | [ir_data.External], |
| 615 | _verify_addressable_unit_attribute_on_external, |
| 616 | parameters={"errors": errors}, |
| 617 | ) |
| 618 | traverse_ir.fast_traverse_ir_top_down( |
| 619 | ir, [ir_data.Field], _verify_field_attributes, parameters={"errors": errors} |
| 620 | ) |
| 621 | return errors |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 622 | |
| 623 | |
| 624 | def normalize_and_verify(ir): |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 625 | """Performs various normalizations and verifications on ir. |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 626 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 627 | Checks for duplicate attributes. |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 628 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 629 | Adds fixed_size_in_bits and addressable_unit_size attributes to types when |
| 630 | they are missing, and checks their correctness when they are not missing. |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 631 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 632 | Arguments: |
| 633 | ir: The IR object to normalize. |
Ben Olmstead | c0d7784 | 2019-07-31 17:34:05 -0700 | [diff] [blame] | 634 | |
Dmitri Prime | 495d3f2 | 2024-09-06 16:56:59 -0700 | [diff] [blame] | 635 | Returns: |
| 636 | A list of validation errors, or an empty list if no errors were encountered. |
| 637 | """ |
| 638 | errors = attribute_util.check_attributes_in_ir( |
| 639 | ir, |
| 640 | types=_ATTRIBUTE_TYPES, |
| 641 | module_attributes=_MODULE_ATTRIBUTES, |
| 642 | struct_attributes=_STRUCT_ATTRIBUTES, |
| 643 | bits_attributes=_BITS_ATTRIBUTES, |
| 644 | enum_attributes=_ENUM_ATTRIBUTES, |
| 645 | external_attributes=_EXTERNAL_ATTRIBUTES, |
| 646 | structure_virtual_field_attributes=_STRUCT_VIRTUAL_FIELD_ATTRIBUTES, |
| 647 | structure_physical_field_attributes=_STRUCT_PHYSICAL_FIELD_ATTRIBUTES, |
| 648 | ) |
| 649 | if errors: |
| 650 | return errors |
| 651 | _add_missing_attributes_on_ir(ir) |
| 652 | return _verify_attributes_on_ir(ir) |