def inline_at_point(p, n, do_analysis=True): node = p.nodes[n] if node.kind != 'Call': return f_nm = node.fname fun = functions[f_nm] (tag, detail) = p.node_tags[n] idx = p.node_tag_revs[(tag, detail)].index(n) p.inline_scripts[tag].append((detail, idx, f_nm)) trace('Inlining %s into %s' % (f_nm, p.name)) if n in p.loop_data: trace(' inlining into loop %d!' % p.loop_id(n)) ex = p.alloc_node(tag, (f_nm, 'RetToCaller')) (ns, vs) = p.add_function(fun, tag, {'Ret': ex}) en = ns[fun.entry] inp_lvs = [(vs[v], typ) for (v, typ) in fun.inputs] p.nodes[n] = Node('Basic', en, azip(inp_lvs, node.args)) out_vs = [mk_var(vs[v], typ) for (v, typ) in fun.outputs] p.nodes[ex] = Node('Basic', node.cont, azip(node.rets, out_vs)) p.cached_analysis.clear() if do_analysis: p.do_analysis() trace('Problem size now %d' % len(p.nodes)) sys.stdin.flush() return ns.values()
def inline_at_point (p, n, do_analysis = True): node = p.nodes[n] if node.kind != 'Call': return f_nm = node.fname fun = functions[f_nm] (tag, detail) = p.node_tags[n] idx = p.node_tag_revs[(tag, detail)].index (n) p.inline_scripts[tag].append ((detail, idx, f_nm)) trace ('Inlining %s into %s' % (f_nm, p.name)) if n in p.loop_data: trace (' inlining into loop %d!' % p.loop_id (n)) ex = p.alloc_node (tag, (f_nm, 'RetToCaller')) (ns, vs) = p.add_function (fun, tag, {'Ret': ex}) en = ns[fun.entry] inp_lvs = [(vs[v], typ) for (v, typ) in fun.inputs] p.nodes[n] = Node ('Basic', en, azip (inp_lvs, node.args)) out_vs = [mk_var (vs[v], typ) for (v, typ) in fun.outputs] p.nodes[ex] = Node ('Basic', node.cont, azip (node.rets, out_vs)) p.cached_analysis.clear () if do_analysis: p.do_analysis () trace ('Problem size now %d' % len(p.nodes)) sys.stdin.flush () return ns.values ()
def adj (seq): (x_stack, y_stack) = seq[stack_idx] xsub = dict ([(v, xv) for (v, (xv, _)) in azip (inps, seq)]) ysub = dict ([(v, yv) for (v, (_, yv)) in azip (inps, seq)]) from logic import var_subst stk_eqs = [(syntax.mk_memacc (x_stack, var_subst (p, xsub), t), syntax.mk_memacc (y_stack, var_subst (p, ysub), t)) for (p, t) in addrs] return seq[:stack_idx] + seq[stack_idx + 1:] + stk_eqs
def adj(seq): (x_stack, y_stack) = seq[stack_idx] xsub = dict([(v, xv) for (v, (xv, _)) in azip(inps, seq)]) ysub = dict([(v, yv) for (v, (_, yv)) in azip(inps, seq)]) from logic import var_subst stk_eqs = [(syntax.mk_memacc(x_stack, var_subst(p, xsub), t), syntax.mk_memacc(y_stack, var_subst(p, ysub), t)) for (p, t) in addrs] return seq[:stack_idx] + seq[stack_idx + 1:] + stk_eqs
def node_const_rets(node): if "instruction'" in node.fname: return inst_const_rets(node) if node.fname in pre_pairings: if pre_pairings[node.fname]['ASM'] != node.fname: return None cc = get_asm_calling_convention_at_node(node) input_set = set( [v for arg in node.args for v in syntax.get_expr_var_set(arg)]) callee_saved_set = set(cc['callee_saved']) return [ mk_var(nm, typ) for (nm, typ) in node.rets if mk_var(nm, typ) in callee_saved_set if (nm, typ) in input_set ] elif preserves_sp(node.fname): if node.fname not in get_functions_with_tag('ASM'): return None f_outs = functions[node.fname].outputs return [ mk_var(nm, typ) for ((nm, typ), (nm2, _)) in azip(node.rets, f_outs) if nm2 == 'r13' ] else: return None
def all_subproblems(self, p, restrs, hyps, name): subproblems = proof_subproblems(p, self.kind, self.args, restrs, hyps, name) subproofs = logic.azip(subproblems, self.subproofs) return [(self, restrs, hyps)] + [ problem for ((restrs2, hyps2, name2), proof) in subproofs for problem in proof.all_subproblems(p, restrs2, hyps2, name2) ]
def proof_checks_rec(p, restrs, hyps, proof, path): checks = proof_checks_imm(p, restrs, hyps, proof, path) subproblems = proof_subproblems(p, proof.kind, proof.args, restrs, hyps, path) for (subprob, subproof) in logic.azip(subproblems, proof.subproofs): (restrs, hyps, path) = subprob checks.extend(proof_checks_rec(p, restrs, hyps, subproof, path)) return checks
def get_asm_calling_convention_at_node (node): cc = get_asm_calling_convention (node.fname) if not cc: return None fun = functions[node.fname] arg_input_map = dict (azip (fun.inputs, node.args)) ret_output_map = dict (azip (fun.outputs, [mk_var (nm, typ) for (nm, typ) in node.rets])) args = [logic.var_subst (arg, arg_input_map) for arg in cc['args']] rets = [logic.var_subst (ret, ret_output_map) for ret in cc['rets']] # these are useful because they happen to map ret r0_input back to # the previous value r0, rather than the useless value r0_input_ignore. rets_inp = [logic.var_subst (ret, arg_input_map) for ret in cc['rets']] saved = [logic.var_subst (v, ret_output_map) for v in cc['callee_saved']] return {'args': args, 'rets': rets, 'rets_inp': rets_inp, 'callee_saved': saved}
def adjusted_var_dep_outputs_for_tag(p, tag): (ent, fname, _) = p.get_entry_details(tag) fun = functions[fname] cc = get_asm_calling_convention(fname) callee_saved_set = set(cc['callee_saved']) ret_set = set([(nm, typ) for ret in cc['rets'] for (nm, typ) in syntax.get_expr_var_set(ret)]) rets = [(nm2, typ) for ((nm, typ), (nm2, _)) in azip(fun.outputs, p.outputs[tag]) if (nm, typ) in ret_set or mk_var(nm, typ) in callee_saved_set] return rets
def adjusted_var_dep_outputs_for_tag (p, tag): (ent, fname, _) = p.get_entry_details (tag) fun = functions[fname] cc = get_asm_calling_convention (fname) callee_saved_set = set (cc['callee_saved']) ret_set = set ([(nm, typ) for ret in cc['rets'] for (nm, typ) in syntax.get_expr_var_set (ret)]) rets = [(nm2, typ) for ((nm, typ), (nm2, _)) in azip (fun.outputs, p.outputs[tag]) if (nm, typ) in ret_set or mk_var (nm, typ) in callee_saved_set] return rets
def get_asm_calling_convention_at_node(node): cc = get_asm_calling_convention(node.fname) if not cc: return None fun = functions[node.fname] arg_input_map = dict(azip(fun.inputs, node.args)) ret_output_map = dict( azip(fun.outputs, [mk_var(nm, typ) for (nm, typ) in node.rets])) args = [logic.var_subst(arg, arg_input_map) for arg in cc['args']] rets = [logic.var_subst(ret, ret_output_map) for ret in cc['rets']] # these are useful because they happen to map ret r0_input back to # the previous value r0, rather than the useless value r0_input_ignore. rets_inp = [logic.var_subst(ret, arg_input_map) for ret in cc['rets']] saved = [logic.var_subst(v, ret_output_map) for v in cc['callee_saved']] return { 'args': args, 'rets': rets, 'rets_inp': rets_inp, 'callee_saved': saved }
def entry_exit_renames(self, tags=None): """computes the rename set of a function's formal parameters to the actual input/output variable names at the various entry and exit points""" mk = lambda xs, ys: dict([(x[0], y[0]) for (x, y) in azip(xs, ys)]) renames = {} if tags == None: tags = self.tags() for tag in tags: (_, fname, args) = self.get_entry_details(tag) fun = functions[fname] out = self.outputs[tag] renames[tag + '_IN'] = mk(fun.inputs, args) renames[tag + '_OUT'] = mk(fun.outputs, out) return renames
def entry_exit_renames(self, tags=None): """computes the rename set of a function's formal parameters to the actual input/output variable names at the various entry and exit points""" mk = lambda xs, ys: dict([(x[0], y[0]) for (x, y) in azip(xs, ys)]) renames = {} if tags == None: tags = self.tags() for tag in tags: (_, fname, args) = self.get_entry_details(tag) fun = functions[fname] out = self.outputs[tag] renames[tag + "_IN"] = mk(fun.inputs, args) renames[tag + "_OUT"] = mk(fun.outputs, out) return renames
def inst_const_rets (node): assert "instruction'" in node.fname bits = set ([s.lower () for s in node.fname.split ('_')]) fun = functions[node.fname] def is_const (nm, typ): if typ in [builtinTs['Mem'], builtinTs['Dom']]: return True if typ != word32T: return False return not (nm in bits or [al for al in reg_aliases.get (nm, []) if al in bits]) is_consts = [is_const (nm, typ) for (nm, typ) in fun.outputs] input_set = set ([v for arg in node.args for v in syntax.get_expr_var_set (arg)]) return [mk_var (nm, typ) for ((nm, typ), const) in azip (node.rets, is_consts) if const and (nm, typ) in input_set]
def mk_function_link_hyps (p, call_vis, tag, adjust_eq_seq = None): (entry, _, args) = p.get_entry_details (tag) ((call_site, restrs), call_tag) = call_vis assert p.nodes[call_site].kind == 'Call' entry_vis = ((entry, ()), p.node_tags[entry][0]) args = [syntax.mk_var (nm, typ) for (nm, typ) in args] pc = pc_true_hyp (call_vis) eq_seq = logic.azip (p.nodes[call_site].args, args) if adjust_eq_seq: eq_seq = adjust_eq_seq (eq_seq) hyps = [pc] + [eq_hyp ((x, call_vis), (y, entry_vis)) for (x, y) in eq_seq if x.typ.kind == 'Word' or x.typ == syntax.builtinTs['Mem'] or x.typ.kind == 'WordArray'] return hyps
def proof_checks_rec (p, restrs, hyps, proof, path): if proof.kind == 'Restr': checks = proof_restr_checks (proof.point, proof.restr_range, p, restrs, hyps) elif proof.kind == 'Split': checks = split_checks (p, restrs, hyps, proof.split) elif proof.kind == 'Leaf': checks = leaf_condition_checks (p, restrs, hyps) elif proof.kind == 'CaseSplit': checks = [] checks = [(hs, hyp, '%s on %s' % (name, path)) for (hs, hyp, name) in checks] subproblems = proof_subproblems (p, proof.kind, proof.args, restrs, hyps, path) for (subprob, subproof) in logic.azip (subproblems, proof.subproofs): (restrs, hyps, path) = subprob checks.extend (proof_checks_rec (p, restrs, hyps, subproof, path)) return checks
def inst_const_rets(node): assert "instruction'" in node.fname bits = set([s.lower() for s in node.fname.split('_')]) fun = functions[node.fname] def is_const(nm, typ): if typ in [builtinTs['Mem'], builtinTs['Dom']]: return True if typ != word32T: return False return not (nm in bits or [al for al in reg_aliases.get(nm, []) if al in bits]) is_consts = [is_const(nm, typ) for (nm, typ) in fun.outputs] input_set = set( [v for arg in node.args for v in syntax.get_expr_var_set(arg)]) return [ mk_var(nm, typ) for ((nm, typ), const) in azip(node.rets, is_consts) if const and (nm, typ) in input_set ]
def check_proof_report_rec(p, restrs, hyps, proof, step_num, ctxt, inducts, do_check=True): printout('Step %d: %s' % (step_num, ctxt)) if proof.kind == 'Restr': (kind, (x, y)) = proof.restr_range if kind == 'Offset': v = inducts[1][proof.point] rexpr = '{%s + %s ..< %s + %s}' % (v, x, v, y) else: rexpr = '{%s ..< %s}' % (x, y) printout(' Prove the number of visits to %d is in %s' % (proof.point, rexpr)) checks = proof_restr_checks(proof.point, proof.restr_range, p, restrs, hyps) cases = [''] elif proof.kind == 'SingleRevInduct': printout(' Proving a predicate by future induction.') (eqs, n) = proof.eqs_proof point = proof.point printout(' proving these invariants by %d-induction' % n) for x in eqs: printout(' %s (@ addr %s)' % (pretty_lambda(x), point)) printout(' then establishing this predicate') (pred, n_bound) = proof.rev_proof printout(' %s (@ addr %s)' % (pretty_lambda(pred), point)) printout(' at large iterations (%d) and by back induction.' % n_bound) cases = [''] checks = all_rev_induct_checks(p, restrs, hyps, point, proof.eqs_proof, proof.rev_proof) elif proof.kind == 'Split': (l_dts, r_dts, eqs, n, lrmx) = proof.split v = next_induct_var(inducts[0]) inducts = (inducts[0] + 1, dict(inducts[1])) inducts[1][l_dts[0]] = v inducts[1][r_dts[0]] = v printout(' prove %s related to %s' % (pretty_vseq(l_dts), pretty_vseq(r_dts))) printout(' with equalities') for (x, y) in eqs: printout(' %s (@ addr %s)' % (pretty_lambda(x), l_dts[0])) printout(' = %s (@ addr %s)' % (pretty_lambda(y), r_dts[0])) printout(' and with invariants') for x in l_dts[2]: printout(' %s (@ addr %s)' % (pretty_lambda(x), l_dts[0])) for x in r_dts[2]: printout(' %s (@ addr %s)' % (pretty_lambda(x), r_dts[0])) checks = split_checks(p, restrs, hyps, proof.split) cases = [ 'case in (%d) where the length of the sequence < %d' % (step_num, n), 'case in (%d) where the length of the sequence is %s + %s' % (step_num, v, n) ] elif proof.kind == 'Leaf': printout(' prove all verification conditions') checks = leaf_condition_checks(p, restrs, hyps) cases = [] elif proof.kind == 'CaseSplit': printout(' case split on whether %d is visited' % proof.point) checks = [] cases = [ 'case in (%d) where %d is visited' % (step_num, proof.point), 'case in (%d) where %d is not visited' % (step_num, proof.point) ] if checks and do_check: groups = proof_check_groups(checks) for group in groups: rep = rep_graph.mk_graph_slice(p) detail = [0] (res, _) = test_hyp_group(rep, group, detail) if not res: printout(' .. failed to prove this.') printout(' (failure kind: %r)' % detail[0]) return printout(' .. proven.') subproblems = proof_subproblems(p, proof.kind, proof.args, restrs, hyps, '') xs = logic.azip(subproblems, proof.subproofs) xs = logic.azip(xs, cases) step_num += 1 for ((subprob, subproof), case) in xs: (restrs, hyps, _) = subprob res = check_proof_report_rec(p, restrs, hyps, subproof, step_num, case, inducts, do_check=do_check) if not res: return (step_num, induct_var_num) = res inducts = (induct_var_num, inducts[1]) return (step_num, inducts[0])
def check_proof_report_rec (p, restrs, hyps, proof, step_num, ctxt, inducts): import sys printout ('Step %d: %s' % (step_num, ctxt)) if proof.kind == 'Restr': (kind, (x, y)) = proof.restr_range if kind == 'Offset': v = inducts[1][proof.point] rexpr = '{%s + %s ..< %s + %s}' % (v, x, v, y) else: rexpr = '{%s ..< %s}' % (x, y) printout (' Prove the number of visits to %d is in %s' % (proof.point, rexpr)) checks = proof_restr_checks (proof.point, proof.restr_range, p, restrs, hyps) cases = [''] elif proof.kind == 'Split': (l_dts, r_dts, eqs, n, lrmx) = proof.split v = next_induct_var (inducts[0]) inducts = (inducts[0] + 1, dict (inducts[1])) inducts[1][l_dts[0]] = v inducts[1][r_dts[0]] = v printout (' prove %s related to %s' % (pretty_vseq (l_dts), pretty_vseq (r_dts))) printout (' with equalities') for (x, y) in eqs: printout (' %s (@ addr %s)' % (pretty_lambda (x), l_dts[0])) printout (' = %s (@ addr %s)' % (pretty_lambda (y), r_dts[0])) printout (' and with invariants') for x in l_dts[2]: printout (' %s (@ addr %s)' % (pretty_lambda (x), l_dts[0])) for x in r_dts[2]: printout (' %s (@ addr %s)' % (pretty_lambda (x), r_dts[0])) checks = split_checks (p, restrs, hyps, proof.split) cases = ['case in (%d) where the length of the sequence < %d' % (step_num, n), 'case in (%d) where the length of the sequence is %s + %s' % (step_num, v, n)] elif proof.kind == 'Leaf': printout (' prove all verification conditions') checks = leaf_condition_checks (p, restrs, hyps) cases = [] elif proof.kind == 'CaseSplit': printout (' case split on whether %d is visited' % proof.point) checks = [] cases = ['case in (%d) where %d is visited' % (step_num, proof.point), 'case in (%d) where %d is not visited' % (step_num, proof.point)] if checks: groups = proof_check_groups (checks) for group in groups: rep = rep_graph.mk_graph_slice (p) (res, _) = test_hyp_group (rep, group) if not res: printout (' .. failed to prove this.') sys.stdout.flush () return printout (' .. proven.') sys.stdout.flush () subproblems = proof_subproblems (p, proof.kind, proof.args, restrs, hyps, '') xs = logic.azip (subproblems, proof.subproofs) xs = logic.azip (xs, cases) step_num += 1 for ((subprob, subproof), case) in xs: (restrs, hyps, _) = subprob res = check_proof_report_rec (p, restrs, hyps, subproof, step_num, case, inducts) if not res: return (step_num, induct_var_num) = res inducts = (induct_var_num, inducts[1]) return (step_num, inducts[0])
def emit_node (self, n): (pc, env) = self.get_node_pc_env (n, request = False) tag = self.p.node_tags[n[0]][0] app_eqs = self.apply_known_eqs_tm (n, tag) # node = logic.simplify_node_elementary (self.p.nodes[n[0]]) # whether to ignore unreachable Cond arcs seems to be a huge # dilemma. if we ignore them, some reachable sites become # unreachable and we can't interpret all hyps # if we don't ignore them, the variable set disagrees with # var_deps and so the abstracted loop pc/env may not be # sufficient and we get EnvMiss again. I don't really know # what to do about this corner case. node = self.p.nodes[n[0]] env = dict (env) if node.kind == 'Call': self.try_inline (n[0], pc, env) if pc == false_term: return [(c, false_term, {}) for c in node.get_conts()] elif node.kind == 'Cond' and node.left == node.right: return [(node.left, pc, env)] elif node.kind == 'Cond' and node.cond == true_term: return [(node.left, pc, env), (node.right, false_term, env)] elif node.kind == 'Basic': upds = [] for (lv, v) in node.upds: if v.kind == 'Var': upds.append ((lv, env[(v.name, v.typ)])) else: name = self.local_name (lv[0], n) v = app_eqs (v) vname = self.add_local_def (n, ('Var', lv), name, v, env) upds.append ((lv, vname)) for (lv, v) in upds: env[lv] = v return [(node.cont, pc, env)] elif node.kind == 'Cond': name = self.cond_name (n) cond = self.p.fresh_var (name, boolT) env[(cond.name, boolT)] = self.add_local_def (n, 'Cond', name, app_eqs (node.cond), env) lpc = mk_and (cond, pc) rpc = mk_and (mk_not (cond), pc) return [(node.left, lpc, env), (node.right, rpc, env)] elif node.kind == 'Call': nm = self.success_name (node.fname, n) success = self.solv.add_var (nm, boolT) success = mk_smt_expr (success, boolT) fun = functions[node.fname] ins = dict ([((x, typ), smt_expr (app_eqs (arg), env, self.solv)) for ((x, typ), arg) in azip (fun.inputs, node.args)]) mem_name = None for (x, typ) in reversed (fun.inputs): if typ == builtinTs['Mem']: mem_name = (node.fname, ins[(x, typ)]) outs = {} for ((x, typ), (y, typ2)) in azip (node.rets, fun.outputs): assert typ2 == typ if self.fast_const_ret (n[0], x, typ): outs[(y, typ2)] = env [(x, typ)] continue name = self.local_name (x, n) env[(x, typ)] = self.solv.add_var_restr (name, typ, mem_name = mem_name) outs[(y, typ2)] = env[(x, typ)] for ((x, typ), (y, _)) in azip (node.rets, fun.outputs): z = self.var_rep_request ((x, typ), 'Call', n, env) if z != None: env[(x, typ)] = z outs[(y, typ)] = z self.add_func (node.fname, ins, outs, success, n) return [(node.cont, pc, env)] else: assert not 'node kind understood'