Esempio n. 1
0
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)
Esempio n. 2
0
 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)
Esempio n. 3
0
 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)
Esempio n. 4
0
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))
Esempio n. 5
0
 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)
Esempio n. 6
0
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)
Esempio n. 7
0
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)
Esempio n. 8
0
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)
Esempio n. 9
0
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_))
Esempio n. 10
0
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
Esempio n. 11
0
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)