def linear_eq_induct_base_checks (p, restrs, hyps, tag, split, eqs): tests = [] details = (split, (0, 1), eqs) for i in [0, 1]: reach = split_visit_one_visit (tag, details, restrs, vc_num (i)) nhyps = [pc_true_hyp (reach)] tests.extend ([(hyps + nhyps, hyp, 'Base check (%s, %d) at induct step for %d' % (desc, i, split)) for (hyp, desc) in linear_eq_hyps_at_visit (tag, split, eqs, restrs, vc_num (i))]) return tests
def single_loop_induct_base_checks(p, restrs, hyps, tag, split, n, eqs): tests = [] details = (split, (0, 1), eqs) for i in range(n + 1): reach = split_visit_one_visit(tag, details, restrs, vc_num(i)) nhyps = [pc_true_hyp(reach)] tests.extend([ (hyps + nhyps, hyp, 'Base check (%s, %d) at induct step for %d' % (desc, i, split)) for (hyp, desc ) in loop_eq_hyps_at_visit(tag, split, eqs, restrs, vc_num(i)) ]) return tests
def init_case_splits (p, hyps, tags = None): if 'init_case_splits' in p.cached_analysis: return p.cached_analysis['init_case_splits'] if tags == None: tags = p.pairing.tags poss = logic.possible_graph_divs (p) if len (set ([p.node_tags[n][0] for n in poss])) < 2: return None rep = rep_graph.mk_graph_slice (p) assert all ([p.nodes[n].kind == 'Cond' for n in poss]) pc_map = logic.dict_list ([(rep.get_pc ((c, ())), c) for n in poss for c in p.nodes[n].get_conts () if c not in p.loop_data]) no_loop_restrs = tuple ([(n, vc_num (0)) for n in p.loop_heads ()]) err_pc_hyps = [rep_graph.pc_false_hyp ((('Err', no_loop_restrs), tag)) for tag in p.pairing.tags] knowledge = EqSearchKnowledge (rep, hyps + err_pc_hyps, list (pc_map)) last_knowledge[0] = knowledge pc_ids = knowledge.classify_vs () id_n_map = logic.dict_list ([(i, n) for (pc, i) in pc_ids.iteritems () for n in pc_map[pc]]) tag_div_ns = [[[n for n in ns if p.node_tags[n][0] == t] for t in tags] for (i, ns) in id_n_map.iteritems ()] split_pairs = [(l_ns[0], r_ns[0]) for (l_ns, r_ns) in tag_div_ns if l_ns and r_ns] p.cached_analysis['init_case_splits'] = split_pairs return split_pairs
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))
def split_hyps_at_visit (tags, split, restrs, visit): (l_details, r_details, eqs, _, _) = split (l_split, (l_seq_start, l_step), l_eqs) = l_details (r_split, (r_seq_start, r_step), r_eqs) = r_details (l_visit, r_visit) = split_visit_visits (tags, split, restrs, visit) (l_start, r_start) = split_visit_visits (tags, split, restrs, vc_num (0)) (l_tag, r_tag) = tags def mksub (v): return lambda exp: logic.var_subst (exp, {('%i', word32T) : v}, must_subst = False) def inst (exp): return logic.inst_eq_at_visit (exp, visit) zsub = mksub (mk_word32 (0)) if visit.kind == 'Number': lsub = mksub (mk_word32 (visit.n)) else: lsub = mksub (mk_plus (mk_var ('%n', word32T), mk_word32 (visit.n))) hyps = [(Hyp ('PCImp', l_visit, r_visit), 'pc imp'), (Hyp ('PCImp', l_visit, l_start), '%s pc imp' % l_tag), (Hyp ('PCImp', r_visit, r_start), '%s pc imp' % r_tag)] hyps += [(eq_hyp ((zsub (l_exp), l_start), (lsub (l_exp), l_visit), (l_split, r_split)), '%s const' % l_tag) for l_exp in l_eqs if inst (l_exp)] hyps += [(eq_hyp ((zsub (r_exp), r_start), (lsub (r_exp), r_visit), (l_split, r_split)), '%s const' % r_tag) for r_exp in r_eqs if inst (r_exp)] hyps += [(eq_hyp ((lsub (l_exp), l_visit), (lsub (r_exp), r_visit), (l_split, r_split)), 'eq') for (l_exp, r_exp) in eqs if inst (l_exp) and inst (r_exp)] return hyps
def split_hyps_at_visit(tags, split, restrs, visit): (l_details, r_details, eqs, _, _) = split (l_split, (l_seq_start, l_step), l_eqs) = l_details (r_split, (r_seq_start, r_step), r_eqs) = r_details (l_visit, r_visit) = split_visit_visits(tags, split, restrs, visit) (l_start, r_start) = split_visit_visits(tags, split, restrs, vc_num(0)) (l_tag, r_tag) = tags def mksub(v): return lambda exp: logic.var_subst(exp, {('%i', word32T): v}, must_subst=False) def inst(exp): return logic.inst_eq_at_visit(exp, visit) zsub = mksub(mk_word32(0)) if visit.kind == 'Number': lsub = mksub(mk_word32(visit.n)) else: lsub = mksub(mk_plus(mk_var('%n', word32T), mk_word32(visit.n))) hyps = [(Hyp('PCImp', l_visit, r_visit), 'pc imp'), (Hyp('PCImp', l_visit, l_start), '%s pc imp' % l_tag), (Hyp('PCImp', r_visit, r_start), '%s pc imp' % r_tag)] hyps += [(eq_hyp((zsub(l_exp), l_start), (lsub(l_exp), l_visit), (l_split, r_split)), '%s const' % l_tag) for l_exp in l_eqs if inst(l_exp)] hyps += [(eq_hyp((zsub(r_exp), r_start), (lsub(r_exp), r_visit), (l_split, r_split)), '%s const' % r_tag) for r_exp in r_eqs if inst(r_exp)] hyps += [(eq_hyp((lsub(l_exp), l_visit), (lsub(r_exp), r_visit), (l_split, r_split)), 'eq') for (l_exp, r_exp) in eqs if inst(l_exp) and inst(r_exp)] return hyps
def get_var (k): head = rep.p.loop_id (i) if i != head: k += 1 restrs2 = restrs + ((head, vc_num (k)), ) (pc, env) = rep.get_node_pc_env ((i, restrs2)) return (to_smt_expr (pc, env, rep.solv), to_smt_expr (v_i, env, rep.solv))
def default_n_vc_cases(p, n): head = p.loop_id(n) general = [(n2, rep_graph.vc_options([0], [1])) for n2 in p.loop_heads() if n2 != head] if head: return [(n, tuple(general + [(head, rep_graph.vc_num(1))])), (n, tuple(general + [(head, rep_graph.vc_offs(1))]))] specific = [(head, rep_graph.vc_offs(1)) for _ in [1] if head] return [(n, tuple(general + specific))]
def default_n_vc_cases(p, n): head = p.loop_id(n) general = [(n2, rep_graph.vc_options([0], [1])) for n2 in p.loop_heads() if n2 != head] if head: return [ (n, tuple(general + [(head, rep_graph.vc_num(1))])), (n, tuple(general + [(head, rep_graph.vc_offs(1))])), ] specific = [(head, rep_graph.vc_offs(1)) for _ in [1] if head] return [(n, tuple(general + specific))]
def split_init_step_checks(p, restrs, hyps, split, tags=None): (_, _, _, n, _) = split if tags == None: tags = p.pairing.tags err_hyp = split_r_err_pc_hyp(p, split, restrs, tags=tags) hyps = [err_hyp] + hyps checks = [] for i in range(n): (l_visit, r_visit) = split_visit_visits(tags, split, restrs, vc_num(i)) lpc_hyp = pc_true_hyp(l_visit) # this trivial 'hyp' ensures the rep is built to include # the matching rhs visits when checking lhs consts rpc_triv_hyp = rep_graph.pc_triv_hyp(r_visit) vis_hyps = split_hyps_at_visit(tags, split, restrs, vc_num(i)) for (hyp, desc) in vis_hyps: checks.append((hyps + [lpc_hyp, rpc_triv_hyp], hyp, 'Induct check at visit %d: %s' % (i, desc))) return checks
def split_init_step_checks (p, restrs, hyps, split, tags = None): (_, _, _, n, _) = split if tags == None: tags = p.pairing.tags err_hyp = split_r_err_pc_hyp (p, split, restrs, tags = tags) hyps = [err_hyp] + hyps checks = [] for i in range (n): (l_visit, r_visit) = split_visit_visits (tags, split, restrs, vc_num (i)) lpc_hyp = pc_true_hyp (l_visit) # this trivial 'hyp' ensures the rep is built to include # the matching rhs visits when checking lhs consts rpc_triv_hyp = rep_graph.pc_triv_hyp (r_visit) vis_hyps = split_hyps_at_visit (tags, split, restrs, vc_num (i)) for (hyp, desc) in vis_hyps: checks.append ((hyps + [lpc_hyp, rpc_triv_hyp], hyp, 'Induct check at visit %d: %s' % (i, desc))) return checks
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 split_visit_one_visit (tag, details, restrs, visit): if details == None: return None (split, (seq_start, step), eqs) = details # the split point sequence at low numbers ('Number') is offset # by the point the sequence starts. At symbolic offsets we ignore # that, instead having the loop counter for the two sequences # be the same number of iterations after the sequence start. if visit.kind == 'Offset': visit = vc_offs (visit.n * step) else: visit = vc_num (seq_start + (visit.n * step)) visit = ((split, ((split, visit), ) + restrs), tag) return visit
def split_visit_one_visit(tag, details, restrs, visit): if details == None: return None (split, (seq_start, step), eqs) = details # the split point sequence at low numbers ('Number') is offset # by the point the sequence starts. At symbolic offsets we ignore # that, instead having the loop counter for the two sequences # be the same number of iterations after the sequence start. if visit.kind == 'Offset': visit = vc_offs(visit.n * step) else: visit = vc_num(seq_start + (visit.n * step)) visit = ((split, ((split, visit), ) + restrs), tag) return visit
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 loop_eq_hyps_at_visit(tag, split, eqs, restrs, visit_num, use_if_at=False): details = (split, (0, 1), eqs) visit = split_visit_one_visit(tag, details, restrs, visit_num) start = split_visit_one_visit(tag, details, restrs, vc_num(0)) def mksub(v): return lambda exp: logic.var_subst(exp, {('%i', word32T): v}, must_subst=False) zsub = mksub(mk_word32(0)) if visit_num.kind == 'Number': isub = mksub(mk_word32(visit_num.n)) else: isub = mksub(mk_plus(mk_var('%n', word32T), mk_word32(visit_num.n))) hyps = [(Hyp('PCImp', visit, start), '%s pc imp' % tag)] hyps += [(eq_hyp((zsub(exp), start), (isub(exp), visit), (split, 0), use_if_at=use_if_at), '%s const' % tag) for exp in eqs if logic.inst_eq_at_visit(exp, visit_num)] return hyps
def linear_eq_hyps_at_visit (tag, split, eqs, restrs, visit_num): details = (split, (0, 1), eqs) visit = split_visit_one_visit (tag, details, restrs, visit_num) start = split_visit_one_visit (tag, details, restrs, vc_num (0)) from syntax import mk_word32, mk_plus, mk_var, word32T def mksub (v): return lambda exp: logic.var_subst (exp, {('%i', word32T) : v}, must_subst = False) zsub = mksub (mk_word32 (0)) if visit_num.kind == 'Number': isub = mksub (mk_word32 (visit_num.n)) else: isub = mksub (mk_plus (mk_var ('%n', word32T), mk_word32 (visit_num.n))) hyps = [(Hyp ('PCImp', visit, start), '%s pc imp' % tag)] hyps += [(eq_hyp ((zsub (exp), start), (isub (exp), visit), (split, 0)), '%s const' % tag) for exp in eqs if logic.inst_eq_at_visit (exp, visit_num)] return hyps
def init_proof_case_split (p, restrs, hyps): ps = init_case_splits (p, hyps) if ps == None: return None p.cached_analysis.setdefault ('finished_init_case_splits', []) fin = p.cached_analysis['finished_init_case_splits'] known_s = set.union (set (restrs), set (hyps)) for rs in fin: if rs <= known_s: return None rep = rep_graph.mk_graph_slice (p) no_loop_restrs = tuple ([(n, vc_num (0)) for n in p.loop_heads ()]) err_pc_hyps = [rep_graph.pc_false_hyp ((('Err', no_loop_restrs), tag)) for tag in p.pairing.tags] for (n1, n2) in ps: pc = rep.get_pc ((n1, ())) if rep.test_hyp_whyps (pc, hyps + err_pc_hyps): continue if rep.test_hyp_whyps (mk_not (pc), hyps + err_pc_hyps): continue case_split_tr.append ((n1, restrs, hyps)) return ('CaseSplit', ((n1, p.node_tags[n1][0]), [n1, n2])) fin.append (known_s) return None
def single_induct_resulting_hyp(p, restrs, rev_induct_args): (point, _, (pred, _)) = rev_induct_args (tag, _) = p.node_tags[point] vis = ((point, restrs + tuple([(point, vc_num(0))])), tag) return rep_graph.true_if_at_hyp(pred, vis)
p.node_tags[n][0])) def proof_restr_checks (n, (kind, (x, y)), p, restrs, hyps): restr = get_proof_restr (n, (kind, (x, y))) ncerr_hyp = non_r_err_pc_hyp (p.pairing.tags, restr_others (p, (restr, ) + restrs, 2)) hyps = [ncerr_hyp] + hyps def visit (vc): return ((n, ((n, vc), ) + restrs), p.node_tags[n][0]) # this cannot be more uniform because the representation of visit # at offset 0 is all a bit odd, with n being the only node so visited: if kind == 'Offset': min_vc = vc_offs (max (0, x - 1)) elif x > 1: min_vc = vc_num (x - 1) else: min_vc = None if min_vc: init_check = [(hyps, pc_true_hyp (visit (min_vc)), 'Check of restr min %d %s for %d' % (x, kind, n))] else: init_check = [] # if we can reach node n with (y - 1) visits to n, then the next # node will have y visits to n, which we are disallowing # thus we show that this visit is impossible top_vc = VisitCount (kind, y - 1) top_check = (hyps, pc_false_hyp (visit (top_vc)), 'Check of restr max %d %s for %d' % (y, kind, n)) return init_check + [top_check]
def split_no_loop_hyps (tags, split, restrs): ((_, (l_seq_start, l_step), _), _, _, n, _) = split (l_visit, _) = split_visit_visits (tags, split, restrs, vc_num (n)) return [pc_false_hyp (l_visit)]
def vis (n, i): if n != p.loop_id (n): i = i + 1 return (n, tuple ([(p.loop_id (n), vc_num (i))]) + restrs)
def proof_restr_checks(n, (kind, (x, y)), p, restrs, hyps): restr = get_proof_restr(n, (kind, (x, y))) ncerr_hyp = non_r_err_pc_hyp(p.pairing.tags, restr_others(p, (restr, ) + restrs, 2)) hyps = [ncerr_hyp] + hyps def visit(vc): return ((n, ((n, vc), ) + restrs), p.node_tags[n][0]) # this cannot be more uniform because the representation of visit # at offset 0 is all a bit odd, with n being the only node so visited: if kind == 'Offset': min_vc = vc_offs(max(0, x - 1)) elif x > 1: min_vc = vc_num(x - 1) else: min_vc = None if min_vc: init_check = [(hyps, pc_true_hyp(visit(min_vc)), 'Check of restr min %d %s for %d' % (x, kind, n))] else: init_check = [] # if we can reach node n with (y - 1) visits to n, then the next # node will have y visits to n, which we are disallowing # thus we show that this visit is impossible top_vc = VisitCount(kind, y - 1) top_check = (hyps, pc_false_hyp(visit(top_vc)), 'Check of restr max %d %s for %d' % (y, kind, n)) return init_check + [top_check]
def split_no_loop_hyps(tags, split, restrs): ((_, (l_seq_start, l_step), _), _, _, n, _) = split (l_visit, _) = split_visit_visits(tags, split, restrs, vc_num(n)) return [pc_false_hyp(l_visit)]