def test_equality(self): fake_pos = span.Pos('<fake>', 0, 0) fake_span = span.Span(fake_pos, fake_pos) p = BitsType(signed=False, size=ParametricSymbol('N', fake_span)) c = BitsType(signed=False, size=32) self.assertTrue(p.__ne__(c)) self.assertFalse(p.__eq__(c))
def test_generate_tuple_argument(self): rng = random.Random(0) args = sample_generator.generate_arguments((TupleType((BitsType( signed=False, size=123), BitsType(signed=True, size=22))), ), rng) self.assertLen(args, 1) self.assertTrue(args[0].is_tuple()) self.assertEqual(args[0].tuple_members[0].get_bit_count(), 123) self.assertEqual(args[0].tuple_members[1].get_bit_count(), 22)
def test_generate_mixed_bits_arguments(self): rng = random.Random(0) args = sample_generator.generate_arguments( (BitsType(signed=False, size=123), BitsType(signed=True, size=22)), rng) self.assertLen(args, 2) self.assertTrue(args[0].is_ubits()) self.assertEqual(args[0].get_bit_count(), 123) self.assertTrue(args[1].is_sbits()) self.assertEqual(args[1].get_bit_count(), 22)
def test_arrayness(self): tabular = [ # (type, is_array, element_count) (TupleType(members=()), False, None), (BitsType(signed=False, size=5), False, None), (ArrayType(BitsType(False, 5), 7), True, 7), (ArrayType(TupleType(members=()), 7), True, 7), ] for t, is_array, element_count in tabular: self.assertEqual(isinstance(t, ArrayType), is_array, msg=str(t)) if is_array: self.assertEqual(t.size, element_count, msg=str(t))
def fsig(arg_types: ArgTypes, name: Text, span: Span, ctx: DeduceCtx, _: Optional[ParametricBindings]) -> ConcreteType: _Checker(arg_types, name, span).len(2).is_uN((0, 1)) return_type = BitsType(signed=False, size=arg_types[0].get_total_bit_count() + arg_types[1].get_total_bit_count()) return FunctionType(arg_types, return_type)
def test_generate_single_bits_arguments(self): rng = random.Random(0) args = sample_generator.generate_arguments( (BitsType(signed=False, size=42), ), rng) self.assertLen(args, 1) self.assertTrue(args[0].is_ubits()) self.assertEqual(args[0].get_bit_count(), 42)
def _deduce_Concat(self: ast.Binop, ctx: DeduceCtx) -> ConcreteType: """Deduces the concrete type of a concatenate Binop AST node.""" lhs_type = deduce(self.lhs, ctx) resolved_lhs_type = resolve(lhs_type, ctx) rhs_type = deduce(self.rhs, ctx) resolved_rhs_type = resolve(rhs_type, ctx) # Array-ness must be the same on both sides. if (isinstance(resolved_lhs_type, ArrayType) != isinstance( resolved_rhs_type, ArrayType)): raise XlsTypeError( self.span, resolved_lhs_type, resolved_rhs_type, 'Attempting to concatenate array/non-array values together.') if (isinstance(resolved_lhs_type, ArrayType) and resolved_lhs_type.get_element_type() != resolved_rhs_type.get_element_type()): raise XlsTypeError( self.span, resolved_lhs_type, resolved_rhs_type, 'Array concatenation requires element types to be the same.') new_size = resolved_lhs_type.size + resolved_rhs_type.size # pytype: disable=attribute-error if isinstance(resolved_lhs_type, ArrayType): return ArrayType(resolved_lhs_type.get_element_type(), new_size) return BitsType(signed=False, size=new_size)
def test_array_vs_multidim_bits_equality(self): a = ArrayType(BitsType(signed=False, size=5), 7) self.assertEqual(str(a), 'uN[5][7]') self.assertEqual(7 * 5, a.get_total_bit_count()) self.assertEqual(7, a.size) self.assertEqual(5, a.get_element_type().size) # pytype: disable=attribute-error self.assertEqual((7, 5), a.get_all_dims()) self.assertEqual((), TupleType(()).get_all_dims())
def test_generate_array_argument(self): rng = random.Random(0) args = sample_generator.generate_arguments( (ArrayType(BitsType(signed=True, size=4), 24), ), rng) self.assertLen(args, 1) self.assertTrue(args[0].is_array()) self.assertLen(args[0].array_payload.elements, 24) self.assertTrue(args[0].array_payload.index(0).is_sbits()) self.assertTrue(args[0].array_payload.index(0).get_bit_count(), 4)
def _verify_constraints(self) -> None: """Verifies that all parametrics adhere to signature constraints. Take the following function signature for example: fn [X: u32, Y: u32 = X + X] f(x: bits[X], y: bits[Y]) -> bits[Y] The parametric Y has two constraints based only off the signature: it must match the bitwidth of the argument y and it must be equal to X + X. This function is responsible for computing any derived parametrics and asserting that their values are consistent with other constraints (arg types). """ for binding, constraint in self.constraints.items(): if constraint is None: # e.g. [X: u32] continue try: fn_name, fn_symbolic_bindings = self.ctx.fn_stack[-1] fn_ctx = (self.ctx.module.name, fn_name, tuple(fn_symbolic_bindings.items())) result = self.ctx.interpret_expr(self.ctx.module, self.ctx.type_info, self.symbolic_bindings, self.bit_widths, constraint, fn_ctx=fn_ctx) except KeyError as e: # We haven't seen enough bindings to evaluate this constraint. continue if binding in self.symbolic_bindings.keys(): if result != self.symbolic_bindings[binding]: raise XlsTypeError( self.span, BitsType(signed=False, size=self.symbolic_bindings[binding]), BitsType(signed=False, size=result), suffix= f'Parametric constraint violated, saw {binding} = {constraint} = {result}; ' f'then {binding} = {self.symbolic_bindings[binding]}') else: self.symbolic_bindings[binding] = result
def test_stringify(self): u32 = BitsType(signed=False, size=32) tabular = [ # type size total_bit_count str (ArrayType(u32, 7), 7, 32 * 7, 'uN[32][7]'), (u32, 32, 32, 'uN[32]'), ] for t, size, total_bit_count, s in tabular: self.assertEqual(t.size, size) self.assertEqual(t.get_total_bit_count(), total_bit_count) self.assertEqual(str(t), s)
def _deduce_ArrayTypeAnnotation(self: ast.ArrayTypeAnnotation, ctx: DeduceCtx) -> ConcreteType: """Deduces the concrete type of an Array type annotation.""" dim = _dim_to_parametric_or_int(self, self.dim, ctx) if (isinstance(self.element_type, ast.BuiltinTypeAnnotation) and self.element_type.bits == 0): # No-volume builtin types like bits, uN, and sN. return BitsType(self.element_type.signedness, dim) element_type = deduce(self.element_type, ctx) result = ArrayType(element_type, dim) logging.vlog(4, 'array type annotation: %s => %s', self, result) return result
def _strength_reduce_enum(type_: ast.Enum, bit_count: int) -> ConcreteType[int]: """Turns an enum to corresponding (bits) concrete type (w/signedness). For example, used in conversion checks. Args: type_: AST node (enum definition) to convert. bit_count: The bit count of the underlying bits type for the enum definition, as determined by type inference or interpretation. Returns: The concrete type that represents the enum's underlying bits type. """ assert isinstance(type_, ast.Enum), type_ signed = type_.get_signedness() return BitsType(signed, bit_count)
def _symbolic_bind_dims(self, param_type: ConcreteType, arg_type: ConcreteType) -> None: """Binds parametric symbols in param_type according to arg_type.""" # Create bindings for symbolic parameter dimensions based on argument # values passed. param_dim = param_type.size # pytype: disable=attribute-error arg_dim = arg_type.size # pytype: disable=attribute-error if not isinstance(param_dim, parametric_expression.ParametricSymbol): return pdim_name = param_dim.identifier if (pdim_name in self.symbolic_bindings and self.symbolic_bindings[pdim_name] != arg_dim): if self.constraints[pdim_name]: # Error on violated constraint. raise XlsTypeError( self.span, BitsType(signed=False, size=self.symbolic_bindings[pdim_name]), arg_type, suffix=f'Parametric constraint violated, saw {pdim_name} ' f'= {self.constraints[pdim_name]} ' f'= {self.symbolic_bindings[pdim_name]}; ' f'then {pdim_name} = {arg_dim}') else: # Error on conflicting argument types. raise XlsTypeError( self.span, param_type, arg_type, suffix= 'Parametric value {} was bound to different values at ' 'different places in invocation; saw: {!r}; then: {!r}'. format(pdim_name, self.symbolic_bindings[pdim_name], arg_dim)) logging.vlog(2, 'Binding %r to %s', pdim_name, arg_dim) self.symbolic_bindings[pdim_name] = arg_dim
def concrete_type_from_value(value: Value) -> ConcreteType[int]: """Returns the concrete type of 'value'. Note that: * Non-zero-length arrays are assumed (for zero length arrays we can't currently deduce the type from the value because the concrete element type is not reified in the array value. * Enums are strength-reduced to their underlying bits (storage) type. Args: value: Value to determine the concrete type for. """ if value.tag in (Tag.UBITS, Tag.SBITS): signed = value.tag == Tag.SBITS return BitsType(signed, value.bits_payload.bit_count) elif value.tag == Tag.ARRAY: element_type = concrete_type_from_value(value.array_payload.index(0)) return ArrayType(element_type, len(value)) elif value.tag == Tag.TUPLE: return TupleType( tuple(concrete_type_from_value(m) for m in value.tuple_members)) else: assert value.tag == Tag.ENUM, value return _strength_reduce_enum(value.type_, value.bits_payload.bit_count)
def _deduce_BuiltinTypeAnnotation( self: ast.BuiltinTypeAnnotation, ctx: DeduceCtx, # pylint: disable=unused-argument ) -> ConcreteType: signedness, bits = self.signedness_and_bits return BitsType(signedness, bits)
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)
def test_array_bit_count(self): e = BitsType(signed=False, size=4) a = ArrayType(e, 3) self.assertEqual(a.get_total_bit_count(), 12)