2 * smatch/smatch_comparison.c
4 * Copyright (C) 2012 Oracle.
6 * Licensed under the Open Software License version 1.1
11 * The point here is to store the relationships between two variables.
13 * To do that we create a state with the two variables in alphabetical order:
14 * ->name = "x vs y" and the state would be "<". On the false path the state
17 * Part of the trick of it is that if x or y is modified then we need to reset
18 * the state. We need to keep a list of all the states which depend on x and
19 * all the states which depend on y. The link_id code handles this.
21 * Future work: If we know that x is greater than y and y is greater than z
22 * then we know that x is greater than z.
26 #include "smatch_extra.h"
27 #include "smatch_slist.h"
29 static int compare_id
;
39 ALLOCATOR(compare_data
, "compare data");
41 static struct smatch_state
*alloc_compare_state(
42 const char *var1
, struct symbol
*sym1
,
43 const char *var2
, struct symbol
*sym2
,
46 struct smatch_state
*state
;
47 struct compare_data
*data
;
49 state
= __alloc_smatch_state(0);
50 state
->name
= alloc_sname(show_special(comparison
));
51 data
= __alloc_compare_data(0);
52 data
->var1
= alloc_sname(var1
);
54 data
->var2
= alloc_sname(var2
);
56 data
->comparison
= comparison
;
61 static int state_to_comparison(struct smatch_state
*state
)
63 if (!state
|| !state
->data
)
65 return ((struct compare_data
*)state
->data
)->comparison
;
69 * flip_op() reverses the op left and right. So "x >= y" becomes "y <= x".
71 static int flip_op(int op
)
78 case SPECIAL_UNSIGNED_LT
:
79 return SPECIAL_UNSIGNED_GT
;
82 case SPECIAL_UNSIGNED_LTE
:
83 return SPECIAL_UNSIGNED_GTE
;
86 case SPECIAL_NOTEQUAL
:
87 return SPECIAL_NOTEQUAL
;
90 case SPECIAL_UNSIGNED_GTE
:
91 return SPECIAL_UNSIGNED_LTE
;
94 case SPECIAL_UNSIGNED_GT
:
95 return SPECIAL_UNSIGNED_LT
;
97 sm_msg("internal smatch bug. unhandled comparison %d", op
);
102 static int falsify_op(int op
)
109 case SPECIAL_UNSIGNED_LT
:
110 return SPECIAL_UNSIGNED_GTE
;
113 case SPECIAL_UNSIGNED_LTE
:
114 return SPECIAL_UNSIGNED_GT
;
116 return SPECIAL_NOTEQUAL
;
117 case SPECIAL_NOTEQUAL
:
118 return SPECIAL_EQUAL
;
121 case SPECIAL_UNSIGNED_GTE
:
122 return SPECIAL_UNSIGNED_LT
;
125 case SPECIAL_UNSIGNED_GT
:
126 return SPECIAL_UNSIGNED_LTE
;
128 sm_msg("internal smatch bug. unhandled comparison %d", op
);
133 static int rl_comparison(struct range_list
*left_rl
, struct range_list
*right_rl
)
135 sval_t left_min
, left_max
, right_min
, right_max
;
137 if (!left_rl
|| !right_rl
)
140 left_min
= rl_min(left_rl
);
141 left_max
= rl_max(left_rl
);
142 right_min
= rl_min(right_rl
);
143 right_max
= rl_max(right_rl
);
145 if (left_min
.value
== left_max
.value
&&
146 right_min
.value
== right_max
.value
&&
147 left_min
.value
== right_min
.value
)
148 return SPECIAL_EQUAL
;
150 if (sval_cmp(left_max
, right_min
) < 0)
152 if (sval_cmp(left_max
, right_min
) == 0)
154 if (sval_cmp(left_min
, right_max
) > 0)
156 if (sval_cmp(left_min
, right_max
) == 0)
162 static struct smatch_state
*unmatched_comparison(struct sm_state
*sm
)
164 struct compare_data
*data
= sm
->state
->data
;
165 struct range_list
*left_rl
, *right_rl
;
171 if (!get_implied_rl_var_sym(data
->var1
, data
->sym1
, &left_rl
))
173 if (!get_implied_rl_var_sym(data
->var2
, data
->sym2
, &right_rl
))
176 op
= rl_comparison(left_rl
, right_rl
);
178 return alloc_compare_state(data
->var1
, data
->sym1
, data
->var2
, data
->sym2
, op
);
183 /* remove_unsigned_from_comparison() is obviously a hack. */
184 static int remove_unsigned_from_comparison(int op
)
187 case SPECIAL_UNSIGNED_LT
:
189 case SPECIAL_UNSIGNED_LTE
:
191 case SPECIAL_UNSIGNED_GTE
:
193 case SPECIAL_UNSIGNED_GT
:
200 static int merge_comparisons(int one
, int two
)
204 one
= remove_unsigned_from_comparison(one
);
205 two
= remove_unsigned_from_comparison(two
);
252 return SPECIAL_NOTEQUAL
;
262 static struct smatch_state
*merge_compare_states(struct smatch_state
*s1
, struct smatch_state
*s2
)
264 struct compare_data
*data
= s1
->data
;
267 op
= merge_comparisons(state_to_comparison(s1
), state_to_comparison(s2
));
269 return alloc_compare_state(data
->var1
, data
->sym1
, data
->var2
, data
->sym2
, op
);
273 struct smatch_state
*alloc_link_state(struct string_list
*links
)
275 struct smatch_state
*state
;
276 static char buf
[256];
280 state
= __alloc_smatch_state(0);
283 FOR_EACH_PTR(links
, tmp
) {
285 snprintf(buf
, sizeof(buf
), "%s", tmp
);
287 snprintf(buf
, sizeof(buf
), "%s, %s", buf
, tmp
);
288 } END_FOR_EACH_PTR(tmp
);
290 state
->name
= alloc_sname(buf
);
295 static void save_start_states(struct statement
*stmt
)
297 struct symbol
*param
;
299 char state_name
[128];
300 struct smatch_state
*state
;
301 struct string_list
*links
;
304 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, param
) {
307 snprintf(orig
, sizeof(orig
), "%s orig", param
->ident
->name
);
308 snprintf(state_name
, sizeof(state_name
), "%s vs %s", param
->ident
->name
, orig
);
309 state
= alloc_compare_state(param
->ident
->name
, param
, alloc_sname(orig
), NULL
, SPECIAL_EQUAL
);
310 set_state(compare_id
, state_name
, NULL
, state
);
312 link
= alloc_sname(state_name
);
314 insert_string(&links
, link
);
315 state
= alloc_link_state(links
);
316 set_state(link_id
, param
->ident
->name
, param
, state
);
317 } END_FOR_EACH_PTR(param
);
320 static struct smatch_state
*merge_links(struct smatch_state
*s1
, struct smatch_state
*s2
)
322 struct smatch_state
*ret
;
323 struct string_list
*links
;
325 links
= combine_string_lists(s1
->data
, s2
->data
);
326 ret
= alloc_link_state(links
);
330 static void save_link_var_sym(const char *var
, struct symbol
*sym
, char *link
)
332 struct smatch_state
*old_state
, *new_state
;
333 struct string_list
*links
;
336 old_state
= get_state(link_id
, var
, sym
);
338 links
= clone_str_list(old_state
->data
);
342 new = alloc_sname(link
);
343 insert_string(&links
, new);
345 new_state
= alloc_link_state(links
);
346 set_state(link_id
, var
, sym
, new_state
);
349 static void save_link(struct expression
*expr
, char *link
)
354 var
= expr_to_var_sym(expr
, &sym
);
358 save_link_var_sym(var
, sym
, link
);
363 static void match_inc(struct sm_state
*sm
)
365 struct string_list
*links
;
366 struct smatch_state
*state
;
369 links
= sm
->state
->data
;
371 FOR_EACH_PTR(links
, tmp
) {
372 state
= get_state(compare_id
, tmp
, NULL
);
374 switch (state_to_comparison(state
)) {
377 case SPECIAL_UNSIGNED_GTE
:
379 case SPECIAL_UNSIGNED_GT
: {
380 struct compare_data
*data
= state
->data
;
381 struct smatch_state
*new;
383 new = alloc_compare_state(data
->var1
, data
->sym1
, data
->var2
, data
->sym2
, '>');
384 set_state(compare_id
, tmp
, NULL
, new);
388 set_state(compare_id
, tmp
, NULL
, &undefined
);
390 } END_FOR_EACH_PTR(tmp
);
393 static void match_dec(struct sm_state
*sm
)
395 struct string_list
*links
;
396 struct smatch_state
*state
;
399 links
= sm
->state
->data
;
401 FOR_EACH_PTR(links
, tmp
) {
402 state
= get_state(compare_id
, tmp
, NULL
);
404 switch (state_to_comparison(state
)) {
407 case SPECIAL_UNSIGNED_LTE
:
409 case SPECIAL_UNSIGNED_LT
: {
410 struct compare_data
*data
= state
->data
;
411 struct smatch_state
*new;
413 new = alloc_compare_state(data
->var1
, data
->sym1
, data
->var2
, data
->sym2
, '<');
414 set_state(compare_id
, tmp
, NULL
, new);
418 set_state(compare_id
, tmp
, NULL
, &undefined
);
420 } END_FOR_EACH_PTR(tmp
);
423 static int match_inc_dec(struct sm_state
*sm
, struct expression
*mod_expr
)
427 if (mod_expr
->type
!= EXPR_PREOP
&& mod_expr
->type
!= EXPR_POSTOP
)
430 if (mod_expr
->op
== SPECIAL_INCREMENT
) {
434 if (mod_expr
->op
== SPECIAL_DECREMENT
) {
441 static void match_modify(struct sm_state
*sm
, struct expression
*mod_expr
)
443 struct string_list
*links
;
446 if (match_inc_dec(sm
, mod_expr
))
449 links
= sm
->state
->data
;
451 FOR_EACH_PTR(links
, tmp
) {
452 set_state(compare_id
, tmp
, NULL
, &undefined
);
453 } END_FOR_EACH_PTR(tmp
);
454 set_state(link_id
, sm
->name
, sm
->sym
, &undefined
);
457 static void match_logic(struct expression
*expr
)
461 struct symbol
*left_sym
, *right_sym
;
463 struct smatch_state
*true_state
, *false_state
;
464 char state_name
[256];
466 if (expr
->type
!= EXPR_COMPARE
)
468 left
= expr_to_var_sym(expr
->left
, &left_sym
);
469 if (!left
|| !left_sym
)
471 right
= expr_to_var_sym(expr
->right
, &right_sym
);
472 if (!right
|| !right_sym
)
475 if (strcmp(left
, right
) > 0) {
476 struct symbol
*tmp_sym
= left_sym
;
477 char *tmp_name
= left
;
480 left_sym
= right_sym
;
483 op
= flip_op(expr
->op
);
487 false_op
= falsify_op(op
);
488 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left
, right
);
489 true_state
= alloc_compare_state(left
, left_sym
, right
, right_sym
, op
);
490 false_state
= alloc_compare_state(left
, left_sym
, right
, right_sym
, false_op
);
492 set_true_false_states(compare_id
, state_name
, NULL
, true_state
, false_state
);
493 save_link(expr
->left
, state_name
);
494 save_link(expr
->right
, state_name
);
500 static void add_comparison_var_sym(const char *left_name
, struct symbol
*left_sym
, int comparison
, const char *right_name
, struct symbol
*right_sym
)
502 struct smatch_state
*state
;
503 char state_name
[256];
505 if (strcmp(left_name
, right_name
) > 0) {
506 struct symbol
*tmp_sym
= left_sym
;
507 const char *tmp_name
= left_name
;
509 left_name
= right_name
;
510 left_sym
= right_sym
;
511 right_name
= tmp_name
;
513 comparison
= flip_op(comparison
);
515 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left_name
, right_name
);
516 state
= alloc_compare_state(left_name
, left_sym
, right_name
, right_sym
, comparison
);
518 set_state(compare_id
, state_name
, NULL
, state
);
519 save_link_var_sym(left_name
, left_sym
, state_name
);
520 save_link_var_sym(right_name
, right_sym
, state_name
);
523 static void add_comparison(struct expression
*left
, int comparison
, struct expression
*right
)
525 char *left_name
= NULL
;
526 char *right_name
= NULL
;
527 struct symbol
*left_sym
, *right_sym
;
529 left_name
= expr_to_var_sym(left
, &left_sym
);
530 if (!left_name
|| !left_sym
)
532 right_name
= expr_to_var_sym(right
, &right_sym
);
533 if (!right_name
|| !right_sym
)
536 add_comparison_var_sym(left_name
, left_sym
, comparison
, right_name
, right_sym
);
539 free_string(left_name
);
540 free_string(right_name
);
543 static void match_assign_add(struct expression
*expr
)
545 struct expression
*right
;
546 struct expression
*r_left
, *r_right
;
547 sval_t left_tmp
, right_tmp
;
549 right
= strip_expr(expr
->right
);
550 r_left
= strip_expr(right
->left
);
551 r_right
= strip_expr(right
->right
);
553 if (!is_capped(expr
->left
)) {
554 get_absolute_max(r_left
, &left_tmp
);
555 get_absolute_max(r_right
, &right_tmp
);
556 if (sval_binop_overflows(left_tmp
, '+', right_tmp
))
560 get_absolute_min(r_left
, &left_tmp
);
561 if (sval_is_negative(left_tmp
))
563 get_absolute_min(r_right
, &right_tmp
);
564 if (sval_is_negative(right_tmp
))
567 if (left_tmp
.value
== 0)
568 add_comparison(expr
->left
, SPECIAL_GTE
, r_right
);
570 add_comparison(expr
->left
, '>', r_right
);
572 if (right_tmp
.value
== 0)
573 add_comparison(expr
->left
, SPECIAL_GTE
, r_left
);
575 add_comparison(expr
->left
, '>', r_left
);
578 static void match_assign_sub(struct expression
*expr
)
580 struct expression
*right
;
581 struct expression
*r_left
, *r_right
;
585 right
= strip_expr(expr
->right
);
586 r_left
= strip_expr(right
->left
);
587 r_right
= strip_expr(right
->right
);
589 if (get_absolute_min(r_right
, &min
) && sval_is_negative(min
))
592 comparison
= get_comparison(r_left
, r_right
);
594 switch (comparison
) {
597 if (implied_not_equal(r_right
, 0))
598 add_comparison(expr
->left
, '>', r_left
);
600 add_comparison(expr
->left
, SPECIAL_GTE
, r_left
);
605 static void match_binop_assign(struct expression
*expr
)
607 struct expression
*right
;
609 right
= strip_expr(expr
->right
);
610 if (right
->op
== '+')
611 match_assign_add(expr
);
612 if (right
->op
== '-')
613 match_assign_sub(expr
);
616 static void copy_comparisons(struct expression
*left
, struct expression
*right
)
618 struct string_list
*links
;
619 struct smatch_state
*state
;
620 struct compare_data
*data
;
621 struct symbol
*left_sym
, *right_sym
;
622 char *left_var
= NULL
;
623 char *right_var
= NULL
;
629 left_var
= expr_to_var_sym(left
, &left_sym
);
630 if (!left_var
|| !left_sym
)
632 right_var
= expr_to_var_sym(right
, &right_sym
);
633 if (!right_var
|| !right_sym
)
636 state
= get_state_expr(link_id
, right
);
641 FOR_EACH_PTR(links
, tmp
) {
642 state
= get_state(compare_id
, tmp
, NULL
);
646 comparison
= data
->comparison
;
649 if (sym
== right_sym
&& strcmp(var
, right_var
) == 0) {
652 comparison
= flip_op(comparison
);
654 add_comparison_var_sym(left_var
, left_sym
, comparison
, var
, sym
);
655 } END_FOR_EACH_PTR(tmp
);
658 free_string(right_var
);
661 static void match_normal_assign(struct expression
*expr
)
663 copy_comparisons(expr
->left
, expr
->right
);
664 add_comparison(expr
->left
, SPECIAL_EQUAL
, expr
->right
);
667 static void match_assign(struct expression
*expr
)
669 struct expression
*right
;
671 right
= strip_expr(expr
->right
);
672 if (right
->type
== EXPR_BINOP
)
673 match_binop_assign(expr
);
675 match_normal_assign(expr
);
678 static int get_comparison_strings(char *one
, char *two
)
681 struct smatch_state
*state
;
685 if (strcmp(one
, two
) > 0) {
693 snprintf(buf
, sizeof(buf
), "%s vs %s", one
, two
);
694 state
= get_state(compare_id
, buf
, NULL
);
696 ret
= state_to_comparison(state
);
704 int get_comparison(struct expression
*a
, struct expression
*b
)
710 one
= expr_to_var(a
);
713 two
= expr_to_var(b
);
717 ret
= get_comparison_strings(one
, two
);
724 void __add_comparison_info(struct expression
*expr
, struct expression
*call
, const char *range
)
726 struct expression
*arg
;
728 const char *c
= range
;
730 if (!str_to_comparison_arg(c
, call
, &comparison
, &arg
))
732 add_comparison(expr
, SPECIAL_LTE
, arg
);
735 static char *range_comparison_to_param_helper(struct expression
*expr
, char starts_with
)
737 struct symbol
*param
;
740 char *ret_str
= NULL
;
744 var
= expr_to_var(expr
);
749 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, param
) {
753 snprintf(buf
, sizeof(buf
), "%s orig", param
->ident
->name
);
754 compare
= get_comparison_strings(var
, buf
);
757 if (show_special(compare
)[0] != starts_with
)
759 snprintf(buf
, sizeof(buf
), "[%sp%d]", show_special(compare
), i
);
760 ret_str
= alloc_sname(buf
);
762 } END_FOR_EACH_PTR(param
);
769 char *expr_equal_to_param(struct expression
*expr
)
771 return range_comparison_to_param_helper(expr
, '=');
774 char *expr_lte_to_param(struct expression
*expr
)
776 return range_comparison_to_param_helper(expr
, '<');
779 static void free_data(struct symbol
*sym
)
783 clear_compare_data_alloc();
786 void register_comparison(int id
)
789 add_hook(&match_logic
, CONDITION_HOOK
);
790 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
791 add_hook(&save_start_states
, AFTER_DEF_HOOK
);
792 add_unmatched_state_hook(compare_id
, unmatched_comparison
);
793 add_merge_hook(compare_id
, &merge_compare_states
);
794 add_hook(&free_data
, AFTER_FUNC_HOOK
);
797 void register_comparison_links(int id
)
800 add_merge_hook(link_id
, &merge_links
);
801 add_modification_hook(link_id
, &match_modify
);