xref: /aosp_15_r20/external/emboss/compiler/front_end/synthetics.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"""Adds auto-generated virtual fields to the IR."""
16*99e0aae7SDavid Rees
17*99e0aae7SDavid Reesfrom compiler.front_end import attributes
18*99e0aae7SDavid Reesfrom compiler.util import error
19*99e0aae7SDavid Reesfrom compiler.util import expression_parser
20*99e0aae7SDavid Reesfrom compiler.util import ir_data
21*99e0aae7SDavid Reesfrom compiler.util import ir_data_utils
22*99e0aae7SDavid Reesfrom compiler.util import ir_util
23*99e0aae7SDavid Reesfrom compiler.util import traverse_ir
24*99e0aae7SDavid Rees
25*99e0aae7SDavid Rees
26*99e0aae7SDavid Reesdef _mark_as_synthetic(proto):
27*99e0aae7SDavid Rees  """Marks all source_locations in proto with is_synthetic=True."""
28*99e0aae7SDavid Rees  if not isinstance(proto, ir_data.Message):
29*99e0aae7SDavid Rees    return
30*99e0aae7SDavid Rees  if hasattr(proto, "source_location"):
31*99e0aae7SDavid Rees    ir_data_utils.builder(proto).source_location.is_synthetic = True
32*99e0aae7SDavid Rees  for spec, value in ir_data_utils.get_set_fields(proto):
33*99e0aae7SDavid Rees    if spec.name != "source_location" and spec.is_dataclass:
34*99e0aae7SDavid Rees      if spec.is_sequence:
35*99e0aae7SDavid Rees        for i in value:
36*99e0aae7SDavid Rees          _mark_as_synthetic(i)
37*99e0aae7SDavid Rees      else:
38*99e0aae7SDavid Rees        _mark_as_synthetic(value)
39*99e0aae7SDavid Rees
40*99e0aae7SDavid Rees
41*99e0aae7SDavid Reesdef _skip_text_output_attribute():
42*99e0aae7SDavid Rees  """Returns the IR for a [text_output: "Skip"] attribute."""
43*99e0aae7SDavid Rees  result = ir_data.Attribute(
44*99e0aae7SDavid Rees      name=ir_data.Word(text=attributes.TEXT_OUTPUT),
45*99e0aae7SDavid Rees      value=ir_data.AttributeValue(string_constant=ir_data.String(text="Skip")))
46*99e0aae7SDavid Rees  _mark_as_synthetic(result)
47*99e0aae7SDavid Rees  return result
48*99e0aae7SDavid Rees
49*99e0aae7SDavid Rees
50*99e0aae7SDavid Rees# The existence condition for an alias for an anonymous bits' field is the union
51*99e0aae7SDavid Rees# of the existence condition for the anonymous bits and the existence condition
52*99e0aae7SDavid Rees# for the field within.  The 'x' and 'x.y' are placeholders here; they'll be
53*99e0aae7SDavid Rees# overwritten in _add_anonymous_aliases.
54*99e0aae7SDavid Rees_ANONYMOUS_BITS_ALIAS_EXISTENCE_SKELETON = expression_parser.parse(
55*99e0aae7SDavid Rees    "$present(x) && $present(x.y)")
56*99e0aae7SDavid Rees
57*99e0aae7SDavid Rees
58*99e0aae7SDavid Reesdef _add_anonymous_aliases(structure, type_definition):
59*99e0aae7SDavid Rees  """Adds synthetic alias fields for all fields in anonymous fields.
60*99e0aae7SDavid Rees
61*99e0aae7SDavid Rees  This essentially completes the rewrite of this:
62*99e0aae7SDavid Rees
63*99e0aae7SDavid Rees      struct Foo:
64*99e0aae7SDavid Rees        0  [+4]  bits:
65*99e0aae7SDavid Rees          0  [+1]  Flag  low
66*99e0aae7SDavid Rees          31 [+1]  Flag  high
67*99e0aae7SDavid Rees
68*99e0aae7SDavid Rees  Into this:
69*99e0aae7SDavid Rees
70*99e0aae7SDavid Rees      struct Foo:
71*99e0aae7SDavid Rees        bits EmbossReservedAnonymous0:
72*99e0aae7SDavid Rees          [text_output: "Skip"]
73*99e0aae7SDavid Rees          0  [+1]  Flag  low
74*99e0aae7SDavid Rees          31 [+1]  Flag  high
75*99e0aae7SDavid Rees        0 [+4]  EmbossReservedAnonymous0  emboss_reserved_anonymous_1
76*99e0aae7SDavid Rees        let low = emboss_reserved_anonymous_1.low
77*99e0aae7SDavid Rees        let high = emboss_reserved_anonymous_1.high
78*99e0aae7SDavid Rees
79*99e0aae7SDavid Rees  Note that this pass runs very, very early -- even before symbols have been
80*99e0aae7SDavid Rees  resolved -- so very little in ir_util will work at this point.
81*99e0aae7SDavid Rees
82*99e0aae7SDavid Rees  Arguments:
83*99e0aae7SDavid Rees      structure: The ir_data.Structure on which to synthesize fields.
84*99e0aae7SDavid Rees      type_definition: The ir_data.TypeDefinition containing structure.
85*99e0aae7SDavid Rees
86*99e0aae7SDavid Rees  Returns:
87*99e0aae7SDavid Rees      None
88*99e0aae7SDavid Rees  """
89*99e0aae7SDavid Rees  new_fields = []
90*99e0aae7SDavid Rees  for field in structure.field:
91*99e0aae7SDavid Rees    new_fields.append(field)
92*99e0aae7SDavid Rees    if not field.name.is_anonymous:
93*99e0aae7SDavid Rees      continue
94*99e0aae7SDavid Rees    field.attribute.extend([_skip_text_output_attribute()])
95*99e0aae7SDavid Rees    for subtype in type_definition.subtype:
96*99e0aae7SDavid Rees      if (subtype.name.name.text ==
97*99e0aae7SDavid Rees          field.type.atomic_type.reference.source_name[-1].text):
98*99e0aae7SDavid Rees        field_type = subtype
99*99e0aae7SDavid Rees        break
100*99e0aae7SDavid Rees    else:
101*99e0aae7SDavid Rees      assert False, ("Unable to find corresponding type {} for anonymous field "
102*99e0aae7SDavid Rees                     "in {}.".format(
103*99e0aae7SDavid Rees                         field.type.atomic_type.reference, type_definition))
104*99e0aae7SDavid Rees    anonymous_reference = ir_data.Reference(source_name=[field.name.name])
105*99e0aae7SDavid Rees    anonymous_field_reference = ir_data.FieldReference(
106*99e0aae7SDavid Rees        path=[anonymous_reference])
107*99e0aae7SDavid Rees    for subfield in field_type.structure.field:
108*99e0aae7SDavid Rees      alias_field_reference = ir_data.FieldReference(
109*99e0aae7SDavid Rees          path=[
110*99e0aae7SDavid Rees              anonymous_reference,
111*99e0aae7SDavid Rees              ir_data.Reference(source_name=[subfield.name.name]),
112*99e0aae7SDavid Rees          ]
113*99e0aae7SDavid Rees      )
114*99e0aae7SDavid Rees      new_existence_condition = ir_data_utils.copy(_ANONYMOUS_BITS_ALIAS_EXISTENCE_SKELETON)
115*99e0aae7SDavid Rees      existence_clauses = ir_data_utils.builder(new_existence_condition).function.args
116*99e0aae7SDavid Rees      existence_clauses[0].function.args[0].field_reference.CopyFrom(
117*99e0aae7SDavid Rees          anonymous_field_reference)
118*99e0aae7SDavid Rees      existence_clauses[1].function.args[0].field_reference.CopyFrom(
119*99e0aae7SDavid Rees          alias_field_reference)
120*99e0aae7SDavid Rees      new_read_transform = ir_data.Expression(
121*99e0aae7SDavid Rees          field_reference=ir_data_utils.copy(alias_field_reference))
122*99e0aae7SDavid Rees      # This treats *most* of the alias field as synthetic, but not its name(s):
123*99e0aae7SDavid Rees      # leaving the name(s) as "real" means that symbol collisions with the
124*99e0aae7SDavid Rees      # surrounding structure will be properly reported to the user.
125*99e0aae7SDavid Rees      _mark_as_synthetic(new_existence_condition)
126*99e0aae7SDavid Rees      _mark_as_synthetic(new_read_transform)
127*99e0aae7SDavid Rees      new_alias = ir_data.Field(
128*99e0aae7SDavid Rees          read_transform=new_read_transform,
129*99e0aae7SDavid Rees          existence_condition=new_existence_condition,
130*99e0aae7SDavid Rees          name=ir_data_utils.copy(subfield.name))
131*99e0aae7SDavid Rees      if subfield.HasField("abbreviation"):
132*99e0aae7SDavid Rees        ir_data_utils.builder(new_alias).abbreviation.CopyFrom(subfield.abbreviation)
133*99e0aae7SDavid Rees      _mark_as_synthetic(new_alias.existence_condition)
134*99e0aae7SDavid Rees      _mark_as_synthetic(new_alias.read_transform)
135*99e0aae7SDavid Rees      new_fields.append(new_alias)
136*99e0aae7SDavid Rees      # Since the alias field's name(s) are "real," it is important to mark the
137*99e0aae7SDavid Rees      # original field's name(s) as synthetic, to avoid duplicate error
138*99e0aae7SDavid Rees      # messages.
139*99e0aae7SDavid Rees      _mark_as_synthetic(subfield.name)
140*99e0aae7SDavid Rees      if subfield.HasField("abbreviation"):
141*99e0aae7SDavid Rees        _mark_as_synthetic(subfield.abbreviation)
142*99e0aae7SDavid Rees  del structure.field[:]
143*99e0aae7SDavid Rees  structure.field.extend(new_fields)
144*99e0aae7SDavid Rees
145*99e0aae7SDavid Rees
146*99e0aae7SDavid Rees_SIZE_BOUNDS = {
147*99e0aae7SDavid Rees    "$max_size_in_bits": expression_parser.parse("$upper_bound($size_in_bits)"),
148*99e0aae7SDavid Rees    "$min_size_in_bits": expression_parser.parse("$lower_bound($size_in_bits)"),
149*99e0aae7SDavid Rees    "$max_size_in_bytes": expression_parser.parse(
150*99e0aae7SDavid Rees        "$upper_bound($size_in_bytes)"),
151*99e0aae7SDavid Rees    "$min_size_in_bytes": expression_parser.parse(
152*99e0aae7SDavid Rees        "$lower_bound($size_in_bytes)"),
153*99e0aae7SDavid Rees}
154*99e0aae7SDavid Rees
155*99e0aae7SDavid Rees
156*99e0aae7SDavid Reesdef _add_size_bound_virtuals(structure, type_definition):
157*99e0aae7SDavid Rees  """Adds ${min,max}_size_in_{bits,bytes} virtual fields to structure."""
158*99e0aae7SDavid Rees  names = {
159*99e0aae7SDavid Rees      ir_data.AddressableUnit.BIT: ("$max_size_in_bits", "$min_size_in_bits"),
160*99e0aae7SDavid Rees      ir_data.AddressableUnit.BYTE: ("$max_size_in_bytes", "$min_size_in_bytes"),
161*99e0aae7SDavid Rees  }
162*99e0aae7SDavid Rees  for name in names[type_definition.addressable_unit]:
163*99e0aae7SDavid Rees    bound_field = ir_data.Field(
164*99e0aae7SDavid Rees        read_transform=_SIZE_BOUNDS[name],
165*99e0aae7SDavid Rees        name=ir_data.NameDefinition(name=ir_data.Word(text=name)),
166*99e0aae7SDavid Rees        existence_condition=expression_parser.parse("true"),
167*99e0aae7SDavid Rees        attribute=[_skip_text_output_attribute()]
168*99e0aae7SDavid Rees    )
169*99e0aae7SDavid Rees    _mark_as_synthetic(bound_field.read_transform)
170*99e0aae7SDavid Rees    structure.field.extend([bound_field])
171*99e0aae7SDavid Rees
172*99e0aae7SDavid Rees
173*99e0aae7SDavid Rees# Each non-virtual field in a structure generates a clause that is passed to
174*99e0aae7SDavid Rees# `$max()` in the definition of `$size_in_bits`/`$size_in_bytes`.  Additionally,
175*99e0aae7SDavid Rees# the `$max()` call is seeded with a `0` argument: this ensures that
176*99e0aae7SDavid Rees# `$size_in_units` is never negative, and ensures that structures with no
177*99e0aae7SDavid Rees# physical fields don't end up with a zero-argument `$max()` call, which would
178*99e0aae7SDavid Rees# fail type checking.
179*99e0aae7SDavid Rees_SIZE_CLAUSE_SKELETON = expression_parser.parse(
180*99e0aae7SDavid Rees    "existence_condition ? start + size : 0")
181*99e0aae7SDavid Rees_SIZE_SKELETON = expression_parser.parse("$max(0)")
182*99e0aae7SDavid Rees
183*99e0aae7SDavid Rees
184*99e0aae7SDavid Reesdef _add_size_virtuals(structure, type_definition):
185*99e0aae7SDavid Rees  """Adds a $size_in_bits or $size_in_bytes virtual field to structure."""
186*99e0aae7SDavid Rees  names = {
187*99e0aae7SDavid Rees      ir_data.AddressableUnit.BIT: "$size_in_bits",
188*99e0aae7SDavid Rees      ir_data.AddressableUnit.BYTE: "$size_in_bytes",
189*99e0aae7SDavid Rees  }
190*99e0aae7SDavid Rees  size_field_name = names[type_definition.addressable_unit]
191*99e0aae7SDavid Rees  size_clauses = []
192*99e0aae7SDavid Rees  for field in structure.field:
193*99e0aae7SDavid Rees    # Virtual fields do not have a physical location, and thus do not contribute
194*99e0aae7SDavid Rees    # to the size of the structure.
195*99e0aae7SDavid Rees    if ir_util.field_is_virtual(field):
196*99e0aae7SDavid Rees      continue
197*99e0aae7SDavid Rees    size_clause_ir = ir_data_utils.copy(_SIZE_CLAUSE_SKELETON)
198*99e0aae7SDavid Rees    size_clause = ir_data_utils.builder(size_clause_ir)
199*99e0aae7SDavid Rees    # Copy the appropriate clauses into `existence_condition ? start + size : 0`
200*99e0aae7SDavid Rees    size_clause.function.args[0].CopyFrom(field.existence_condition)
201*99e0aae7SDavid Rees    size_clause.function.args[1].function.args[0].CopyFrom(field.location.start)
202*99e0aae7SDavid Rees    size_clause.function.args[1].function.args[1].CopyFrom(field.location.size)
203*99e0aae7SDavid Rees    size_clauses.append(size_clause_ir)
204*99e0aae7SDavid Rees  size_expression = ir_data_utils.copy(_SIZE_SKELETON)
205*99e0aae7SDavid Rees  size_expression.function.args.extend(size_clauses)
206*99e0aae7SDavid Rees  _mark_as_synthetic(size_expression)
207*99e0aae7SDavid Rees  size_field = ir_data.Field(
208*99e0aae7SDavid Rees      read_transform=size_expression,
209*99e0aae7SDavid Rees      name=ir_data.NameDefinition(name=ir_data.Word(text=size_field_name)),
210*99e0aae7SDavid Rees      existence_condition=ir_data.Expression(
211*99e0aae7SDavid Rees          boolean_constant=ir_data.BooleanConstant(value=True)
212*99e0aae7SDavid Rees      ),
213*99e0aae7SDavid Rees      attribute=[_skip_text_output_attribute()]
214*99e0aae7SDavid Rees  )
215*99e0aae7SDavid Rees  structure.field.extend([size_field])
216*99e0aae7SDavid Rees
217*99e0aae7SDavid Rees
218*99e0aae7SDavid Rees# The replacement for the "$next" keyword is a simple "start + size" expression.
219*99e0aae7SDavid Rees# 'x' and 'y' are placeholders, to be replaced.
220*99e0aae7SDavid Rees_NEXT_KEYWORD_REPLACEMENT_EXPRESSION = expression_parser.parse("x + y")
221*99e0aae7SDavid Rees
222*99e0aae7SDavid Rees
223*99e0aae7SDavid Reesdef _maybe_replace_next_keyword_in_expression(expression_ir, last_location,
224*99e0aae7SDavid Rees                                              source_file_name, errors):
225*99e0aae7SDavid Rees  if not expression_ir.HasField("builtin_reference"):
226*99e0aae7SDavid Rees    return
227*99e0aae7SDavid Rees  if ir_data_utils.reader(expression_ir).builtin_reference.canonical_name.object_path[0] != "$next":
228*99e0aae7SDavid Rees    return
229*99e0aae7SDavid Rees  if not last_location:
230*99e0aae7SDavid Rees    errors.append([
231*99e0aae7SDavid Rees      error.error(source_file_name, expression_ir.source_location,
232*99e0aae7SDavid Rees                  "`$next` may not be used in the first physical field of a " +
233*99e0aae7SDavid Rees                  "structure; perhaps you meant `0`?")
234*99e0aae7SDavid Rees    ])
235*99e0aae7SDavid Rees    return
236*99e0aae7SDavid Rees  original_location = expression_ir.source_location
237*99e0aae7SDavid Rees  expression = ir_data_utils.builder(expression_ir)
238*99e0aae7SDavid Rees  expression.CopyFrom(_NEXT_KEYWORD_REPLACEMENT_EXPRESSION)
239*99e0aae7SDavid Rees  expression.function.args[0].CopyFrom(last_location.start)
240*99e0aae7SDavid Rees  expression.function.args[1].CopyFrom(last_location.size)
241*99e0aae7SDavid Rees  expression.source_location.CopyFrom(original_location)
242*99e0aae7SDavid Rees  _mark_as_synthetic(expression.function)
243*99e0aae7SDavid Rees
244*99e0aae7SDavid Rees
245*99e0aae7SDavid Reesdef _check_for_bad_next_keyword_in_size(expression, source_file_name, errors):
246*99e0aae7SDavid Rees  if not expression.HasField("builtin_reference"):
247*99e0aae7SDavid Rees    return
248*99e0aae7SDavid Rees  if expression.builtin_reference.canonical_name.object_path[0] != "$next":
249*99e0aae7SDavid Rees    return
250*99e0aae7SDavid Rees  errors.append([
251*99e0aae7SDavid Rees    error.error(source_file_name, expression.source_location,
252*99e0aae7SDavid Rees                "`$next` may only be used in the start expression of a " +
253*99e0aae7SDavid Rees                "physical field.")
254*99e0aae7SDavid Rees  ])
255*99e0aae7SDavid Rees
256*99e0aae7SDavid Rees
257*99e0aae7SDavid Reesdef _replace_next_keyword(structure, source_file_name, errors):
258*99e0aae7SDavid Rees  last_physical_field_location = None
259*99e0aae7SDavid Rees  new_errors = []
260*99e0aae7SDavid Rees  for field in structure.field:
261*99e0aae7SDavid Rees    if ir_util.field_is_virtual(field):
262*99e0aae7SDavid Rees      # TODO(bolms): It could be useful to allow `$next` in a virtual field, in
263*99e0aae7SDavid Rees      # order to reuse the value (say, to allow overlapping fields in a
264*99e0aae7SDavid Rees      # mostly-packed structure), but it seems better to add `$end_of(field)`,
265*99e0aae7SDavid Rees      # `$offset_of(field)`, and `$size_of(field)` constructs of some sort,
266*99e0aae7SDavid Rees      # instead.
267*99e0aae7SDavid Rees      continue
268*99e0aae7SDavid Rees    traverse_ir.fast_traverse_node_top_down(
269*99e0aae7SDavid Rees        field.location.size, [ir_data.Expression],
270*99e0aae7SDavid Rees        _check_for_bad_next_keyword_in_size,
271*99e0aae7SDavid Rees        parameters={
272*99e0aae7SDavid Rees            "errors": new_errors,
273*99e0aae7SDavid Rees            "source_file_name": source_file_name,
274*99e0aae7SDavid Rees        })
275*99e0aae7SDavid Rees    # If `$next` is misused in a field size, it can end up causing a
276*99e0aae7SDavid Rees    # `RecursionError` in fast_traverse_node_top_down.  (When the `$next` node
277*99e0aae7SDavid Rees    # in the next field is replaced, its replacement gets traversed, but the
278*99e0aae7SDavid Rees    # replacement also contains a `$next` node, leading to infinite recursion.)
279*99e0aae7SDavid Rees    #
280*99e0aae7SDavid Rees    # Technically, we could scan all of the sizes instead of bailing early, but
281*99e0aae7SDavid Rees    # it seems relatively unlikely that someone will have `$next` in multiple
282*99e0aae7SDavid Rees    # sizes and not figure out what is going on relatively quickly.
283*99e0aae7SDavid Rees    if new_errors:
284*99e0aae7SDavid Rees      errors.extend(new_errors)
285*99e0aae7SDavid Rees      return
286*99e0aae7SDavid Rees    traverse_ir.fast_traverse_node_top_down(
287*99e0aae7SDavid Rees        field.location.start, [ir_data.Expression],
288*99e0aae7SDavid Rees        _maybe_replace_next_keyword_in_expression,
289*99e0aae7SDavid Rees        parameters={
290*99e0aae7SDavid Rees            "last_location": last_physical_field_location,
291*99e0aae7SDavid Rees            "errors": new_errors,
292*99e0aae7SDavid Rees            "source_file_name": source_file_name,
293*99e0aae7SDavid Rees        })
294*99e0aae7SDavid Rees    # The only possible error from _maybe_replace_next_keyword_in_expression is
295*99e0aae7SDavid Rees    # `$next` occurring in the start expression of the first physical field,
296*99e0aae7SDavid Rees    # which leads to similar recursion issue if `$next` is used in the start
297*99e0aae7SDavid Rees    # expression of the next physical field.
298*99e0aae7SDavid Rees    if new_errors:
299*99e0aae7SDavid Rees      errors.extend(new_errors)
300*99e0aae7SDavid Rees      return
301*99e0aae7SDavid Rees    last_physical_field_location = field.location
302*99e0aae7SDavid Rees
303*99e0aae7SDavid Rees
304*99e0aae7SDavid Reesdef _add_virtuals_to_structure(structure, type_definition):
305*99e0aae7SDavid Rees  _add_anonymous_aliases(structure, type_definition)
306*99e0aae7SDavid Rees  _add_size_virtuals(structure, type_definition)
307*99e0aae7SDavid Rees  _add_size_bound_virtuals(structure, type_definition)
308*99e0aae7SDavid Rees
309*99e0aae7SDavid Rees
310*99e0aae7SDavid Reesdef desugar(ir):
311*99e0aae7SDavid Rees  """Translates pure syntactic sugar to its desugared form.
312*99e0aae7SDavid Rees
313*99e0aae7SDavid Rees  Replaces `$next` symbols with the start+length of the previous physical
314*99e0aae7SDavid Rees  field.
315*99e0aae7SDavid Rees
316*99e0aae7SDavid Rees  Adds aliases for all fields in anonymous `bits` to the enclosing structure.
317*99e0aae7SDavid Rees
318*99e0aae7SDavid Rees  Arguments:
319*99e0aae7SDavid Rees      ir: The IR to desugar.
320*99e0aae7SDavid Rees
321*99e0aae7SDavid Rees  Returns:
322*99e0aae7SDavid Rees      A list of errors, or an empty list.
323*99e0aae7SDavid Rees  """
324*99e0aae7SDavid Rees  errors = []
325*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
326*99e0aae7SDavid Rees      ir, [ir_data.Structure], _replace_next_keyword,
327*99e0aae7SDavid Rees      parameters={"errors": errors})
328*99e0aae7SDavid Rees  if errors:
329*99e0aae7SDavid Rees    return errors
330*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
331*99e0aae7SDavid Rees      ir, [ir_data.Structure], _add_virtuals_to_structure)
332*99e0aae7SDavid Rees  return []
333