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