def mk_bin_inst_spec(fname): if not fname.startswith("instruction'"): return if functions[fname].entry: return (_, ident) = fname.split("'", 1) (ident, addr) = split_inst_name_addr(ident) (regs, ident) = split_inst_name_regs(ident) ident = instruction_name_aliases.get(ident, ident) base_ident = ident.split("_")[0] if base_ident not in instruction_fun_specs: return (impl_fname, regspecs) = instruction_fun_specs[base_ident] add_impl_fun(impl_fname, regspecs) assert len(regspecs) == len(regs), (fname, regs, regspecs) inp_regs = [reg for (reg, d) in zip(regs, regspecs) if d == 'I'] out_regs = [reg for (reg, d) in zip(regs, regspecs) if d == 'O'] call = syntax.Node( 'Call', 'Ret', ('l_' + impl_fname, [syntax.mk_var(reg, syntax.word32T) for reg in inp_regs] + [syntax.mk_token(ident)] + [syntax.mk_var(nm, typ) for (nm, typ) in bin_globs], [(reg, syntax.word32T) for reg in out_regs] + bin_globs)) assert not functions[fname].nodes functions[fname].nodes[1] = call functions[fname].entry = 1
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 mk_bin_inst_spec (fname): if not fname.startswith ("instruction'"): return if functions[fname].entry: return (_, ident) = fname.split ("'", 1) (ident, addr) = split_inst_name_addr (ident) (regs, ident) = split_inst_name_regs (ident) ident = instruction_name_aliases.get (ident, ident) base_ident = ident.split ("_")[0] if base_ident not in instruction_fun_specs: return (impl_fname, regspecs) = instruction_fun_specs[base_ident] add_impl_fun (impl_fname, regspecs) assert len (regspecs) == len (regs), (fname, regs, regspecs) inp_regs = [reg for (reg, d) in zip (regs, regspecs) if d == 'I'] out_regs = [reg for (reg, d) in zip (regs, regspecs) if d == 'O'] call = syntax.Node ('Call', 'Ret', (impl_fname, [syntax.mk_var (reg, syntax.word32T) for reg in inp_regs] + [syntax.mk_token (ident)] + [syntax.mk_var (nm, typ) for (nm, typ) in bin_globs], [(reg, syntax.word32T) for reg in out_regs] + bin_globs)) assert not functions[fname].nodes functions[fname].nodes[1] = call functions[fname].entry = 1
def get_stack_sp(p, tag): """get stack and stack-pointer variables""" entry = p.get_entry(tag) renames = p.entry_exit_renames(tags=[tag]) r = renames[tag + '_IN'] sp = syntax.rename_expr(mk_var('r13', syntax.word32T), r) stack = syntax.rename_expr(mk_var('stack', syntax.builtinTs['Mem']), r) return (stack, sp)
def get_stack_sp (p, tag): """get stack and stack-pointer variables""" entry = p.get_entry (tag) renames = p.entry_exit_renames (tags = [tag]) r = renames[tag + '_IN'] sp = syntax.rename_expr (mk_var ('r13', syntax.word32T), r) stack = syntax.rename_expr (mk_var ('stack', syntax.builtinTs['Mem']), r) return (stack, sp)
def node_const_rets (node): if "instruction'" in node.fname: return inst_const_rets (node) if node.fname not in pre_pairings: return None 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]
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 stack_virtualise_expr(expr, sp_offs): if expr.is_op('MemAcc') and is_stack(expr.vals[0]): [m, p] = expr.vals if expr.typ == syntax.word8T: ps = [(syntax.mk_minus(p, syntax.mk_word32(n)), n) for n in [0, 1, 2, 3]] elif expr.typ == syntax.word32T: ps = [(p, 0)] else: assert expr.typ == syntax.word32T, expr ptrs = [(p, 'MemAcc') for (p, _) in ps] if sp_offs == None: return (ptrs, None) # FIXME: very 32-bit specific ps = [(p, n) for (p, n) in ps if p in sp_offs if sp_offs[p][1] % 4 == 0] if not ps: return (ptrs, expr) [(p, n)] = ps if p not in sp_offs: raise StackOffsMissing() (k, offs) = sp_offs[p] v = mk_var(('Fake', k, offs), syntax.word32T) if n != 0: v = syntax.mk_shiftr(v, n * 8) v = syntax.mk_cast(v, expr.typ) return (ptrs, v) elif expr.kind == 'Op': vs = [stack_virtualise_expr(v, sp_offs) for v in expr.vals] return ([p for (ptrs, _) in vs for p in ptrs], syntax.adjust_op_vals(expr, [v for (_, v) in vs])) else: return ([], expr)
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 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 mk_seq_eqs (p, split, step, with_rodata): # eqs take the form of a number of constant expressions eqs = [] # the variable 'loop' will be converted to the point in # the sequence - note this should be multiplied by the step size loop = mk_var ('%i', word32T) if step == 1: minus_loop_step = mk_uminus (loop) else: minus_loop_step = mk_times (loop, mk_word32 (- step)) for (var, data) in get_loop_var_analysis_at (p, split): if data == 'LoopVariable': if with_rodata and var.typ == builtinTs['Mem']: eqs.append (logic.mk_rodata (var)) elif data == 'LoopConst': if var.typ not in syntax.phantom_types: eqs.append (var) elif data == 'LoopLeaf': continue elif data[0] == 'LoopLinearSeries': (_, form, _) = data eqs.append (form (var, minus_loop_step)) else: assert not 'var_deps type understood' return eqs
def stack_virtualise_expr (expr, sp_offs): if expr.is_op ('MemAcc') and is_stack (expr.vals[0]): [m, p] = expr.vals if expr.typ == syntax.word8T: ps = [(syntax.mk_minus (p, syntax.mk_word32 (n)), n) for n in [0, 1, 2, 3]] elif expr.typ == syntax.word32T: ps = [(p, 0)] else: assert expr.typ == syntax.word32T, expr ptrs = [(p, 'MemAcc') for (p, _) in ps] if sp_offs == None: return (ptrs, None) # FIXME: very 32-bit specific ps = [(p, n) for (p, n) in ps if p in sp_offs if sp_offs[p][1] % 4 == 0] if not ps: return (ptrs, expr) [(p, n)] = ps (k, offs) = sp_offs[p] v = mk_var (('Fake', k, offs), syntax.word32T) if n != 0: v = syntax.mk_shiftr (v, n * 8) v = syntax.mk_cast (v, expr.typ) return (ptrs, v) elif expr.kind == 'Op': vs = [stack_virtualise_expr (v, sp_offs) for v in expr.vals] return ([p for (ptrs, _) in vs for p in ptrs], syntax.Expr ('Op', expr.typ, name = expr.name, vals = [v for (_, v) in vs])) else: return ([], expr)
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 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 adjust_ret_ptr (ptr): """this is a bit of a hack. the return slots are named based on r0_input, which will be unchanged, which is handy, but we really want to be talking about r0, which will produce meaningful offsets against the pointers actually used in the program.""" return logic.var_subst (ptr, {('r0_input', syntax.word32T): syntax.mk_var ('r0', syntax.word32T)}, must_subst = False)
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 compute_loop_var_analysis (nodes, var_deps, n, loop, preds): upd_vs = set ([v for n2 in loop if not nodes[n2].is_noop () for v in nodes[n2].get_lvals ()]) const_vs = set ([v for n2 in loop for v in var_deps[n2] if v not in upd_vs]) vca = compute_var_cycle_analysis (nodes, n, loop, preds, const_vs, set (var_deps[n])) vca = [(syntax.mk_var (nm, typ), data) for ((nm, typ), data) in vca.items ()] return vca
def get_asm_calling_convention_inner(num_c_args, num_c_rets, const_mem): key = ('Inner', num_c_args, num_c_rets, const_mem) if key in asm_cc_cache: return asm_cc_cache[key] from logic import mk_var_list, mk_stack_sequence from syntax import mk_var, word32T, builtinTs arg_regs = mk_var_list(['r0', 'r1', 'r2', 'r3'], word32T) r0 = arg_regs[0] sp = mk_var('r13', word32T) st = mk_var('stack', builtinTs['Mem']) r0_input = mk_var('ret_addr_input', word32T) mem = mk_var('mem', builtinTs['Mem']) dom = mk_var('dom', builtinTs['Dom']) dom_stack = mk_var('dom_stack', builtinTs['Dom']) global_args = [mem, dom, st, dom_stack, sp, mk_var('ret', word32T)] sregs = mk_stack_sequence(sp, 4, st, word32T, num_c_args + 1) arg_seq = [r for r in arg_regs] + [s for (s, _) in sregs] if num_c_rets > 1: # the 'return-too-much' issue. # instead r0 is a save-returns-here pointer arg_seq.pop(0) rets = mk_stack_sequence(r0_input, 4, st, word32T, num_c_rets) rets = [r for (r, _) in rets] else: rets = [r0] callee_saved_vars = ( [mk_var(v, word32T) for v in 'r4 r5 r6 r7 r8 r9 r10 r11 r13'.split()] + [dom, dom_stack]) if const_mem: callee_saved_vars += [mem] else: rets += [mem] rets += [st] cc = { 'args': arg_seq[:num_c_args] + global_args, 'rets': rets, 'callee_saved': callee_saved_vars } asm_cc_cache[key] = cc return cc
def add_impl_fun(impl_fname, regspecs): l_fname = 'l_' + impl_fname r_fname = 'r_' + impl_fname if l_fname in functions: return assert r_fname not in functions ident_v = ("inst_ident", syntax.builtinTs['Token']) inps = [s for s in regspecs if s == 'I'] inps = ['reg_val%d' % (i + 1) for (i, s) in enumerate(inps)] rets = [s for s in regspecs if s == 'O'] rets = ['ret_val%d' % (i + 1) for (i, s) in enumerate(rets)] l_fun = mk_fun(l_fname, inps, [ident_v], rets, [], bin_globs) r_fun = mk_fun(r_fname, inps, [ident_v], rets, [], bin_globs) inp_eqs = [((mk_var(nm, typ), 'ASM_IN'), (mk_var(nm, typ), 'C_IN')) for (nm, typ) in l_fun.inputs] inp_eqs += [((logic.mk_rodata(mk_var(nm, typ)), 'ASM_IN'), (syntax.true_term, 'C_IN')) for (nm, typ) in bin_globs] out_eqs = [((mk_var(nm, typ), 'ASM_OUT'), (mk_var(nm, typ), 'C_OUT')) for (nm, typ) in l_fun.outputs] out_eqs += [((logic.mk_rodata(mk_var(nm, typ)), 'ASM_OUT'), (syntax.true_term, 'C_OUT')) for (nm, typ) in bin_globs] pair = logic.Pairing(['ASM', 'C'], { 'ASM': l_fname, 'C': r_fname }, (inp_eqs, out_eqs)) assert l_fname not in pairings assert r_fname not in pairings functions[l_fname] = l_fun functions[r_fname] = r_fun pairings[l_fname] = [pair] pairings[r_fname] = [pair]
def adjust_ret_ptr(ptr): """this is a bit of a hack. the return slots are named based on r0_input, which will be unchanged, which is handy, but we really want to be talking about r0, which will produce meaningful offsets against the pointers actually used in the program.""" return logic.var_subst(ptr, { ('ret_addr_input', syntax.word32T): syntax.mk_var('r0', syntax.word32T) }, must_subst=False)
def pseudo_node_lvals_rvals(node): assert node.kind == 'Call' cc = get_asm_calling_convention_at_node(node) if not cc: return None arg_vars = set( [var for arg in cc['args'] for var in syntax.get_expr_var_set(arg)]) callee_saved_set = set(cc['callee_saved']) rets = [(nm, typ) for (nm, typ) in node.rets if mk_var(nm, typ) not in callee_saved_set] return (rets, arg_vars)
def pseudo_node_lvals_rvals (node): assert node.kind == 'Call' cc = get_asm_calling_convention_at_node (node) if not cc: return None arg_vars = set ([var for arg in cc['args'] for var in syntax.get_expr_var_set (arg)]) callee_saved_set = set (cc['callee_saved']) rets = [(nm, typ) for (nm, typ) in node.rets if mk_var (nm, typ) not in callee_saved_set] return (rets, arg_vars)
def get_asm_calling_convention_inner (num_c_args, num_c_rets, const_mem): key = ('Inner', num_c_args, num_c_rets, const_mem) if key in asm_cc_cache: return asm_cc_cache[key] from logic import mk_var_list, mk_stack_sequence from syntax import mk_var, word32T, builtinTs arg_regs = mk_var_list (['r0', 'r1', 'r2', 'r3'], word32T) r0 = arg_regs[0] sp = mk_var ('r13', word32T) st = mk_var ('stack', builtinTs['Mem']) r0_input = mk_var ('r0_input', word32T) mem = mk_var ('mem', builtinTs['Mem']) dom = mk_var ('dom', builtinTs['Dom']) dom_stack = mk_var ('dom_stack', builtinTs['Dom']) global_args = [mem, dom, st, dom_stack, sp, mk_var ('ret', word32T)] sregs = mk_stack_sequence (sp, 4, st, word32T, num_c_args + 1) arg_seq = [r for r in arg_regs] + [s for (s, _) in sregs] if num_c_rets > 1: # the 'return-too-much' issue. # instead r0 is a save-returns-here pointer arg_seq.pop (0) rets = mk_stack_sequence (r0_input, 4, st, word32T, num_c_rets) rets = [r for (r, _) in rets] else: rets = [r0] callee_saved_vars = ([mk_var (v, word32T) for v in 'r4 r5 r6 r7 r8 r9 r10 r11 r13'.split ()] + [dom, dom_stack]) if const_mem: callee_saved_vars += [mem] else: rets += [mem] rets += [st] cc = {'args': arg_seq[: num_c_args] + global_args, 'rets': rets, 'callee_saved': callee_saved_vars} asm_cc_cache[key] = cc return cc
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 candidate_additional_eqs(p, split): eq_vals = set() def visitor(expr): if expr.is_op('Equals') and expr.vals[0].typ.kind == 'Word': [x, y] = expr.vals eq_vals.update([(x, y), (y, x)]) for n in p.loop_body(split): p.nodes[n].visit(lambda x: (), visitor) for (x, y) in list(eq_vals): if is_zero(x) and y.is_op('Plus'): [x, y] = y.vals eq_vals.add((x, syntax.mk_uminus(y))) eq_vals.add((y, syntax.mk_uminus(x))) elif is_zero(x) and y.is_op('Minus'): [x, y] = y.vals eq_vals.add((x, y)) eq_vals.add((y, x)) loop = syntax.mk_var('%i', syntax.word32T) minus_loop_step = syntax.mk_uminus(loop) vas = search.get_loop_var_analysis_at(p, split) ls_vas = dict([(var, [data]) for (var, data) in vas if data[0] == 'LoopLinearSeries']) cmp_series = [(x, y, rew, offs) for (x, y) in eq_vals for (_, rew, offs) in ls_vas.get(x, [])] odd_eqs = [] for (x, y, rew, offs) in cmp_series: x_init_cmp1 = syntax.mk_less_eq(x, rew(x, minus_loop_step)) x_init_cmp2 = syntax.mk_less_eq(rew(x, minus_loop_step), x) fin_cmp1 = syntax.mk_less(x, y) fin_cmp2 = syntax.mk_less(y, x) odd_eqs.append(syntax.mk_eq(x_init_cmp1, fin_cmp1)) odd_eqs.append(syntax.mk_eq(x_init_cmp2, fin_cmp1)) odd_eqs.append(syntax.mk_eq(x_init_cmp1, fin_cmp2)) odd_eqs.append(syntax.mk_eq(x_init_cmp2, fin_cmp2)) ass_eqs = [] var_deps = p.compute_var_dependencies() for hook in target_objects.hooks('extra_wcet_assertions'): for assn in hook(var_deps[split]): ass_eqs.append(assn) return odd_eqs + ass_eqs
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 get_extra_sp_defs (rep, tag): """all functions will keep the stack pointer equal, whether they have pairing partners or not. add these extra defs/equalities for the purposes of stack depth analysis.""" # FIXME how to parametrise this? sp = mk_var ('r13', syntax.word32T) defs = {} items = [(n_vc, x) for (n_vc, x) in rep.funcs.iteritems () if logic.is_int (n_vc[0])] for ((n, vc), (inputs, outputs, _)) in items: if rep.p.node_tags[n][0] == tag: inp_sp = solver.smt_expr (sp, inputs, rep.solv) inp_sp = solver.parse_s_expression (inp_sp) out_sp = solver.smt_expr (sp, outputs, rep.solv) out_sp = solver.parse_s_expression (out_sp) if inp_sp != out_sp: defs[out_sp] = inp_sp return defs
def candidate_additional_eqs(p, split): eq_vals = set() def visitor(expr): if expr.is_op("Equals") and expr.vals[0].typ.kind == "Word": [x, y] = expr.vals eq_vals.update([(x, y), (y, x)]) for n in p.loop_body(split): p.nodes[n].visit(lambda x: (), visitor) for (x, y) in list(eq_vals): if is_zero(x) and y.is_op("Plus"): [x, y] = y.vals eq_vals.add((x, syntax.mk_uminus(y))) eq_vals.add((y, syntax.mk_uminus(x))) elif is_zero(x) and y.is_op("Minus"): [x, y] = y.vals eq_vals.add((x, y)) eq_vals.add((y, x)) loop = syntax.mk_var("%i", syntax.word32T) minus_loop_step = syntax.mk_uminus(loop) vas = search.get_loop_var_analysis_at(p, split) ls_vas = dict([(var, [data]) for (var, data) in vas if data[0] == "LoopLinearSeries"]) cmp_series = [(x, y, rew, offs) for (x, y) in eq_vals for (_, rew, offs) in ls_vas.get(x, [])] odd_eqs = [] for (x, y, rew, offs) in cmp_series: x_init_cmp1 = syntax.mk_less_eq(x, rew(x, minus_loop_step)) x_init_cmp2 = syntax.mk_less_eq(rew(x, minus_loop_step), x) fin_cmp1 = syntax.mk_less(x, y) fin_cmp2 = syntax.mk_less(y, x) odd_eqs.append(syntax.mk_eq(x_init_cmp1, fin_cmp1)) odd_eqs.append(syntax.mk_eq(x_init_cmp2, fin_cmp1)) odd_eqs.append(syntax.mk_eq(x_init_cmp1, fin_cmp2)) odd_eqs.append(syntax.mk_eq(x_init_cmp2, fin_cmp2)) ass_eqs = [] var_deps = p.compute_var_dependencies() for hook in target_objects.hooks("extra_wcet_assertions"): for assn in hook(var_deps[split]): ass_eqs.append(assn) return odd_eqs + ass_eqs
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 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 stack_virtualise_expr (expr, sp_offs): if expr.is_op ('MemAcc') and is_stack (expr.vals[0]): [m, p] = expr.vals assert expr.typ == syntax.word32T ptrs = [(p, 'MemAcc')] if sp_offs == None: return (ptrs, None) else: if p not in sp_offs: return (ptrs, expr) (k, offs) = sp_offs[p] return (ptrs, mk_var (('Fake', k, offs), syntax.word32T)) elif expr.kind == 'Op': vs = [stack_virtualise_expr (v, sp_offs) for v in expr.vals] return ([p for (ptrs, _) in vs for p in ptrs], syntax.Expr ('Op', expr.typ, name = expr.name, vals = [v for (_, v) in vs])) else: return ([], expr)
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 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 get_extra_sp_defs(rep, tag): """add extra defs/equalities about stack pointer for the purposes of stack depth analysis.""" # FIXME how to parametrise this? sp = mk_var('r13', syntax.word32T) defs = {} fcalls = [ n_vc for n_vc in rep.funcs if logic.is_int(n_vc[0]) if rep.p.node_tags[n_vc[0]][0] == tag if preserves_sp(rep.p.nodes[n_vc[0]].fname) ] for (n, vc) in fcalls: (inputs, outputs, _) = rep.funcs[(n, vc)] if (sp.name, sp.typ) not in outputs: continue inp_sp = solver.smt_expr(sp, inputs, rep.solv) inp_sp = solver.parse_s_expression(inp_sp) out_sp = solver.smt_expr(sp, outputs, rep.solv) out_sp = solver.parse_s_expression(out_sp) if inp_sp != out_sp: defs[out_sp] = inp_sp return defs
def add_impl_fun (impl_fname, regspecs): if impl_fname in functions: return ident_v = ("inst_ident", syntax.builtinTs['Token']) inps = [s for s in regspecs if s == 'I'] inps = ['reg_val%d' % (i + 1) for (i, s) in enumerate (inps)] rets = [s for s in regspecs if s == 'O'] rets = ['ret_val%d' % (i + 1) for (i, s) in enumerate (rets)] fun = mk_fun (impl_fname, inps, [ident_v], rets, [], bin_globs) inp_eqs = [((mk_var (nm, typ), 'ASM_IN'), (mk_var (nm, typ), 'C_IN')) for (nm, typ) in fun.inputs] inp_eqs += [((logic.mk_rodata (mk_var (nm, typ)), 'ASM_IN'), (syntax.true_term, 'C_IN')) for (nm, typ) in bin_globs] out_eqs = [((mk_var (nm, typ), 'ASM_OUT'), (mk_var (nm, typ), 'C_OUT')) for (nm, typ) in fun.outputs] out_eqs += [((logic.mk_rodata (mk_var (nm, typ)), 'ASM_OUT'), (syntax.true_term, 'C_OUT')) for (nm, typ) in bin_globs] pair = logic.Pairing (['ASM', 'C'], {'C': impl_fname, 'ASM': impl_fname}, (inp_eqs, out_eqs)) assert impl_fname not in pairings functions[impl_fname] = fun pairings[impl_fname] = [pair]
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 pretty_lambda (t): v = syntax.mk_var ('#seq-visits', word32T) t = logic.var_subst (t, {('%i', word32T) : v}, must_subst = False) return syntax.pretty_expr (t, print_type = True)
def fresh_var(self, name, typ): name = fresh_name(name, self.vs, typ) return mk_var(name, typ)
def avail_val(vs, typ): for (nm, typ2) in vs: if typ2 == typ: return mk_var(nm, typ2) return logic.default_val(typ)
def mk_var_list (vs, typ): return [syntax.mk_var (v, typ) for v in vs]
def avail_val (vs, typ): for (nm, typ2) in vs: if typ2 == typ: return mk_var (nm, typ2) return logic.default_val (typ)
def const_ret_hook(node, nm, typ): consts = node_const_rets(node) return consts and mk_var(nm, typ) in consts
def fresh_var (self, name, typ): name = fresh_name (name, self.vs, typ) return mk_var (name, typ)
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 pretty_lambda(t): v = syntax.mk_var('#seq-visits', word32T) t = logic.var_subst(t, {('%i', word32T): v}, must_subst=False) return syntax.pretty_expr(t, print_type=True)
def const_ret_hook (node, nm, typ): consts = node_const_rets (node) return consts and mk_var (nm, typ) in consts
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"