clean up in merge_slist()
[smatch.git] / smatch_implied.c
blob53a277ca348704c2659475bac651fcc1ee734062
1 /*
2 * sparse/smatch_implied.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
11 * Imagine we have this code:
12 * foo = 1;
13 * if (bar)
14 * foo = 99;
15 * else
16 * frob();
17 * // <-- point #1
18 * if (foo == 99) // <-- point #2
19 * bar->baz; // <-- point #3
22 * At point #3 bar is non null and can be dereferenced.
24 * It's smatch_implied.c which sets bar to non null at point #2.
26 * At point #1 merge_slist() stores the list of states from both
27 * the true and false paths. On the true path foo == 99 and on
28 * the false path foo == 1. merge_slist() sets their my_pools
29 * list to show the other states which were there when foo == 99.
31 * When it comes to the if (foo == 99) the smatch implied hook
32 * looks for all the pools where foo was not 99. It makes a list
33 * of those.
35 * Then for bar (and all the other states) it says, ok bar is a
36 * merged state that came from these previous states. We'll
37 * chop out all the states where it came from a pool where
38 * foo != 99 and merge it all back together.
40 * That is the implied state of bar.
42 * merge_slist() sets up ->my_pools.
43 * merge_sm_state() sets ->pre_merge.
44 * If an sm_state is not the same on both sides of a merge, it
45 * gets a ->my_pool set for both sides. The result is a merged
46 * state that has it's ->pre_merge pointers set. Merged states
47 * do not immediately have any my_pools set, but maybe will later
48 * when they themselves are merged.
49 * A pool is a list of all the states that were set at the time.
52 #include "smatch.h"
53 #include "smatch_slist.h"
54 #include "smatch_extra.h"
56 int debug_implied_states = 0;
57 int option_no_implied = 0;
59 static int pool_in_pools(struct state_list *pool,
60 struct state_list_stack *pools)
62 struct state_list *tmp;
64 FOR_EACH_PTR(pools, tmp) {
65 if (tmp == pool)
66 return 1;
67 if (tmp > pool)
68 return 0;
69 } END_FOR_EACH_PTR(tmp);
70 return 0;
73 struct sm_state *remove_my_pools(struct sm_state *sm,
74 struct state_list_stack *pools, int *modified)
76 struct sm_state *ret = NULL;
77 struct sm_state *left;
78 struct sm_state *right;
79 int removed = 0;
81 if (!sm)
82 return NULL;
84 if (pool_in_pools(sm->my_pool, pools)) {
85 DIMPLIED("removed %s = %s from %d\n", sm->name,
86 show_state(sm->state), sm->line);
87 *modified = 1;
88 return NULL;
91 if (!is_merged(sm)) {
92 DIMPLIED("kept %s = %s from %d\n", sm->name, show_state(sm->state),
93 sm->line);
94 return sm;
97 DIMPLIED("checking %s = %s from %d\n", sm->name, show_state(sm->state), sm->line);
98 left = remove_my_pools(sm->pre_left, pools, &removed);
99 right = remove_my_pools(sm->pre_right, pools, &removed);
100 if (!removed) {
101 DIMPLIED("kept %s = %s from %d\n", sm->name, show_state(sm->state), sm->line);
102 return sm;
104 *modified = 1;
105 if (!left && !right) {
106 DIMPLIED("removed %s = %s from %d\n", sm->name, show_state(sm->state), sm->line);
107 return NULL;
110 if (!left) {
111 ret = clone_state(right);
112 ret->merged = 1;
113 ret->pre_right = right;
114 ret->pre_left = NULL;
115 ret->my_pool = sm->my_pool;
116 } else if (!right) {
117 ret = clone_state(left);
118 ret->merged = 1;
119 ret->pre_left = left;
120 ret->pre_right = NULL;
121 ret->my_pool = sm->my_pool;
122 } else {
123 ret = merge_sm_states(left, right);
124 ret->my_pool = sm->my_pool;
126 DIMPLIED("partial %s = %s from %d\n", sm->name, show_state(sm->state), sm->line);
127 return ret;
130 static struct state_list *filter_stack(struct state_list *pre_list,
131 struct state_list_stack *stack)
133 struct state_list *ret = NULL;
134 struct sm_state *tmp;
135 struct sm_state *filtered_state;
136 int modified;
137 int counter = 0;
139 if (!stack)
140 return NULL;
142 FOR_EACH_PTR(pre_list, tmp) {
143 modified = 0;
144 filtered_state = remove_my_pools(tmp, stack, &modified);
145 if (filtered_state && modified) {
146 add_ptr_list(&ret, filtered_state);
147 if ((counter++)%10 && out_of_memory())
148 return NULL;
151 } END_FOR_EACH_PTR(tmp);
152 return ret;
155 static int is_checked(struct state_list *checked, struct sm_state *sm)
157 struct sm_state *tmp;
159 FOR_EACH_PTR(checked, tmp) {
160 if (tmp == sm)
161 return 1;
162 } END_FOR_EACH_PTR(tmp);
163 return 0;
166 static void separate_pools(struct sm_state *sm_state, int comparison, int num,
167 int left,
168 struct state_list_stack **true_stack,
169 struct state_list_stack **false_stack,
170 struct state_list **checked)
172 struct sm_state *s;
173 int istrue, isfalse;
174 int free_checked = 0;
175 struct state_list *checked_states = NULL;
176 static int stopper;
178 if (!sm_state)
179 return;
181 if (checked == NULL) {
182 stopper = 0;
183 checked = &checked_states;
184 free_checked = 1;
186 if (is_checked(*checked, sm_state)) {
187 return;
189 add_ptr_list(checked, sm_state);
191 if (stopper++ >= 500) {
192 smatch_msg("internal error: too much recursion going on here");
193 return;
196 if (sm_state->my_pool) {
197 s = get_sm_state_slist(sm_state->my_pool, sm_state->name, sm_state->owner,
198 sm_state->sym);
200 istrue = !possibly_false(comparison,
201 (struct data_info *)s->state->data, num,
202 left);
203 isfalse = !possibly_true(comparison,
204 (struct data_info *)s->state->data,
205 num, left);
207 if (debug_implied_states || debug_states) {
208 if (istrue && isfalse) {
209 printf("'%s = %s' from %d does not exist.\n",
210 s->name, show_state(s->state),
211 s->line);
212 } else if (istrue) {
213 printf("'%s = %s' from %d is true.\n",
214 s->name, show_state(s->state),
215 s->line);
216 } else if (isfalse) {
217 printf("'%s = %s' from %d is false.\n",
218 s->name, show_state(s->state),
219 s->line);
220 } else {
221 printf("'%s = %s' from %d could be true or "
222 "false.\n", s->name,
223 show_state(s->state), s->line);
226 if (istrue) {
227 add_pool(true_stack, s->my_pool);
229 if (isfalse) {
230 add_pool(false_stack, s->my_pool);
233 separate_pools(sm_state->pre_left, comparison, num, left, true_stack, false_stack, checked);
234 separate_pools(sm_state->pre_right, comparison, num, left, true_stack, false_stack, checked);
235 if (free_checked)
236 free_slist(checked);
239 static void get_eq_neq(struct sm_state *sm_state, int comparison, int num,
240 int left,
241 struct state_list *pre_list,
242 struct state_list **true_states,
243 struct state_list **false_states)
245 struct state_list_stack *true_stack = NULL;
246 struct state_list_stack *false_stack = NULL;
248 if (debug_implied_states || debug_states) {
249 if (left)
250 smatch_msg("checking implications: (%s %s %d)",
251 sm_state->name, show_special(comparison), num);
252 else
253 smatch_msg("checking implications: (%d %s %s)",
254 num, show_special(comparison), sm_state->name);
257 separate_pools(sm_state, comparison, num, left, &true_stack, &false_stack, NULL);
259 DIMPLIED("filtering true stack.\n");
260 *true_states = filter_stack(pre_list, false_stack);
261 DIMPLIED("filtering false stack.\n");
262 *false_states = filter_stack(pre_list, true_stack);
263 free_stack(&true_stack);
264 free_stack(&false_stack);
265 if (debug_implied_states || debug_states) {
266 printf("These are the implied states for the true path:\n");
267 __print_slist(*true_states);
268 printf("These are the implied states for the false path:\n");
269 __print_slist(*false_states);
273 static char *get_implication_variable(struct expression *expr, struct symbol **symp)
275 expr = strip_expr(expr);
276 if (expr->type == EXPR_ASSIGNMENT)
277 return get_implication_variable(expr->left, symp);
278 return get_variable_from_expr(expr, symp);
281 static void handle_comparison(struct expression *expr,
282 struct state_list **implied_true,
283 struct state_list **implied_false)
285 struct symbol *sym;
286 char *name;
287 struct sm_state *state;
288 int value;
289 int left = 0;
291 value = get_value(expr->left);
292 if (value == UNDEFINED) {
293 value = get_value(expr->right);
294 if (value == UNDEFINED)
295 return;
296 left = 1;
298 if (left)
299 name = get_implication_variable(expr->left, &sym);
300 else
301 name = get_implication_variable(expr->right, &sym);
302 if (!name || !sym)
303 goto free;
304 state = get_sm_state(name, SMATCH_EXTRA, sym);
305 if (!state)
306 goto free;
307 if (!is_merged(state)) {
308 DIMPLIED("%d '%s' is not merged.\n", get_lineno(), state->name);
309 goto free;
311 get_eq_neq(state, expr->op, value, left, __get_cur_slist(), implied_true, implied_false);
312 delete_state_slist(implied_true, name, SMATCH_EXTRA, sym);
313 delete_state_slist(implied_false, name, SMATCH_EXTRA, sym);
314 free:
315 free_string(name);
318 static void get_tf_states(struct expression *expr,
319 struct state_list **implied_true,
320 struct state_list **implied_false)
322 struct symbol *sym;
323 char *name;
324 struct sm_state *state;
326 if (expr->type == EXPR_COMPARE) {
327 handle_comparison(expr, implied_true, implied_false);
328 return;
330 if (expr->type == EXPR_ASSIGNMENT) {
331 /* most of the time ->my_pools will be empty here because we
332 just set the state, but if have assigned a conditional
333 function there are implications. */
334 expr = expr->left;
337 name = get_variable_from_expr(expr, &sym);
338 if (!name || !sym)
339 goto free;
340 state = get_sm_state(name, SMATCH_EXTRA, sym);
341 if (!state)
342 goto free;
343 if (!is_merged(state)) {
344 DIMPLIED("%d '%s' has no pools.\n", get_lineno(), state->name);
345 goto free;
347 get_eq_neq(state, SPECIAL_NOTEQUAL, 0, 1, __get_cur_slist(), implied_true, implied_false);
348 delete_state_slist(implied_true, name, SMATCH_EXTRA, sym);
349 delete_state_slist(implied_false, name, SMATCH_EXTRA, sym);
350 free:
351 free_string(name);
354 static void implied_states_hook(struct expression *expr)
356 struct sm_state *state;
357 struct state_list *implied_true = NULL;
358 struct state_list *implied_false = NULL;
360 if (option_no_implied)
361 return;
363 get_tf_states(expr, &implied_true, &implied_false);
365 FOR_EACH_PTR(implied_true, state) {
366 __set_true_false_sm(state, NULL);
367 } END_FOR_EACH_PTR(state);
368 free_slist(&implied_true);
370 FOR_EACH_PTR(implied_false, state) {
371 __set_true_false_sm(NULL, state);
372 } END_FOR_EACH_PTR(state);
373 free_slist(&implied_false);
376 void get_implications(char *name, struct symbol *sym, int comparison, int num,
377 struct state_list **true_states,
378 struct state_list **false_states)
380 struct sm_state *sm;
382 sm = get_sm_state(name, SMATCH_EXTRA, sym);
383 if (!sm)
384 return;
385 if (slist_has_state(sm->possible, &undefined))
386 return;
387 get_eq_neq(sm, comparison, num, 1, __get_cur_slist(), true_states, false_states);
390 struct state_list *__implied_case_slist(struct expression *switch_expr,
391 struct expression *case_expr,
392 struct state_list **raw_slist)
394 char *name = NULL;
395 struct symbol *sym;
396 struct sm_state *sm;
397 struct sm_state *true_sm;
398 struct sm_state *false_sm;
399 struct state_list *true_states = NULL;
400 struct state_list *false_states = NULL;
401 struct state_list *ret = clone_slist(*raw_slist);
402 long long val;
404 if (!case_expr)
405 return ret;
406 name = get_variable_from_expr(switch_expr, &sym);
407 if (!name || !sym)
408 goto free;
409 sm = get_sm_state_slist(*raw_slist, name, SMATCH_EXTRA, sym);
410 val = get_value(case_expr);
411 if (val == UNDEFINED)
412 goto free;
413 if (sm) {
414 get_eq_neq(sm, SPECIAL_EQUAL, val, 1, *raw_slist, &true_states, &false_states);
417 true_sm = get_sm_state_slist(true_states, name, SMATCH_EXTRA, sym);
418 if (!true_sm)
419 set_state_slist(&true_states, name, SMATCH_EXTRA, sym, alloc_extra_state(val));
420 false_sm = get_sm_state_slist(false_states, name, SMATCH_EXTRA, sym);
421 if (!false_sm)
422 set_state_slist(&false_states, name, SMATCH_EXTRA, sym, add_filter(sm?sm->state:NULL, val));
423 overwrite_slist(false_states, raw_slist);
424 overwrite_slist(true_states, &ret);
425 free_slist(&true_states);
426 free_slist(&false_states);
427 free:
428 free_string(name);
429 return ret;
432 void __extra_match_condition(struct expression *expr);
433 void register_implications(int id)
435 add_hook(&implied_states_hook, CONDITION_HOOK);
436 add_hook(&__extra_match_condition, CONDITION_HOOK);