def setup_split_search (rep, head, restrs, hyps, i_opts, j_opts, unfold_limit = None, tags = None): p = rep.p if not tags: tags = p.pairing.tags if unfold_limit == None: unfold_limit = max ([start + (2 * step) + 1 for (start, step) in i_opts + j_opts]) trace ('Split search at %d, unfold limit %d.' % (head, unfold_limit)) l_tag, r_tag = tags loop_elts = [(n, start, step) for n in p.splittable_points (head) for (start, step) in i_opts] init_to_split = init_loops_to_split (p, restrs) r_to_split = [n for n in init_to_split if p.node_tags[n][0] == r_tag] cand_r_loop_elts = [(n2, start, step) for n in r_to_split for n2 in p.splittable_points (n) for (start, step) in j_opts] err_restrs = restr_others (p, tuple ([(sp, vc_upto (unfold_limit)) for sp in r_to_split]) + restrs, 1) nrerr_pc = mk_not (rep.get_pc (('Err', err_restrs), tag = r_tag)) def get_pc (n, k): head = p.loop_id (n) assert head in init_to_split if n != head: k += 1 restrs2 = restrs + ((head, vc_num (k)), ) return rep.get_pc ((n, restrs2)) for n in r_to_split: get_pc (n, unfold_limit) get_pc (head, unfold_limit) premise = foldr1 (mk_and, [nrerr_pc] + map (rep.interpret_hyp, hyps)) premise = logic.weaken_assert (premise) knowledge = SearchKnowledge (rep, 'search at %d (unfold limit %d)' % (head, unfold_limit), restrs, hyps, tags, (loop_elts, cand_r_loop_elts)) knowledge.premise = premise last_knowledge[0] = knowledge # make sure the representation is in sync rep.test_hyp_whyps (true_term, hyps) # make sure all mem eqs are being tracked mem_vs = [v for v in knowledge.v_ids if v[0].typ == builtinTs['Mem']] for (i, v) in enumerate (mem_vs): for v2 in mem_vs[:i]: for pred in expand_var_eqs (knowledge, (v, v2)): smt_expr (pred, {}, rep.solv) for v in knowledge.v_ids: for pred in expand_var_eqs (knowledge, (v, 'Const')): smt_expr (pred, {}, rep.solv) return knowledge
def find_split_limit (p, n, restrs, hyps, kind, bound = 51, must_find = True, hints = [], use_rep = None): tag = p.node_tags[n][0] trace ('Finding split limit: %d (%s) %s' % (n, tag, restrs)) trace (' (restrs = %s)' % (restrs, )) trace (' (hyps = %s)' % (hyps, ), push = 1) if use_rep == None: rep = mk_graph_slice (p, fast = True) else: rep = use_rep check_order = hints + [i for i in split_sample_set (bound) if i not in hints] for i in check_order: restrs2 = restrs + ((n, VisitCount (kind, i)), ) pc = rep.get_pc ((n, restrs2)) restrs3 = restr_others (p, restrs2, 2) epc = rep.get_pc (('Err', restrs3), tag = tag) hyp = mk_implies (mk_not (epc), mk_not (pc)) if rep.test_hyp_whyps (hyp, hyps): trace ('split limit found: %d' % i, push = -1) return i trace ('No split limit found for %d (%s).' % (n, tag), push = -1) if must_find: assert not 'split limit found' return None
def find_case_split (p, head, restrs, hyps, tags = None): # are there multiple paths to the loop head 'head' and can we # restrict to one of them? preds = set () frontier = list (set ([n2 for n in p.loop_body (head) for n2 in p.preds[n] if p.loop_id (n2) != head])) while frontier: n2 = frontier.pop () if n2 in preds: continue preds.add (n2) frontier.extend (p.preds[n2]) divs = [n2 for n2 in preds if len (set ([n3 for n3 in p.nodes[n2].get_conts () if n3 in preds or n3 == head])) > 1 if n2 not in p.loop_data] trace ('find_case_split: possible divs %s.' % divs) rep = mk_graph_slice (p) err_restrs = restr_others (p, restrs, 2) if tags: l_tag, r_tag = tags else: l_tag, r_tag = p.pairing.tags hvis_restrs = tuple ([(head, rep_graph.vc_num (0))]) + restrs lhyps = hyps + [rep_graph.pc_false_hyp ((('Err', err_restrs), r_tag)), rep_graph.pc_true_hyp (((head, hvis_restrs), p.node_tags[head][0]))] # for this to be a usable case split, both paths must be possible for div in divs: dhyps = lhyps + [rep_graph.pc_true_hyp (((div, restrs), p.node_tags[div][0]))] assert p.nodes[div].kind == 'Cond' (_, env) = rep.get_node_pc_env ((div, restrs)) c = to_smt_expr (p.nodes[div].cond, env, rep.solv) if (rep.test_hyp_whyps (c, dhyps) or rep.test_hyp_whyps (mk_not (c), dhyps)): continue trace ("attempting case split at %d" % div) sides = [n for n in p.nodes[div].get_conts () if n not in p.loop_data if p.preds[n] == [div]] if not sides: trace ("skipping case split, no notable succ") n = sides[0] return ('CaseSplit', (n, p.node_tags[n][0])) trace ('find_case_split: no case splits possible') return (None, ())
def tryLoopBound(p_n, p, bounds, rep, restrs=None, hints=None, kind='Number', bin_return=False, hyps=None): if restrs == None: restrs = () if hints == None: hints = [] if hyps == None: hyps = [] tag = p.node_tags[p_n][0] from stack_logic import default_n_vc print 'trying bound: %s' % bounds ret_bounds = [] for (index, i) in enumerate(bounds): print 'testing %d' % i restrs2 = restrs + ((p_n, VisitCount(kind, i)), ) try: pc = rep.get_pc((p_n, restrs2)) except: print 'get_pc failed' if bin_return: return False else: return -1 #print 'got rep_.get_pc' restrs3 = restr_others(p, restrs2, 2) epc = rep.get_pc(('Err', restrs3), tag=tag) hyp = mk_implies(mk_not(epc), mk_not(pc)) hyps = hyps + noHaltHyps(p_n, p) #hyps = [] #print 'calling test_hyp_whyps' if rep.test_hyp_whyps(hyp, hyps): print 'p_n %d: split limit found: %d' % (p_n, i) if bin_return: return True return index if bin_return: return False print 'loop bound not found!' return -1 assert False, 'failed to find loop bound for p_n %d' % p_n
def loop_no_match_unroll (rep, restrs, hyps, split, other_tag, unroll): p = rep.p assert p.node_tags[split][0] != other_tag restr = ((split, vc_num (unroll)), ) restrs2 = restr_others (p, restr + restrs, 2) loop_cond = rep.get_pc ((split, restr + restrs)) ret_cond = rep.get_pc (('Ret', restrs2), tag = other_tag) # loop should be reachable if rep.test_hyp_whyps (mk_not (loop_cond), hyps): trace ('Loop weak at %d (unroll count %d).' % (split, unroll)) return True # reaching the loop should imply reaching a loop on the other side hyp = mk_not (mk_and (loop_cond, ret_cond)) if not rep.test_hyp_whyps (hyp, hyps): trace ('Loop independent at %d (unroll count %d).' % (split, unroll)) return True return False
def tryLoopBound(p_n, p, bounds, rep, restrs=None, hints=None, kind="Number", bin_return=False, hyps=None): if restrs == None: restrs = () if hints == None: hints = [] if hyps == None: hyps = [] tag = p.node_tags[p_n][0] from stack_logic import default_n_vc print "trying bound: %s" % bounds ret_bounds = [] for (index, i) in enumerate(bounds): print "testing %d" % i restrs2 = restrs + ((p_n, VisitCount(kind, i)),) try: pc = rep.get_pc((p_n, restrs2)) except: print "get_pc failed" if bin_return: return False else: return -1 # print 'got rep_.get_pc' restrs3 = restr_others(p, restrs2, 2) epc = rep.get_pc(("Err", restrs3), tag=tag) hyp = mk_implies(mk_not(epc), mk_not(pc)) hyps = hyps + noHaltHyps(p_n, p) # hyps = [] # print 'calling test_hyp_whyps' if rep.test_hyp_whyps(hyp, hyps): print "p_n %d: split limit found: %d" % (p_n, i) if bin_return: return True return index if bin_return: return False print "loop bound not found!" return -1 assert False, "failed to find loop bound for p_n %d" % p_n
def build_proof_rec_with_restrs (split_points, kind, searcher, p, restrs, hyps): sp = split_points[0] use_hyps = list (hyps) if p.node_tags[sp][0] != p.pairing.tags[1]: nrerr_hyp = check.non_r_err_pc_hyp (p.pairing.tags, restr_others (p, restrs, 2)) use_hyps = use_hyps + [nrerr_hyp] limit = find_split_limit (p, sp, restrs, use_hyps, kind) # double-check this limit with a rep constructed without the 'fast' flag limit = find_split_limit (p, sp, restrs, use_hyps, kind, hints = [limit, limit + 1], use_rep = mk_graph_slice (p)) if kind == 'Number': vc_opts = vc_upto (limit + 1) else: vc_opts = vc_offset_upto (limit + 1) restrs = restrs + ((sp, vc_opts), ) if len (split_points) == 1: subproof = build_proof_rec (searcher, p, restrs, hyps) else: subproof = build_proof_rec_with_restrs (split_points[1:], kind, searcher, p, restrs, hyps) return ProofNode ('Restr', (sp, (kind, (0, limit + 1))), [subproof])
def build_proof_rec_with_restrs (split_points, kind, searcher, p, restrs, hyps, must_find = True, name = "problem"): if not split_points: return build_proof_rec (searcher, p, restrs, hyps, name = name) sp = split_points[0] use_hyps = list (hyps) if p.node_tags[sp][0] != p.pairing.tags[1]: nrerr_hyp = check.non_r_err_pc_hyp (p.pairing.tags, restr_others (p, restrs, 2)) use_hyps = use_hyps + [nrerr_hyp] if p.loop_id (sp): lim_pair = get_proof_split_limit (p, sp, restrs, use_hyps, kind, must_find = must_find) else: lim_pair = get_proof_visit_restr (p, sp, restrs, use_hyps, kind, must_find = must_find) if not lim_pair: assert not must_find return build_proof_rec_with_restrs (split_points[1:], kind, searcher, p, restrs, hyps, must_find = must_find, name = name) (min_v, max_v) = lim_pair if kind == 'Number': vc_opts = rep_graph.vc_options (range (min_v, max_v), []) else: vc_opts = rep_graph.vc_options ([], range (min_v, max_v)) restrs = restrs + ((sp, vc_opts), ) subproof = build_proof_rec_with_restrs (split_points[1:], kind, searcher, p, restrs, hyps, must_find = must_find, name = name) return ProofNode ('Restr', (sp, (kind, (min_v, max_v))), [subproof])
def find_split (rep, head, restrs, hyps, i_opts, j_opts, unfold_limit, tags = None): p = rep.p if tags: l_tag, r_tag = tags else: l_tag, r_tag = p.pairing.tags loop_elts = [(n, start, step) for n in p.loop_data[head][1] if p.loop_splittables[n] for (start, step) in i_opts] init_to_split = init_loops_to_split (p, restrs) r_to_split = [n for n in init_to_split if p.node_tags[n][0] == r_tag] cand_r_loop_elts = [(n2, start, step) for n in r_to_split for n2 in p.loop_data[n][1] if p.loop_splittables[n2] for (start, step) in j_opts] err_restrs = restr_others (p, tuple ([(sp, vc_upto (unfold_limit)) for sp in r_to_split]) + restrs, 1) nrerr_pc = mk_not (rep.get_pc (('Err', err_restrs), tag = r_tag)) def get_pc (n, k): head = p.loop_id (n) assert head in init_to_split if n != head: k += 1 restrs2 = restrs + ((head, vc_num (k)), ) return rep.get_pc ((n, restrs2)) for n in r_to_split: get_pc (n, unfold_limit) get_pc (head, unfold_limit) premise = foldr1 (mk_and, [nrerr_pc] + map (rep.interpret_hyp, hyps)) premise = logic.weaken_assert (premise) knowledge = (rep, (restrs, loop_elts, cand_r_loop_elts, premise), init_knowledge_pairs (rep, loop_elts, cand_r_loop_elts), set ()) last_knowledge[0] = knowledge # make sure the representation is in sync rep.test_hyp_whyps (true_term, hyps) # make sure all mem eqs are being tracked (_, _, (pairs, vs), _) = knowledge mem_vs = [v for v in vs if v[0].typ == builtinTs['Mem']] for (i, v) in enumerate (mem_vs): for v2 in mem_vs[:i]: for pred in expand_var_eqs (knowledge, (v, v2)): smt_expr (pred, {}, rep.solv) for v in vs: for pred in expand_var_eqs (knowledge, (v, 'Const')): smt_expr (pred, {}, rep.solv) # start the process with a model add_model (knowledge, [mk_not (get_pc (head, unfold_limit))]) num_eqs = 3 while True: trace ('Search at unfold limit %d' % unfold_limit) trace ('Computing live pairings') pair_eqs = [(pair, mk_pairing_v_eqs (knowledge, pair)) for pair in sorted (pairs) if pairs[pair][0] != 'Failed'] endorsed = [(pair, eqs) for (pair, eqs) in pair_eqs if eqs != None] trace (' ... %d live pairings, %d endorsed' % (len (pair_eqs), len (endorsed))) for (pair, eqs) in endorsed: split = v_eqs_to_split (p, pair, eqs, restrs, hyps, tags = tags) if split == None: pairs[pair] = ('Failed', 'SplitWeak', eqs) continue if check_split_induct (p, restrs, hyps, split, tags = tags): trace ('Tested v_eqs!') return ('Split', split) else: pairs[pair] = ('Failed', 'InductFailed', eqs) u_eqs = unknown_eqs (knowledge, num_eqs) if not u_eqs: trace (('Exhausted split candidates for loop at %d,' + ' unfold limit %d') % (head, unfold_limit)) fails = [it for it in pairs.items () if it[1][0] == 'Failed'] fails10 = fails[:10] trace (' %d of %d failed pairings:' % (len (fails10), len (fails))) last_failed_pairings.append (fails) del last_failed_pairings[:-10] for f in fails: trace (' %s' % (f,)) ind_fails = [it for it in fails if str (it[1][1]) == 'InductFailed'] if ind_fails: trace ( 'Inductive failures!') for f in ind_fails: trace (' %s' % (f,)) return (None, ind_fails) add_model_wrapper (knowledge, u_eqs) num_eqs = 4 - num_eqs # oscillate between 3, 1
def get_necessary_split_opts (p, head, restrs, hyps, tags = None, iters = None): if not tags: tags = p.pairing.tags [l_tag, r_tag] = tags assert p.node_tags[head][0] == l_tag l_seq_vs = get_interesting_linear_series_exprs (p, head) if not l_seq_vs: return None r_seq_vs = {} for n in init_loops_to_split (p, restrs): if p.node_tags[n][0] == r_tag: vs = get_interesting_linear_series_exprs (p, n) r_seq_vs.update (vs) if not r_seq_vs: return None rep = rep_graph.mk_graph_slice (p, fast = True) def vis (n, i): if n != p.loop_id (n): i = i + 1 return (n, tuple ([(p.loop_id (n), vc_num (i))]) + restrs) smt = lambda expr, n, i: rep.to_smt_expr (expr, vis (n, i)) smt_pc = lambda n, i: rep.get_pc (vis (n, i)) # remove duplicates by concretising l_seq_vs = dict ([(smt (expr, n, 2), (kind, n, expr)) for n in l_seq_vs for (kind, expr) in l_seq_vs[n]]).values () r_seq_vs = dict ([(smt (expr, n, 2), (kind, n, expr)) for n in r_seq_vs for (kind, expr) in r_seq_vs[n]]).values () if iters == None: if [n for n in p.loop_body (head) if p.nodes[n].kind == 'Call']: iters = 5 else: iters = 8 r_seq_end = 1 + 2 * iters l_seq_end = 1 + iters l_seq_ineq = 1 + max ([1 << n for n in range (iters) if 1 << n <= iters]) hyps = hyps + [rep_graph.pc_triv_hyp ((vis (n, r_seq_end), r_tag)) for n in set ([n for (_, n, _) in r_seq_vs])] hyps = hyps + [rep_graph.pc_triv_hyp ((vis (n, l_seq_end), l_tag)) for n in set ([n for (_, n, _) in l_seq_vs])] ex_restrs = [(n, rep_graph.vc_upto (r_seq_end + 1)) for n in set ([p.loop_id (n) for (_, n, _) in r_seq_vs])] hyps = hyps + [check.non_r_err_pc_hyp (tags, restr_others (p, restrs + tuple (ex_restrs), 2))] necessary_split_opts_trace[:] = [] necessary_split_opts_long_trace[:] = [] for (kind, n, expr) in sorted (l_seq_vs): rel_r_seq_vs = [v for v in r_seq_vs if v[0] == kind] if not rel_r_seq_vs: necessary_split_opts_trace.append ((n, 'NoneRelevant')) continue m = {} eq = mk_eq (smt (expr, n, 1), smt (expr, n, l_seq_ineq)) ex_hyps = [rep_graph.pc_true_hyp ((vis (n, i), l_tag)) for i in range (1, l_seq_end + 1)] res = rep.test_hyp_whyps (eq, hyps + ex_hyps, model = m) necessary_split_opts_long_trace.append ((n, eq, hyps + ex_hyps, res, m, smt, smt_pc, (kind, n, expr), r_seq_vs, iters)) if not m: necessary_split_opts_trace.append ((n, None)) continue seq_eq = get_linear_seq_eq (rep, m, smt, smt_pc, (kind, n, expr), r_seq_vs, iters) necessary_split_opts_trace.append ((n, ('Seq', seq_eq))) if not seq_eq: continue ((n2, expr2), (l_start, l_step), (r_start, r_step)) = seq_eq eqs = [rep_graph.eq_hyp ((expr, (vis (n, l_start + (i * l_step)), l_tag)), (expr2, (vis (n2, r_start + (i * r_step)), r_tag))) for i in range (10) if l_start + (i * l_step) <= l_seq_end if r_start + (i * r_step) <= r_seq_end] eq = foldr1 (mk_and, map (rep.interpret_hyp, eqs)) if rep.test_hyp_whyps (eq, hyps): mk_i = lambda i: (l_start + (i * l_step), l_step) mk_j = lambda j: (r_start + (j * r_step), r_step) return [([mk_i (0)], [mk_j (0)]), ([mk_i (0), mk_i (1)], [mk_j (0), mk_j (1)])] n_vcs = entry_path_no_loops (rep, l_tag, m, head) path_hyps = [rep_graph.pc_true_hyp ((n_vc, l_tag)) for n_vc in n_vcs] if rep.test_hyp_whyps (eq, hyps + path_hyps): # immediate case split on difference between entry paths checks = [(hyps, eq_hyp, 'eq') for eq_hyp in eqs] return derive_case_split (rep, n_vcs, checks) necessary_split_opts_trace.append ((n, 'Seq check failed')) return None