def _generate_logical_binop(self, env: Env) -> Tuple[ast.Binop, ast.TypeAnnotation]: """Generates a logical binary operation (e.g. and, xor, or).""" make_lhs, lhs_type = self._choose_env_value(env, self._not_tuple_or_array) make_rhs, rhs_type = self._choose_env_value(env, self._not_tuple_or_array) # Convert into one-bit numbers by checking whether lhs and rhs values are 0. lhs = ast.Binop(self.m, self.fake_span, ast.BinopKind.NE, make_lhs(), self._make_number(0, lhs_type)) rhs = ast.Binop(self.m, self.fake_span, ast.BinopKind.NE, make_rhs(), self._make_number(0, rhs_type)) # Pick some operation to do. op = self.rng.choice([ast.BinopKind.LOGICAL_AND, ast.BinopKind.LOGICAL_OR]) return ast.Binop(self.m, self.fake_span, op, lhs, rhs), self._make_type_annotation(False, 1)
def _generate_concat(self, env: Env) -> Tuple[ast.Expr, ast.TypeAnnotation]: """Returns a (potentially vacuous) concatenate operation of values in `env`. Args: env: Environment of values that can be selected from for concatenation. Note: the concat operation will not exceed the maximum bit width so the concat may end up being a nop. """ if self._env_contains_array(env) and self.rng.choice([True, False]): return self._generate_array_concat(env) count = self._generate_nary_operand_count(env) + 2 operands = [] operand_types = [] for i in range(count): make_arg, arg_type = self._choose_env_value( env, self._is_builtin_unsigned) operands.append(make_arg()) operand_types.append(arg_type) result = operands[0] result_bits = builtin_type_to_bits(operand_types[0]) for i in range(1, count): this_bits = builtin_type_to_bits(operand_types[i]) if result_bits + this_bits > self.options.max_width_bits_types: break result = ast.Binop(self.m, self.fake_span, ast.BinopKind.CONCAT, result, operands[i]) result_bits += this_bits assert result_bits <= self.options.max_width_bits_types, result_bits return (result, self._make_type_annotation(False, result_bits))
def _generate_array_concat( self, env: Env) -> Tuple[ast.Expr, ast.TypeAnnotation]: """Returns a binary concatenation of two arrays in env. The two arrays to concatenate in env will have the same element type. Args: env: Environment of values that can be selected from for array concatenation. Precondition: There must be an array value present in env. """ make_lhs, lhs_type = self._choose_env_value( env, lambda t: isinstance(t, ast.ArrayTypeAnnotation)) assert isinstance(lhs_type, ast.ArrayTypeAnnotation), lhs_type def array_same_elem_type(t: ast.TypeAnnotation) -> bool: return (isinstance(t, ast.ArrayTypeAnnotation) and t.element_type == lhs_type.element_type) make_rhs, rhs_type = self._choose_env_value(env, array_same_elem_type) result = ast.Binop(self.m, self.fake_span, ast.BinopKind.CONCAT, make_lhs(), make_rhs()) lhs_size = self._get_array_size(lhs_type) bits_per_elem = self._get_type_bit_count(lhs_type) // lhs_size result_size = lhs_size + self._get_array_size(rhs_type) dim = self._make_number(result_size, None) result_type = ast.ArrayTypeAnnotation(self.m, self.fake_span, lhs_type.element_type, dim) self._type_bit_counts[str(result_type)] = bits_per_elem * result_size return (result, result_type)
def _generate_array_concat(self, env: Env) -> Tuple[ast.Expr, ast.TypeAnnotation]: """Returns a binary concatenation of two arrays in env. The two arrays to concatenate in env will have the same element type. Args: env: Environment of values that can be selected from for array concatenation. Precondition: There must be an array value present in env. """ make_lhs, lhs_type = self._choose_env_value(env, self._is_array) # Returns true if the type 't' is an array with the same element type as the # lhs and when concatentated, the type does not exceed bit count limits. def array_compatible(t: ast.TypeAnnotation) -> bool: return (isinstance(t, ast.ArrayTypeAnnotation) and t.element_type == lhs_type.element_type and (self._get_type_bit_count(lhs_type) + self._get_type_bit_count(t) < self.options.max_width_aggregate_types)) make_rhs, rhs_type = self._choose_env_value(env, array_compatible) result = ast.Binop(self.m, self.fake_span, ast.BinopKind.CONCAT, make_lhs(), make_rhs()) result_size = ( self._get_array_size(lhs_type) + self._get_array_size(rhs_type)) dim = self._make_number(result_size, None) result_type = ast.ArrayTypeAnnotation(self.m, self.fake_span, lhs_type.element_type, dim) return (result, result_type)
def _generate_binop_same_input_type( self, lhs: ast.Expr, rhs: ast.Expr, input_type: ast.TypeAnnotation) -> Tuple[ast.Binop, ast.TypeAnnotation]: """Generates a binary operator on lhs/rhs which have the same input type.""" if self.rng.random() < 0.1: op = self.rng.choice(ast_helpers.BINOP_COMPARISON_KIND_LIST) output_type = self._make_type_annotation(False, 1) else: op = self.rng.choice(self._binops) if op in ast_helpers.BINOP_SHIFTS and self.rng.random() < 0.8: # Clamp the RHS to be in range most of the time. assert self._is_bit_vector(input_type), input_type bit_count = self._get_type_bit_count(input_type) new_upper = self.rng.randrange(bit_count) rhs = self._generate_umin(rhs, input_type, new_upper) output_type = input_type return ast.Binop(self.m, self.fake_span, op, lhs, rhs), output_type
def _make_ge(self, lhs: ast.Expr, rhs: ast.Expr) -> ast.Expr: return ast.Binop(self.m, self.fake_span, ast.BinopKind.GE, lhs, rhs)
def test_binop(self): m = cpp_ast.Module('test') ft = cpp_ast.Number(m, self.fake_span, '42') sf = cpp_ast.Number(m, self.fake_span, '64') add = cpp_ast.Binop(m, self.fake_span, cpp_ast.BinopKind.ADD, ft, sf) self.assertEqual(str(add), '(42) + (64)')
def test_format_binop(self): m = ast.Module('test') fake_pos = self.fake_pos fake_span = Span(fake_pos, fake_pos) le = ast.Binop(m, fake_span, ast.BinopKind.LE, self.five, self.five) self.assertEqual('(5) <= (5)', str(le))