--- old/usr/src/tools/smatch/src/smatch_implied.c	Mon Aug  5 08:38:35 2019
+++ new/usr/src/tools/smatch/src/smatch_implied.c	Mon Aug  5 08:38:35 2019
@@ -65,10 +65,12 @@
 #include "smatch_extra.h"
 
 char *implied_debug_msg;
-#define DIMPLIED(msg...) do { if (option_debug_implied || option_debug) printf(msg); } while (0)
 
-int option_debug_implied = 0;
+bool implications_off;
 
+#define implied_debug 0
+#define DIMPLIED(msg...) do { if (implied_debug) printf(msg); } while (0)
+
 /*
  * tmp_range_list():
  * It messes things up to free range list allocations.  This helper fuction
@@ -87,7 +89,7 @@
 
 static void print_debug_tf(struct sm_state *sm, int istrue, int isfalse)
 {
-	if (!option_debug_implied && !option_debug)
+	if (!implied_debug && !option_debug)
 		return;
 
 	if (istrue && isfalse) {
@@ -143,8 +145,8 @@
 		return 0;
 	}
 
-	if (option_debug)
-		sm_info("fake_history: %s vs %s.  %s %s %s. --> T: %s F: %s",
+	if (implied_debug)
+		sm_msg("fake_history: %s vs %s.  %s %s %s. --> T: %s F: %s",
 		       sm->name, show_rl(rl), sm->state->name, show_special(comparison), show_rl(rl),
 		       show_rl(true_rl), show_rl(false_rl));
 
@@ -151,10 +153,10 @@
 	true_sm = clone_sm(sm);
 	false_sm = clone_sm(sm);
 
-	true_sm->state = alloc_estate_rl(cast_rl(estate_type(sm->state), true_rl));
+	true_sm->state = clone_partial_estate(sm->state, true_rl);
 	free_slist(&true_sm->possible);
 	add_possible_sm(true_sm, true_sm);
-	false_sm->state = alloc_estate_rl(cast_rl(estate_type(sm->state), false_rl));
+	false_sm->state = clone_partial_estate(sm->state, false_rl);
 	free_slist(&false_sm->possible);
 	add_possible_sm(false_sm, false_sm);
 
@@ -266,7 +268,6 @@
 		add_pool(false_stack, sm);
 	else
 		add_pool(maybe_stack, sm);
-
 }
 
 static int is_checked(struct state_list *checked, struct sm_state *sm)
@@ -292,34 +293,24 @@
 			struct state_list **true_stack,
 			struct state_list **maybe_stack,
 			struct state_list **false_stack,
-			struct state_list **checked, int *mixed, struct sm_state *gate_sm)
+			struct state_list **checked, int *mixed, struct sm_state *gate_sm,
+			struct timeval *start_time)
 {
 	int free_checked = 0;
 	struct state_list *checked_states = NULL;
+	struct timeval now;
 
 	if (!sm)
 		return;
 
-	/*
-	 * If it looks like this is going to take too long as-is, then don't
-	 * create even more fake history.
-	 */
-	if (mixed && sm->nr_children > 100)
-		*mixed = 1;
-
-	/*
-	   Sometimes the implications are just too big to deal with
-	   so we bail.  Theoretically, bailing out here can cause more false
-	   positives but won't hide actual bugs.
-	*/
-	if (sm->nr_children > 4000) {
-		if (option_debug || option_debug_implied) {
-			static char buf[1028];
-			snprintf(buf, sizeof(buf), "debug: %s: nr_children over 4000 (%d). (%s %s)",
-				 __func__, sm->nr_children, sm->name, show_state(sm->state));
-			implied_debug_msg = buf;
+	gettimeofday(&now, NULL);
+	if (now.tv_usec - start_time->tv_usec > 1000000) {
+		if (implied_debug) {
+			sm_msg("debug: %s: implications taking too long.  (%s %s %s)",
+			       __func__, sm->state->name, show_special(comparison), show_rl(rl));
 		}
-		return;
+		if (mixed)
+			*mixed = 1;
 	}
 
 	if (checked == NULL) {
@@ -332,8 +323,8 @@
 
 	do_compare(sm, comparison, rl, true_stack, maybe_stack, false_stack, mixed, gate_sm);
 
-	__separate_pools(sm->left, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm);
-	__separate_pools(sm->right, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm);
+	__separate_pools(sm->left, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm, start_time);
+	__separate_pools(sm->right, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm, start_time);
 	if (free_checked)
 		free_slist(checked);
 }
@@ -345,10 +336,13 @@
 {
 	struct state_list *maybe_stack = NULL;
 	struct sm_state *tmp;
+	struct timeval start_time;
 
-	__separate_pools(sm, comparison, rl, true_stack, &maybe_stack, false_stack, checked, mixed, sm);
 
-	if (option_debug) {
+	gettimeofday(&start_time, NULL);
+	__separate_pools(sm, comparison, rl, true_stack, &maybe_stack, false_stack, checked, mixed, sm, &start_time);
+
+	if (implied_debug) {
 		struct sm_state *sm;
 
 		FOR_EACH_PTR(*true_stack, sm) {
@@ -356,7 +350,8 @@
 		} END_FOR_EACH_PTR(sm);
 
 		FOR_EACH_PTR(maybe_stack, sm) {
-			sm_msg("MAYBE %s [stree %d]", show_sm(sm), get_stree_id(sm->pool));
+			sm_msg("MAYBE %s %s[stree %d]",
+			       show_sm(sm), sm->merged ? "(merged) ": "", get_stree_id(sm->pool));
 		} END_FOR_EACH_PTR(sm);
 
 		FOR_EACH_PTR(*false_stack, sm) {
@@ -392,24 +387,52 @@
 	return 0;
 }
 
-static int taking_too_long(void)
+static int going_too_slow(void)
 {
 	static void *printed;
 
-	if (out_of_memory())
+	if (out_of_memory()) {
+		implications_off = true;
 		return 1;
+	}
 
-	if (time_parsing_function() < option_timeout)
+	if (!option_timeout || time_parsing_function() < option_timeout) {
+		implications_off = false;
 		return 0;
+	}
 
 	if (!__inline_fn && printed != cur_func_sym) {
 		if (!is_skipped_function())
-			sm_perror("turning off implications after 60 seconds");
+			sm_perror("turning off implications after %d seconds", option_timeout);
 		printed = cur_func_sym;
 	}
+	implications_off = true;
 	return 1;
 }
 
+static char *sm_state_info(struct sm_state *sm)
+{
+	static char buf[512];
+	int n = 0;
+
+	n += snprintf(buf + n, sizeof(buf) - n, "[stree %d line %d] ",
+		      get_stree_id(sm->pool),  sm->line);
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "%s ", show_sm(sm));
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "left = %s [stree %d] ",
+		      sm->left ? sm->left->state->name : "<none>",
+		      sm->left ? get_stree_id(sm->left->pool) : -1);
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "right = %s [stree %d]",
+		      sm->right ? sm->right->state->name : "<none>",
+		      sm->right ? get_stree_id(sm->right->pool) : -1);
+	return buf;
+}
+
 /*
  * NOTE: If a state is in both the keep stack and the remove stack then that is
  * a bug.  Only add states which are definitely true or definitely false.  If
@@ -417,11 +440,12 @@
  * split history where one side is true and one side is false.  Otherwise, if
  * you can't do that, then don't add it to either list.
  */
+#define RECURSE_LIMIT 300
 struct sm_state *filter_pools(struct sm_state *sm,
 			      const struct state_list *remove_stack,
 			      const struct state_list *keep_stack,
 			      int *modified, int *recurse_cnt,
-			      struct timeval *start)
+			      struct timeval *start, int *skip, int *bail)
 {
 	struct sm_state *ret = NULL;
 	struct sm_state *left;
@@ -431,51 +455,46 @@
 
 	if (!sm)
 		return NULL;
-	if (sm->skip_implications)
-		return sm;
-	if (taking_too_long())
-		return sm;
-
+	if (*bail)
+		return NULL;
 	gettimeofday(&now, NULL);
-	if ((*recurse_cnt)++ > 1000 || now.tv_sec - start->tv_sec > 5) {
-		if (local_debug || option_debug_implied) {
-			static char buf[1028];
-			snprintf(buf, sizeof(buf), "debug: %s: nr_children over 4000 (%d). (%s %s)",
-				 __func__, sm->nr_children, sm->name, show_state(sm->state));
-			implied_debug_msg = buf;
-		}
-		sm->skip_implications = 1;
-		return sm;
+	if (now.tv_usec - start->tv_usec > 3000000) {
+		DIMPLIED("%s: implications taking too long: %s\n", __func__, sm_state_info(sm));
+		*bail = 1;
+		return NULL;
 	}
+	if ((*recurse_cnt)++ > RECURSE_LIMIT) {
+		DIMPLIED("%s: recursed too far:  %s\n", __func__, sm_state_info(sm));
+		*skip = 1;
+		return NULL;
+	}
 
 	if (pool_in_pools(sm->pool, remove_stack)) {
-		DIMPLIED("removed [stree %d] %s from %d\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: remove: %s\n", __func__, sm_state_info(sm));
 		*modified = 1;
 		return NULL;
 	}
 
 	if (!is_merged(sm) || pool_in_pools(sm->pool, keep_stack) || sm_in_keep_leafs(sm, keep_stack)) {
-		DIMPLIED("kept [stree %d] %s from %d. %s. %s. %s.\n", get_stree_id(sm->pool), show_sm(sm), sm->line,
+		DIMPLIED("%s: keep %s (%s, %s, %s): %s\n", __func__, sm->state->name,
 			is_merged(sm) ? "merged" : "not merged",
 			pool_in_pools(sm->pool, keep_stack) ? "not in keep pools" : "in keep pools",
-			sm_in_keep_leafs(sm, keep_stack) ? "reachable keep leaf" : "no keep leaf");
+			sm_in_keep_leafs(sm, keep_stack) ? "reachable keep leaf" : "no keep leaf",
+			sm_state_info(sm));
 		return sm;
 	}
 
-	DIMPLIED("checking [stree %d] %s from %d (%d) left = %s [stree %d] right = %s [stree %d]\n",
-		 get_stree_id(sm->pool),
-		 show_sm(sm), sm->line, sm->nr_children,
-		 sm->left ? sm->left->state->name : "<none>", sm->left ? get_stree_id(sm->left->pool) : -1,
-		 sm->right ? sm->right->state->name : "<none>", sm->right ? get_stree_id(sm->right->pool) : -1);
-	left = filter_pools(sm->left, remove_stack, keep_stack, &removed, recurse_cnt, start);
-	right = filter_pools(sm->right, remove_stack, keep_stack, &removed, recurse_cnt, start);
+	left = filter_pools(sm->left, remove_stack, keep_stack, &removed, recurse_cnt, start, skip, bail);
+	right = filter_pools(sm->right, remove_stack, keep_stack, &removed, recurse_cnt, start, skip, bail);
+	if (*bail || *skip)
+		return NULL;
 	if (!removed) {
-		DIMPLIED("kept [stree %d] %s from %d\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: kept all: %s\n", __func__, sm_state_info(sm));
 		return sm;
 	}
 	*modified = 1;
 	if (!left && !right) {
-		DIMPLIED("removed [stree %d] %s from %d <none>\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: removed all: %s\n", __func__, sm_state_info(sm));
 		return NULL;
 	}
 
@@ -505,8 +524,7 @@
 
 	ret->pool = sm->pool;
 
-	DIMPLIED("partial %s => ", show_sm(sm));
-	DIMPLIED("%s from %d [stree %d]\n", show_sm(ret), sm->line, get_stree_id(sm->pool));
+	DIMPLIED("%s: partial: %s\n", __func__, sm_state_info(sm));
 	return ret;
 }
 
@@ -521,33 +539,32 @@
 	int modified;
 	int recurse_cnt;
 	struct timeval start;
+	int skip;
+	int bail = 0;
 
 	if (!remove_stack)
 		return NULL;
 
-	if (taking_too_long())
-		return NULL;
-
+	gettimeofday(&start, NULL);
 	FOR_EACH_SM(pre_stree, tmp) {
-		if (option_debug)
-			sm_msg("%s: %s", __func__, show_sm(tmp));
-		if (!tmp->merged)
+		if (!tmp->merged || sm_in_keep_leafs(tmp, keep_stack))
 			continue;
-		if (sm_in_keep_leafs(tmp, keep_stack))
-			continue;
 		modified = 0;
 		recurse_cnt = 0;
-		gettimeofday(&start, NULL);
-		filtered_sm = filter_pools(tmp, remove_stack, keep_stack, &modified, &recurse_cnt, &start);
-		if (!filtered_sm || !modified)
+		skip = 0;
+		filtered_sm = filter_pools(tmp, remove_stack, keep_stack, &modified, &recurse_cnt, &start, &skip, &bail);
+		if (going_too_slow())
+			return NULL;
+		if (bail)
+			return ret;  /* Return the implications we figured out before time ran out. */
+
+
+		if (skip || !filtered_sm || !modified)
 			continue;
 		/* the assignments here are for borrowed implications */
 		filtered_sm->name = tmp->name;
 		filtered_sm->sym = tmp->sym;
 		avl_insert(&ret, filtered_sm);
-		if (out_of_memory() || taking_too_long())
-			return NULL;
-
 	} END_FOR_EACH_SM(tmp);
 	return ret;
 }
@@ -566,16 +583,14 @@
 
 	gettimeofday(&time_before, NULL);
 
+	DIMPLIED("checking implications: (%s (%s) %s %s)\n",
+		 sm->name, sm->state->name, show_special(comparison), show_rl(rl));
+
 	if (!is_merged(sm)) {
-		DIMPLIED("%d '%s' is not merged.\n", get_lineno(), sm->name);
+		DIMPLIED("%d '%s' from line %d is not merged.\n", get_lineno(), sm->name, sm->line);
 		return;
 	}
 
-	if (option_debug_implied || option_debug) {
-		sm_msg("checking implications: (%s %s %s)",
-		       sm->name, show_special(comparison), show_rl(rl));
-	}
-
 	separate_pools(sm, comparison, rl, &true_stack, &false_stack, NULL, mixed);
 
 	DIMPLIED("filtering true stack.\n");
@@ -584,19 +599,18 @@
 	*false_states = filter_stack(sm, pre_stree, true_stack, false_stack);
 	free_slist(&true_stack);
 	free_slist(&false_stack);
-	if (option_debug_implied || option_debug) {
-		printf("These are the implied states for the true path: (%s %s %s)\n",
-		       sm->name, show_special(comparison), show_rl(rl));
+	if (implied_debug) {
+		printf("These are the implied states for the true path: (%s (%s) %s %s)\n",
+		       sm->name, sm->state->name, show_special(comparison), show_rl(rl));
 		__print_stree(*true_states);
-		printf("These are the implied states for the false path: (%s %s %s)\n",
-		       sm->name, show_special(comparison), show_rl(rl));
+		printf("These are the implied states for the false path: (%s (%s) %s %s)\n",
+		       sm->name, sm->state->name, show_special(comparison), show_rl(rl));
 		__print_stree(*false_states);
 	}
 
 	gettimeofday(&time_after, NULL);
 	sec = time_after.tv_sec - time_before.tv_sec;
-	if (sec > option_timeout) {
-		sm->nr_children = 4000;
+	if (option_timeout && sec > option_timeout) {
 		sm_perror("Function too hairy.  Ignoring implications after %d seconds.", sec);
 	}
 }
@@ -814,7 +828,6 @@
 	return 1;
 }
 
-static int found_implications;
 static struct stree *saved_implied_true;
 static struct stree *saved_implied_false;
 static struct stree *extra_saved_implied_true;
@@ -848,24 +861,20 @@
 			  struct stree **implied_false)
 {
 	if (handled_by_comparison_hook(expr, implied_true, implied_false))
-		goto found;
+		return;
 
 	if (handled_by_extra_states(expr, implied_true, implied_false)) {
 		separate_extra_states(implied_true, implied_false);
-		goto found;
+		return;
 	}
 
 	if (handled_by_stored_conditions(expr, implied_true, implied_false))
-		goto found;
-
-	return;
-found:
-	found_implications = 1;
+		return;
 }
 
 static void save_implications_hook(struct expression *expr)
 {
-	if (taking_too_long())
+	if (going_too_slow())
 		return;
 	get_tf_states(expr, &saved_implied_true, &saved_implied_false);
 }
@@ -906,6 +915,9 @@
 	struct stree *implied_false = NULL;
 	struct range_list *orig, *limit;
 
+	if (time_parsing_function() > 40)
+		return;
+
 	while (expr->type == EXPR_ASSIGNMENT)
 		expr = strip_expr(expr->right);
 	if (expr->type != EXPR_CALL)
@@ -1072,7 +1084,6 @@
 	in_fake_env++;
 	final_pass = 0;
 	__push_fake_cur_stree();
-	found_implications = 0;
 	__split_whole_condition(expr);
 	final_pass = orig_final_pass;
 	in_fake_env--;
