db/fixup_kernel.sh: fix clear_user() handling
[smatch.git] / smatch_ssa.c
blob37365558dd31405caecc6d4f144c7d269395d460
1 /*
2 * Copyright (C) 2021 Oracle.
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
19 * The truth is I don't know very much about compilers. I've heard about
20 * Single Static Assignment and it seems like a useful feature but it's also
21 * possible I have misunderstood the whole thing.
23 * Anyway, the point is that say we have code like:
24 * ret = alloc();
25 * if (ret < 0)
26 * return ret;
27 * p->foo = ret;
28 * ret = something else;
30 * So the problem here is "p->foo" and "ret" are equivalent at first but then
31 * not at the end. We don't really care if "p->foo" is freed or "ret" is freed,
32 * we care if the value which was initially stored in "ret" is freed. This is
33 * different from equiv because "ret" and "p->foo" are not equiv at the end.
34 * The SSA module doesn't deal with == only with =.
36 * Using this is a bit different and more complicated than I would like. If
37 * there is a new state then call set_ssa_state(). When you're getting the
38 * state it's probably easiest to always use get_ssa_sm_state() because
39 * afterwards you will need to call update_ssa_state(my_id, sm->name, sm->sym,
40 * &state); to change the state. If you call set_ssa_state() that basically
41 * works too but it's slower because it adds a layer of indirection.
45 #include "smatch.h"
46 #include "smatch_extra.h"
47 #include "smatch_slist.h"
49 static int my_id;
51 static unsigned long ssa_id = 1;
53 char *ssa_name(const char *name)
55 char *ret;
56 char *p;
58 ret = alloc_sname(name);
59 p = strchr(ret, ':');
60 if (p)
61 *p = '\0';
62 return ret;
65 static struct smatch_state *alloc_ssa_new(const char *name)
67 struct smatch_state *state;
68 char buf[64];
70 state = __alloc_smatch_state(0);
71 snprintf(buf, sizeof(buf), "%s:%ld", name, ssa_id);
72 state->name = alloc_sname(buf);
74 ssa_id++;
76 return state;
79 static struct smatch_state *alloc_ssa_copy(struct sm_state *sm)
81 struct smatch_state *state;
83 if (sm->state == &undefined || sm->state == &merged)
84 return sm->state;
86 state = __alloc_smatch_state(0);
87 state->name = alloc_sname(sm->state->name);
88 return state;
91 static bool whatever_close_enough(struct expression *left, struct expression *right)
93 struct symbol *left_type, *right_type;
95 left_type = get_type(left);
96 right_type = get_type(right);
98 if (type_bits(right_type) == 64) {
99 if (type_bits(left_type) == 64)
100 return true;
101 return false;
104 if (type_bits(right_type) == 32) {
105 if (type_bits(left_type) == 64 || type_bits(left_type) == 32)
106 return true;
107 return false;
110 return false;
113 static void match_assign(struct expression *expr)
115 struct smatch_state *left_state;
116 struct sm_state *orig, *clone;
117 struct symbol *left_sym, *right_sym;
118 char *left_name = NULL, *right_name = NULL;
120 if (__in_fake_assign)
121 return;
123 if (expr->op != '=')
124 return;
126 /* The whatever function is more likely to return true #Faster */
127 if (!whatever_close_enough(expr->left, expr->right) &&
128 !values_fit_type(expr->left, expr->right))
129 return;
131 left_name = expr_to_var_sym(expr->left, &left_sym);
132 if (!left_name)
133 goto free;
136 * The ordering of this is really tricky. The issue here is that we
137 * have: "dev = of_node_get(node);". The first thing that happens is
138 * the modified hook sets "dev" to undefined. Then the check for
139 * tracking of_node_get/put() allocates an ssa state for "dev". So if
140 * it's set here we can just return. Otherwise track the SSA state.
142 left_state = get_state(my_id, left_name, left_sym);
143 if (left_state && left_state != &undefined)
144 goto free;
146 right_name = expr_to_var_sym(expr->right, &right_sym);
147 if (!right_name)
148 goto free;
150 orig = get_sm_state(my_id, right_name, right_sym);
151 if (!orig || orig->state == &undefined)
152 orig = set_state(my_id, right_name, right_sym, alloc_ssa_new(right_name));
154 /* This can happen in unreachable code or outside of functions I gess */
155 if (!orig)
156 return;
159 * Cloning is only really necessary for &merged states but it's better
160 * to only have one code path where possible.
163 clone = clone_sm(orig);
164 clone->state = alloc_ssa_copy(orig);
165 clone->name = alloc_sname(left_name);
166 clone->sym = left_sym;
167 __set_sm(clone);
169 free:
170 free_string(left_name);
171 free_string(right_name);
174 void set_ssa_state(int owner, const char *name, struct symbol *sym,
175 struct smatch_state *state)
177 struct sm_state *sm;
179 if (!name)
180 return;
182 sm = get_sm_state(my_id, name, sym);
183 if (!sm || sm->state == &undefined)
184 sm = set_state(my_id, name, sym, alloc_ssa_new(name));
185 if (!sm)
186 return;
187 if (sm->state == &merged) {
188 sm = clone_sm(sm);
189 sm->state = alloc_ssa_new(name);
190 add_possible_sm(sm, sm);
191 __set_sm(sm);
193 if (!sm)
194 return;
195 set_state(owner, sm->state->name, NULL, state);
198 void update_ssa_state(int owner, const char *name, struct symbol *sym,
199 struct smatch_state *state)
201 set_state(owner, name, NULL, state);
204 void update_ssa_sm(int owner, struct sm_state *ssa_sm, struct smatch_state *state)
206 struct sm_state *tmp;
208 FOR_EACH_PTR(ssa_sm->possible, tmp) {
209 if (tmp->state == &merged ||
210 tmp->state == &undefined)
211 continue;
212 set_state(owner, tmp->state->name, NULL, state);
213 } END_FOR_EACH_PTR(tmp);
216 void set_ssa_state_expr(int owner, struct expression *expr,
217 struct smatch_state *state)
219 struct symbol *sym;
220 char *name;
222 name = expr_to_var_sym(expr, &sym);
223 if (!name)
224 return;
225 set_ssa_state(owner, name, sym, state);
226 free_string(name);
229 struct sm_state *get_ssa_sm_state(int owner, const char *name, struct symbol *sym)
231 struct sm_state *sm, *tmp, *owner_sm;
232 struct sm_state *ret = NULL;
234 sm = get_sm_state(my_id, name, sym);
235 if (!sm || sm->state == &undefined)
236 return NULL;
238 FOR_EACH_PTR(sm->possible, tmp) {
239 if (tmp->state == &merged ||
240 tmp->state == &undefined)
241 continue;
242 owner_sm = get_sm_state(owner, tmp->state->name, NULL);
243 if (owner_sm) {
244 if (!ret)
245 ret = clone_sm(owner_sm);
246 else
247 ret = merge_sm_states(ret, owner_sm);
249 } END_FOR_EACH_PTR(tmp);
251 if (!ret)
252 return NULL;
254 tmp = ret;
255 ret = clone_sm(sm);
256 ret->state = tmp->state;
258 return ret;
261 struct sm_state *get_ssa_sm_state_expr(int owner, struct expression *expr)
263 struct sm_state *ret;
264 struct symbol *sym;
265 char *name;
267 name = expr_to_var_sym(expr, &sym);
268 if (!name)
269 return NULL;
270 ret = get_ssa_sm_state(owner, name, sym);
271 free_string(name);
272 return ret;
275 struct smatch_state *get_ssa_state(int owner, const char *name, struct symbol *sym)
277 struct sm_state *sm;
279 sm = get_ssa_sm_state(my_id, name, sym);
280 if (!sm)
281 return NULL;
282 return sm->state;
285 struct smatch_state *get_ssa_state_expr(int owner, struct expression *expr)
287 struct sm_state *sm;
289 sm = get_ssa_sm_state_expr(my_id, expr);
290 if (!sm)
291 return NULL;
292 return sm->state;
295 void register_ssa(int id)
297 my_id = id;
299 set_dynamic_states(my_id);
300 add_hook(&match_assign, ASSIGNMENT_HOOK_AFTER);
301 add_modification_hook(my_id, &set_undefined);