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.m, 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.m, 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_type_annotation(False, 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 self._is_unsigned_bit_vector( t) and 0 < self._get_type_bit_count(t) <= 8 try: make_lhs, lhs_type = self._choose_env_value(env, choose_value) except EmptyEnvError: # If there's no natural environment value to use as the LHS, make up a # number and number of bits. bits = self.rng.randrange(1, 8) make_lhs = lambda: self._generate_number(env, bits, signedness=False)[0] lhs_type = self._make_type_annotation(False, bits) make_rhs, rhs_type = self._choose_env_value(env, self._not_tuple_or_array) cases = [make_rhs] assert self._is_unsigned_bit_vector(lhs_type), lhs_type total_operands = self._get_type_bit_count(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.m, 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_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(False, 1) return (ast.Invocation(self.m, self.fake_span, callee, (make_arg(), )), 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_type = map_fn.params[0].type_ assert self._is_bit_vector(map_arg_type), map_arg_type map_arg_signedness = not self._is_unsigned_bit_vector(map_arg_type) map_arg_bits = self._get_type_bit_count(map_arg_type) max_array_size = ( self.options.max_width_aggregate_types // self._get_type_bit_count(map_fn.return_type)) array_size = self.rng.randint(1, max(1, max_array_size)) 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.m, 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.m, self.fake_span, self._builtin_name_ref('map'), (args, fn_ref)) return invocation, return_type
def _make_range(self, zero: ast.Expr, arg: ast.Expr): return ast.Invocation(self.m, self.fake_span, self._builtin_name_ref('range'), args=(zero, arg))