def _generate_unop_builtin( self, env: Env) -> Tuple[ast.Invocation, ast.TypeAnnotation]: """Generates a call to a unary builtin.""" make_arg, arg_type = self._choose_env_value(env, self._is_builtin_unsigned) choices = ['clz'] # Since one_hot adds a bit, only use it when we have head room beneath # max_width_bits_types to add another bit. one_hot_ok = builtin_type_to_bits( arg_type) < self.options.max_width_bits_types if one_hot_ok: choices.append('one_hot') to_invoke = self.rng.choice(choices) if to_invoke == 'clz': invocation = ast.Invocation(self.fake_span, self._builtin_name_ref(to_invoke), args=(make_arg(), )) result_type = arg_type else: assert to_invoke == 'one_hot' lsb_or_msb = self.rng.choice((True, False)) invocation = ast.Invocation(self.fake_span, self._builtin_name_ref(to_invoke), args=(make_arg(), self._make_bool(lsb_or_msb))) result_bits = builtin_type_to_bits(arg_type) + 1 result_type = self._make_large_type_annotation('UN', result_bits) return invocation, result_type
def _generate_one_hot_select_builtin( self, env: Env) -> Tuple[ast.Invocation, ast.TypeAnnotation]: """Generates an invocation of the one_hot_sel builtin.""" # We need to choose a selector with a certain number of bits, then form an # array from that many values in the environment. def choose_value(t: ast.TypeAnnotation) -> bool: return not isinstance( t, ast.TupleTypeAnnotation) and not isinstance( t, ast.ArrayTypeAnnotation) and builtin_type_to_bits(t) < 8 try: make_lhs, lhs_type = self._choose_env_value(env, choose_value) except EmptyEnvError: bits = self.rng.randrange(1, 8) make_lhs = lambda: self._generate_number( env, bits, signedness=False)[0] lhs_type = self._make_large_type_annotation('UN', bits) make_rhs, rhs_type = self._choose_env_value(env, self._not_tuple_or_array) cases = [make_rhs] total_operands = builtin_type_to_bits(lhs_type) for _ in range(total_operands - 1): make_rhs, rhs_type = self._choose_env_value( env, lambda t: t == rhs_type) cases.append(make_rhs) invocation = ast.Invocation( self.fake_span, self._builtin_name_ref('one_hot_sel'), args=(make_lhs(), self._make_array(tuple(make_case() for make_case in cases)))) return invocation, rhs_type
def _generate_map(self, level, env: Env) -> Tuple[ast.Invocation, ast.TypeAnnotation]: """Generates an invocation of the map builtin.""" map_fn_name = self.gensym() # generate_function, in turn, can call generate_map, so we need some way of # bounding the recursion. To limit explosion, we increase level by three # (chosen empirically) instead of just one. map_fn = self.generate_function(map_fn_name, level + 3, param_count=1) self._functions.append(map_fn) map_arg_signedness, map_arg_bits = builtin_type_to_signedness_and_bits( map_fn.params[0].type_) array_size = self.rng.randrange( 1, max(2, self.options.max_width_aggregate_types // map_arg_bits)) return_type = self._make_array_type(map_fn.return_type, array_size) # Seems pretty unlikely that we'll have the exact array we need, so we'll # just create one. # TODO(b/144724970): Consider creating arrays from values in the env. def get_number(): return self._generate_number(env, map_arg_bits, map_arg_signedness) args = self._make_constant_array( tuple(get_number()[0] for i in range(array_size))) args.type_ = ast.ArrayTypeAnnotation( self.fake_span, map_fn.params[0].type_, self._make_number(array_size, None)) fn_ref = self._make_name_ref(self._make_name_def(map_fn_name)) invocation = ast.Invocation(self.fake_span, self._builtin_name_ref('map'), (args, fn_ref)) return invocation, return_type
def _create_element_invocation(owner: ast.AstNodeOwner, span_: span.Span, callee: Union[ast.NameRef, ast.ModRef], arg_array: ast.Expr) -> ast.Invocation: """Creates a function invocation on the first element of the given array. We need to create a fake invocation to deduce the type of a function in the case where map is called with a builtin as the map function. Normally, map functions (including parametric ones) have their types deduced when their ast.Function nodes are encountered (where a similar fake ast.Invocation node is created). Builtins don't have ast.Function nodes, so that inference can't occur, so we essentually perform that synthesis and deduction here. Args: owner: AST node owner. span_: The location in the code where analysis is occurring. callee: The function to be invoked. arg_array: The array of arguments (at least one) to the function. Returns: An invocation node for the given function when called with an element in the argument array. """ annotation = ast_helpers.make_builtin_type_annotation( owner, span_, scanner.Token(scanner.TokenKind.KEYWORD, span_, scanner.Keyword.U32), ()) index_number = ast.Number(owner, span_, '32', ast.NumberKind.OTHER, annotation) index = ast.Index(owner, span_, arg_array, index_number) return ast.Invocation(owner, span_, callee, (index, ))
def _generate_bitwise_reduction( self, env: Env) -> Tuple[ast.Invocation, ast.TypeAnnotation]: """Generates one of the bitwise reductions as an Invocation node.""" make_arg, _ = self._choose_env_value(env, self._is_builtin_unsigned) ops = ['and_reduce', 'or_reduce', 'xor_reduce'] callee = self._builtin_name_ref(self.rng.choice(ops)) type_ = self._make_type_annotation('u1') return (ast.Invocation(self.fake_span, callee, (make_arg(), )), type_)
def _make_range(self, zero: ast.Expr, arg: ast.Expr): return ast.Invocation(self.fake_span, self._builtin_name_ref('range'), args=(zero, arg))