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
;
32 static struct smatch_state compare_states
[] = {
37 [SPECIAL_UNSIGNED_LT
] = {
39 .data
= (void *)SPECIAL_UNSIGNED_LT
,
43 .data
= (void *)SPECIAL_LTE
,
45 [SPECIAL_UNSIGNED_LTE
] = {
47 .data
= (void *)SPECIAL_UNSIGNED_LTE
,
51 .data
= (void *)SPECIAL_EQUAL
,
53 [SPECIAL_NOTEQUAL
] = {
55 .data
= (void *)SPECIAL_NOTEQUAL
,
59 .data
= (void *)SPECIAL_GTE
,
61 [SPECIAL_UNSIGNED_GTE
] = {
63 .data
= (void *)SPECIAL_UNSIGNED_GTE
,
69 [SPECIAL_UNSIGNED_GT
] = {
71 .data
= (void *)SPECIAL_UNSIGNED_GT
,
75 static int flip_op(int op
)
82 case SPECIAL_UNSIGNED_LT
:
83 return SPECIAL_UNSIGNED_GT
;
86 case SPECIAL_UNSIGNED_LTE
:
87 return SPECIAL_UNSIGNED_GTE
;
90 case SPECIAL_NOTEQUAL
:
91 return SPECIAL_NOTEQUAL
;
94 case SPECIAL_UNSIGNED_GTE
:
95 return SPECIAL_UNSIGNED_LTE
;
98 case SPECIAL_UNSIGNED_GT
:
99 return SPECIAL_UNSIGNED_LT
;
101 sm_msg("internal smatch bug. unhandled comparison %d", op
);
106 static int falsify_op(int op
)
113 case SPECIAL_UNSIGNED_LT
:
114 return SPECIAL_UNSIGNED_GTE
;
117 case SPECIAL_UNSIGNED_LTE
:
118 return SPECIAL_UNSIGNED_GT
;
120 return SPECIAL_NOTEQUAL
;
121 case SPECIAL_NOTEQUAL
:
122 return SPECIAL_EQUAL
;
125 case SPECIAL_UNSIGNED_GTE
:
126 return SPECIAL_UNSIGNED_LT
;
129 case SPECIAL_UNSIGNED_GT
:
130 return SPECIAL_UNSIGNED_LTE
;
132 sm_msg("internal smatch bug. unhandled comparison %d", op
);
137 struct smatch_state
*alloc_link_state(struct string_list
*links
)
139 struct smatch_state
*state
;
140 static char buf
[256];
144 state
= __alloc_smatch_state(0);
147 FOR_EACH_PTR(links
, tmp
) {
149 snprintf(buf
, sizeof(buf
), "%s", tmp
);
151 snprintf(buf
, sizeof(buf
), "%s, %s", buf
, tmp
);
152 } END_FOR_EACH_PTR(tmp
);
154 state
->name
= alloc_sname(buf
);
159 static void save_start_states(struct statement
*stmt
)
161 struct symbol
*param
;
163 char state_name
[128];
164 struct smatch_state
*state
;
165 struct string_list
*links
;
168 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, param
) {
171 snprintf(orig
, sizeof(orig
), "%s orig", param
->ident
->name
);
172 snprintf(state_name
, sizeof(state_name
), "%s vs %s", param
->ident
->name
, orig
);
173 state
= &compare_states
[SPECIAL_EQUAL
];
174 set_state(compare_id
, state_name
, NULL
, state
);
176 link
= alloc_sname(state_name
);
178 insert_string(&links
, link
);
179 state
= alloc_link_state(links
);
180 set_state(link_id
, param
->ident
->name
, param
, state
);
181 } END_FOR_EACH_PTR(param
);
184 static struct smatch_state
*merge_func(struct smatch_state
*s1
, struct smatch_state
*s2
)
186 struct smatch_state
*ret
;
187 struct string_list
*links
;
189 links
= combine_string_lists(s1
->data
, s2
->data
);
190 ret
= alloc_link_state(links
);
194 static void save_link(struct expression
*expr
, char *link
)
196 struct smatch_state
*old_state
, *new_state
;
197 struct string_list
*links
;
200 old_state
= get_state_expr(link_id
, expr
);
202 links
= clone_str_list(old_state
->data
);
206 new = alloc_sname(link
);
207 insert_string(&links
, new);
209 new_state
= alloc_link_state(links
);
210 set_state_expr(link_id
, expr
, new_state
);
213 static void match_inc(struct sm_state
*sm
)
215 struct string_list
*links
;
216 struct smatch_state
*state
;
219 links
= sm
->state
->data
;
221 FOR_EACH_PTR(links
, tmp
) {
222 state
= get_state(compare_id
, tmp
, NULL
);
223 if (state
== &compare_states
[SPECIAL_EQUAL
] ||
224 state
== &compare_states
[SPECIAL_GTE
] ||
225 state
== &compare_states
[SPECIAL_UNSIGNED_GTE
] ||
226 state
== &compare_states
['>'] ||
227 state
== &compare_states
[SPECIAL_UNSIGNED_GT
]) {
228 set_state(compare_id
, tmp
, NULL
, &compare_states
['>']);
230 set_state(compare_id
, tmp
, NULL
, &undefined
);
232 } END_FOR_EACH_PTR(tmp
);
235 static void match_dec(struct sm_state
*sm
)
237 struct string_list
*links
;
238 struct smatch_state
*state
;
241 links
= sm
->state
->data
;
243 FOR_EACH_PTR(links
, tmp
) {
244 state
= get_state(compare_id
, tmp
, NULL
);
245 if (state
== &compare_states
[SPECIAL_EQUAL
] ||
246 state
== &compare_states
[SPECIAL_LTE
] ||
247 state
== &compare_states
[SPECIAL_UNSIGNED_LTE
] ||
248 state
== &compare_states
['<'] ||
249 state
== &compare_states
[SPECIAL_UNSIGNED_LT
]) {
250 set_state(compare_id
, tmp
, NULL
, &compare_states
['<']);
252 set_state(compare_id
, tmp
, NULL
, &undefined
);
254 } END_FOR_EACH_PTR(tmp
);
257 static int match_inc_dec(struct sm_state
*sm
, struct expression
*mod_expr
)
261 if (mod_expr
->type
!= EXPR_PREOP
&& mod_expr
->type
!= EXPR_POSTOP
)
264 if (mod_expr
->op
== SPECIAL_INCREMENT
) {
268 if (mod_expr
->op
== SPECIAL_DECREMENT
) {
275 static void match_modify(struct sm_state
*sm
, struct expression
*mod_expr
)
277 struct string_list
*links
;
280 if (match_inc_dec(sm
, mod_expr
))
283 links
= sm
->state
->data
;
285 FOR_EACH_PTR(links
, tmp
) {
286 set_state(compare_id
, tmp
, NULL
, &undefined
);
287 } END_FOR_EACH_PTR(tmp
);
288 set_state(link_id
, sm
->name
, sm
->sym
, &undefined
);
291 static void match_logic(struct expression
*expr
)
295 struct symbol
*left_sym
, *right_sym
;
297 struct smatch_state
*true_state
, *false_state
;
298 char state_name
[256];
300 if (expr
->type
!= EXPR_COMPARE
)
302 left
= expr_to_var_sym(expr
->left
, &left_sym
);
303 if (!left
|| !left_sym
)
305 right
= expr_to_var_sym(expr
->right
, &right_sym
);
306 if (!right
|| !right_sym
)
309 if (strcmp(left
, right
) > 0) {
313 op
= flip_op(expr
->op
);
317 false_op
= falsify_op(op
);
318 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left
, right
);
319 true_state
= &compare_states
[op
];
320 false_state
= &compare_states
[false_op
];
322 set_true_false_states(compare_id
, state_name
, NULL
, true_state
, false_state
);
323 save_link(expr
->left
, state_name
);
324 save_link(expr
->right
, state_name
);
330 static void add_comparison(struct expression
*left
, int comparison
, struct expression
*right
)
332 char *left_name
= NULL
;
333 char *right_name
= NULL
;
334 struct symbol
*left_sym
, *right_sym
;
335 struct smatch_state
*state
;
336 char state_name
[256];
338 left_name
= expr_to_var_sym(left
, &left_sym
);
339 if (!left_name
|| !left_sym
)
341 right_name
= expr_to_var_sym(right
, &right_sym
);
342 if (!right_name
|| !right_sym
)
345 if (strcmp(left_name
, right_name
) > 0) {
346 char *tmp
= left_name
;
347 left_name
= right_name
;
349 comparison
= flip_op(comparison
);
351 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left_name
, right_name
);
352 state
= &compare_states
[comparison
];
354 set_state(compare_id
, state_name
, NULL
, state
);
355 save_link(left
, state_name
);
356 save_link(right
, state_name
);
358 free_string(left_name
);
359 free_string(right_name
);
363 static void match_assign_add(struct expression
*expr
)
365 struct expression
*right
;
366 struct expression
*r_left
, *r_right
;
367 sval_t left_tmp
, right_tmp
;
369 right
= strip_expr(expr
->right
);
370 r_left
= strip_expr(right
->left
);
371 r_right
= strip_expr(right
->right
);
373 if (!is_capped(expr
->left
)) {
374 get_absolute_max(r_left
, &left_tmp
);
375 get_absolute_max(r_right
, &right_tmp
);
376 if (sval_binop_overflows(left_tmp
, '+', right_tmp
))
380 get_absolute_min(r_left
, &left_tmp
);
381 if (sval_is_negative(left_tmp
))
383 get_absolute_min(r_right
, &right_tmp
);
384 if (sval_is_negative(right_tmp
))
387 if (left_tmp
.value
== 0)
388 add_comparison(expr
->left
, SPECIAL_GTE
, r_right
);
390 add_comparison(expr
->left
, '>', r_right
);
392 if (right_tmp
.value
== 0)
393 add_comparison(expr
->left
, SPECIAL_GTE
, r_left
);
395 add_comparison(expr
->left
, '>', r_left
);
398 static void match_assign_sub(struct expression
*expr
)
400 struct expression
*right
;
401 struct expression
*r_left
, *r_right
;
405 right
= strip_expr(expr
->right
);
406 r_left
= strip_expr(right
->left
);
407 r_right
= strip_expr(right
->right
);
409 if (get_absolute_min(r_right
, &min
) && sval_is_negative(min
))
412 comparison
= get_comparison(r_left
, r_right
);
414 switch (comparison
) {
417 if (implied_not_equal(r_right
, 0))
418 add_comparison(expr
->left
, '>', r_left
);
420 add_comparison(expr
->left
, SPECIAL_GTE
, r_left
);
425 static void match_binop_assign(struct expression
*expr
)
427 struct expression
*right
;
429 right
= strip_expr(expr
->right
);
430 if (right
->op
== '+')
431 match_assign_add(expr
);
432 if (right
->op
== '-')
433 match_assign_sub(expr
);
436 static void match_normal_assign(struct expression
*expr
)
438 add_comparison(expr
->left
, SPECIAL_EQUAL
, expr
->right
);
441 static void match_assign(struct expression
*expr
)
443 struct expression
*right
;
445 right
= strip_expr(expr
->right
);
446 if (right
->type
== EXPR_BINOP
)
447 match_binop_assign(expr
);
449 match_normal_assign(expr
);
452 static int get_comparison_strings(char *one
, char *two
)
455 struct smatch_state
*state
;
459 if (strcmp(one
, two
) > 0) {
467 snprintf(buf
, sizeof(buf
), "%s vs %s", one
, two
);
468 state
= get_state(compare_id
, buf
, NULL
);
470 ret
= PTR_INT(state
->data
);
478 int get_comparison(struct expression
*a
, struct expression
*b
)
484 one
= expr_to_var(a
);
487 two
= expr_to_var(b
);
491 ret
= get_comparison_strings(one
, two
);
498 void __add_comparison_info(struct expression
*expr
, struct expression
*call
, const char *range
)
500 struct expression
*arg
;
502 const char *c
= range
;
504 if (!str_to_comparison_arg(c
, call
, &comparison
, &arg
, NULL
))
506 add_comparison(expr
, SPECIAL_LTE
, arg
);
509 char *range_comparison_to_param(struct expression
*expr
)
511 struct symbol
*param
;
514 char *ret_str
= NULL
;
519 var
= expr_to_var(expr
);
522 get_absolute_min(expr
, &min
);
525 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, param
) {
529 snprintf(buf
, sizeof(buf
), "%s orig", param
->ident
->name
);
530 compare
= get_comparison_strings(var
, buf
);
533 if (compare
== SPECIAL_EQUAL
) {
534 snprintf(buf
, sizeof(buf
), "[%sp%d]", show_special(compare
), i
);
535 ret_str
= alloc_sname(buf
);
536 } else if (show_special(compare
)[0] == '<') {
537 snprintf(buf
, sizeof(buf
), "%s-[%sp%d]", sval_to_str(min
),
538 show_special(compare
), i
);
539 ret_str
= alloc_sname(buf
);
542 } END_FOR_EACH_PTR(param
);
549 void register_comparison(int id
)
552 add_hook(&match_logic
, CONDITION_HOOK
);
553 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
554 add_hook(&save_start_states
, AFTER_DEF_HOOK
);
557 void register_comparison_links(int id
)
560 add_merge_hook(link_id
, &merge_func
);
561 add_modification_hook(link_id
, &match_modify
);