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_induct_eq_hyp(p, split, restrs, n): details = (split, (0, 1), []) (tag, _) = p.node_tags[split] visit = split_visit_one_visit(tag, details, restrs, vc_offs(0)) from syntax import mk_var, word32T, mk_word32 return eq_hyp((mk_var("%n", word32T), visit), (mk_word32(n), visit), (split, 0))
def build_compound_problem_with_links(call_stack, f): funs = [get_body_addrs_fun(addr) for addr in call_stack] + [f] (p, hyps, addr_map, tag_pairs) = build_compound_problem(funs) call_tags = zip(tag_pairs[:-1], tag_pairs[1:]) call_hyps = [ get_call_link_hyps(p, addr_map[n], from_tp, to_tp) for (n, (from_tp, to_tp)) in zip(call_stack, call_tags) ] wcet_hyps = [] from rep_graph import eq_hyp for (entry, tag, _, inputs) in p.entries: entry_vis = ((entry, ()), tag) for f in target_objects.hooks("extra_wcet_assertions"): for assn in f(inputs): wcet_hyps.append(eq_hyp((assn, entry_vis), (syntax.true_term, entry_vis))) return (p, hyps + [h for hs in call_hyps for h in hs] + wcet_hyps, addr_map)
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 inst_eqs(p, restrs, eqs, tag_map={}): addr_map = {} if not tag_map: tag_map = dict([(tag, tag) for tag in p.tags()]) for (pair_tag, p_tag) in tag_map.iteritems(): addr_map[pair_tag + '_IN'] = ((p.get_entry(p_tag), ()), p_tag) addr_map[pair_tag + '_OUT'] = (('Ret', restrs), p_tag) renames = p.entry_exit_renames(tag_map.values()) for (pair_tag, p_tag) in tag_map.iteritems(): renames[pair_tag + '_IN'] = renames[p_tag + '_IN'] renames[pair_tag + '_OUT'] = renames[p_tag + '_OUT'] hyps = [] for (lhs, rhs) in eqs: vals = [(rename_expr(x, renames[x_addr]), addr_map[x_addr]) for (x, x_addr) in (lhs, rhs)] hyps.append(eq_hyp(vals[0], vals[1])) return hyps
def inst_eqs (p, restrs, eqs, tag_map = {}): addr_map = {} if not tag_map: tag_map = dict ([(tag, tag) for tag in p.tags ()]) for (pair_tag, p_tag) in tag_map.iteritems (): addr_map[pair_tag + '_IN'] = ((p.get_entry (p_tag), ()), p_tag) addr_map[pair_tag + '_OUT'] = (('Ret', restrs), p_tag) renames = p.entry_exit_renames (tag_map.values ()) for (pair_tag, p_tag) in tag_map.iteritems (): renames[pair_tag + '_IN'] = renames[p_tag + '_IN'] renames[pair_tag + '_OUT'] = renames[p_tag + '_OUT'] hyps = [] for (lhs, rhs) in eqs: vals = [(rename_expr (x, renames[x_addr]), addr_map[x_addr]) for (x, x_addr) in (lhs, rhs)] hyps.append (eq_hyp (vals[0], vals[1])) return hyps
def build_compound_problem_with_links(call_stack, f): funs = [get_body_addrs_fun(addr) for addr in call_stack] + [f] (p, hyps, addr_map, tag_pairs) = build_compound_problem(funs) call_tags = zip(tag_pairs[:-1], tag_pairs[1:]) call_hyps = [ get_call_link_hyps(p, addr_map[n], from_tp, to_tp) for (n, (from_tp, to_tp)) in zip(call_stack, call_tags) ] wcet_hyps = [] from rep_graph import eq_hyp for (entry, tag, _, inputs) in p.entries: entry_vis = ((entry, ()), tag) for f in target_objects.hooks('extra_wcet_assertions'): for assn in f(inputs): wcet_hyps.append( eq_hyp((assn, entry_vis), (syntax.true_term, entry_vis))) return (p, hyps + [h for hs in call_hyps for h in hs] + wcet_hyps, addr_map)
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 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 add_recursion_ident (f, group, idents, extra_unfolds): from syntax import mk_eq, mk_implies, mk_var p = problem.Problem (None, name = 'Recursion Test') chain = [] tag = 'fun0' p.add_entry_function (functions[f], tag) p.do_analysis () assns = [] recursion_last_assns[0] = assns while True: res = find_unknown_recursion (p, group, idents, tag, assns, extra_unfolds) if res == None: break if p.nodes[res].fname not in group: problem.inline_at_point (p, res) continue fname = p.nodes[res].fname chain.append (fname) tag = 'fun%d' % len (chain) (args, _, entry) = p.add_entry_function (functions[fname], tag) p.do_analysis () assns += function_link_assns (p, res, tag) if chain == []: return None recursion_trace.append (' created fun chain %s' % chain) word_args = [(i, mk_var (s, typ)) for (i, (s, typ)) in enumerate (args) if typ.kind == 'Word'] rep = rep_graph.mk_graph_slice (p, fast = True) (_, env) = rep.get_node_pc_env ((entry, ())) m = {} res = rep.test_hyp_whyps (syntax.false_term, assns, model = m) assert m if find_unknown_recursion (p, group, idents, tag, [], []) == None: idents.setdefault (fname, []) idents[fname].append (syntax.true_term) recursion_trace.append (' found final ident for %s' % fname) return syntax.true_term assert word_args recursion_trace.append (' scanning for ident for %s' % fname) for (i, arg) in word_args: (nm, typ) = functions[fname].inputs[i] arg_smt = solver.to_smt_expr (arg, env, rep.solv) val = search.eval_model_expr (m, rep.solv, arg_smt) if not rep.test_hyp_whyps (mk_eq (arg_smt, val), assns): recursion_trace.append (' discarded %s = 0x%x, not stable' % (nm, val.val)) continue entry_vis = ((entry, ()), tag) ass = rep_graph.eq_hyp ((arg, entry_vis), (val, entry_vis)) res = find_unknown_recursion (p, group, idents, tag, assns + [ass], []) if res: fname2 = p.nodes[res].fname recursion_trace.append (' discarded %s, allows recursion to %s' % (nm, fname2)) continue eq = syntax.mk_eq (mk_var (nm, typ), val) idents.setdefault (fname, []) idents[fname].append (eq) recursion_trace.append (' found ident for %s: %s' % (fname, eq)) return eq assert not "identifying assertion found"
def mk_loop_counter_eq_hyp(p, split, restrs, n): details = (split, (0, 1), []) (tag, _) = p.node_tags[split] visit = split_visit_one_visit(tag, details, restrs, vc_offs(0)) return eq_hyp((mk_var('%n', word32T), visit), (mk_word32(n), visit), (split, 0))
def init_true_hyp(p, tag, expr): n = p.get_entry(tag) vis = ((n, ()), tag) assert expr.typ == syntax.boolT, expr return rep_graph.eq_hyp((expr, vis), (syntax.true_term, vis))
def add_recursion_ident(f, group, idents, extra_unfolds): from syntax import mk_eq, mk_implies, mk_var p = problem.Problem(None, name='Recursion Test') chain = [] tag = 'fun0' p.add_entry_function(functions[f], tag) p.do_analysis() assns = [] recursion_last_assns[0] = assns while True: res = find_unknown_recursion(p, group, idents, tag, assns, extra_unfolds) if res == None: break if p.nodes[res].fname not in group: problem.inline_at_point(p, res) continue fname = p.nodes[res].fname chain.append(fname) tag = 'fun%d' % len(chain) (args, _, entry) = p.add_entry_function(functions[fname], tag) p.do_analysis() assns += function_link_assns(p, res, tag) if chain == []: return None recursion_trace.append(' created fun chain %s' % chain) word_args = [(i, mk_var(s, typ)) for (i, (s, typ)) in enumerate(args) if typ.kind == 'Word'] rep = rep_graph.mk_graph_slice(p, fast=True) (_, env) = rep.get_node_pc_env((entry, ())) m = {} res = rep.test_hyp_whyps(syntax.false_term, assns, model=m) assert m if find_unknown_recursion(p, group, idents, tag, [], []) == None: idents.setdefault(fname, []) idents[fname].append(syntax.true_term) recursion_trace.append(' found final ident for %s' % fname) return syntax.true_term assert word_args recursion_trace.append(' scanning for ident for %s' % fname) for (i, arg) in word_args: (nm, typ) = functions[fname].inputs[i] arg_smt = solver.to_smt_expr(arg, env, rep.solv) val = search.eval_model_expr(m, rep.solv, arg_smt) if not rep.test_hyp_whyps(mk_eq(arg_smt, val), assns): recursion_trace.append(' discarded %s = 0x%x, not stable' % (nm, val.val)) continue entry_vis = ((entry, ()), tag) ass = rep_graph.eq_hyp((arg, entry_vis), (val, entry_vis)) res = find_unknown_recursion(p, group, idents, tag, assns + [ass], []) if res: fname2 = p.nodes[res].fname recursion_trace.append( ' discarded %s, allows recursion to %s' % (nm, fname2)) continue eq = syntax.mk_eq(mk_var(nm, typ), val) idents.setdefault(fname, []) idents[fname].append(eq) recursion_trace.append(' found ident for %s: %s' % (fname, eq)) return eq assert not "identifying assertion found"
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