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