11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2016 Oracle.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
186523a3aaSJohn Levon #include <ctype.h>
196523a3aaSJohn Levon 
201f5207b7SJohn Levon #include "smatch.h"
211f5207b7SJohn Levon #include "smatch_extra.h"
221f5207b7SJohn Levon #include "smatch_slist.h"
231f5207b7SJohn Levon 
241f5207b7SJohn Levon static int my_id;
251f5207b7SJohn Levon 
261f5207b7SJohn Levon STATE(inc);
27*b3263c98SJohn Levon STATE(start_state);
281f5207b7SJohn Levon STATE(dec);
291f5207b7SJohn Levon 
unmatched_state(struct sm_state * sm)306523a3aaSJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm)
316523a3aaSJohn Levon {
32*b3263c98SJohn Levon 	/*
33*b3263c98SJohn Levon 	 * We default to decremented.  For example, say we have:
34*b3263c98SJohn Levon 	 * 	if (p)
35*b3263c98SJohn Levon 	 *		atomic_dec(p);
36*b3263c98SJohn Levon 	 *      <- p is decreemented.
37*b3263c98SJohn Levon 	 *
38*b3263c98SJohn Levon 	 */
39*b3263c98SJohn Levon 	if ((sm->state == &dec) &&
40*b3263c98SJohn Levon 	    parent_is_gone_var_sym(sm->name, sm->sym))
416523a3aaSJohn Levon 		return sm->state;
42*b3263c98SJohn Levon 	return &start_state;
436523a3aaSJohn Levon }
446523a3aaSJohn Levon 
456523a3aaSJohn Levon static struct stree *start_states;
466523a3aaSJohn Levon static struct stree_stack *saved_stack;
set_start_state(const char * name,struct symbol * sym,struct smatch_state * start)476523a3aaSJohn Levon static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
486523a3aaSJohn Levon {
496523a3aaSJohn Levon 	struct smatch_state *orig;
506523a3aaSJohn Levon 
516523a3aaSJohn Levon 	orig = get_state_stree(start_states, my_id, name, sym);
526523a3aaSJohn Levon 	if (!orig)
536523a3aaSJohn Levon 		set_state_stree(&start_states, my_id, name, sym, start);
546523a3aaSJohn Levon 	else if (orig != start)
556523a3aaSJohn Levon 		set_state_stree(&start_states, my_id, name, sym, &undefined);
566523a3aaSJohn Levon }
576523a3aaSJohn Levon 
get_best_match(const char * key)586523a3aaSJohn Levon static struct sm_state *get_best_match(const char *key)
596523a3aaSJohn Levon {
606523a3aaSJohn Levon 	struct sm_state *sm;
616523a3aaSJohn Levon 	struct sm_state *match;
626523a3aaSJohn Levon 	int cnt = 0;
636523a3aaSJohn Levon 	int start_pos, state_len, key_len, chunks, i;
646523a3aaSJohn Levon 
656523a3aaSJohn Levon 	if (strncmp(key, "$->", 3) == 0)
666523a3aaSJohn Levon 		key += 3;
676523a3aaSJohn Levon 
686523a3aaSJohn Levon 	key_len = strlen(key);
696523a3aaSJohn Levon 	chunks = 0;
706523a3aaSJohn Levon 	for (i = key_len - 1; i > 0; i--) {
716523a3aaSJohn Levon 		if (key[i] == '>' || key[i] == '.')
726523a3aaSJohn Levon 			chunks++;
736523a3aaSJohn Levon 		if (chunks == 2) {
746523a3aaSJohn Levon 			key += (i + 1);
756523a3aaSJohn Levon 			key_len = strlen(key);
766523a3aaSJohn Levon 			break;
776523a3aaSJohn Levon 		}
786523a3aaSJohn Levon 	}
796523a3aaSJohn Levon 
806523a3aaSJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
816523a3aaSJohn Levon 		state_len = strlen(sm->name);
826523a3aaSJohn Levon 		if (state_len < key_len)
836523a3aaSJohn Levon 			continue;
846523a3aaSJohn Levon 		start_pos = state_len - key_len;
856523a3aaSJohn Levon 		if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
866523a3aaSJohn Levon 		    strcmp(sm->name + start_pos, key) == 0) {
876523a3aaSJohn Levon 			cnt++;
886523a3aaSJohn Levon 			match = sm;
896523a3aaSJohn Levon 		}
906523a3aaSJohn Levon 	} END_FOR_EACH_SM(sm);
916523a3aaSJohn Levon 
926523a3aaSJohn Levon 	if (cnt == 1)
936523a3aaSJohn Levon 		return match;
946523a3aaSJohn Levon 	return NULL;
956523a3aaSJohn Levon }
966523a3aaSJohn Levon 
db_inc_dec(struct expression * expr,int param,const char * key,int inc_dec)97*b3263c98SJohn Levon static void db_inc_dec(struct expression *expr, int param, const char *key, int inc_dec)
981f5207b7SJohn Levon {
996523a3aaSJohn Levon 	struct sm_state *start_sm;
1001f5207b7SJohn Levon 	struct expression *arg;
1011f5207b7SJohn Levon 	char *name;
1021f5207b7SJohn Levon 	struct symbol *sym;
1036523a3aaSJohn Levon 	bool free_at_end = true;
1041f5207b7SJohn Levon 
1051f5207b7SJohn Levon 	while (expr->type == EXPR_ASSIGNMENT)
1061f5207b7SJohn Levon 		expr = strip_expr(expr->right);
1071f5207b7SJohn Levon 	if (expr->type != EXPR_CALL)
1081f5207b7SJohn Levon 		return;
1091f5207b7SJohn Levon 
1101f5207b7SJohn Levon 	arg = get_argument_from_call_expr(expr->args, param);
1111f5207b7SJohn Levon 	if (!arg)
1121f5207b7SJohn Levon 		return;
1131f5207b7SJohn Levon 
1141f5207b7SJohn Levon 	name = get_variable_from_key(arg, key, &sym);
1151f5207b7SJohn Levon 	if (!name || !sym)
1161f5207b7SJohn Levon 		goto free;
1171f5207b7SJohn Levon 
1186523a3aaSJohn Levon 	start_sm = get_sm_state(my_id, name, sym);
1196523a3aaSJohn Levon 	if (!start_sm && inc_dec == ATOMIC_DEC) {
1206523a3aaSJohn Levon 		start_sm = get_best_match(key);
1216523a3aaSJohn Levon 		if (start_sm) {
1226523a3aaSJohn Levon 			free_string(name);
1236523a3aaSJohn Levon 			free_at_end = false;
1246523a3aaSJohn Levon 			name = (char *)start_sm->name;
1256523a3aaSJohn Levon 			sym = start_sm->sym;
1266523a3aaSJohn Levon 		}
1276523a3aaSJohn Levon 	}
1281f5207b7SJohn Levon 
1291f5207b7SJohn Levon 	if (inc_dec == ATOMIC_INC) {
1306523a3aaSJohn Levon 		if (!start_sm)
1316523a3aaSJohn Levon 			set_start_state(name, sym, &dec);
1326523a3aaSJohn Levon //		set_refcount_inc(name, sym);
1331f5207b7SJohn Levon 		set_state(my_id, name, sym, &inc);
1341f5207b7SJohn Levon 	} else {
1356523a3aaSJohn Levon //		set_refcount_dec(name, sym);
1366523a3aaSJohn Levon 		if (!start_sm)
1376523a3aaSJohn Levon 			set_start_state(name, sym, &inc);
1386523a3aaSJohn Levon 
1396523a3aaSJohn Levon 		if (start_sm && start_sm->state == &inc)
140*b3263c98SJohn Levon 			set_state(my_id, name, sym, &start_state);
1411f5207b7SJohn Levon 		else
1421f5207b7SJohn Levon 			set_state(my_id, name, sym, &dec);
1431f5207b7SJohn Levon 	}
1441f5207b7SJohn Levon 
1451f5207b7SJohn Levon free:
1466523a3aaSJohn Levon 	if (free_at_end)
1476523a3aaSJohn Levon 		free_string(name);
1481f5207b7SJohn Levon }
1491f5207b7SJohn Levon 
150*b3263c98SJohn Levon static const char *primitive_funcs[] = {
151*b3263c98SJohn Levon 	"atomic_inc_return",
152*b3263c98SJohn Levon 	"atomic_add_return",
153*b3263c98SJohn Levon 	"atomic_sub_return",
154*b3263c98SJohn Levon 	"atomic_sub_and_test",
155*b3263c98SJohn Levon 	"atomic_dec_and_test",
156*b3263c98SJohn Levon 	"_atomic_dec_and_lock",
157*b3263c98SJohn Levon 	"atomic_dec",
158*b3263c98SJohn Levon 	"atomic_long_inc",
159*b3263c98SJohn Levon 	"atomic_long_dec",
160*b3263c98SJohn Levon 	"atomic_inc",
161*b3263c98SJohn Levon 	"atomic_sub",
162*b3263c98SJohn Levon 	"refcount_inc",
163*b3263c98SJohn Levon 	"refcount_dec",
164*b3263c98SJohn Levon 	"refcount_add",
165*b3263c98SJohn Levon 	"refcount_add_not_zero",
166*b3263c98SJohn Levon 	"refcount_inc_not_zero",
167*b3263c98SJohn Levon 	"refcount_sub_and_test",
168*b3263c98SJohn Levon 	"refcount_dec_and_test",
169*b3263c98SJohn Levon 	"atomic_dec_if_positive",
170*b3263c98SJohn Levon };
171*b3263c98SJohn Levon 
is_inc_dec_primitive(struct expression * expr)172*b3263c98SJohn Levon static bool is_inc_dec_primitive(struct expression *expr)
173*b3263c98SJohn Levon {
174*b3263c98SJohn Levon 	int i;
175*b3263c98SJohn Levon 
176*b3263c98SJohn Levon 	while (expr->type == EXPR_ASSIGNMENT)
177*b3263c98SJohn Levon 		expr = strip_expr(expr->right);
178*b3263c98SJohn Levon 	if (expr->type != EXPR_CALL)
179*b3263c98SJohn Levon 		return false;
180*b3263c98SJohn Levon 
181*b3263c98SJohn Levon 	if (expr->fn->type != EXPR_SYMBOL)
182*b3263c98SJohn Levon 		return false;
183*b3263c98SJohn Levon 
184*b3263c98SJohn Levon 	for (i = 0; i < ARRAY_SIZE(primitive_funcs); i++) {
185*b3263c98SJohn Levon 		if (sym_name_is(primitive_funcs[i], expr->fn))
186*b3263c98SJohn Levon 			return true;
187*b3263c98SJohn Levon 	}
188*b3263c98SJohn Levon 
189*b3263c98SJohn Levon 	return false;
190*b3263c98SJohn Levon }
191*b3263c98SJohn Levon 
db_inc(struct expression * expr,int param,char * key,char * value)1921f5207b7SJohn Levon static void db_inc(struct expression *expr, int param, char *key, char *value)
1931f5207b7SJohn Levon {
194*b3263c98SJohn Levon 	if (is_inc_dec_primitive(expr))
195*b3263c98SJohn Levon 		return;
196*b3263c98SJohn Levon 	db_inc_dec(expr, param, key, ATOMIC_INC);
1971f5207b7SJohn Levon }
1981f5207b7SJohn Levon 
db_dec(struct expression * expr,int param,char * key,char * value)1991f5207b7SJohn Levon static void db_dec(struct expression *expr, int param, char *key, char *value)
2001f5207b7SJohn Levon {
201*b3263c98SJohn Levon 	if (is_inc_dec_primitive(expr))
202*b3263c98SJohn Levon 		return;
203*b3263c98SJohn Levon 	db_inc_dec(expr, param, key, ATOMIC_DEC);
2041f5207b7SJohn Levon }
2051f5207b7SJohn Levon 
match_atomic_inc(const char * fn,struct expression * expr,void * _unused)2061f5207b7SJohn Levon static void match_atomic_inc(const char *fn, struct expression *expr, void *_unused)
2071f5207b7SJohn Levon {
208*b3263c98SJohn Levon 	db_inc_dec(expr, 0, "$->counter", ATOMIC_INC);
2091f5207b7SJohn Levon }
2101f5207b7SJohn Levon 
match_atomic_dec(const char * fn,struct expression * expr,void * _unused)2111f5207b7SJohn Levon static void match_atomic_dec(const char *fn, struct expression *expr, void *_unused)
2121f5207b7SJohn Levon {
213*b3263c98SJohn Levon 	db_inc_dec(expr, 0, "$->counter", ATOMIC_DEC);
2141f5207b7SJohn Levon }
2151f5207b7SJohn Levon 
match_atomic_add(const char * fn,struct expression * expr,void * _unused)2161f5207b7SJohn Levon static void match_atomic_add(const char *fn, struct expression *expr, void *_unused)
2171f5207b7SJohn Levon {
2181f5207b7SJohn Levon 	struct expression *amount;
2191f5207b7SJohn Levon 	sval_t sval;
2201f5207b7SJohn Levon 
2211f5207b7SJohn Levon 	amount = get_argument_from_call_expr(expr->args, 0);
2221f5207b7SJohn Levon 	if (get_implied_value(amount, &sval) && sval_is_negative(sval)) {
223*b3263c98SJohn Levon 		db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC);
2241f5207b7SJohn Levon 		return;
2251f5207b7SJohn Levon 	}
2261f5207b7SJohn Levon 
227*b3263c98SJohn Levon 	db_inc_dec(expr, 1, "$->counter", ATOMIC_INC);
2281f5207b7SJohn Levon }
2291f5207b7SJohn Levon 
match_atomic_sub(const char * fn,struct expression * expr,void * _unused)2301f5207b7SJohn Levon static void match_atomic_sub(const char *fn, struct expression *expr, void *_unused)
2311f5207b7SJohn Levon {
232*b3263c98SJohn Levon 	db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC);
2331f5207b7SJohn Levon }
2341f5207b7SJohn Levon 
refcount_inc(const char * fn,struct expression * expr,void * param)2351f5207b7SJohn Levon static void refcount_inc(const char *fn, struct expression *expr, void *param)
2361f5207b7SJohn Levon {
237*b3263c98SJohn Levon 	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC);
2381f5207b7SJohn Levon }
2391f5207b7SJohn Levon 
refcount_dec(const char * fn,struct expression * expr,void * param)2401f5207b7SJohn Levon static void refcount_dec(const char *fn, struct expression *expr, void *param)
2411f5207b7SJohn Levon {
242*b3263c98SJohn Levon 	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_DEC);
243*b3263c98SJohn Levon }
244*b3263c98SJohn Levon 
pm_runtime_get_sync(const char * fn,struct expression * expr,void * param)245*b3263c98SJohn Levon static void pm_runtime_get_sync(const char *fn, struct expression *expr, void *param)
246*b3263c98SJohn Levon {
247*b3263c98SJohn Levon 	db_inc_dec(expr, PTR_INT(param), "$->power.usage_count.counter", ATOMIC_INC);
248*b3263c98SJohn Levon }
249*b3263c98SJohn Levon 
match_implies_inc(const char * fn,struct expression * call_expr,struct expression * assign_expr,void * param)250*b3263c98SJohn Levon static void match_implies_inc(const char *fn, struct expression *call_expr,
251*b3263c98SJohn Levon 			      struct expression *assign_expr, void *param)
252*b3263c98SJohn Levon {
253*b3263c98SJohn Levon 	db_inc_dec(call_expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC);
254*b3263c98SJohn Levon }
255*b3263c98SJohn Levon 
match_implies_atomic_dec(const char * fn,struct expression * call_expr,struct expression * assign_expr,void * param)256*b3263c98SJohn Levon static void match_implies_atomic_dec(const char *fn, struct expression *call_expr,
257*b3263c98SJohn Levon 			      struct expression *assign_expr, void *param)
258*b3263c98SJohn Levon {
259*b3263c98SJohn Levon 	db_inc_dec(call_expr, PTR_INT(param), "$->counter", ATOMIC_DEC);
260*b3263c98SJohn Levon }
261*b3263c98SJohn Levon 
is_maybe_dec(struct sm_state * sm)262*b3263c98SJohn Levon static bool is_maybe_dec(struct sm_state *sm)
263*b3263c98SJohn Levon {
264*b3263c98SJohn Levon 	if (sm->state == &dec)
265*b3263c98SJohn Levon 		return true;
266*b3263c98SJohn Levon 	if (slist_has_state(sm->possible, &dec) &&
267*b3263c98SJohn Levon 	    !slist_has_state(sm->possible, &inc))
268*b3263c98SJohn Levon 		return true;
269*b3263c98SJohn Levon 	return false;
2701f5207b7SJohn Levon }
2711f5207b7SJohn Levon 
match_return_info(int return_id,char * return_ranges,struct expression * expr)2721f5207b7SJohn Levon static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
2731f5207b7SJohn Levon {
2741f5207b7SJohn Levon 	struct sm_state *sm;
2751f5207b7SJohn Levon 	const char *param_name;
2761f5207b7SJohn Levon 	int param;
2771f5207b7SJohn Levon 
278*b3263c98SJohn Levon 	if (is_impossible_path())
279*b3263c98SJohn Levon 		return;
280*b3263c98SJohn Levon 
2811f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
282*b3263c98SJohn Levon 		if (sm->state != &inc && !is_maybe_dec(sm))
283*b3263c98SJohn Levon 			continue;
284*b3263c98SJohn Levon 		if (sm->state == get_state_stree(start_states, my_id, sm->name, sm->sym))
2851f5207b7SJohn Levon 			continue;
2866523a3aaSJohn Levon 		if (parent_is_gone_var_sym(sm->name, sm->sym))
2876523a3aaSJohn Levon 			continue;
2881f5207b7SJohn Levon 		param = get_param_num_from_sym(sm->sym);
2891f5207b7SJohn Levon 		if (param < 0)
2901f5207b7SJohn Levon 			continue;
2911f5207b7SJohn Levon 		param_name = get_param_name(sm);
2921f5207b7SJohn Levon 		if (!param_name)
2931f5207b7SJohn Levon 			continue;
2941f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges,
2951f5207b7SJohn Levon 					 (sm->state == &inc) ? ATOMIC_INC : ATOMIC_DEC,
2961f5207b7SJohn Levon 					 param, param_name, "");
2971f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
2981f5207b7SJohn Levon }
2991f5207b7SJohn Levon 
3001f5207b7SJohn Levon enum {
3016523a3aaSJohn Levon 	EMPTY, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS
3021f5207b7SJohn Levon };
3031f5207b7SJohn Levon 
success_fail_positive(struct range_list * rl)3041f5207b7SJohn Levon static int success_fail_positive(struct range_list *rl)
3051f5207b7SJohn Levon {
3061f5207b7SJohn Levon 	if (!rl)
3076523a3aaSJohn Levon 		return EMPTY;
3081f5207b7SJohn Levon 
309*b3263c98SJohn Levon 	if (!is_whole_rl(rl) && sval_is_negative(rl_min(rl)))
3101f5207b7SJohn Levon 		return NEGATIVE;
3111f5207b7SJohn Levon 
3121f5207b7SJohn Levon 	if (rl_min(rl).value == 0)
3131f5207b7SJohn Levon 		return ZERO;
3141f5207b7SJohn Levon 
3151f5207b7SJohn Levon 	return POSITIVE;
3161f5207b7SJohn Levon }
3171f5207b7SJohn Levon 
check_counter(const char * name,struct symbol * sym)3181f5207b7SJohn Levon static void check_counter(const char *name, struct symbol *sym)
3191f5207b7SJohn Levon {
3201f5207b7SJohn Levon 	struct range_list *inc_lines = NULL;
3211f5207b7SJohn Levon 	struct range_list *dec_lines = NULL;
3226523a3aaSJohn Levon 	int inc_buckets[NUM_BUCKETS] = {};
3236523a3aaSJohn Levon 	int dec_buckets[NUM_BUCKETS] = {};
3246523a3aaSJohn Levon 	struct stree *stree, *orig_stree;
325*b3263c98SJohn Levon 	struct smatch_state *state;
3261f5207b7SJohn Levon 	struct sm_state *return_sm;
3271f5207b7SJohn Levon 	struct sm_state *sm;
3281f5207b7SJohn Levon 	sval_t line = sval_type_val(&int_ctype, 0);
3296523a3aaSJohn Levon 	int bucket;
3301f5207b7SJohn Levon 
331*b3263c98SJohn Levon 	/* static variable are probably just counters */
332*b3263c98SJohn Levon 	if (sym->ctype.modifiers & MOD_STATIC &&
333*b3263c98SJohn Levon 	    !(sym->ctype.modifiers & MOD_TOPLEVEL))
334*b3263c98SJohn Levon 		return;
335*b3263c98SJohn Levon 
3361f5207b7SJohn Levon 	FOR_EACH_PTR(get_all_return_strees(), stree) {
3376523a3aaSJohn Levon 		orig_stree = __swap_cur_stree(stree);
3386523a3aaSJohn Levon 
339*b3263c98SJohn Levon 		if (is_impossible_path())
340*b3263c98SJohn Levon 			goto swap_stree;
341*b3263c98SJohn Levon 
3426523a3aaSJohn Levon 		return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
3431f5207b7SJohn Levon 		if (!return_sm)
3446523a3aaSJohn Levon 			goto swap_stree;
3451f5207b7SJohn Levon 		line.value = return_sm->line;
3461f5207b7SJohn Levon 
3476523a3aaSJohn Levon 		if (get_state_stree(start_states, my_id, name, sym) == &inc)
3486523a3aaSJohn Levon 			goto swap_stree;
3496523a3aaSJohn Levon 
3506523a3aaSJohn Levon 		if (parent_is_gone_var_sym(name, sym))
3516523a3aaSJohn Levon 			goto swap_stree;
3526523a3aaSJohn Levon 
3536523a3aaSJohn Levon 		sm = get_sm_state(my_id, name, sym);
354*b3263c98SJohn Levon 		if (sm)
355*b3263c98SJohn Levon 			state = sm->state;
356*b3263c98SJohn Levon 		else
357*b3263c98SJohn Levon 			state = &start_state;
3581f5207b7SJohn Levon 
359*b3263c98SJohn Levon 		if (state != &inc &&
360*b3263c98SJohn Levon 		    state != &dec &&
361*b3263c98SJohn Levon 		    state != &start_state)
3626523a3aaSJohn Levon 			goto swap_stree;
3636523a3aaSJohn Levon 
3646523a3aaSJohn Levon 		bucket = success_fail_positive(estate_rl(return_sm->state));
3651f5207b7SJohn Levon 
366*b3263c98SJohn Levon 		if (state == &inc) {
3671f5207b7SJohn Levon 			add_range(&inc_lines, line, line);
3686523a3aaSJohn Levon 			inc_buckets[bucket] = true;
3691f5207b7SJohn Levon 		}
370*b3263c98SJohn Levon 		if (state == &dec || state == &start_state) {
3711f5207b7SJohn Levon 			add_range(&dec_lines, line, line);
3726523a3aaSJohn Levon 			dec_buckets[bucket] = true;
3736523a3aaSJohn Levon 		}
3746523a3aaSJohn Levon swap_stree:
3756523a3aaSJohn Levon 		__swap_cur_stree(orig_stree);
3761f5207b7SJohn Levon 	} END_FOR_EACH_PTR(stree);
3771f5207b7SJohn Levon 
3781f5207b7SJohn Levon 	if (inc_buckets[NEGATIVE] &&
3791f5207b7SJohn Levon 	    inc_buckets[ZERO]) {
3801f5207b7SJohn Levon 		// sm_warning("XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines));
3811f5207b7SJohn Levon 	}
3821f5207b7SJohn Levon 
3831f5207b7SJohn Levon }
3841f5207b7SJohn Levon 
match_check_missed(struct symbol * sym)3851f5207b7SJohn Levon static void match_check_missed(struct symbol *sym)
3861f5207b7SJohn Levon {
3871f5207b7SJohn Levon 	struct sm_state *sm;
3881f5207b7SJohn Levon 
3891f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) {
3901f5207b7SJohn Levon 		check_counter(sm->name, sm->sym);
3911f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
3921f5207b7SJohn Levon }
3931f5207b7SJohn Levon 
on_atomic_dec_path(void)3941f5207b7SJohn Levon int on_atomic_dec_path(void)
3951f5207b7SJohn Levon {
3961f5207b7SJohn Levon 	struct sm_state *sm;
3971f5207b7SJohn Levon 
3981f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
3991f5207b7SJohn Levon 		if (sm->state == &dec)
4001f5207b7SJohn Levon 			return 1;
4011f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
4021f5207b7SJohn Levon 
4031f5207b7SJohn Levon 	return 0;
4041f5207b7SJohn Levon }
4051f5207b7SJohn Levon 
was_inced(const char * name,struct symbol * sym)4061f5207b7SJohn Levon int was_inced(const char *name, struct symbol *sym)
4071f5207b7SJohn Levon {
4081f5207b7SJohn Levon 	return get_state(my_id, name, sym) == &inc;
4091f5207b7SJohn Levon }
4101f5207b7SJohn Levon 
match_save_states(struct expression * expr)4116523a3aaSJohn Levon static void match_save_states(struct expression *expr)
4126523a3aaSJohn Levon {
4136523a3aaSJohn Levon 	push_stree(&saved_stack, start_states);
4146523a3aaSJohn Levon 	start_states = NULL;
4156523a3aaSJohn Levon }
4166523a3aaSJohn Levon 
match_restore_states(struct expression * expr)4176523a3aaSJohn Levon static void match_restore_states(struct expression *expr)
4186523a3aaSJohn Levon {
4196523a3aaSJohn Levon 	start_states = pop_stree(&saved_stack);
4206523a3aaSJohn Levon }
4216523a3aaSJohn Levon 
match_after_func(struct symbol * sym)4226523a3aaSJohn Levon static void match_after_func(struct symbol *sym)
4236523a3aaSJohn Levon {
4246523a3aaSJohn Levon 	free_stree(&start_states);
4256523a3aaSJohn Levon }
4266523a3aaSJohn Levon 
check_atomic_inc_dec(int id)4271f5207b7SJohn Levon void check_atomic_inc_dec(int id)
4281f5207b7SJohn Levon {
4291f5207b7SJohn Levon 	my_id = id;
4301f5207b7SJohn Levon 
4311f5207b7SJohn Levon 	if (option_project != PROJ_KERNEL)
4321f5207b7SJohn Levon 		return;
4331f5207b7SJohn Levon 
4346523a3aaSJohn Levon 	add_unmatched_state_hook(my_id, &unmatched_state);
4356523a3aaSJohn Levon 
4366523a3aaSJohn Levon 	add_split_return_callback(match_return_info);
4371f5207b7SJohn Levon 	select_return_states_hook(ATOMIC_INC, &db_inc);
4381f5207b7SJohn Levon 	select_return_states_hook(ATOMIC_DEC, &db_dec);
4396523a3aaSJohn Levon 
4401f5207b7SJohn Levon 	add_function_hook("atomic_inc_return", &match_atomic_inc, NULL);
4411f5207b7SJohn Levon 	add_function_hook("atomic_add_return", &match_atomic_add, NULL);
4421f5207b7SJohn Levon 	add_function_hook("atomic_sub_return", &match_atomic_sub, NULL);
4431f5207b7SJohn Levon 	add_function_hook("atomic_sub_and_test", &match_atomic_sub, NULL);
444*b3263c98SJohn Levon 	add_function_hook("atomic_long_sub_and_test", &match_atomic_sub, NULL);
445*b3263c98SJohn Levon 	add_function_hook("atomic64_sub_and_test", &match_atomic_sub, NULL);
4461f5207b7SJohn Levon 	add_function_hook("atomic_dec_and_test", &match_atomic_dec, NULL);
447*b3263c98SJohn Levon 	add_function_hook("atomic_long_dec_and_test", &match_atomic_dec, NULL);
448*b3263c98SJohn Levon 	add_function_hook("atomic64_dec_and_test", &match_atomic_dec, NULL);
4491f5207b7SJohn Levon 	add_function_hook("_atomic_dec_and_lock", &match_atomic_dec, NULL);
4501f5207b7SJohn Levon 	add_function_hook("atomic_dec", &match_atomic_dec, NULL);
451*b3263c98SJohn Levon 	add_function_hook("atomic_dec_return", &match_atomic_dec, NULL);
4521f5207b7SJohn Levon 	add_function_hook("atomic_long_inc", &match_atomic_inc, NULL);
4531f5207b7SJohn Levon 	add_function_hook("atomic_long_dec", &match_atomic_dec, NULL);
4541f5207b7SJohn Levon 	add_function_hook("atomic_inc", &match_atomic_inc, NULL);
4551f5207b7SJohn Levon 	add_function_hook("atomic_sub", &match_atomic_sub, NULL);
4561f5207b7SJohn Levon 
4576523a3aaSJohn Levon 	add_function_hook("refcount_inc", &refcount_inc, INT_PTR(0));
4586523a3aaSJohn Levon 	add_function_hook("refcount_dec", &refcount_dec, INT_PTR(0));
4596523a3aaSJohn Levon 	add_function_hook("refcount_add", &refcount_inc, INT_PTR(1));
460*b3263c98SJohn Levon 
461*b3263c98SJohn Levon 	return_implies_state("refcount_add_not_zero", 1, 1, &match_implies_inc, INT_PTR(1));
462*b3263c98SJohn Levon 	return_implies_state("refcount_inc_not_zero", 1, 1, &match_implies_inc, INT_PTR(0));
463*b3263c98SJohn Levon 
464*b3263c98SJohn Levon 	return_implies_state("atomic_dec_if_positive", 0, INT_MAX, &match_implies_atomic_dec, INT_PTR(0));
465*b3263c98SJohn Levon 
4661f5207b7SJohn Levon 	add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1));
4676523a3aaSJohn Levon 	add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(0));
4681f5207b7SJohn Levon 
469*b3263c98SJohn Levon 	add_function_hook("pm_runtime_get_sync", &pm_runtime_get_sync, INT_PTR(0));
470*b3263c98SJohn Levon 
4711f5207b7SJohn Levon 	add_hook(&match_check_missed, END_FUNC_HOOK);
4726523a3aaSJohn Levon 
4736523a3aaSJohn Levon 	add_hook(&match_after_func, AFTER_FUNC_HOOK);
4746523a3aaSJohn Levon 	add_hook(&match_save_states, INLINE_FN_START);
4756523a3aaSJohn Levon 	add_hook(&match_restore_states, INLINE_FN_END);
4761f5207b7SJohn Levon }
477