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 proof_subproblems (p, kind, args, restrs, hyps, path): tags = p.pairing.tags if kind == 'Leaf': return [] elif kind == 'Restr': restr = get_proof_restr (args[0], args[1]) hyps = hyps + [restr_trivial_hyp (p, args[0], args[1], restrs)] return [((restr,) + restrs, hyps, '%s (%d limited)' % (path, args[0]))] elif kind == 'Split': split = args return [(restrs, hyps + split_no_loop_hyps (tags, split, restrs), '%d init case in %s' % (split[0][0], path)), (restrs, hyps + split_loop_hyps (tags, split, restrs, exit = True), '%d loop case in %s' % (split[0][0], path))] elif kind == 'CaseSplit': (point, tag) = args visit = ((point, restrs), tag) true_hyps = hyps + [pc_true_hyp (visit)] false_hyps = hyps + [pc_false_hyp (visit)] return [(restrs, true_hyps, 'true case (%d visited) in %s' % (point, path)), (restrs, false_hyps, 'false case (%d not visited) in %s' % (point, path))] else: assert not 'proof node kind understood', proof.kind
def noHaltHyps(split,p): ret = [] all_halts = callNodes(p,fs=['halt']) for x in all_halts: ret += [rep_graph.pc_false_hyp((n_vc, p.node_tags[x][0])) for n_vc in default_n_vc_cases (p, x)] return ret
def proof_subproblems(p, kind, args, restrs, hyps, path): tags = p.pairing.tags if kind == 'Leaf': return [] elif kind == 'Restr': restr = get_proof_restr(args[0], args[1]) hyps = hyps + [restr_trivial_hyp(p, args[0], args[1], restrs)] return [((restr, ) + restrs, hyps, '%s (%d limited)' % (path, args[0])) ] elif kind == 'SingleRevInduct': hyp = single_induct_resulting_hyp(p, restrs, args) return [(restrs, hyps + [hyp], path)] elif kind == 'Split': split = args return [ (restrs, hyps + split_no_loop_hyps(tags, split, restrs), '%d init case in %s' % (split[0][0], path)), (restrs, hyps + split_loop_hyps(tags, split, restrs, exit=True), '%d loop case in %s' % (split[0][0], path)) ] elif kind == 'CaseSplit': (point, tag) = args visit = ((point, restrs), tag) true_hyps = hyps + [pc_true_hyp(visit)] false_hyps = hyps + [pc_false_hyp(visit)] return [(restrs, true_hyps, 'true case (%d visited) in %s' % (point, path)), (restrs, false_hyps, 'false case (%d not visited) in %s' % (point, path))] else: assert not 'proof node kind understood', proof.kind
def noHaltHyps(split, p): ret = [] all_halts = callNodes(p, fs=['halt']) for x in all_halts: ret += [ rep_graph.pc_false_hyp((n_vc, p.node_tags[x][0])) for n_vc in default_n_vc_cases(p, x) ] return ret
def mk_not_callable_hyps (p): hyps = [] for n in p.nodes: if p.nodes[n].kind != 'Call': continue if get_asm_callable (p.nodes[n].fname): continue tag = p.node_tags[n][0] hyp = rep_graph.pc_false_hyp ((default_n_vc (p, n), tag)) hyps.append (hyp) return hyps
def mk_not_callable_hyps(p): hyps = [] for n in p.nodes: if p.nodes[n].kind != 'Call': continue if get_asm_callable(p.nodes[n].fname): continue tag = p.node_tags[n][0] hyp = rep_graph.pc_false_hyp((default_n_vc(p, n), tag)) hyps.append(hyp) return hyps
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_loop_hyps (tags, split, restrs, exit): ((r_split, _, _), _, _, n, _) = split (l_visit, _) = split_visit_visits (tags, split, restrs, vc_offs (n - 1)) (l_cont, _) = split_visit_visits (tags, split, restrs, vc_offs (n)) (l_tag, r_tag) = tags l_enter = pc_true_hyp (l_visit) l_exit = pc_false_hyp (l_cont) if exit: hyps = [l_enter, l_exit] else: hyps = [l_enter] return hyps + [hyp for offs in map (vc_offs, range (n)) for (hyp, _) in split_hyps_at_visit (tags, split, restrs, offs)]
def split_loop_hyps(tags, split, restrs, exit): ((r_split, _, _), _, _, n, _) = split (l_visit, _) = split_visit_visits(tags, split, restrs, vc_offs(n - 1)) (l_cont, _) = split_visit_visits(tags, split, restrs, vc_offs(n)) (l_tag, r_tag) = tags l_enter = pc_true_hyp(l_visit) l_exit = pc_false_hyp(l_cont) if exit: hyps = [l_enter, l_exit] else: hyps = [l_enter] return hyps + [ hyp for offs in map(vc_offs, range(n)) for (hyp, _) in split_hyps_at_visit(tags, split, restrs, offs) ]
def build_compound_problem (fnames): """mirrors build_problem from check for multiple functions""" printout ('Building compound problem for %s' % fnames) last_compound_problem_req[0] = list (fnames) p = problem.Problem (None, name = ', '.join(fnames)) fun_tag_pairs = [] all_tags = {} for (i, fn) in enumerate (fnames): i = len (fnames) - i [pair] = pairings[fn] next_tags = {} scripts = get_problem_inline_scripts (pair) for (pair_tag, fname) in pair.funs.items (): tag = '%s_%d_%s' % (fname, i, pair_tag) tag = syntax.fresh_name (tag, all_tags) next_tags[pair_tag] = tag p.add_entry_function (functions[fname], tag) p.hook_tag_hints[tag] = pair_tag p.replay_inline_script (tag, scripts[pair_tag]) fun_tag_pairs.append ((next_tags, pair)) p.pad_merge_points () p.do_analysis () free_hyps = [] for (tags, pair) in fun_tag_pairs: (inp_eqs, _) = pair.eqs free_hyps += check.inst_eqs (p, (), inp_eqs, tags) err_vis_opts = rep_graph.vc_options ([0, 1, 2], [1]) err_vis_vc = tuple ([(n, err_vis_opts) for n in p.loop_heads () if p.node_tags[n][0] == tags['C']]) err_vis = (('Err', err_vis_vc), tags['C']) free_hyps.append (rep_graph.pc_false_hyp (err_vis)) addr_map = {} for n in p.nodes: if not p.node_tags[n][0].endswith ('_ASM'): continue if type (p.node_tags[n][1]) == tuple: (fname, data) = p.node_tags[n][1] if (logic.is_int (data) and is_addr (data) and not fname.startswith ("instruction'")): assert data not in addr_map, data addr_map[data] = n return (p, free_hyps, addr_map, fun_tag_pairs)
def build_compound_problem(fnames): """mirrors build_problem from check for multiple functions""" printout('Building compound problem for %s' % fnames) last_compound_problem_req[0] = list(fnames) p = problem.Problem(None, name=', '.join(fnames)) fun_tag_pairs = [] all_tags = {} for (i, fn) in enumerate(fnames): i = len(fnames) - i [pair] = pairings[fn] next_tags = {} scripts = get_problem_inline_scripts(pair) for (pair_tag, fname) in pair.funs.items(): tag = '%s_%d_%s' % (fname, i, pair_tag) tag = syntax.fresh_name(tag, all_tags) next_tags[pair_tag] = tag p.add_entry_function(functions[fname], tag) p.hook_tag_hints[tag] = pair_tag p.replay_inline_script(tag, scripts[pair_tag]) fun_tag_pairs.append((next_tags, pair)) p.pad_merge_points() p.do_analysis() free_hyps = [] for (tags, pair) in fun_tag_pairs: (inp_eqs, _) = pair.eqs free_hyps += check.inst_eqs(p, (), inp_eqs, tags) err_vis_opts = rep_graph.vc_options([0, 1, 2], [1]) err_vis_vc = tuple([(n, err_vis_opts) for n in p.loop_heads() if p.node_tags[n][0] == tags['C']]) err_vis = (('Err', err_vis_vc), tags['C']) free_hyps.append(rep_graph.pc_false_hyp(err_vis)) addr_map = {} for n in p.nodes: if not p.node_tags[n][0].endswith('_ASM'): continue if type(p.node_tags[n][1]) == tuple: (fname, data) = p.node_tags[n][1] if (logic.is_int(data) and is_addr(data) and not fname.startswith("instruction'")): assert data not in addr_map, data addr_map[data] = n return (p, free_hyps, addr_map, fun_tag_pairs)
def ident_callables (fname, callees, idents): from solver import to_smt_expr, smt_expr from syntax import mk_not, mk_and, true_term auto_callables = dict ([((ident, f, true_term), True) for ident in idents.get (fname, [true_term]) for f in callees if f not in idents]) if not [f for f in callees if f in idents]: return auto_callables fun = functions[fname] p = fun.as_problem (problem.Problem, name = 'Target') check_ns = [(n, ident, cond) for n in p.nodes if p.nodes[n].kind == 'Call' if p.nodes[n].fname in idents for (ident, cond) in ident_conds (p.nodes[n].fname, idents)] p.do_analysis () assert check_ns rep = rep_graph.mk_graph_slice (p, fast = True) err_hyp = rep_graph.pc_false_hyp ((default_n_vc (p, 'Err'), 'Target')) callables = auto_callables nhyps = mk_not_callable_hyps (p) for (ident, cond) in ident_conds (fname, idents): renames = p.entry_exit_renames (tags = ['Target']) cond = syntax.rename_expr (cond, renames['Target_IN']) entry = p.get_entry ('Target') e_vis = ((entry, ()), 'Target') hyps = [err_hyp, rep_graph.eq_hyp ((cond, e_vis), (true_term, e_vis))] for (n, ident2, cond2) in check_ns: k = (ident, p.nodes[n].fname, ident2) (inp_env, _, _) = rep.get_func (default_n_vc (p, n)) pc = rep.get_pc (default_n_vc (p, n)) cond2 = to_smt_expr (cond2, inp_env, rep.solv) if rep.test_hyp_whyps (mk_not (mk_and (pc, cond2)), hyps + nhyps): callables[k] = False else: callables[k] = True return callables
def ident_callables(fname, callees, idents): from solver import to_smt_expr, smt_expr from syntax import mk_not, mk_and, true_term auto_callables = dict([((ident, f, true_term), True) for ident in idents.get(fname, [true_term]) for f in callees if f not in idents]) if not [f for f in callees if f in idents]: return auto_callables fun = functions[fname] p = fun.as_problem(problem.Problem, name='Target') check_ns = [(n, ident, cond) for n in p.nodes if p.nodes[n].kind == 'Call' if p.nodes[n].fname in idents for (ident, cond) in ident_conds(p.nodes[n].fname, idents)] p.do_analysis() assert check_ns rep = rep_graph.mk_graph_slice(p, fast=True) err_hyp = rep_graph.pc_false_hyp((default_n_vc(p, 'Err'), 'Target')) callables = auto_callables nhyps = mk_not_callable_hyps(p) for (ident, cond) in ident_conds(fname, idents): renames = p.entry_exit_renames(tags=['Target']) cond = syntax.rename_expr(cond, renames['Target_IN']) entry = p.get_entry('Target') e_vis = ((entry, ()), 'Target') hyps = [err_hyp, rep_graph.eq_hyp((cond, e_vis), (true_term, e_vis))] for (n, ident2, cond2) in check_ns: k = (ident, p.nodes[n].fname, ident2) (inp_env, _, _) = rep.get_func(default_n_vc(p, n)) pc = rep.get_pc(default_n_vc(p, n)) cond2 = to_smt_expr(cond2, inp_env, rep.solv) if rep.test_hyp_whyps(mk_not(mk_and(pc, cond2)), hyps + nhyps): callables[k] = False else: callables[k] = True return callables
def leaf_condition_checks(p, restrs, hyps): '''checks of the final refinement conditions''' nrerr_pc_hyp = non_r_err_pc_hyp(p.pairing.tags, restrs) hyps = [nrerr_pc_hyp] + hyps [l_tag, r_tag] = p.pairing.tags nlerr_pc = pc_false_hyp((('Err', restrs), l_tag)) # this 'hypothesis' ensures that the representation is built all # the way to Ret. in particular this ensures that function relations # are available to use in proving single-side equalities ret_eq = eq_hyp((true_term, (('Ret', restrs), l_tag)), (true_term, (('Ret', restrs), r_tag))) ### TODO: previously we considered the case where 'Ret' was unreachable ### (as a result of unsatisfiable hyps) and proved a simpler property. ### we might want to restore this (_, out_eqs) = p.pairing.eqs checks = [(hyps + [nlerr_pc, ret_eq], hyp, 'Leaf eq check') for hyp in inst_eqs(p, restrs, out_eqs)] return [(hyps + [ret_eq], nlerr_pc, 'Leaf path-cond imp')] + checks
def leaf_condition_checks (p, restrs, hyps): '''checks of the final refinement conditions''' nrerr_pc_hyp = non_r_err_pc_hyp (p.pairing.tags, restrs) hyps = [nrerr_pc_hyp] + hyps [l_tag, r_tag] = p.pairing.tags nlerr_pc = pc_false_hyp ((('Err', restrs), l_tag)) # this 'hypothesis' ensures that the representation is built all # the way to Ret. in particular this ensures that function relations # are available to use in proving single-side equalities ret_eq = eq_hyp ((true_term, (('Ret', restrs), l_tag)), (true_term, (('Ret', restrs), r_tag))) ### TODO: previously we considered the case where 'Ret' was unreachable ### (as a result of unsatisfiable hyps) and proved a simpler property. ### we might want to restore this (_, out_eqs) = p.pairing.eqs checks = [(hyps + [nlerr_pc, ret_eq], hyp, 'Leaf eq check') for hyp in inst_eqs (p, restrs, out_eqs)] return [(hyps + [ret_eq], nlerr_pc, 'Leaf path-cond imp')] + checks
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
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_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)
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 non_r_err_pc_hyp (tags, restrs): return pc_false_hyp ((('Err', restrs), tags[1]))
def non_r_err_pc_hyp(tags, restrs): return pc_false_hyp((('Err', restrs), tags[1]))
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)]
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_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)