def concrete_type_accepts_value(type_: ConcreteType, value: Value) -> bool: """Returns whether 'value' conforms to this concrete type.""" if value.tag == Tag.UBITS: return (isinstance(type_, BitsType) and not type_.signed and value.bits_payload.bit_count == type_.get_total_bit_count()) if value.tag == Tag.SBITS: return (isinstance(type_, BitsType) and type_.signed and value.bits_payload.bit_count == type_.get_total_bit_count()) if value.tag in (Tag.ARRAY, Tag.TUPLE, Tag.ENUM): return _value_compatible_with_type(type_, value) raise NotImplementedError(type_, value)
def check_bitwidth(self, concrete_type: ConcreteType) -> None: # TODO(leary): 2019-04-19 Because we punt on typechecking symbolic concrete # types here we'll need to do another pass to check whether numerical values # fit once the parametric symbols are instantiated as integers. if (isinstance(concrete_type, BitsType) and isinstance(concrete_type.get_total_bit_count(), int) and not bit_helpers.fits_in_bits(self.get_value_as_int(), concrete_type.get_total_bit_count())): msg = 'value {!r} does not fit in the bitwidth of a {} ({})'.format( self.value, concrete_type, concrete_type.get_total_bit_count()) raise TypeInferenceError(span=self.span, type_=concrete_type, suffix=msg)
def _type_to_ir(self, concrete_type: ConcreteType) -> type_mod.Type: """Converts a concrete type to its corresponding IR representation.""" assert isinstance(concrete_type, ConcreteType), concrete_type logging.vlog(4, 'Converting concrete type to IR: %s', concrete_type) if isinstance(concrete_type, ArrayType): element_type = self._type_to_ir(concrete_type.get_element_type()) element_count = concrete_type.size if not isinstance(element_count, int): raise ValueError( 'Expect array element count to be integer; got {!r}'.format( element_count)) result = self.package.get_array_type(element_count, element_type) logging.vlog( 4, 'Converted type to IR; concrete type: %s ir: %s element_count: %d', concrete_type, result, concrete_type.size) return result elif isinstance(concrete_type, BitsType) or isinstance( concrete_type, EnumType): return self.package.get_bits_type(concrete_type.get_total_bit_count()) else: if not isinstance(concrete_type, TupleType): raise ValueError( 'Expect type to be bits/enum, array, or tuple; got: ' f'{concrete_type} ({concrete_type!r})') members = tuple( self._type_to_ir(m) for m in concrete_type.get_unnamed_members()) return self.package.get_tuple_type(members)
def _generate_unbiased_argument(concrete_type: ConcreteType, rng: Random) -> Value: if isinstance(concrete_type, BitsType): bit_count = concrete_type.get_total_bit_count() return _generate_bit_value(bit_count, rng, concrete_type.get_signedness()) else: raise NotImplementedError( 'Generate argument for type: {}'.format(concrete_type))
def _cast_to_array(self, node: ast.Cast, output_type: ConcreteType) -> None: bits = self._use(node.expr) slices = [] element_bit_count = output_type.get_element_type().get_total_bit_count() # pytype: disable=attribute-error # MSb becomes lowest-indexed array element. for i in range(0, output_type.get_total_bit_count(), element_bit_count): slices.append(self.fb.add_bit_slice(bits, i, element_bit_count)) slices.reverse() element_type = self.package.get_bits_type(element_bit_count) self._def(node, self.fb.add_array, slices, element_type)
def concrete_type_to_annotation( concrete_type: concrete_type_mod.ConcreteType) -> ast.TypeAnnotation: if isinstance(concrete_type, concrete_type_mod.BitsType): keyword = SN_KEYWORD if concrete_type.get_signedness() else UN_KEYWORD num_tok = scanner.Token(scanner.TokenKind.NUMBER, FAKE_SPAN, concrete_type.get_total_bit_count()) return ast.make_builtin_type_annotation( FAKE_SPAN, keyword, dims=(ast.Number(num_tok),)) raise NotImplementedError(concrete_type)
def _value_compatible_with_type(type_: ConcreteType, value: Value) -> bool: """Returns whether value is compatible with type_ (recursively).""" assert isinstance(value, Value), value if isinstance(type_, TupleType) and value.is_tuple(): return all( _value_compatible_with_type(ct, m) for ct, m in zip(type_.get_unnamed_members(), value.tuple_members)) if isinstance(type_, ArrayType) and value.is_array(): et = type_.get_element_type() return all( _value_compatible_with_type(et, m) for m in value.array_payload.elements) if isinstance(type_, EnumType) and value.tag == Tag.ENUM: return type_.nominal_type == value.type_ if isinstance(type_, BitsType) and not type_.signed and value.tag == Tag.UBITS: return value.bits_payload.bit_count == type_.get_total_bit_count() if isinstance(type_, BitsType) and type_.signed and value.tag == Tag.SBITS: return value.bits_payload.bit_count == type_.get_total_bit_count() if value.tag == Tag.ENUM and isinstance(type_, BitsType): return (value.type_.get_signedness() == type_.get_signedness() and value.bits_payload.bit_count == type_.get_total_bit_count()) if value.tag == Tag.ARRAY and is_ubits(type_): flat_bit_count = value.array_payload.flatten().bits_payload.bit_count return flat_bit_count == type_.get_total_bit_count() if isinstance(type_, EnumType) and value.is_bits(): return (type_.get_signedness() == (value.tag == Tag.SBITS) and type_.get_total_bit_count() == value.get_bit_count()) raise NotImplementedError(type_, value)
def generate_argument(arg_type: ConcreteType, rng: Random, prior: Sequence[Value]) -> Value: """Generates an argument value of the same type as the concrete type.""" if isinstance(arg_type, TupleType): return Value.make_tuple( tuple( generate_argument(t, rng, prior) for t in arg_type.get_unnamed_members())) elif isinstance(arg_type, ArrayType): return Value.make_array( tuple( generate_argument(arg_type.get_element_type(), rng, prior) for _ in range(arg_type.size))) else: assert isinstance(arg_type, BitsType) if not prior or rng.random() < 0.5: return _generate_unbiased_argument(arg_type, rng) to_mutate = rng.choice(prior) bit_count = arg_type.get_total_bit_count() if bit_count > to_mutate.get_bit_count(): to_mutate = to_mutate.bits_payload.concat( _generate_bit_value(bit_count - to_mutate.get_bit_count(), rng, signed=False).bits_payload) else: to_mutate = to_mutate.bits_payload.slice(0, bit_count, lsb_is_0=False) assert to_mutate.bit_count == bit_count value = to_mutate.value mutation_count = randrange_biased_towards_zero(bit_count, rng) for _ in range(mutation_count): # Pick a random bit and flip it. bitno = rng.randrange(bit_count) value ^= 1 << bitno signed = arg_type.get_signedness() constructor = Value.make_sbits if signed else Value.make_ubits return constructor(value=value, bit_count=bit_count)
def concrete_type_convert_value(type_: ConcreteType, value: Value, span: Span, enum_values: Optional[Tuple[Value, ...]], enum_signed: Optional[bool]) -> Value: """Converts 'value' into a value of this concrete type.""" logging.vlog(3, 'Converting value %s to type %s', value, type_) if value.tag == Tag.UBITS and isinstance(type_, ArrayType): bits_per_element = type_.get_element_type().get_total_bit_count() bits = value.bits_payload def bit_slice_value_at_index(i): return Value( Tag.UBITS, bits.slice(i * bits_per_element, (i + 1) * bits_per_element, lsb_is_0=False)) return Value.make_array( tuple(bit_slice_value_at_index(i) for i in range(type_.size))) if (isinstance(type_, EnumType) and value.tag in (Tag.UBITS, Tag.SBITS, Tag.ENUM) and value.get_bit_count() == type_.get_total_bit_count()): # Check that the bits we're converting from are present in the enum type # we're converting to. for enum_value in enum_values: if value.bits_payload == enum_value.bits_payload: break else: raise FailureError( span, 'Value is not valid for enum {}: {}'.format( type_.nominal_type.identifier, value)) return Value.make_enum(value.bits_payload, type_.nominal_type) if (value.tag == Tag.ENUM and isinstance(type_, BitsType) and type_.get_total_bit_count() == value.get_bit_count()): constructor = Value.make_sbits if type_.signed else Value.make_ubits # pytype: disable=attribute-error bit_count = type_.get_total_bit_count() return constructor(bit_count, value.bits_payload.value) def zero_ext() -> Value: assert isinstance(type_, BitsType) constructor = Value.make_sbits if type_.signed else Value.make_ubits bit_count = type_.get_total_bit_count() return constructor( bit_count, value.get_bits_value() & bit_helpers.to_mask(bit_count)) def sign_ext() -> Value: assert isinstance(type_, BitsType) constructor = Value.make_sbits if type_.signed else Value.make_ubits bit_count = type_.get_total_bit_count() logging.vlog(3, 'Sign extending %s to %s', value, bit_count) return constructor(bit_count, value.bits_payload.sign_ext(bit_count).value) if value.tag == Tag.UBITS: return zero_ext() if value.tag == Tag.SBITS: return sign_ext() if value.tag == Tag.ENUM: assert enum_signed is not None return sign_ext() if enum_signed else zero_ext() # If we're converting an array into bits, flatten the array payload. if value.tag == Tag.ARRAY and isinstance(type_, BitsType): return value.array_payload.flatten() if concrete_type_accepts_value(type_, value): # Vacuous conversion. return value raise FailureError( span, 'Interpreter failure: cannot convert value %s (of type %s) to type %s' % (value, concrete_type_from_value(value), type_))
def _is_acceptable_cast(from_: ConcreteType, to: ConcreteType) -> bool: if {type(from_), type(to)} == {ArrayType, BitsType}: return from_.get_total_bit_count() == to.get_total_bit_count() return True
def _deduce_slice_type(self: ast.Index, ctx: DeduceCtx, lhs_type: ConcreteType) -> ConcreteType: """Deduces the concrete type of an Index AST node with a slice spec.""" index_slice = self.index assert isinstance(index_slice, (ast.Slice, ast.WidthSlice)), index_slice # TODO(leary): 2019-10-28 Only slicing bits types for now, and only with # number ast nodes, generalize to arrays and constant expressions. if not isinstance(lhs_type, BitsType): raise XlsTypeError(self.span, lhs_type, None, 'Value to slice is not of "bits" type.') bit_count = lhs_type.get_total_bit_count() if isinstance(index_slice, ast.WidthSlice): start = index_slice.start if isinstance(start, ast.Number) and start.type_ is None: start_type = lhs_type.to_ubits() resolved_start_type = resolve(start_type, ctx) if not bit_helpers.fits_in_bits( start.get_value_as_int(), resolved_start_type.get_total_bit_count()): raise TypeInferenceError( start.span, resolved_start_type, 'Cannot fit {} in {} bits (inferred from bits to slice).'. format(start.get_value_as_int(), resolved_start_type.get_total_bit_count())) ctx.type_info[start] = start_type else: start_type = deduce(start, ctx) # Check the start is unsigned. if start_type.signed: raise TypeInferenceError( start.span, type_=start_type, suffix='Start index for width-based slice must be unsigned.') width_type = deduce(index_slice.width, ctx) if isinstance(width_type.get_total_bit_count(), int) and isinstance( lhs_type.get_total_bit_count(), int) and width_type.get_total_bit_count( ) > lhs_type.get_total_bit_count(): raise XlsTypeError( start.span, lhs_type, width_type, 'Slice type must have <= original number of bits; attempted slice from {} to {} bits.' .format(lhs_type.get_total_bit_count(), width_type.get_total_bit_count())) # Check the width type is bits-based (no enums, since value could be out # of range of the enum values). if not isinstance(width_type, BitsType): raise TypeInferenceError( self.span, type_=width_type, suffix='Require a bits-based type for width-based slice.') # The width type is the thing returned from the width-slice. return width_type assert isinstance(index_slice, ast.Slice), index_slice limit = index_slice.limit.get_value_as_int() if index_slice.limit else None # PyType has trouble figuring out that start is definitely an Number at this # point. start = index_slice.start assert isinstance(start, (ast.Number, type(None))) start = start.get_value_as_int() if start else None _, fn_symbolic_bindings = ctx.fn_stack[-1] if isinstance(bit_count, ParametricExpression): bit_count = bit_count.evaluate(fn_symbolic_bindings) start, width = bit_helpers.resolve_bit_slice_indices( bit_count, start, limit) key = tuple(fn_symbolic_bindings.items()) ctx.type_info.add_slice_start_width(index_slice, key, (start, width)) return BitsType(signed=False, size=width)