def get_conjuncts(qvars, conjunction): if conjunction.decl().kind() == z3.Z3_OP_AND: return [ z3.substitute_vars(kid, *qvars) for kid in conjunction.children() ] else: return [z3.substitute_vars(conjunction, *qvars)]
def __init__(self,rule): """ unpacks a rule of the kind (I) z3.Forall(variables, head) or (II) z3.Forall(variables, z3.Implies(z3.And([tail,children]),head)) """ self.rule = rule # original rule self.variables = None # quantified variables self.predicate = None # predicate declaration: name(pc,...) self.tail = None # body predicate self.children = None # other body elements self.head = None # head predicate # the rule is a z3 quantifier if z3.is_quantifier(rule): # quantified variables self.variables = list() for i in range(rule.num_vars()): sort = self.var_sort(rule,i).kind() if sort == z3.Z3_INT_SORT: self.variables.append(z3.Int(self.var_name(rule,i))) elif sort == z3.Z3_BOOL_SORT: self.variables.append(z3.Bool(self.var_name(rule,i))) else: raise ValueError('unsopported sort:',sort) # unpacks the rule body head = rule.body() if z3.is_app_of(head,z3.Z3_OP_IMPLIES): # the rule is of kind (II) body = head.arg(0) # z3.Implies body if z3.is_app_of(body,z3.Z3_OP_AND): # unpacks the other body elements # (assumes single uninterpreted predicate in body) self.children = list() for c in body.children(): child = z3.substitute_vars(c,*self.variables) # unpacks the body predicate if z3.is_app_of(child,z3.Z3_OP_UNINTERPRETED) \ and self.tail is None: self.tail = child # body predicate else: self.children.append(child) else: raise ValueError('unsupported implication body:',body) head = head.arg(1) # z3.Implies head if z3.is_app_of(head,z3.Z3_OP_UNINTERPRETED): # the rule is of kind (I) name = head.decl().name() # predicate name parameters = list() # predicate parameters arguments = list() # predicate arguments for i in range(head.num_args()): arg = head.arg(i) parameters.append(arg.sort()) arguments.append(z3.substitute_vars(arg,*self.variables)) parameters.append(z3.BoolSort()) self.predicate = z3.Function(name,*parameters) self.head = self.predicate(*arguments) # head predicate else: raise ValueError('unsupported rule body:',body)
def __init__(self, rule): """ unpacks a rule of the kind (I) z3.Forall(variables, head) or (II) z3.Forall(variables, z3.Implies(z3.And([tail,children]),head)) """ self.rule = rule # original rule self.variables = None # quantified variables self.predicate = None # predicate declaration: name(pc,...) self.tail = None # body predicate self.children = None # other body elements self.head = None # head predicate # the rule is a z3 quantifier if z3.is_quantifier(rule): # quantified variables self.variables = list() for i in range(rule.num_vars()): sort = self.var_sort(rule, i).kind() if sort == z3.Z3_INT_SORT: self.variables.append(z3.Int(self.var_name(rule, i))) elif sort == z3.Z3_BOOL_SORT: self.variables.append(z3.Bool(self.var_name(rule, i))) else: raise ValueError("unsopported sort:", sort) # unpacks the rule body head = rule.body() if z3.is_app_of(head, z3.Z3_OP_IMPLIES): # the rule is of kind (II) body = head.arg(0) # z3.Implies body if z3.is_app_of(body, z3.Z3_OP_AND): # unpacks the other body elements # (assumes single uninterpreted predicate in body) self.children = list() for c in body.children(): child = z3.substitute_vars(c, *self.variables) # unpacks the body predicate if z3.is_app_of(child, z3.Z3_OP_UNINTERPRETED) and self.tail is None: self.tail = child # body predicate else: self.children.append(child) else: raise ValueError("unsupported implication body:", body) head = head.arg(1) # z3.Implies head if z3.is_app_of(head, z3.Z3_OP_UNINTERPRETED): # the rule is of kind (I) name = head.decl().name() # predicate name parameters = list() # predicate parameters arguments = list() # predicate arguments for i in range(head.num_args()): arg = head.arg(i) parameters.append(arg.sort()) arguments.append(z3.substitute_vars(arg, *self.variables)) parameters.append(z3.BoolSort()) self.predicate = z3.Function(name, *parameters) self.head = self.predicate(*arguments) # head predicate else: raise ValueError("unsupported rule body:", body)
def substitute_vars(t, *m): """Substitute the free variables in t with the expression in m.""" # Redefining this here so that the clients of this class aren't forced to # import any replacement-related functions directly from z3, thus making # it a little easier to add other backends in the future. #logger.info("Will substitute into {} arguments {} of sorts {}".format(t, m, map(lambda v : v.sort(), m))) return z3.substitute_vars(t, *m)
def write_clause_smt2(forall, pref, writer): if not (z3.is_quantifier(forall) and forall.is_forall()): raise Exception("Illegal clause for write_clause_smt2: {}".format( forall.decl())) writer.write("{}(forall (".format(pref)) for idx in range(0, forall.num_vars()): writer.write(" ({} {})".format(forall.var_name(idx), forall.var_sort(idx).sexpr())) writer.write(" ) \n".format(pref)) implication = forall.body() subst = list( reversed([ z3.Const(forall.var_name(n), forall.var_sort(n)) for n in range(0, forall.num_vars()) ])) implication = z3.substitute_vars(implication, *subst) write_implication_smt2(implication, pref + ' ', None, writer) writer.write("\n{})".format(pref))
def stripQuantifierBlock (expr) : """ strips the outermost quantifier block in a given expression and returns the pair (<list of consts replacing free vars>, <body with consts substituted for de-bruijn variables>) Example: assume expr.is_forall () vars, body = strip_quantifier (expr) qexpr = z3.ForAll (vars, body) assert qexpr.eq (expr) """ if not z3.is_quantifier (expr) : return ([], expr) global qb_counter z3ctx = expr.ctx consts = list () # outside-in order of variables; z3 numbers variables inside-out but # substitutes outside-in for i in reversed (range (expr.num_vars ())) : #v_name = expr.var_name (i) + "!" + str(qb_counter) v_name = expr.var_name (i) qb_counter+=1 v_sort = expr.var_sort (i) consts.append (z3.Const (v_name, z3_translate (v_sort, z3ctx))) matrix = z3.substitute_vars (expr.body (), *consts) return (consts, matrix)
def eval(self, term): fn = self.get_fn(term.decl().name()) # lambdas seem to be broken at the moment # this is a work around body = fn.body() body = z3.substitute_vars(body, *reversed(term.children())) return body
def stripQuantifierBlock (expr) : """ strips the outermost quantifier block in a given expression and returns the pair (<list of consts replacing free vars>, <body with consts substituted for de-bruijn variables>) Example: assume expr.is_forall () vars, body = strip_quantifier (expr) qexpr = z3.ForAll (vars, body) assert qexpr.eq (expr) """ if not z3.is_quantifier (expr) : return ([], expr) global qb_counter z3ctx = expr.ctx consts = list () # outside-in order of variables; z3 numbers variables inside-out but # substitutes outside-in for i in reversed (range (expr.num_vars ())) : v_name = expr.var_name (i) + "!" + str(qb_counter) qb_counter+=1 v_sort = expr.var_sort (i) consts.append (z3.Const (v_name, z3_translate (v_sort, z3ctx))) matrix = z3.substitute_vars (expr.body (), *consts) return (consts, matrix)
def fp_get_cover_delta(fp, pred, level=-1): sub = [] for i in range(0, pred.num_args()): sub.append(pred.arg(i)) lemma = fp.get_cover_delta(level, pred.decl()) if z3core.Z3_get_bool_value(fp.ctx.ctx, lemma.as_ast()) != z3.unsat: lemma = z3.substitute_vars(lemma, *sub) return lemma
def fp_get_cover_delta (fp, pred, level=-1): sub = [] for i in range (0, pred.num_args ()): sub.append (pred.arg (i)) lemma = fp.get_cover_delta (level, pred.decl ()) if z3core.Z3_get_bool_value (fp.ctx.ctx, lemma.as_ast ()) != z3.unsat: lemma = z3.substitute_vars (lemma, *sub) return lemma
def strip_qblock(expr): if not z3.is_quantifier(expr): return ([], expr) consts = list() for i in reversed(range(expr.num_vars())): v_name = expr.var_name(i) v_sort = expr.var_sort(i) consts.append(z3.Const(v_name, v_sort)) matrix = z3.substitute_vars(expr.body(), *consts) return (consts, matrix)
def ground_quantifier(qexpr): body = qexpr.body() vars = list() for i in reversed(range(qexpr.num_vars())): vi_name = qexpr.var_name(i) vi_sort = qexpr.var_sort(i) vi = z3.Const(vi_name, vi_sort) vars.append(vi) body = z3.substitute_vars(body, *vars) return (body, vars)
def extract_implies(clause): qvars, implies = fix_quantifier(clause) implies = z3.substitute_vars(implies, *list(reversed(qvars))) kids = implies.children() tail = get_conjuncts(qvars, kids[0]) head = kids[1] return { 'qvars': qvars, 'tail': tail, 'head': head, }
def lemma_to_string(lemma, pred): """ convert a lemma returned by get_cover_delta into a string that parse_smt2_string can parse """ const_list = [z3.Const(pred.name()+"_"+str(j), pred.domain(j)) for j in range(pred.arity())] lhs = pred(*const_list) rhs = z3.substitute_vars(lemma, *(const_list)) imp = z3.Implies(lhs, rhs) forall = z3.ForAll(list(reversed(const_list)), imp) lemma_str = "(assert %s)"%forall.sexpr() print("\toriginal lemma:", lemma) print("\tforall lemma:", forall.body().arg(1)) assert lemma == forall.body().arg(1) return lemma_str
def extract_implies(clause): clause = fix_quantifier(clause) qvars = [ z3.Const(clause.var_name(n), clause.var_sort(n)) for n in range(0, clause.num_vars()) ] implies = fix_implies(clause.body()) kids = implies.children() body = get_conjuncts(qvars, kids[0]) head = z3.substitute_vars(kids[1], *qvars) return { 'qvars': qvars, 'body': body, 'head': head, }
def write_clauses_datalog(declarations, clauses, writer): reserved_exit_point = 'reserved_exit_point' writer.write('(declare-rel {} ())\n\n'.format(reserved_exit_point)) for decl in declarations: symbol = quote_symbol_if_needed(decl.name()) writer.write('(declare-rel {} ('.format(symbol)) for n in range(0, decl.arity()): writer.write((" {}".format(decl.domain(n)))) writer.write(' ))\n') known_vars = set([]) implications = [] writer.write('\n') for clause in clauses: if not (z3.is_quantifier(clause) and clause.is_forall()): raise Exception("Illegal clause for write_clause_smt2: {}".format( clause.decl())) for idx in range(0, clause.num_vars()): if (clause.var_name(idx), clause.var_sort(idx)) not in known_vars: known_vars.add((clause.var_name(idx), clause.var_sort(idx))) writer.write("(declare-var {} {})\n".format( clause.var_name(idx), clause.var_sort(idx).sexpr())) implication = clause.body() subst = list( reversed([ z3.Const(clause.var_name(n), clause.var_sort(n)) for n in range(0, clause.num_vars()) ])) implications.append(z3.substitute_vars(implication, *subst)) writer.write('\n') for implication in implications: writer.write('(rule\n') write_implication_smt2(implication, ' ', reserved_exit_point, writer) writer.write('\n)\n') writer.write('\n\n(query {})\n\n'.format(reserved_exit_point))
def negated_body(formula): """Given a z3.QuantiferRef formula with z3.Int variables, return negation of the body. Returns: A z3.BoolRef which is the negation of the formula body. """ assert z3.is_quantifier(formula), ('Formula is not a quantifier:\n', formula) var_names = [formula.var_name(i) for i in range(formula.num_vars())] vs = [z3.Int(n) for n in var_names] # Here simply doing z3.Not(formula.body()) doesn't work. formula.body() # returns an expression without any bounded variable, i.e., it refers # variables using indices in the order they appear instead of its names, # Var(0), Var(1), Var(2). Thus, we have to re-bind the variables using # substitute_vars. It is also necessary to reverse the list of variables. # See https://github.com/Z3Prover/z3/issues/402 for more details. return z3.Not(z3.substitute_vars(formula.body(), *reversed(vs)))
def termination(self,entry,header,kind,pieces,loop,exit): """ instruments the rules in fp with a ranking function in order to extract a NON-TERMINATING execution such that rank is negative within the loop """ def mx(rankings): """ builds a MAX combination of a list of ranking functions """ if len(rankings) <= 1: return rankings[0] else: r = mx(rankings[1:]) return z3.If(rankings[0] < r,r,rankings[0]) # new z3.Fixedpoint object fp = z3.Fixedpoint() fp.set(engine='spacer') fp.set('xform.inline_eager', False) fp.set('xform.slice', False) fp.set('xform.inline_linear', False) fp.set('pdr.utvpi', False) # fp.set('timeout',1000) # variable declarations for x in self.variables: fp.declare_var(x) # ranking function counter components = len(pieces) R = [z3.Int('R%d' % i) for i in range(components)] for component in R: fp.declare_var(component) RR = [z3.Int('RR%d' % i) for i in range(components)] for component in RR: fp.declare_var(component) # modifying and adding rules for rule in self.rules: r = Rule(rule.rule) if r.is_entry(entry): # ranking function(s) initialization before the loop rank = [[z3.substitute_vars(x,*r.head_args()[1:]) for x in component] for component in pieces] r.add_rank(R,[mx(component) for component in rank],RR) fp.register_relation(r.predicate) # print 'ENTRY:', r elif r.is_loop(loop): # ranking function(s) strict decrease within the loop rank = [[z3.substitute_vars(x,*r.head_args()[1:]) for x in component] for component in pieces] r.add_decrease(kind,R,[mx(component) for component in rank],RR) # print 'LOOP:', r # ranking function(s) boundedness from below within the loop q = Rule(rule.rule) q.add_bound(kind,R) q.head_pc(-1) fp.rule(q.head,[q.tail] + q.children) # print 'EXIT:', q # query query = z3.Exists(q.variables,q.head) else: r.add_vars(R) # print 'NONE:', r # adding modified rules if r.tail is None: fp.rule(r.head) else: fp.rule(r.head,[r.tail] + r.children) # print '\nFP:', fp # querying for non-terminating execution result = fp.query(query) if result == z3.sat: answer = fp.get_ground_sat_answer() first = answer.children()[-2] point = [x.as_long() for x in first.children()[1:-1]] return list(zip(self.arguments[1:],point)) else: return []
def substitute_vars (t, *m): rw = SubstituteVars (*m) res = rw (t) assert res.eq (z3.substitute_vars (t, *m)) return res
def termination(self, entry, header, kind, pieces, loop, exit): """ instruments the rules in fp with a ranking function in order to extract a NON-TERMINATING execution such that rank is negative within the loop """ def mx(rankings): """ builds a MAX combination of a list of ranking functions """ if len(rankings) <= 1: return rankings[0] else: r = mx(rankings[1:]) return z3.If(rankings[0] < r, r, rankings[0]) # new z3.Fixedpoint object fp = z3.Fixedpoint() fp.set(engine="spacer") fp.set("xform.inline_eager", False) fp.set("xform.slice", False) fp.set("xform.inline_linear", False) fp.set("pdr.utvpi", False) # fp.set('timeout',1000) # variable declarations for x in self.variables: fp.declare_var(x) # ranking function counter components = len(pieces) R = [z3.Int("R%d" % i) for i in range(components)] for component in R: fp.declare_var(component) RR = [z3.Int("RR%d" % i) for i in range(components)] for component in RR: fp.declare_var(component) # modifying and adding rules for rule in self.rules: r = Rule(rule.rule) if r.is_entry(entry): # ranking function(s) initialization before the loop rank = [[z3.substitute_vars(x, *r.head_args()[1:]) for x in component] for component in pieces] r.add_rank(R, [mx(component) for component in rank], RR) fp.register_relation(r.predicate) # print 'ENTRY:', r elif r.is_loop(loop): # ranking function(s) strict decrease within the loop rank = [[z3.substitute_vars(x, *r.head_args()[1:]) for x in component] for component in pieces] r.add_decrease(kind, R, [mx(component) for component in rank], RR) # print 'LOOP:', r # ranking function(s) boundedness from below within the loop q = Rule(rule.rule) q.add_bound(kind, R) q.head_pc(-1) fp.rule(q.head, [q.tail] + q.children) # print 'EXIT:', q # query query = z3.Exists(q.variables, q.head) else: r.add_vars(R) # print 'NONE:', r # adding modified rules if r.tail is None: fp.rule(r.head) else: fp.rule(r.head, [r.tail] + r.children) # print '\nFP:', fp # querying for non-terminating execution result = fp.query(query) if result == z3.sat: answer = fp.get_ground_sat_answer() first = answer.children()[-2] point = [x.as_long() for x in first.children()[1:-1]] return zip(self.arguments[1:], point) else: return []
def piecewise(fp): # program CFG program = Program(fp) parameters = program.parameters[1:] variables = [ z3.Var(i, sort) for (i, sort) in zip(list(range(len(parameters))), parameters) ] arguments = program.arguments[1:] # loops identification loops = program.loops_identification() # proving termination... rank = dict() for node in loops: # ...for all loops involving each node if loops[node]: if debug: print('\nloop:', loops[node], '\n') loop = set([n for path in loops[node] for n in path]) # loop nodes entry = set([(n, node) for n in program.prev[node] - loop]) # entry edges edges = set([ n for path in loops[node] for n in zip(path[:-1], path[1:]) ]) # loop edges # exit = set([(node,n) # for n in program.next[node] - loop]) # exit edges exit = set([(i, n) for i in loop for n in program.next[i] - loop]) # exit edges bits = list() # (potentially) terminating bits pieces = [[z3.IntSort().cast(0)]] # candidate ranking functions bit = program.get_bit(entry, node, 'max', pieces, edges, exit) while bit: bit[node][0][-len(pieces)] -= bit[node][-1][-len(pieces)] bits.append(bit[node][0][:len(bit[node][0]) - len(pieces) + 1]) rankings = ranking(bits, variables) for i in range(len(bits)): rankings = ranking(bits[:i + 1], variables) if not rankings: if debug: print() del bits[:-1] rankings = ranking(bits, variables) if debug: print( 'bit:', list( zip(arguments + ['-' for component in pieces], bits[-1]))) pieces[0].extend(rankings) if debug: print('pieces:', [[ z3.substitute_vars(x, *arguments) for x in component ] for component in pieces]) bit = program.get_bit(entry, node, 'max', pieces, edges, exit) # check candidate ranking functions point = program.termination(entry, node, 'max', pieces, edges, exit) pieces = [[z3.substitute_vars(x, *arguments) for x in component] for component in pieces] if point: rank[node] = (False, point) if debug: print('\nnon-terminating execution:', point) if debug: print('(partial) loop ranking functions:', pieces) else: rank[node] = (True, pieces) if debug: print('\nloop ranking functions:', pieces) if debug: print('\nranking functions:', rank) if all([r[0] for r in list(rank.values())]): stat("Result", "TRUE") else: stat("Result", "FALSE")
def get_bit(self, entry, header, kind, pieces, loop, exit): """ instruments the program rules with a ranking function in order to extract a TERMINATING execution such that rank is negative at loop exit """ def mx(rankings): """ builds a MAX combination of a list of ranking functions """ if len(rankings) <= 1: return rankings[0] else: r = mx(rankings[1:]) return z3.If(rankings[0] < r, r, rankings[0]) # new z3.Fixedpoint object fp = z3.Fixedpoint() fp.set(engine="spacer") fp.set("xform.inline_eager", False) fp.set("xform.slice", False) fp.set("xform.inline_linear", False) fp.set("pdr.utvpi", False) # fp.set('timeout',1000) # variable declarations for x in self.variables: fp.declare_var(x) # ranking function counter components = len(pieces) R = [z3.Int("R%d" % i) for i in range(components)] for component in R: fp.declare_var(component) RR = [z3.Int("RR%d" % i) for i in range(components)] for component in RR: fp.declare_var(component) # modifying and adding rules for rule in self.rules: r = Rule(rule.rule) if r.is_entry(entry): # ranking function(s) initialization before the loop rank = [[z3.substitute_vars(x, *r.head_args()[1:]) for x in component] for component in pieces] r.add_rank(R, [mx(component) for component in rank], RR) fp.register_relation(r.predicate) elif r.is_loop(loop): # ranking function(s) strict decrease within the loop rank = [[z3.substitute_vars(x, *r.head_args()[1:]) for x in component] for component in pieces] r.add_decrease(kind, R, [mx(component) for component in rank], RR) elif r.is_exit(exit): r.add_vars(R) # ranking function(s) boundedness from below after the loop q = Rule(rule.rule) q.add_bound(kind, R) q.head_pc(-1) fp.rule(q.head, [q.tail] + q.children) # query query = z3.Exists(q.variables, q.head) else: r.add_vars(R) # print 'NONE:', r # adding modified rules if r.tail is None: fp.rule(r.head) else: body = [r.tail] + r.children fp.rule(r.head, body) # querying for terminating execution with negative ranking function bit = dict() result = fp.query(query) if result == z3.sat: answer = fp.get_ground_sat_answer() children = answer.children() # print 'CHILDREN:\n', children bit = dict() for child in children: if child.children(): # some predicate might not have values values = [x.as_long() for x in child.children()] if values[0] not in bit: bit[values[0]] = [values[1:]] else: bit[values[0]].insert(0, values[1:]) # adding the first child to loop header to ensure progress # (when the source of exit edges is not the loop header) first = [child for child in children if child.children()][0] values = [x.as_long() for x in first.children()] if header not in bit: bit[header] = [values[1:]] else: bit[header].append(values[1:]) return bit else: return bit
def piecewise(fp): # program CFG program = Program(fp) parameters = program.parameters[1:] variables = [z3.Var(i, sort) for (i,sort) in zip(list(range(len(parameters))),parameters)] arguments = program.arguments[1:] # loops identification loops = program.loops_identification() # proving termination... rank = dict() for node in loops: # ...for all loops involving each node if loops[node]: if debug: print '\nloop:', loops[node], '\n' loop = set([n for path in loops[node] for n in path]) # loop nodes entry = set([(n,node) for n in program.prev[node] - loop]) # entry edges edges = set([n for path in loops[node] for n in zip(path[:-1],path[1:])]) # loop edges # exit = set([(node,n) # for n in program.next[node] - loop]) # exit edges exit = set([(i,n) for i in loop for n in program.next[i] - loop]) # exit edges bits = list() # (potentially) terminating bits pieces = [[z3.IntSort().cast(0)]] # candidate ranking functions bit = program.get_bit(entry,node,'max',pieces,edges,exit) while bit: bit[node][0][-len(pieces)] -= bit[node][-1][-len(pieces)] bits.append(bit[node][0][:len(bit[node][0])-len(pieces)+1]) rankings = ranking(bits,variables) for i in range(len(bits)): rankings = ranking(bits[:i+1],variables) if not rankings: if debug: print del bits[:-1] rankings = ranking(bits,variables) if debug: print 'bit:', zip(arguments + ['-' for component in pieces],bits[-1]) pieces[0].extend(rankings) if debug: print 'pieces:', [[z3.substitute_vars(x,*arguments) for x in component] for component in pieces] bit = program.get_bit(entry,node,'max',pieces,edges,exit) # check candidate ranking functions point = program.termination(entry,node,'max',pieces,edges,exit) pieces = [[z3.substitute_vars(x,*arguments) for x in component] for component in pieces] if point: rank[node] = (False,point) if debug: print '\nnon-terminating execution:', point if debug: print '(partial) loop ranking functions:', pieces else: rank[node] = (True,pieces) if debug: print '\nloop ranking functions:', pieces if debug: print '\nranking functions:', rank if all([r[0] for r in rank.values()]): stat("Result", "TRUE") else: stat("Result", "FALSE")
def get_bit(self,entry,header,kind,pieces,loop,exit): """ instruments the program rules with a ranking function in order to extract a TERMINATING execution such that rank is negative at loop exit """ def mx(rankings): """ builds a MAX combination of a list of ranking functions """ if len(rankings) <= 1: return rankings[0] else: r = mx(rankings[1:]) return z3.If(rankings[0] < r,r,rankings[0]) # new z3.Fixedpoint object fp = z3.Fixedpoint() fp.set(engine='spacer') fp.set('xform.inline_eager', False) fp.set('xform.slice', False) fp.set('xform.inline_linear', False) fp.set('pdr.utvpi', False) # fp.set('timeout',1000) # variable declarations for x in self.variables: fp.declare_var(x) # ranking function counter components = len(pieces) R = [z3.Int('R%d' % i) for i in range(components)] for component in R: fp.declare_var(component) RR = [z3.Int('RR%d' % i) for i in range(components)] for component in RR: fp.declare_var(component) # modifying and adding rules for rule in self.rules: r = Rule(rule.rule) if r.is_entry(entry): # ranking function(s) initialization before the loop rank = [[z3.substitute_vars(x,*r.head_args()[1:]) for x in component] for component in pieces] r.add_rank(R,[mx(component) for component in rank],RR) fp.register_relation(r.predicate) elif r.is_loop(loop): # ranking function(s) strict decrease within the loop rank = [[z3.substitute_vars(x,*r.head_args()[1:]) for x in component] for component in pieces] r.add_decrease(kind,R,[mx(component) for component in rank],RR) elif r.is_exit(exit): r.add_vars(R) # ranking function(s) boundedness from below after the loop q = Rule(rule.rule) q.add_bound(kind,R) q.head_pc(-1) fp.rule(q.head,[q.tail] + q.children) # query query = z3.Exists(q.variables,q.head) else: r.add_vars(R) # print 'NONE:', r # adding modified rules if r.tail is None: fp.rule(r.head) else: body = [r.tail] + r.children fp.rule(r.head,body) # querying for terminating execution with negative ranking function bit = dict() result = fp.query(query) if result == z3.sat: answer = fp.get_ground_sat_answer() children = answer.children() # print 'CHILDREN:\n', children bit = dict() for child in children: if child.children(): # some predicate might not have values values = [x.as_long() for x in child.children()] if values[0] not in bit: bit[values[0]] = [values[1:]] else: bit[values[0]].insert(0,values[1:]) # adding the first child to loop header to ensure progress # (when the source of exit edges is not the loop header) first = [child for child in children if child.children()][0] values = [x.as_long() for x in first.children()] if header not in bit: bit[header] = [values[1:]] else: bit[header].append(values[1:]) return bit else: return bit
def substitute(self, other, expr): def update_term(t, args): n = len(args) # Need to pass an AstArray type into Z3_update_term, not a python list args_ast_arr = (z3.Ast * n)() for i in range(n): args_ast_arr[i] = args[i].as_ast() return _to_expr_ref( z3.Z3_update_term(t.ctx_ref(), t.as_ast(), n, args_ast_arr), t.ctx) cache = z3.AstMap(ctx=expr.ctx) for addr, state in self._expanded_global_state.items(): other_state = other.initial_contract_state(addr) cache[state.storage] = other_state.storage cache[state.balance] = other_state.balance cache[state.nonce] = other_state.nonce fnsubs = [] for k, v in self._cache.items(): if isinstance(v, mem.Memory): # Uses of Memory objects will produce an expression containing # the underlying array object (_mem). If required the index # (_idx) will have been substituted with the actual indexing # expression at that point, so we do not need to consider it # here. cache[v._mem] = getattr(other, k)()._mem elif z3.is_app(v) and v.num_args() > 0: fnsubs.append((v, getattr(other, k)())) else: cache[v] = getattr(other, k)() todo = [expr] while todo: n = todo[-1] if n in cache: todo.pop() elif z3.is_var(n): cache[n] = n todo.pop() elif z3.is_app(n): new_args = [] for i in range(n.num_args()): arg = n.arg(i) if arg not in cache: todo.append(arg) else: new_args.append(cache[arg]) # Only actually do the substitution if all the arguments have # already been processed if len(new_args) == n.num_args(): todo.pop() fn = n.decl() for oldfn, newfn in fnsubs: if z3.eq(fn, oldfn): new_fn = z3.substitute_vars(newfn, *new_args) break else: # TODO only if new_args != old_args if len(new_args) != fn.arity(): new_fn = update_term(n, new_args) else: new_fn = fn(*new_args) cache[n] = new_fn else: assert z3.is_quantifier(n) # Not currently implemented as don't use quanitifers at the # moment raise NotImplementedError() return cache[expr]