stree fallout: implications not working 100%
[smatch.git] / smatch_slist.c
blob176c072d37b262db8e1b8902810336d1d16fd8d9
1 /*
2 * Copyright (C) 2008,2009 Dan Carpenter.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include "smatch.h"
21 #include "smatch_slist.h"
23 #undef CHECKORDER
25 ALLOCATOR(smatch_state, "smatch state");
26 ALLOCATOR(sm_state, "sm state");
27 ALLOCATOR(named_stree, "named slist");
28 __DO_ALLOCATOR(char, 1, 4, "state names", sname);
30 static int sm_state_counter;
32 char *show_sm(struct sm_state *sm)
34 static char buf[256];
35 struct sm_state *tmp;
36 int pos;
37 int i;
39 pos = snprintf(buf, sizeof(buf), "[%s] '%s' = %s (",
40 check_name(sm->owner), sm->name, show_state(sm->state));
41 if (pos > sizeof(buf))
42 goto truncate;
44 i = 0;
45 FOR_EACH_PTR(sm->possible, tmp) {
46 if (i++)
47 pos += snprintf(buf + pos, sizeof(buf) - pos, ", ");
48 if (pos > sizeof(buf))
49 goto truncate;
50 pos += snprintf(buf + pos, sizeof(buf) - pos, "%s",
51 show_state(tmp->state));
52 if (pos > sizeof(buf))
53 goto truncate;
54 } END_FOR_EACH_PTR(tmp);
55 snprintf(buf + pos, sizeof(buf) - pos, ")");
57 return buf;
59 truncate:
60 for (i = 0; i < 3; i++)
61 buf[sizeof(buf) - 2 - i] = '.';
62 return buf;
65 void __print_stree(struct stree *stree)
67 struct sm_state *sm;
69 printf("dumping stree at %d\n", get_lineno());
70 FOR_EACH_SM(stree, sm) {
71 printf("%s\n", show_sm(sm));
72 } END_FOR_EACH_SM(sm);
73 printf("---\n");
76 /* NULL states go at the end to simplify merge_slist */
77 int cmp_tracker(const struct sm_state *a, const struct sm_state *b)
79 int ret;
81 if (a == b)
82 return 0;
83 if (!b)
84 return -1;
85 if (!a)
86 return 1;
88 if (a->owner > b->owner)
89 return -1;
90 if (a->owner < b->owner)
91 return 1;
93 ret = strcmp(a->name, b->name);
94 if (ret)
95 return ret;
97 if (!b->sym && a->sym)
98 return -1;
99 if (!a->sym && b->sym)
100 return 1;
101 if (a->sym > b->sym)
102 return -1;
103 if (a->sym < b->sym)
104 return 1;
106 return 0;
109 static int cmp_sm_states(const struct sm_state *a, const struct sm_state *b, int preserve)
111 int ret;
113 ret = cmp_tracker(a, b);
114 if (ret)
115 return ret;
117 /* todo: add hook for smatch_extra.c */
118 if (a->state > b->state)
119 return -1;
120 if (a->state < b->state)
121 return 1;
122 /* This is obviously a massive disgusting hack but we need to preserve
123 * the unmerged states for smatch extra because we use them in
124 * smatch_db.c. Meanwhile if we preserve all the other unmerged states
125 * then it uses a lot of memory and we don't use it. Hence this hack.
127 * Also sometimes even just preserving every possible SMATCH_EXTRA state
128 * takes too much resources so we have to cap that. Capping is probably
129 * not often a problem in real life.
131 if (a->owner == SMATCH_EXTRA && preserve) {
132 if (a == b)
133 return 0;
134 if (a->merged == 1 && b->merged == 0)
135 return -1;
136 if (a->merged == 0)
137 return 1;
140 return 0;
143 static struct sm_state *alloc_sm_state(int owner, const char *name,
144 struct symbol *sym, struct smatch_state *state)
146 struct sm_state *sm_state = __alloc_sm_state(0);
148 sm_state_counter++;
150 sm_state->name = alloc_sname(name);
151 sm_state->owner = owner;
152 sm_state->sym = sym;
153 sm_state->state = state;
154 sm_state->line = get_lineno();
155 sm_state->merged = 0;
156 sm_state->implied = 0;
157 sm_state->pool = NULL;
158 sm_state->left = NULL;
159 sm_state->right = NULL;
160 sm_state->nr_children = 1;
161 sm_state->possible = NULL;
162 add_ptr_list(&sm_state->possible, sm_state);
163 return sm_state;
166 static struct sm_state *alloc_state_no_name(int owner, const char *name,
167 struct symbol *sym,
168 struct smatch_state *state)
170 struct sm_state *tmp;
172 tmp = alloc_sm_state(owner, NULL, sym, state);
173 tmp->name = name;
174 return tmp;
177 int too_many_possible(struct sm_state *sm)
179 if (ptr_list_size((struct ptr_list *)sm->possible) >= 100)
180 return 1;
181 return 0;
184 void add_possible_sm(struct sm_state *to, struct sm_state *new)
186 struct sm_state *tmp;
187 int preserve = 1;
189 if (too_many_possible(to))
190 preserve = 0;
192 FOR_EACH_PTR(to->possible, tmp) {
193 if (cmp_sm_states(tmp, new, preserve) < 0)
194 continue;
195 else if (cmp_sm_states(tmp, new, preserve) == 0) {
196 return;
197 } else {
198 INSERT_CURRENT(new, tmp);
199 return;
201 } END_FOR_EACH_PTR(tmp);
202 add_ptr_list(&to->possible, new);
205 static void copy_possibles(struct sm_state *to, struct sm_state *from)
207 struct sm_state *tmp;
209 FOR_EACH_PTR(from->possible, tmp) {
210 add_possible_sm(to, tmp);
211 } END_FOR_EACH_PTR(tmp);
214 char *alloc_sname(const char *str)
216 char *tmp;
218 if (!str)
219 return NULL;
220 tmp = __alloc_sname(strlen(str) + 1);
221 strcpy(tmp, str);
222 return tmp;
225 int out_of_memory()
228 * I decided to use 50M here based on trial and error.
229 * It works out OK for the kernel and so it should work
230 * for most other projects as well.
232 if (sm_state_counter * sizeof(struct sm_state) >= 50000000)
233 return 1;
234 return 0;
237 int low_on_memory(void)
239 if (sm_state_counter * sizeof(struct sm_state) >= 25000000)
240 return 1;
241 return 0;
244 static void free_sm_state(struct sm_state *sm)
246 free_slist(&sm->possible);
248 * fixme. Free the actual state.
249 * Right now we leave it until the end of the function
250 * because we don't want to double free it.
251 * Use the freelist to not double free things
255 static void free_all_sm_states(struct allocation_blob *blob)
257 unsigned int size = sizeof(struct sm_state);
258 unsigned int offset = 0;
260 while (offset < blob->offset) {
261 free_sm_state((struct sm_state *)(blob->data + offset));
262 offset += size;
266 /* At the end of every function we free all the sm_states */
267 void free_every_single_sm_state(void)
269 struct allocator_struct *desc = &sm_state_allocator;
270 struct allocation_blob *blob = desc->blobs;
272 desc->blobs = NULL;
273 desc->allocations = 0;
274 desc->total_bytes = 0;
275 desc->useful_bytes = 0;
276 desc->freelist = NULL;
277 while (blob) {
278 struct allocation_blob *next = blob->next;
279 free_all_sm_states(blob);
280 blob_free(blob, desc->chunking);
281 blob = next;
283 clear_sname_alloc();
284 clear_smatch_state_alloc();
286 sm_state_counter = 0;
289 struct sm_state *clone_sm(struct sm_state *s)
291 struct sm_state *ret;
293 ret = alloc_state_no_name(s->owner, s->name, s->sym, s->state);
294 ret->merged = s->merged;
295 ret->implied = s->implied;
296 ret->line = s->line;
297 /* clone_sm() doesn't copy the pools. Each state needs to have
298 only one pool. */
299 ret->possible = clone_slist(s->possible);
300 ret->left = s->left;
301 ret->right = s->right;
302 ret->nr_children = s->nr_children;
303 return ret;
306 int is_merged(struct sm_state *sm)
308 return sm->merged;
311 int is_implied(struct sm_state *sm)
313 return sm->implied;
316 int slist_has_state(struct state_list *slist, struct smatch_state *state)
318 struct sm_state *tmp;
320 FOR_EACH_PTR(slist, tmp) {
321 if (tmp->state == state)
322 return 1;
323 } END_FOR_EACH_PTR(tmp);
324 return 0;
327 struct state_list *clone_slist(struct state_list *from_slist)
329 struct sm_state *sm;
330 struct state_list *to_slist = NULL;
332 FOR_EACH_PTR(from_slist, sm) {
333 add_ptr_list(&to_slist, sm);
334 } END_FOR_EACH_PTR(sm);
335 return to_slist;
338 struct smatch_state *merge_states(int owner, const char *name,
339 struct symbol *sym,
340 struct smatch_state *state1,
341 struct smatch_state *state2)
343 struct smatch_state *ret;
345 if (state1 == state2)
346 ret = state1;
347 else if (__has_merge_function(owner))
348 ret = __client_merge_function(owner, state1, state2);
349 else if (!state1 || !state2)
350 ret = &undefined;
351 else
352 ret = &merged;
353 return ret;
356 struct sm_state *merge_sm_states(struct sm_state *one, struct sm_state *two)
358 struct smatch_state *s;
359 struct sm_state *result;
361 if (one == two)
362 return one;
363 s = merge_states(one->owner, one->name, one->sym, one->state, two->state);
364 result = alloc_state_no_name(one->owner, one->name, one->sym, s);
365 result->merged = 1;
366 result->left = one;
367 result->right = two;
368 result->nr_children = one->nr_children + two->nr_children;
369 copy_possibles(result, one);
370 copy_possibles(result, two);
372 if (option_debug ||
373 strcmp(check_name(one->owner), option_debug_check) == 0) {
374 struct sm_state *tmp;
375 int i = 0;
377 printf("%d merge [%s] '%s' %s(L %d) + %s(L %d) => %s (",
378 get_lineno(), check_name(one->owner), one->name,
379 show_state(one->state), one->line,
380 show_state(two->state), two->line,
381 show_state(s));
383 FOR_EACH_PTR(result->possible, tmp) {
384 if (i++)
385 printf(", ");
386 printf("%s", show_state(tmp->state));
387 } END_FOR_EACH_PTR(tmp);
388 printf(")\n");
391 return result;
394 struct sm_state *get_sm_state_stree(struct stree *stree, int owner, const char *name,
395 struct symbol *sym)
397 struct tracker tracker = {
398 .owner = owner,
399 .name = (char *)name,
400 .sym = sym,
403 if (!name)
404 return NULL;
407 return avl_lookup(stree, (struct sm_state *)&tracker);
410 struct smatch_state *get_state_stree(struct stree *stree,
411 int owner, const char *name,
412 struct symbol *sym)
414 struct sm_state *sm;
416 sm = get_sm_state_stree(stree, owner, name, sym);
417 if (sm)
418 return sm->state;
419 return NULL;
422 /* FIXME: this is almost exactly the same as set_sm_state_slist() */
423 void overwrite_sm_state_stree(struct stree **stree, struct sm_state *new)
425 avl_insert(stree, new);
428 void overwrite_sm_state_stree_stack(struct stree_stack **stack,
429 struct sm_state *sm)
431 struct stree *stree;
433 stree = pop_stree(stack);
434 overwrite_sm_state_stree(&stree, sm);
435 push_stree(stack, stree);
438 struct sm_state *set_state_stree(struct stree **stree, int owner, const char *name,
439 struct symbol *sym, struct smatch_state *state)
441 struct sm_state *new = alloc_sm_state(owner, name, sym, state);
443 avl_insert(stree, new);
444 return new;
447 void delete_state_stree(struct stree **stree, int owner, const char *name,
448 struct symbol *sym)
450 struct tracker tracker = {
451 .owner = owner,
452 .name = (char *)name,
453 .sym = sym,
456 avl_remove(stree, (struct sm_state *)&tracker);
459 void delete_state_stree_stack(struct stree_stack **stack, int owner, const char *name,
460 struct symbol *sym)
462 struct stree *stree;
464 stree = pop_stree(stack);
465 delete_state_stree(&stree, owner, name, sym);
466 push_stree(stack, stree);
469 void push_stree(struct stree_stack **stack, struct stree *stree)
471 add_ptr_list(stack, stree);
474 struct stree *pop_stree(struct stree_stack **stack)
476 struct stree *stree;
478 stree = last_ptr_list((struct ptr_list *)*stack);
479 delete_ptr_list_last((struct ptr_list **)stack);
480 return stree;
483 void free_slist(struct state_list **slist)
485 __free_ptr_list((struct ptr_list **)slist);
488 void free_stree_stack(struct stree_stack **stack)
490 __free_ptr_list((struct ptr_list **)stack);
493 void free_stack_and_strees(struct stree_stack **stree_stack)
495 struct stree *stree;
497 FOR_EACH_PTR(*stree_stack, stree) {
498 free_stree(&stree);
499 } END_FOR_EACH_PTR(stree);
500 free_stree_stack(stree_stack);
503 struct sm_state *set_state_stree_stack(struct stree_stack **stack, int owner, const char *name,
504 struct symbol *sym, struct smatch_state *state)
506 struct stree *stree;
507 struct sm_state *sm;
509 stree = pop_stree(stack);
510 sm = set_state_stree(&stree, owner, name, sym, state);
511 push_stree(stack, stree);
513 return sm;
517 * get_sm_state_stack() gets the state for the top slist on the stack.
519 struct sm_state *get_sm_state_stree_stack(struct stree_stack *stack,
520 int owner, const char *name,
521 struct symbol *sym)
523 struct stree *stree;
524 struct sm_state *ret;
526 stree = pop_stree(&stack);
527 ret = get_sm_state_stree(stree, owner, name, sym);
528 push_stree(&stack, stree);
529 return ret;
532 struct smatch_state *get_state_stree_stack(struct stree_stack *stack,
533 int owner, const char *name,
534 struct symbol *sym)
536 struct sm_state *sm;
538 sm = get_sm_state_stree_stack(stack, owner, name, sym);
539 if (sm)
540 return sm->state;
541 return NULL;
544 static void match_states_stree(struct stree **one, struct stree **two)
546 struct smatch_state *tmp_state;
547 struct sm_state *tmp_sm;
548 struct stree *add_to_one = NULL;
549 struct stree *add_to_two = NULL;
550 AvlIter one_iter;
551 AvlIter two_iter;
553 avl_iter_begin(&one_iter, *one, FORWARD);
554 avl_iter_begin(&two_iter, *two, FORWARD);
556 for (;;) {
557 if (!one_iter.sm && !two_iter.sm)
558 break;
559 if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
560 __set_fake_cur_stree_fast(*two);
561 tmp_state = __client_unmatched_state_function(one_iter.sm);
562 __pop_fake_cur_stree_fast();
563 tmp_sm = alloc_state_no_name(one_iter.sm->owner, one_iter.sm->name,
564 one_iter.sm->sym, tmp_state);
565 avl_insert(&add_to_two, tmp_sm);
566 avl_iter_next(&one_iter);
567 } else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
568 avl_iter_next(&one_iter);
569 avl_iter_next(&two_iter);
570 } else {
571 __set_fake_cur_stree_fast(*one);
572 tmp_state = __client_unmatched_state_function(two_iter.sm);
573 __pop_fake_cur_stree_fast();
574 tmp_sm = alloc_state_no_name(two_iter.sm->owner, two_iter.sm->name,
575 two_iter.sm->sym, tmp_state);
576 avl_insert(&add_to_one, tmp_sm);
577 avl_iter_next(&two_iter);
581 overwrite_stree(add_to_one, one);
582 overwrite_stree(add_to_two, two);
585 static void clone_pool_havers_stree(struct stree **stree)
587 struct sm_state *sm, *tmp;
588 struct state_list *slist = NULL;
590 FOR_EACH_SM(*stree, sm) {
591 if (sm->pool) {
592 tmp = clone_sm(sm);
593 add_ptr_list(&slist, tmp);
595 } END_FOR_EACH_SM(sm);
597 FOR_EACH_PTR(slist, sm) {
598 avl_insert(stree, sm);
599 } END_FOR_EACH_PTR(sm);
601 free_slist(&slist);
604 int __slist_id;
606 * Sets the first state to the slist_id.
608 static void set_stree_id(struct stree *stree)
610 struct smatch_state *state;
611 struct sm_state *new;
613 /* FIXME: This is horrible. Anyway, slist_id should be a part of the
614 stree pointer */
616 state = alloc_state_num(++__slist_id);
617 new = alloc_sm_state(-1, "unnull_path", NULL, state);
619 if (!avl_member(stree, new))
620 return;
621 avl_insert(&stree, new);
624 int get_stree_id(struct stree *stree)
626 struct sm_state *sm;
628 sm = get_sm_state_stree(stree, -1, "unnull_path", NULL);
629 if (sm)
630 return PTR_INT(sm->state->data);
631 return 0;
635 * merge_slist() is called whenever paths merge, such as after
636 * an if statement. It takes the two slists and creates one.
638 void merge_stree(struct stree **to, struct stree *stree)
640 struct stree *results = NULL;
641 struct stree *implied_one = NULL;
642 struct stree *implied_two = NULL;
643 AvlIter one_iter;
644 AvlIter two_iter;
645 struct sm_state *tmp_sm;
647 if (out_of_memory())
648 return;
650 /* merging a null and nonnull path gives you only the nonnull path */
651 if (!stree)
652 return;
654 if (!*to) {
655 *to = clone_stree(stree);
656 return;
659 implied_one = clone_stree(*to);
660 implied_two = clone_stree(stree);
662 match_states_stree(&implied_one, &implied_two);
664 clone_pool_havers_stree(&implied_one);
665 clone_pool_havers_stree(&implied_two);
667 set_stree_id(implied_one);
668 set_stree_id(implied_two);
670 avl_iter_begin(&one_iter, implied_one, FORWARD);
671 avl_iter_begin(&two_iter, implied_two, FORWARD);
673 for (;;) {
674 if (!one_iter.sm && !two_iter.sm)
675 break;
676 if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
677 sm_msg("error: Internal smatch error.");
678 avl_iter_next(&one_iter);
679 } else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
680 if (one_iter.sm != two_iter.sm) {
681 one_iter.sm->pool = implied_one;
682 two_iter.sm->pool = implied_two;
684 tmp_sm = merge_sm_states(one_iter.sm, two_iter.sm);
685 avl_insert(&results, tmp_sm);
686 avl_iter_next(&one_iter);
687 avl_iter_next(&two_iter);
688 } else {
689 sm_msg("error: Internal smatch error.");
690 avl_iter_next(&two_iter);
694 free_stree(to);
695 *to = results;
699 * filter_slist() removes any sm states "slist" holds in common with "filter"
701 void filter_stree(struct stree **stree, struct stree *filter)
703 struct stree *results = NULL;
704 AvlIter one_iter;
705 AvlIter two_iter;
707 avl_iter_begin(&one_iter, *stree, FORWARD);
708 avl_iter_begin(&two_iter, filter, FORWARD);
710 /* FIXME: This should probably be re-written with trees in mind */
712 for (;;) {
713 if (!one_iter.sm && !two_iter.sm)
714 break;
715 if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
716 avl_insert(&results, one_iter.sm);
717 avl_iter_next(&one_iter);
718 } else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
719 if (one_iter.sm != two_iter.sm)
720 avl_insert(&results, one_iter.sm);
721 avl_iter_next(&one_iter);
722 avl_iter_next(&two_iter);
723 } else {
724 avl_iter_next(&two_iter);
728 free_stree(stree);
729 *stree = results;
734 * and_slist_stack() pops the top two slists, overwriting the one with
735 * the other and pushing it back on the stack.
737 void and_stree_stack(struct stree_stack **stack)
739 struct sm_state *tmp;
740 struct stree *right_stree = pop_stree(stack);
742 FOR_EACH_SM(right_stree, tmp) {
743 overwrite_sm_state_stree_stack(stack, tmp);
744 } END_FOR_EACH_SM(tmp);
745 free_stree(&right_stree);
749 * or_slist_stack() is for if we have: if (foo || bar) { foo->baz;
750 * It pops the two slists from the top of the stack and merges them
751 * together in a way that preserves the things they have in common
752 * but creates a merged state for most of the rest.
753 * You could have code that had: if (foo || foo) { foo->baz;
754 * It's this function which ensures smatch does the right thing.
756 void or_stree_stack(struct stree_stack **pre_conds,
757 struct stree *cur_stree,
758 struct stree_stack **stack)
760 struct stree *new;
761 struct stree *old;
762 struct stree *pre_stree;
763 struct stree *res;
764 struct stree *tmp_stree;
766 new = pop_stree(stack);
767 old = pop_stree(stack);
769 pre_stree = pop_stree(pre_conds);
770 push_stree(pre_conds, clone_stree(pre_stree));
772 res = clone_stree(pre_stree);
773 overwrite_stree(old, &res);
775 tmp_stree = clone_stree(cur_stree);
776 overwrite_stree(new, &tmp_stree);
778 merge_stree(&res, tmp_stree);
779 filter_stree(&res, pre_stree);
781 push_stree(stack, res);
782 free_stree(&tmp_stree);
783 free_stree(&pre_stree);
784 free_stree(&new);
785 free_stree(&old);
789 * get_named_stree() is only used for gotos.
791 struct stree **get_named_stree(struct named_stree_stack *stack,
792 const char *name)
794 struct named_stree *tmp;
796 FOR_EACH_PTR(stack, tmp) {
797 if (!strcmp(tmp->name, name))
798 return &tmp->stree;
799 } END_FOR_EACH_PTR(tmp);
800 return NULL;
803 /* FIXME: These parameters are in a different order from expected */
804 void overwrite_stree(struct stree *from, struct stree **to)
806 struct sm_state *tmp;
808 FOR_EACH_SM(from, tmp) {
809 overwrite_sm_state_stree(to, tmp);
810 } END_FOR_EACH_SM(tmp);