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"""Routines to check miscellaneous constraints on 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 ir_data 20*99e0aae7SDavid Reesfrom compiler.util import ir_data_utils 21*99e0aae7SDavid Reesfrom compiler.util import ir_util 22*99e0aae7SDavid Reesfrom compiler.util import resources 23*99e0aae7SDavid Reesfrom compiler.util import traverse_ir 24*99e0aae7SDavid Rees 25*99e0aae7SDavid Rees 26*99e0aae7SDavid Reesdef _render_type(type_ir, ir): 27*99e0aae7SDavid Rees """Returns the human-readable notation of the given type.""" 28*99e0aae7SDavid Rees assert type_ir.HasField("atomic_type"), ( 29*99e0aae7SDavid Rees "TODO(bolms): Implement _render_type for array types.") 30*99e0aae7SDavid Rees if type_ir.HasField("size_in_bits"): 31*99e0aae7SDavid Rees return _render_atomic_type_name( 32*99e0aae7SDavid Rees type_ir, 33*99e0aae7SDavid Rees ir, 34*99e0aae7SDavid Rees suffix=":" + str(ir_util.constant_value(type_ir.size_in_bits))) 35*99e0aae7SDavid Rees else: 36*99e0aae7SDavid Rees return _render_atomic_type_name(type_ir, ir) 37*99e0aae7SDavid Rees 38*99e0aae7SDavid Rees 39*99e0aae7SDavid Reesdef _render_atomic_type_name(type_ir, ir, suffix=None): 40*99e0aae7SDavid Rees assert type_ir.HasField("atomic_type"), ( 41*99e0aae7SDavid Rees "_render_atomic_type_name() requires an atomic type") 42*99e0aae7SDavid Rees if not suffix: 43*99e0aae7SDavid Rees suffix = "" 44*99e0aae7SDavid Rees type_definition = ir_util.find_object(type_ir.atomic_type.reference, ir) 45*99e0aae7SDavid Rees if type_definition.name.is_anonymous: 46*99e0aae7SDavid Rees return "anonymous type" 47*99e0aae7SDavid Rees else: 48*99e0aae7SDavid Rees return "type '{}{}'".format(type_definition.name.name.text, suffix) 49*99e0aae7SDavid Rees 50*99e0aae7SDavid Rees 51*99e0aae7SDavid Reesdef _check_that_inner_array_dimensions_are_constant( 52*99e0aae7SDavid Rees type_ir, source_file_name, errors): 53*99e0aae7SDavid Rees """Checks that inner array dimensions are constant.""" 54*99e0aae7SDavid Rees if type_ir.WhichOneof("size") == "automatic": 55*99e0aae7SDavid Rees errors.append([error.error( 56*99e0aae7SDavid Rees source_file_name, 57*99e0aae7SDavid Rees ir_data_utils.reader(type_ir).element_count.source_location, 58*99e0aae7SDavid Rees "Array dimensions can only be omitted for the outermost dimension.")]) 59*99e0aae7SDavid Rees elif type_ir.WhichOneof("size") == "element_count": 60*99e0aae7SDavid Rees if not ir_util.is_constant(type_ir.element_count): 61*99e0aae7SDavid Rees errors.append([error.error(source_file_name, 62*99e0aae7SDavid Rees type_ir.element_count.source_location, 63*99e0aae7SDavid Rees "Inner array dimensions must be constant.")]) 64*99e0aae7SDavid Rees else: 65*99e0aae7SDavid Rees assert False, 'Expected "element_count" or "automatic" array size.' 66*99e0aae7SDavid Rees 67*99e0aae7SDavid Rees 68*99e0aae7SDavid Reesdef _check_that_array_base_types_are_fixed_size(type_ir, source_file_name, 69*99e0aae7SDavid Rees errors, ir): 70*99e0aae7SDavid Rees """Checks that the sizes of array elements are known at compile time.""" 71*99e0aae7SDavid Rees if type_ir.base_type.HasField("array_type"): 72*99e0aae7SDavid Rees # An array is fixed size if its base_type is fixed size and its array 73*99e0aae7SDavid Rees # dimension is constant. This function will be called again on the inner 74*99e0aae7SDavid Rees # array, and we do not want to cascade errors if the inner array's base_type 75*99e0aae7SDavid Rees # is not fixed size. The array dimensions are separately checked by 76*99e0aae7SDavid Rees # _check_that_inner_array_dimensions_are_constant, which will provide an 77*99e0aae7SDavid Rees # appropriate error message for that case. 78*99e0aae7SDavid Rees return 79*99e0aae7SDavid Rees assert type_ir.base_type.HasField("atomic_type") 80*99e0aae7SDavid Rees if type_ir.base_type.HasField("size_in_bits"): 81*99e0aae7SDavid Rees # If the base_type has a size_in_bits, then it is fixed size. 82*99e0aae7SDavid Rees return 83*99e0aae7SDavid Rees base_type = ir_util.find_object(type_ir.base_type.atomic_type.reference, ir) 84*99e0aae7SDavid Rees base_type_fixed_size = ir_util.get_integer_attribute( 85*99e0aae7SDavid Rees base_type.attribute, attributes.FIXED_SIZE) 86*99e0aae7SDavid Rees if base_type_fixed_size is None: 87*99e0aae7SDavid Rees errors.append([error.error(source_file_name, 88*99e0aae7SDavid Rees type_ir.base_type.atomic_type.source_location, 89*99e0aae7SDavid Rees "Array elements must be fixed size.")]) 90*99e0aae7SDavid Rees 91*99e0aae7SDavid Rees 92*99e0aae7SDavid Reesdef _check_that_array_base_types_in_structs_are_multiples_of_bytes( 93*99e0aae7SDavid Rees type_ir, type_definition, source_file_name, errors, ir): 94*99e0aae7SDavid Rees # TODO(bolms): Remove this limitation. 95*99e0aae7SDavid Rees """Checks that the sizes of array elements are multiples of 8 bits.""" 96*99e0aae7SDavid Rees if type_ir.base_type.HasField("array_type"): 97*99e0aae7SDavid Rees # Only check the innermost array for multidimensional arrays. 98*99e0aae7SDavid Rees return 99*99e0aae7SDavid Rees assert type_ir.base_type.HasField("atomic_type") 100*99e0aae7SDavid Rees if type_ir.base_type.HasField("size_in_bits"): 101*99e0aae7SDavid Rees assert ir_util.is_constant(type_ir.base_type.size_in_bits) 102*99e0aae7SDavid Rees base_type_size = ir_util.constant_value(type_ir.base_type.size_in_bits) 103*99e0aae7SDavid Rees else: 104*99e0aae7SDavid Rees fixed_size = ir_util.fixed_size_of_type_in_bits(type_ir.base_type, ir) 105*99e0aae7SDavid Rees if fixed_size is None: 106*99e0aae7SDavid Rees # Variable-sized elements are checked elsewhere. 107*99e0aae7SDavid Rees return 108*99e0aae7SDavid Rees base_type_size = fixed_size 109*99e0aae7SDavid Rees if base_type_size % type_definition.addressable_unit != 0: 110*99e0aae7SDavid Rees assert type_definition.addressable_unit == ir_data.AddressableUnit.BYTE 111*99e0aae7SDavid Rees errors.append([error.error(source_file_name, 112*99e0aae7SDavid Rees type_ir.base_type.source_location, 113*99e0aae7SDavid Rees "Array elements in structs must have sizes " 114*99e0aae7SDavid Rees "which are a multiple of 8 bits.")]) 115*99e0aae7SDavid Rees 116*99e0aae7SDavid Rees 117*99e0aae7SDavid Reesdef _check_constancy_of_constant_references(expression, source_file_name, 118*99e0aae7SDavid Rees errors, ir): 119*99e0aae7SDavid Rees """Checks that constant_references are constant.""" 120*99e0aae7SDavid Rees if expression.WhichOneof("expression") != "constant_reference": 121*99e0aae7SDavid Rees return 122*99e0aae7SDavid Rees # This is a bit of a hack: really, we want to know that the referred-to object 123*99e0aae7SDavid Rees # has no dependencies on any instance variables of its parent structure; i.e., 124*99e0aae7SDavid Rees # that its value does not depend on having a view of the structure. 125*99e0aae7SDavid Rees if not ir_util.is_constant_type(expression.type): 126*99e0aae7SDavid Rees referred_name = expression.constant_reference.canonical_name 127*99e0aae7SDavid Rees referred_object = ir_util.find_object(referred_name, ir) 128*99e0aae7SDavid Rees errors.append([ 129*99e0aae7SDavid Rees error.error( 130*99e0aae7SDavid Rees source_file_name, expression.source_location, 131*99e0aae7SDavid Rees "Static references must refer to constants."), 132*99e0aae7SDavid Rees error.note( 133*99e0aae7SDavid Rees referred_name.module_file, referred_object.source_location, 134*99e0aae7SDavid Rees "{} is not constant.".format(referred_name.object_path[-1])) 135*99e0aae7SDavid Rees ]) 136*99e0aae7SDavid Rees 137*99e0aae7SDavid Rees 138*99e0aae7SDavid Reesdef _check_that_enum_values_are_representable(enum_type, type_definition, 139*99e0aae7SDavid Rees source_file_name, errors): 140*99e0aae7SDavid Rees """Checks that enumeration values can fit in their specified int type.""" 141*99e0aae7SDavid Rees values = [] 142*99e0aae7SDavid Rees max_enum_size = ir_util.get_integer_attribute( 143*99e0aae7SDavid Rees type_definition.attribute, attributes.ENUM_MAXIMUM_BITS) 144*99e0aae7SDavid Rees is_signed = ir_util.get_boolean_attribute( 145*99e0aae7SDavid Rees type_definition.attribute, attributes.IS_SIGNED) 146*99e0aae7SDavid Rees if is_signed: 147*99e0aae7SDavid Rees enum_range = (-(2**(max_enum_size-1)), 2**(max_enum_size-1)-1) 148*99e0aae7SDavid Rees else: 149*99e0aae7SDavid Rees enum_range = (0, 2**max_enum_size-1) 150*99e0aae7SDavid Rees for value in enum_type.value: 151*99e0aae7SDavid Rees values.append((ir_util.constant_value(value.value), value)) 152*99e0aae7SDavid Rees out_of_range = [v for v in values 153*99e0aae7SDavid Rees if not enum_range[0] <= v[0] <= enum_range[1]] 154*99e0aae7SDavid Rees # If all values are in range, this loop will have zero iterations. 155*99e0aae7SDavid Rees for value in out_of_range: 156*99e0aae7SDavid Rees errors.append([ 157*99e0aae7SDavid Rees error.error( 158*99e0aae7SDavid Rees source_file_name, value[1].value.source_location, 159*99e0aae7SDavid Rees "Value {} is out of range for {}-bit {} enumeration.".format( 160*99e0aae7SDavid Rees value[0], max_enum_size, "signed" if is_signed else "unsigned")) 161*99e0aae7SDavid Rees ]) 162*99e0aae7SDavid Rees 163*99e0aae7SDavid Rees 164*99e0aae7SDavid Reesdef _field_size(field, type_definition): 165*99e0aae7SDavid Rees """Calculates the size of the given field in bits, if it is constant.""" 166*99e0aae7SDavid Rees size = ir_util.constant_value(field.location.size) 167*99e0aae7SDavid Rees if size is None: 168*99e0aae7SDavid Rees return None 169*99e0aae7SDavid Rees return size * type_definition.addressable_unit 170*99e0aae7SDavid Rees 171*99e0aae7SDavid Rees 172*99e0aae7SDavid Reesdef _check_type_requirements_for_field(type_ir, type_definition, field, ir, 173*99e0aae7SDavid Rees source_file_name, errors): 174*99e0aae7SDavid Rees """Checks that the `requires` attribute of each field's type is fulfilled.""" 175*99e0aae7SDavid Rees if not type_ir.HasField("atomic_type"): 176*99e0aae7SDavid Rees return 177*99e0aae7SDavid Rees 178*99e0aae7SDavid Rees if field.type.HasField("atomic_type"): 179*99e0aae7SDavid Rees field_min_size = (int(field.location.size.type.integer.minimum_value) * 180*99e0aae7SDavid Rees type_definition.addressable_unit) 181*99e0aae7SDavid Rees field_max_size = (int(field.location.size.type.integer.maximum_value) * 182*99e0aae7SDavid Rees type_definition.addressable_unit) 183*99e0aae7SDavid Rees field_is_atomic = True 184*99e0aae7SDavid Rees else: 185*99e0aae7SDavid Rees field_is_atomic = False 186*99e0aae7SDavid Rees 187*99e0aae7SDavid Rees if type_ir.HasField("size_in_bits"): 188*99e0aae7SDavid Rees element_size = ir_util.constant_value(type_ir.size_in_bits) 189*99e0aae7SDavid Rees else: 190*99e0aae7SDavid Rees element_size = None 191*99e0aae7SDavid Rees 192*99e0aae7SDavid Rees referenced_type_definition = ir_util.find_object( 193*99e0aae7SDavid Rees type_ir.atomic_type.reference, ir) 194*99e0aae7SDavid Rees type_is_anonymous = referenced_type_definition.name.is_anonymous 195*99e0aae7SDavid Rees type_size_attr = ir_util.get_attribute( 196*99e0aae7SDavid Rees referenced_type_definition.attribute, attributes.FIXED_SIZE) 197*99e0aae7SDavid Rees if type_size_attr: 198*99e0aae7SDavid Rees type_size = ir_util.constant_value(type_size_attr.expression) 199*99e0aae7SDavid Rees else: 200*99e0aae7SDavid Rees type_size = None 201*99e0aae7SDavid Rees 202*99e0aae7SDavid Rees if (element_size is not None and type_size is not None and 203*99e0aae7SDavid Rees element_size != type_size): 204*99e0aae7SDavid Rees errors.append([ 205*99e0aae7SDavid Rees error.error( 206*99e0aae7SDavid Rees source_file_name, type_ir.size_in_bits.source_location, 207*99e0aae7SDavid Rees "Explicit size of {} bits does not match fixed size ({} bits) of " 208*99e0aae7SDavid Rees "{}.".format(element_size, type_size, 209*99e0aae7SDavid Rees _render_atomic_type_name(type_ir, ir))), 210*99e0aae7SDavid Rees error.note( 211*99e0aae7SDavid Rees type_ir.atomic_type.reference.canonical_name.module_file, 212*99e0aae7SDavid Rees type_size_attr.source_location, 213*99e0aae7SDavid Rees "Size specified here.") 214*99e0aae7SDavid Rees ]) 215*99e0aae7SDavid Rees return 216*99e0aae7SDavid Rees 217*99e0aae7SDavid Rees # If the type had no size specifier (the ':32' in 'UInt:32'), but the type is 218*99e0aae7SDavid Rees # fixed size, then continue as if the type's size were explicitly stated. 219*99e0aae7SDavid Rees if element_size is None: 220*99e0aae7SDavid Rees element_size = type_size 221*99e0aae7SDavid Rees 222*99e0aae7SDavid Rees # TODO(bolms): When the full dynamic size expression for types is generated, 223*99e0aae7SDavid Rees # add a check that dynamically-sized types can, at least potentially, fit in 224*99e0aae7SDavid Rees # their fields. 225*99e0aae7SDavid Rees 226*99e0aae7SDavid Rees if field_is_atomic and element_size is not None: 227*99e0aae7SDavid Rees # If the field has a fixed size, and the (atomic) type contained therein is 228*99e0aae7SDavid Rees # also fixed size, then the sizes should match. 229*99e0aae7SDavid Rees # 230*99e0aae7SDavid Rees # TODO(bolms): Maybe change the case where the field is bigger than 231*99e0aae7SDavid Rees # necessary into a warning? 232*99e0aae7SDavid Rees if (field_max_size == field_min_size and 233*99e0aae7SDavid Rees (element_size > field_max_size or 234*99e0aae7SDavid Rees (element_size < field_min_size and not type_is_anonymous))): 235*99e0aae7SDavid Rees errors.append([ 236*99e0aae7SDavid Rees error.error( 237*99e0aae7SDavid Rees source_file_name, type_ir.source_location, 238*99e0aae7SDavid Rees "Fixed-size {} cannot be placed in field of size {} bits; " 239*99e0aae7SDavid Rees "requires {} bits.".format( 240*99e0aae7SDavid Rees _render_type(type_ir, ir), field_max_size, element_size)) 241*99e0aae7SDavid Rees ]) 242*99e0aae7SDavid Rees return 243*99e0aae7SDavid Rees elif element_size > field_max_size: 244*99e0aae7SDavid Rees errors.append([ 245*99e0aae7SDavid Rees error.error( 246*99e0aae7SDavid Rees source_file_name, type_ir.source_location, 247*99e0aae7SDavid Rees "Field of maximum size {} bits cannot hold fixed-size {}, which " 248*99e0aae7SDavid Rees "requires {} bits.".format( 249*99e0aae7SDavid Rees field_max_size, _render_type(type_ir, ir), element_size)) 250*99e0aae7SDavid Rees ]) 251*99e0aae7SDavid Rees return 252*99e0aae7SDavid Rees 253*99e0aae7SDavid Rees # If we're here, then field/type sizes are consistent. 254*99e0aae7SDavid Rees if (element_size is None and field_is_atomic and 255*99e0aae7SDavid Rees field_min_size == field_max_size): 256*99e0aae7SDavid Rees # From here down, we just use element_size. 257*99e0aae7SDavid Rees element_size = field_min_size 258*99e0aae7SDavid Rees 259*99e0aae7SDavid Rees errors.extend(_check_physical_type_requirements( 260*99e0aae7SDavid Rees type_ir, field.source_location, element_size, ir, source_file_name)) 261*99e0aae7SDavid Rees 262*99e0aae7SDavid Rees 263*99e0aae7SDavid Reesdef _check_type_requirements_for_parameter_type( 264*99e0aae7SDavid Rees runtime_parameter, ir, source_file_name, errors): 265*99e0aae7SDavid Rees """Checks that the type of a parameter is valid.""" 266*99e0aae7SDavid Rees physical_type = runtime_parameter.physical_type_alias 267*99e0aae7SDavid Rees logical_type = runtime_parameter.type 268*99e0aae7SDavid Rees size = ir_util.constant_value(physical_type.size_in_bits) 269*99e0aae7SDavid Rees if logical_type.WhichOneof("type") == "integer": 270*99e0aae7SDavid Rees integer_errors = _integer_bounds_errors( 271*99e0aae7SDavid Rees logical_type.integer, "parameter", source_file_name, 272*99e0aae7SDavid Rees physical_type.source_location) 273*99e0aae7SDavid Rees if integer_errors: 274*99e0aae7SDavid Rees errors.extend(integer_errors) 275*99e0aae7SDavid Rees return 276*99e0aae7SDavid Rees errors.extend(_check_physical_type_requirements( 277*99e0aae7SDavid Rees physical_type, runtime_parameter.source_location, 278*99e0aae7SDavid Rees size, ir, source_file_name)) 279*99e0aae7SDavid Rees elif logical_type.WhichOneof("type") == "enumeration": 280*99e0aae7SDavid Rees if physical_type.HasField("size_in_bits"): 281*99e0aae7SDavid Rees # This seems a little weird: for `UInt`, `Int`, etc., the explicit size is 282*99e0aae7SDavid Rees # required, but for enums it is banned. This is because enums have a 283*99e0aae7SDavid Rees # "native" 64-bit size in expressions, so the physical size is just 284*99e0aae7SDavid Rees # ignored. 285*99e0aae7SDavid Rees errors.extend([[ 286*99e0aae7SDavid Rees error.error( 287*99e0aae7SDavid Rees source_file_name, physical_type.size_in_bits.source_location, 288*99e0aae7SDavid Rees "Parameters with enum type may not have explicit size.") 289*99e0aae7SDavid Rees 290*99e0aae7SDavid Rees ]]) 291*99e0aae7SDavid Rees else: 292*99e0aae7SDavid Rees assert False, "Non-integer/enum parameters should have been caught earlier." 293*99e0aae7SDavid Rees 294*99e0aae7SDavid Rees 295*99e0aae7SDavid Reesdef _check_physical_type_requirements( 296*99e0aae7SDavid Rees type_ir, usage_source_location, size, ir, source_file_name): 297*99e0aae7SDavid Rees """Checks that the given atomic `type_ir` is allowed to be `size` bits.""" 298*99e0aae7SDavid Rees referenced_type_definition = ir_util.find_object( 299*99e0aae7SDavid Rees type_ir.atomic_type.reference, ir) 300*99e0aae7SDavid Rees if referenced_type_definition.HasField("enumeration"): 301*99e0aae7SDavid Rees if size is None: 302*99e0aae7SDavid Rees return [[ 303*99e0aae7SDavid Rees error.error( 304*99e0aae7SDavid Rees source_file_name, type_ir.source_location, 305*99e0aae7SDavid Rees "Enumeration {} cannot be placed in a dynamically-sized " 306*99e0aae7SDavid Rees "field.".format(_render_type(type_ir, ir))) 307*99e0aae7SDavid Rees ]] 308*99e0aae7SDavid Rees else: 309*99e0aae7SDavid Rees max_enum_size = ir_util.get_integer_attribute( 310*99e0aae7SDavid Rees referenced_type_definition.attribute, attributes.ENUM_MAXIMUM_BITS) 311*99e0aae7SDavid Rees if size < 1 or size > max_enum_size: 312*99e0aae7SDavid Rees return [[ 313*99e0aae7SDavid Rees error.error( 314*99e0aae7SDavid Rees source_file_name, type_ir.source_location, 315*99e0aae7SDavid Rees "Enumeration {} cannot be {} bits; {} must be between " 316*99e0aae7SDavid Rees "1 and {} bits, inclusive.".format( 317*99e0aae7SDavid Rees _render_atomic_type_name(type_ir, ir), size, 318*99e0aae7SDavid Rees _render_atomic_type_name(type_ir, ir), max_enum_size)) 319*99e0aae7SDavid Rees ]] 320*99e0aae7SDavid Rees 321*99e0aae7SDavid Rees if size is None: 322*99e0aae7SDavid Rees bindings = {"$is_statically_sized": False} 323*99e0aae7SDavid Rees else: 324*99e0aae7SDavid Rees bindings = { 325*99e0aae7SDavid Rees "$is_statically_sized": True, 326*99e0aae7SDavid Rees "$static_size_in_bits": size 327*99e0aae7SDavid Rees } 328*99e0aae7SDavid Rees requires_attr = ir_util.get_attribute( 329*99e0aae7SDavid Rees referenced_type_definition.attribute, attributes.STATIC_REQUIREMENTS) 330*99e0aae7SDavid Rees if requires_attr and not ir_util.constant_value(requires_attr.expression, 331*99e0aae7SDavid Rees bindings): 332*99e0aae7SDavid Rees # TODO(bolms): Figure out a better way to build this error message. 333*99e0aae7SDavid Rees # The "Requirements specified here." message should print out the actual 334*99e0aae7SDavid Rees # source text of the requires attribute, so that should help, but it's still 335*99e0aae7SDavid Rees # a bit generic and unfriendly. 336*99e0aae7SDavid Rees return [[ 337*99e0aae7SDavid Rees error.error( 338*99e0aae7SDavid Rees source_file_name, usage_source_location, 339*99e0aae7SDavid Rees "Requirements of {} not met.".format( 340*99e0aae7SDavid Rees type_ir.atomic_type.reference.canonical_name.object_path[-1])), 341*99e0aae7SDavid Rees error.note( 342*99e0aae7SDavid Rees type_ir.atomic_type.reference.canonical_name.module_file, 343*99e0aae7SDavid Rees requires_attr.source_location, 344*99e0aae7SDavid Rees "Requirements specified here.") 345*99e0aae7SDavid Rees ]] 346*99e0aae7SDavid Rees return [] 347*99e0aae7SDavid Rees 348*99e0aae7SDavid Rees 349*99e0aae7SDavid Reesdef _check_allowed_in_bits(type_ir, type_definition, source_file_name, ir, 350*99e0aae7SDavid Rees errors): 351*99e0aae7SDavid Rees if not type_ir.HasField("atomic_type"): 352*99e0aae7SDavid Rees return 353*99e0aae7SDavid Rees referenced_type_definition = ir_util.find_object( 354*99e0aae7SDavid Rees type_ir.atomic_type.reference, ir) 355*99e0aae7SDavid Rees if (type_definition.addressable_unit % 356*99e0aae7SDavid Rees referenced_type_definition.addressable_unit != 0): 357*99e0aae7SDavid Rees assert type_definition.addressable_unit == ir_data.AddressableUnit.BIT 358*99e0aae7SDavid Rees assert (referenced_type_definition.addressable_unit == 359*99e0aae7SDavid Rees ir_data.AddressableUnit.BYTE) 360*99e0aae7SDavid Rees errors.append([ 361*99e0aae7SDavid Rees error.error(source_file_name, type_ir.source_location, 362*99e0aae7SDavid Rees "Byte-oriented {} cannot be used in a bits field.".format( 363*99e0aae7SDavid Rees _render_type(type_ir, ir))) 364*99e0aae7SDavid Rees ]) 365*99e0aae7SDavid Rees 366*99e0aae7SDavid Rees 367*99e0aae7SDavid Reesdef _check_size_of_bits(type_ir, type_definition, source_file_name, errors): 368*99e0aae7SDavid Rees """Checks that `bits` types are fixed size, less than 64 bits.""" 369*99e0aae7SDavid Rees del type_ir # Unused 370*99e0aae7SDavid Rees if type_definition.addressable_unit != ir_data.AddressableUnit.BIT: 371*99e0aae7SDavid Rees return 372*99e0aae7SDavid Rees fixed_size = ir_util.get_integer_attribute( 373*99e0aae7SDavid Rees type_definition.attribute, attributes.FIXED_SIZE) 374*99e0aae7SDavid Rees if fixed_size is None: 375*99e0aae7SDavid Rees errors.append([error.error(source_file_name, 376*99e0aae7SDavid Rees type_definition.source_location, 377*99e0aae7SDavid Rees "`bits` types must be fixed size.")]) 378*99e0aae7SDavid Rees return 379*99e0aae7SDavid Rees if fixed_size > 64: 380*99e0aae7SDavid Rees errors.append([error.error(source_file_name, 381*99e0aae7SDavid Rees type_definition.source_location, 382*99e0aae7SDavid Rees "`bits` types must be 64 bits or smaller.")]) 383*99e0aae7SDavid Rees 384*99e0aae7SDavid Rees 385*99e0aae7SDavid Rees_RESERVED_WORDS = None 386*99e0aae7SDavid Rees 387*99e0aae7SDavid Rees 388*99e0aae7SDavid Reesdef get_reserved_word_list(): 389*99e0aae7SDavid Rees if _RESERVED_WORDS is None: 390*99e0aae7SDavid Rees _initialize_reserved_word_list() 391*99e0aae7SDavid Rees return _RESERVED_WORDS 392*99e0aae7SDavid Rees 393*99e0aae7SDavid Rees 394*99e0aae7SDavid Reesdef _initialize_reserved_word_list(): 395*99e0aae7SDavid Rees global _RESERVED_WORDS 396*99e0aae7SDavid Rees _RESERVED_WORDS = {} 397*99e0aae7SDavid Rees language = None 398*99e0aae7SDavid Rees for line in resources.load( 399*99e0aae7SDavid Rees "compiler.front_end", "reserved_words").splitlines(): 400*99e0aae7SDavid Rees stripped_line = line.partition("#")[0].strip() 401*99e0aae7SDavid Rees if not stripped_line: 402*99e0aae7SDavid Rees continue 403*99e0aae7SDavid Rees if stripped_line.startswith("--"): 404*99e0aae7SDavid Rees language = stripped_line.partition("--")[2].strip() 405*99e0aae7SDavid Rees else: 406*99e0aae7SDavid Rees # For brevity's sake, only use the first language for error messages. 407*99e0aae7SDavid Rees if stripped_line not in _RESERVED_WORDS: 408*99e0aae7SDavid Rees _RESERVED_WORDS[stripped_line] = language 409*99e0aae7SDavid Rees 410*99e0aae7SDavid Rees 411*99e0aae7SDavid Reesdef _check_name_for_reserved_words(obj, source_file_name, errors, context_name): 412*99e0aae7SDavid Rees if obj.name.name.text in get_reserved_word_list(): 413*99e0aae7SDavid Rees errors.append([ 414*99e0aae7SDavid Rees error.error( 415*99e0aae7SDavid Rees source_file_name, obj.name.name.source_location, 416*99e0aae7SDavid Rees "{} reserved word may not be used as {}.".format( 417*99e0aae7SDavid Rees get_reserved_word_list()[obj.name.name.text], 418*99e0aae7SDavid Rees context_name)) 419*99e0aae7SDavid Rees ]) 420*99e0aae7SDavid Rees 421*99e0aae7SDavid Rees 422*99e0aae7SDavid Reesdef _check_field_name_for_reserved_words(field, source_file_name, errors): 423*99e0aae7SDavid Rees return _check_name_for_reserved_words(field, source_file_name, errors, 424*99e0aae7SDavid Rees "a field name") 425*99e0aae7SDavid Rees 426*99e0aae7SDavid Rees 427*99e0aae7SDavid Reesdef _check_enum_name_for_reserved_words(enum, source_file_name, errors): 428*99e0aae7SDavid Rees return _check_name_for_reserved_words(enum, source_file_name, errors, 429*99e0aae7SDavid Rees "an enum name") 430*99e0aae7SDavid Rees 431*99e0aae7SDavid Rees 432*99e0aae7SDavid Reesdef _check_type_name_for_reserved_words(type_definition, source_file_name, 433*99e0aae7SDavid Rees errors): 434*99e0aae7SDavid Rees return _check_name_for_reserved_words( 435*99e0aae7SDavid Rees type_definition, source_file_name, errors, "a type name") 436*99e0aae7SDavid Rees 437*99e0aae7SDavid Rees 438*99e0aae7SDavid Reesdef _bounds_can_fit_64_bit_unsigned(minimum, maximum): 439*99e0aae7SDavid Rees return minimum >= 0 and maximum <= 2**64 - 1 440*99e0aae7SDavid Rees 441*99e0aae7SDavid Rees 442*99e0aae7SDavid Reesdef _bounds_can_fit_64_bit_signed(minimum, maximum): 443*99e0aae7SDavid Rees return minimum >= -(2**63) and maximum <= 2**63 - 1 444*99e0aae7SDavid Rees 445*99e0aae7SDavid Rees 446*99e0aae7SDavid Reesdef _bounds_can_fit_any_64_bit_integer_type(minimum, maximum): 447*99e0aae7SDavid Rees return (_bounds_can_fit_64_bit_unsigned(minimum, maximum) or 448*99e0aae7SDavid Rees _bounds_can_fit_64_bit_signed(minimum, maximum)) 449*99e0aae7SDavid Rees 450*99e0aae7SDavid Rees 451*99e0aae7SDavid Reesdef _integer_bounds_errors_for_expression(expression, source_file_name): 452*99e0aae7SDavid Rees """Checks that `expression` is in range for int64_t or uint64_t.""" 453*99e0aae7SDavid Rees # Only check non-constant subexpressions. 454*99e0aae7SDavid Rees if (expression.WhichOneof("expression") == "function" and 455*99e0aae7SDavid Rees not ir_util.is_constant_type(expression.type)): 456*99e0aae7SDavid Rees errors = [] 457*99e0aae7SDavid Rees for arg in expression.function.args: 458*99e0aae7SDavid Rees errors += _integer_bounds_errors_for_expression(arg, source_file_name) 459*99e0aae7SDavid Rees if errors: 460*99e0aae7SDavid Rees # Don't cascade bounds errors: report them at the lowest level they 461*99e0aae7SDavid Rees # appear. 462*99e0aae7SDavid Rees return errors 463*99e0aae7SDavid Rees if expression.type.WhichOneof("type") == "integer": 464*99e0aae7SDavid Rees errors = _integer_bounds_errors(expression.type.integer, "expression", 465*99e0aae7SDavid Rees source_file_name, 466*99e0aae7SDavid Rees expression.source_location) 467*99e0aae7SDavid Rees if errors: 468*99e0aae7SDavid Rees return errors 469*99e0aae7SDavid Rees if (expression.WhichOneof("expression") == "function" and 470*99e0aae7SDavid Rees not ir_util.is_constant_type(expression.type)): 471*99e0aae7SDavid Rees int64_only_clauses = [] 472*99e0aae7SDavid Rees uint64_only_clauses = [] 473*99e0aae7SDavid Rees for clause in [expression] + list(expression.function.args): 474*99e0aae7SDavid Rees if clause.type.WhichOneof("type") == "integer": 475*99e0aae7SDavid Rees arg_minimum = int(clause.type.integer.minimum_value) 476*99e0aae7SDavid Rees arg_maximum = int(clause.type.integer.maximum_value) 477*99e0aae7SDavid Rees if not _bounds_can_fit_64_bit_signed(arg_minimum, arg_maximum): 478*99e0aae7SDavid Rees uint64_only_clauses.append(clause) 479*99e0aae7SDavid Rees elif not _bounds_can_fit_64_bit_unsigned(arg_minimum, arg_maximum): 480*99e0aae7SDavid Rees int64_only_clauses.append(clause) 481*99e0aae7SDavid Rees if int64_only_clauses and uint64_only_clauses: 482*99e0aae7SDavid Rees error_set = [ 483*99e0aae7SDavid Rees error.error( 484*99e0aae7SDavid Rees source_file_name, expression.source_location, 485*99e0aae7SDavid Rees "Either all arguments to '{}' and its result must fit in a " 486*99e0aae7SDavid Rees "64-bit unsigned integer, or all must fit in a 64-bit signed " 487*99e0aae7SDavid Rees "integer.".format(expression.function.function_name.text)) 488*99e0aae7SDavid Rees ] 489*99e0aae7SDavid Rees for signedness, clause_list in (("unsigned", uint64_only_clauses), 490*99e0aae7SDavid Rees ("signed", int64_only_clauses)): 491*99e0aae7SDavid Rees for clause in clause_list: 492*99e0aae7SDavid Rees error_set.append(error.note( 493*99e0aae7SDavid Rees source_file_name, clause.source_location, 494*99e0aae7SDavid Rees "Requires {} 64-bit integer.".format(signedness))) 495*99e0aae7SDavid Rees return [error_set] 496*99e0aae7SDavid Rees return [] 497*99e0aae7SDavid Rees 498*99e0aae7SDavid Rees 499*99e0aae7SDavid Reesdef _integer_bounds_errors(bounds, name, source_file_name, 500*99e0aae7SDavid Rees error_source_location): 501*99e0aae7SDavid Rees """Returns appropriate errors, if any, for the given integer bounds.""" 502*99e0aae7SDavid Rees assert bounds.minimum_value, "{}".format(bounds) 503*99e0aae7SDavid Rees assert bounds.maximum_value, "{}".format(bounds) 504*99e0aae7SDavid Rees if (bounds.minimum_value == "-infinity" or 505*99e0aae7SDavid Rees bounds.maximum_value == "infinity"): 506*99e0aae7SDavid Rees return [[ 507*99e0aae7SDavid Rees error.error( 508*99e0aae7SDavid Rees source_file_name, error_source_location, 509*99e0aae7SDavid Rees "Integer range of {} must not be unbounded; it must fit " 510*99e0aae7SDavid Rees "in a 64-bit signed or unsigned integer.".format(name)) 511*99e0aae7SDavid Rees ]] 512*99e0aae7SDavid Rees if not _bounds_can_fit_any_64_bit_integer_type(int(bounds.minimum_value), 513*99e0aae7SDavid Rees int(bounds.maximum_value)): 514*99e0aae7SDavid Rees if int(bounds.minimum_value) == int(bounds.maximum_value): 515*99e0aae7SDavid Rees return [[ 516*99e0aae7SDavid Rees error.error( 517*99e0aae7SDavid Rees source_file_name, error_source_location, 518*99e0aae7SDavid Rees "Constant value {} of {} cannot fit in a 64-bit signed or " 519*99e0aae7SDavid Rees "unsigned integer.".format(bounds.minimum_value, name)) 520*99e0aae7SDavid Rees ]] 521*99e0aae7SDavid Rees else: 522*99e0aae7SDavid Rees return [[ 523*99e0aae7SDavid Rees error.error( 524*99e0aae7SDavid Rees source_file_name, error_source_location, 525*99e0aae7SDavid Rees "Potential range of {} is {} to {}, which cannot fit " 526*99e0aae7SDavid Rees "in a 64-bit signed or unsigned integer.".format( 527*99e0aae7SDavid Rees name, bounds.minimum_value, bounds.maximum_value)) 528*99e0aae7SDavid Rees ]] 529*99e0aae7SDavid Rees return [] 530*99e0aae7SDavid Rees 531*99e0aae7SDavid Rees 532*99e0aae7SDavid Reesdef _check_bounds_on_runtime_integer_expressions(expression, source_file_name, 533*99e0aae7SDavid Rees in_attribute, errors): 534*99e0aae7SDavid Rees if in_attribute and in_attribute.name.text == attributes.STATIC_REQUIREMENTS: 535*99e0aae7SDavid Rees # [static_requirements] is never evaluated at runtime, and $size_in_bits is 536*99e0aae7SDavid Rees # unbounded, so it should not be checked. 537*99e0aae7SDavid Rees return 538*99e0aae7SDavid Rees # The logic for gathering errors and suppressing cascades is simpler if 539*99e0aae7SDavid Rees # errors are just returned, rather than appended to a shared list. 540*99e0aae7SDavid Rees errors += _integer_bounds_errors_for_expression(expression, source_file_name) 541*99e0aae7SDavid Rees 542*99e0aae7SDavid Reesdef _attribute_in_attribute_action(a): 543*99e0aae7SDavid Rees return {"in_attribute": a} 544*99e0aae7SDavid Rees 545*99e0aae7SDavid Reesdef check_constraints(ir): 546*99e0aae7SDavid Rees """Checks miscellaneous validity constraints in ir. 547*99e0aae7SDavid Rees 548*99e0aae7SDavid Rees Checks that auto array sizes are only used for the outermost size of 549*99e0aae7SDavid Rees multidimensional arrays. That is, Type[3][] is OK, but Type[][3] is not. 550*99e0aae7SDavid Rees 551*99e0aae7SDavid Rees Checks that fixed-size fields are a correct size to hold statically-sized 552*99e0aae7SDavid Rees types. 553*99e0aae7SDavid Rees 554*99e0aae7SDavid Rees Checks that inner array dimensions are constant. 555*99e0aae7SDavid Rees 556*99e0aae7SDavid Rees Checks that only constant-size types are used in arrays. 557*99e0aae7SDavid Rees 558*99e0aae7SDavid Rees Arguments: 559*99e0aae7SDavid Rees ir: An ir_data.EmbossIr object to check. 560*99e0aae7SDavid Rees 561*99e0aae7SDavid Rees Returns: 562*99e0aae7SDavid Rees A list of ConstraintViolations, or an empty list if there are none. 563*99e0aae7SDavid Rees """ 564*99e0aae7SDavid Rees errors = [] 565*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 566*99e0aae7SDavid Rees ir, [ir_data.Structure, ir_data.Type], _check_allowed_in_bits, 567*99e0aae7SDavid Rees parameters={"errors": errors}) 568*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 569*99e0aae7SDavid Rees # TODO(bolms): look for [ir_data.ArrayType], [ir_data.AtomicType], and 570*99e0aae7SDavid Rees # simplify _check_that_array_base_types_are_fixed_size. 571*99e0aae7SDavid Rees ir, [ir_data.ArrayType], _check_that_array_base_types_are_fixed_size, 572*99e0aae7SDavid Rees parameters={"errors": errors}) 573*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 574*99e0aae7SDavid Rees ir, [ir_data.Structure, ir_data.ArrayType], 575*99e0aae7SDavid Rees _check_that_array_base_types_in_structs_are_multiples_of_bytes, 576*99e0aae7SDavid Rees parameters={"errors": errors}) 577*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 578*99e0aae7SDavid Rees ir, [ir_data.ArrayType, ir_data.ArrayType], 579*99e0aae7SDavid Rees _check_that_inner_array_dimensions_are_constant, 580*99e0aae7SDavid Rees parameters={"errors": errors}) 581*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 582*99e0aae7SDavid Rees ir, [ir_data.Structure], _check_size_of_bits, 583*99e0aae7SDavid Rees parameters={"errors": errors}) 584*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 585*99e0aae7SDavid Rees ir, [ir_data.Structure, ir_data.Type], _check_type_requirements_for_field, 586*99e0aae7SDavid Rees parameters={"errors": errors}) 587*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 588*99e0aae7SDavid Rees ir, [ir_data.Field], _check_field_name_for_reserved_words, 589*99e0aae7SDavid Rees parameters={"errors": errors}) 590*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 591*99e0aae7SDavid Rees ir, [ir_data.EnumValue], _check_enum_name_for_reserved_words, 592*99e0aae7SDavid Rees parameters={"errors": errors}) 593*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 594*99e0aae7SDavid Rees ir, [ir_data.TypeDefinition], _check_type_name_for_reserved_words, 595*99e0aae7SDavid Rees parameters={"errors": errors}) 596*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 597*99e0aae7SDavid Rees ir, [ir_data.Expression], _check_constancy_of_constant_references, 598*99e0aae7SDavid Rees parameters={"errors": errors}) 599*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 600*99e0aae7SDavid Rees ir, [ir_data.Enum], _check_that_enum_values_are_representable, 601*99e0aae7SDavid Rees parameters={"errors": errors}) 602*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 603*99e0aae7SDavid Rees ir, [ir_data.Expression], _check_bounds_on_runtime_integer_expressions, 604*99e0aae7SDavid Rees incidental_actions={ir_data.Attribute: _attribute_in_attribute_action}, 605*99e0aae7SDavid Rees skip_descendants_of={ir_data.EnumValue, ir_data.Expression}, 606*99e0aae7SDavid Rees parameters={"errors": errors, "in_attribute": None}) 607*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 608*99e0aae7SDavid Rees ir, [ir_data.RuntimeParameter], 609*99e0aae7SDavid Rees _check_type_requirements_for_parameter_type, 610*99e0aae7SDavid Rees parameters={"errors": errors}) 611*99e0aae7SDavid Rees return errors 612