Exemple #1
0
 def visit_float_is_normal(self, e):
     arg = e.arg(0)
     self._check_fp_sort(arg)
     arg_sort = e.arg(0).sort()
     smallest_positive_normal = self._get_smallest_positive_normal_for(
         arg_sort)
     largest_negative_normal = self._get_largest_negative_normal_for(
         arg_sort)
     temp = z3.Or(
         z3.And(z3.fpGEQ(arg, smallest_positive_normal),
                z3.fpLT(arg, z3.fpPlusInfinity(arg_sort))),
         z3.And(z3.fpLEQ(arg, largest_negative_normal),
                z3.fpGT(arg, z3.fpMinusInfinity(arg_sort))))
     self.visit(temp)
Exemple #2
0
 def _op_raw_fpGEQ(self, a, b):
     return z3.fpGEQ(a, b, ctx=self._context)
Exemple #3
0
 def _op_raw_fpGEQ(self, a, b):
     return z3.fpGEQ(a, b, ctx=self._context)
Exemple #4
0
class BaseSMTTranslator():
    __metaclass__ = MetaTranslator

    def __init__(self, type_model):
        self.types = type_model
        self.fresh = 0
        self.defs = []  # current defined-ness conditions
        self.nops = []  # current non-poison conditions
        self.qvars = []
        self.attrs = collections.defaultdict(dict)
        # term -> attr -> var

    def eval(self, term):
        '''smt.eval(term) -> Z3 expression

    Translate the term (and subterms), adding its definedness conditons,
    nonpoison conditions, and quantifier variables to the state.
    '''
        logger.debug('eval %s', term)
        return eval(term, self)

    def __call__(self, term):
        '''smt(term) -> Z3 expression, def conds, nonpoison conds, qvars

    Clear the current state, translate the term (and subterms), and
    return the translation, definedness conditions, nonpoison conditions,
    and quantified variables.

    Quantified variables are guaranteed to be unique between different
    calls to the same SMTTranslator object.
    '''
        logger.debug('call %s', term)
        self.defs = []
        self.nops = []
        self.qvars = []
        v = eval(term, self)
        return v, self.defs, self.nops, self.qvars

    def add_defs(self, *defs):
        self.defs += defs

    def add_nops(self, *nops):
        self.nops += nops

    def add_qvar(self, *qvars):
        self.qvars += qvars

    def type(self, term):
        return self.types[term]

    def fresh_bool(self):
        self.fresh += 1
        return z3.Bool('ana_' + str(self.fresh))

    def fresh_var(self, ty, prefix='undef_'):
        self.fresh += 1
        return z3.Const(prefix + str(self.fresh), _ty_sort(ty))

    def _conditional_value(self, conds, v, name=''):
        raise NotImplementedError

    def _conditional_conv_value(self, conds, v, name=''):
        raise NotImplementedError

    def _binary_operator(self, term, op, defined, poisons):
        x = self.eval(term.x)
        y = self.eval(term.y)

        if defined:
            self.add_defs(*defined(x, y))

        if poisons:
            for f in poisons:
                if f in self.attrs[term]:
                    self.add_nops(
                        z3.Implies(self.attrs[term][f], poisons[f](x, y)))
                elif f in term.flags:
                    self.add_nops(poisons[f](x, y))

        return op(x, y)

    def _float_binary_operator(self, term, op):
        logger.debug('_fbo: %s\n%s', term, self.attrs[term])
        x = self.eval(term.x)
        y = self.eval(term.y)
        z = op(x, y)

        conds = []
        if 'nnan' in self.attrs[term]:
            df = z3.And(z3.Not(z3.fpIsNaN(x)), z3.Not(z3.fpIsNaN(y)),
                        z3.Not(z3.fpIsNaN(z)))
            conds.append(z3.Implies(self.attrs[term]['nnan'], df))

        elif 'nnan' in term.flags:
            conds += [
                z3.Not(z3.fpIsNaN(x)),
                z3.Not(z3.fpIsNaN(y)),
                z3.Not(z3.fpIsNaN(z))
            ]

        if 'ninf' in self.attrs[term]:
            df = z3.And(z3.Not(z3.fpIsInf(x)), z3.Not(z3.fpIsInf(y)),
                        z3.Not(z3.fpIsInf(z)))
            conds.append(z3.Implies(self.attrs[term]['ninf'], df))

        elif 'ninf' in term.flags:
            conds += [
                z3.Not(z3.fpIsInf(x)),
                z3.Not(z3.fpIsInf(y)),
                z3.Not(z3.fpIsInf(z))
            ]

        if 'nsz' in self.attrs[term] or 'nsz' in term.flags:
            # NOTE: this will return a different qvar for each (in)direct reference
            # to this term. Is this desirable?
            b = self.fresh_bool()
            self.add_qvar(b)
            z = op(x, y)

            c = z3.fpIsZero(z)
            if 'nsz' in self.attrs[term]:
                c = z3.And(self.attrs[term]['nsz'], c)

            s = _ty_sort(self.type(term))
            z = z3.If(c, z3.If(b, 0, z3.fpMinusZero(s)), z)

            if isinstance(term, FDivInst):
                c = [z3.Not(z3.fpIsZero(x)), z3.fpIsZero(y)]
                if 'nsz' in self.attrs[term]:
                    c.append(self.attrs[term]['nsz'])

                z = z3.If(
                    z3.And(c),
                    z3.If(b, z3.fpPlusInfinity(s), z3.fpMinusInfinity(s)), z)

        return self._conditional_value(conds, z, term.name)

    _icmp_ops = {
        'eq': operator.eq,
        'ne': operator.ne,
        'ugt': z3.UGT,
        'uge': z3.UGE,
        'ult': z3.ULT,
        'ule': z3.ULE,
        'sgt': operator.gt,
        'sge': operator.ge,
        'slt': operator.lt,
        'sle': operator.le,
    }

    _fcmp_ops = {
        'false': lambda x, y: z3.BoolVal(False),
        'oeq': z3.fpEQ,
        'ogt': z3.fpGT,
        'oge': z3.fpGEQ,
        'olt': z3.fpLT,
        'ole': z3.fpLEQ,
        'one': lambda x, y: z3.Not(fpUEQ(x, y)),
        'ord': lambda x, y: z3.Not(z3.Or(z3.fpIsNaN(x), z3.fpIsNaN(y))),
        'ueq': fpUEQ,
        'ugt': lambda x, y: z3.Not(z3.fpLEQ(x, y)),
        'uge': lambda x, y: z3.Not(z3.fpLT(x, y)),
        'ult': lambda x, y: z3.Not(z3.fpGEQ(x, y)),
        'ule': lambda x, y: z3.Not(z3.fpGT(x, y)),
        'une': z3.fpNEQ,
        'uno': lambda x, y: z3.Or(z3.fpIsNaN(x), z3.fpIsNaN(y)),
        'true': lambda x, y: z3.BoolVal(True),
    }

    def _must_analysis(self, term, op):
        args = (self.eval(a) for a in term._args)

        if all(isinstance(a, Constant) for a in term._args):
            return op(*args)

        c = self.fresh_bool()
        self.add_defs(z3.Implies(c, op(*args)))
        return c

    def _has_attr(self, attr, term):
        if attr in term.flags:
            return z3.BoolVal(True)

        return self._get_attr_var(attr, term)

    def _get_attr_var(self, attr, term):
        if attr in self.attrs[term]:
            return self.attrs[term][attr]

        # TODO: pick name better
        b = self.fresh_bool()
        logger.debug('Creating attr var %s for %s:%s', b, term, attr)
        self.attrs[term][attr] = b
        return b
Exemple #5
0
class BaseSMTEncoder():
  __metaclass__ = MetaEncoder

  def __init__(self, type_model):
    self.types = type_model
    self.fresh = 0
    self.reset()
    self._analysis = collections.defaultdict(dict)
      # name -> key -> var

  def reset(self):
    self.defs = []  # current defined-ness conditions
    self.nops = []  # current non-poison conditions
    self._safe = [] # current safety conditions
    self._aux  = [] # current auxiliary conditions
    self.qvars = []

  def eval(self, term):
    """Return the SMT translation of the term. Any side conditions are added
    to the translator context.
    """
    logger.debug('eval %s', term)
    return eval(term, self)

  def __call__(self, term):
    """Interpret the term in a fresh translator context.

    Quantified variables are guaranteed to be unique between different
    calls to the same SMTTranslator object.
    """
    # WARNING: calling eval after __call__ will modify the Interp
    # returned by __call__. Maybe better to clear everything after calling
    # eval? Perhaps best to clear both, but that wastes effort.
    logger.debug('call %s', term)
    self.reset()
    v = eval(term, self)
    return Interp(
      value = v,
      defined = self.defs,
      nonpoison = self.nops,
      safe = self._safe,
      aux = self._aux,
      qvars = self.qvars,
    )

  def _conjunction(self, clauses):
    context = []

    for c in clauses:
      with self.local_safe() as s:
        p = self.eval(c)

      self.add_safe(*mk_implies(context, s))
      context.append(p)

    return context

  def conjunction(self, clauses):
    """Interpret a list of predicates in a fresh context.

    The Interp.value returned will be a list of SMT expressions.
    """
    self.reset()
    ps = self._conjunction(clauses)
    return Interp(
      value = ps,
      defined = self.defs,
      nonpoison = self.nops,
      safe = self._safe,
      aux = self._aux,
      qvars = self.qvars,
    )


  def add_defs(self, *defs):
    """Add well-defined conditions to the current translator context.
    """
    self.defs += defs

  @contextmanager
  def local_defined(self):
    """Create a context with well-defined conditions independent from any prior
    conditions.

    Returns the list of well-defined conditions associated with the operations
    in the context.

    Usage:
      with smt.local_defined() as s:
        <operations>
    """
    old = self.defs
    try:
      new = []
      self.defs = new
      yield new
    finally:
      self.defs = old

  def add_nonpoison(self, *nonpoisons):
    """Add non-poison conditions to current translator context.
    """
    self.nops += nonpoisons

  add_nops = add_nonpoison # TODO: deprecate

  @contextmanager
  def local_nonpoison(self):
    """Create a context with nonpoison conditions independent from any prior
    conditions.

    Returns the list of nonpoison conditions associated with the operations in
    the context.

    Usage:
      with smt.local_nonpoison() as s:
        <operations>
    """
    old = self.nops
    try:
      new = []
      self.nops = new
      yield new
    finally:
      self.nops = old

  def add_safe(self, *safe):
    """Add safety conditions to the current translator context.
    """
    self._safe += safe

  @contextmanager
  def local_safe(self):
    """Create a context with safety conditions independent from any prior
    conditions.

    Returns the list of safety conditions associated with the operations in
    the context.

    Usage:
      with smt.local_safe() as s:
        <operations>
    """
    old = self._safe
    try:
      new = []
      self._safe = new
      yield new
    finally:
      self._safe = old

  def add_aux(self, *auxs):
    """Add auxiliary conditions to the current translator context.
    """
    self._aux += auxs

  def add_qvar(self, *qvars):
    """Add quantified variables to the current translator context.
    """
    self.qvars += qvars

  def type(self, term):
    return self.types[term]

  def fresh_bool(self):
    self.fresh += 1
    return z3.Bool('ana_' + str(self.fresh))

  def fresh_var(self, ty, prefix='undef_'):
    self.fresh += 1
    return z3.Const(prefix + str(self.fresh), _ty_sort(ty))

  def has_analysis(self, name, key):
    return name in self._analysis and key in self._analysis[name]

  def get_analysis(self, name, key):
    return self._analysis[name][key]

  def new_analysis(self, name, key, type=None):
    if key in self._analysis[name]:
      raise ValueError('Attempt to recreate analysis {} for {}'.format(
        name, key))

    self.fresh += 1
    r = z3.Const(
      'ana_{}_{}'.format(name, self.fresh),
      z3.BoolSort() if type is None else _ty_sort(type))

    self._analysis[name][key] = r
    return r

  def _conditional_value(self, conds, v, name=''):
    raise NotImplementedError('{} does not support floating-point'.format(
      type(self).__name__.lower()))

  def _conditional_conv_value(self, conds, v, name=''):
    raise NotImplementedError(
      '{} does not support floating-point conversion'.format(
      type(self).__name__.lower()))

  def _binary_operator(self, term, op, defined, nonpoison, poisons):
    x = self.eval(term.x)
    y = self.eval(term.y)

    if defined:
      self.add_defs(*defined(x,y))

    if nonpoison:
      self.add_nonpoison(*nonpoison(x,y))

    if poisons:
      for f in poisons:
        try:
          b = self.get_analysis(f, term)
          self.add_nonpoison(z3.Implies(b, poisons[f](x, y)))
        except KeyError:
          if f in term.flags:
            self.add_nonpoison(poisons[f](x, y))

    return op(x,y)

  def _float_binary_operator(self, term, op):
    x = self.eval(term.x)
    y = self.eval(term.y)
    z = op(x,y)

    conds = []
    if self.has_analysis('nnan', term):
      df = z3.And(z3.Not(z3.fpIsNaN(x)), z3.Not(z3.fpIsNaN(y)),
        z3.Not(z3.fpIsNaN(z)))
      conds.append(z3.Implies(self.get_analysis('nnan', term), df))

    elif 'nnan' in term.flags:
      conds += [z3.Not(z3.fpIsNaN(x)), z3.Not(z3.fpIsNaN(y)),
        z3.Not(z3.fpIsNaN(z))]

    if self.has_analysis('ninf', term):
      df = z3.And(z3.Not(z3.fpIsInf(x)), z3.Not(z3.fpIsInf(y)),
        z3.Not(z3.fpIsInf(z)))
      conds.append(z3.Implies(self.get_analysis('ninf', term), df))

    elif 'ninf' in term.flags:
      conds += [z3.Not(z3.fpIsInf(x)), z3.Not(z3.fpIsInf(y)),
        z3.Not(z3.fpIsInf(z))]

    if self.has_analysis('nsz', term) or 'nsz' in term.flags:
      # NOTE: this will return a different qvar for each (in)direct reference
      # to this term. Is this desirable?
      b = self.fresh_bool()
      self.add_qvar(b)
      z = op(x,y)

      c = z3.fpIsZero(z)
      if self.has_analysis('nsz', term):
        c = z3.And(self.get_analysis('nsz', term), c)

      s = _ty_sort(self.type(term))
      z = z3.If(c, z3.If(b, 0, z3.fpMinusZero(s)), z)

      if isinstance(term, FDivInst):
        c = [z3.Not(z3.fpIsZero(x)), z3.fpIsZero(y)]
        if self.has_analysis('nsz', term):
          c.append(self.get_analysis('nsw', term))

        z = z3.If(z3.And(c),
          z3.If(b, z3.fpPlusInfinity(s), z3.fpMinusInfinity(s)),
          z)

    return self._conditional_value(conds, z, term.name)


  _icmp_ops = {
    'eq': operator.eq,
    'ne': operator.ne,
    'ugt': z3.UGT,
    'uge': z3.UGE,
    'ult': z3.ULT,
    'ule': z3.ULE,
    'sgt': operator.gt,
    'sge': operator.ge,
    'slt': operator.lt,
    'sle': operator.le,
  }

  _fcmp_ops = {
    'false': lambda x,y: z3.BoolVal(False),
    'oeq': z3.fpEQ,
    'ogt': z3.fpGT,
    'oge': z3.fpGEQ,
    'olt': z3.fpLT,
    'ole': z3.fpLEQ,
    'one': lambda x,y: z3.Not(fpUEQ(x,y)),
    'ord': lambda x,y: z3.Not(z3.Or(z3.fpIsNaN(x), z3.fpIsNaN(y))),
    'ueq': fpUEQ,
    'ugt': lambda x,y: z3.Not(z3.fpLEQ(x,y)),
    'uge': lambda x,y: z3.Not(z3.fpLT(x,y)),
    'ult': lambda x,y: z3.Not(z3.fpGEQ(x,y)),
    'ule': lambda x,y: z3.Not(z3.fpGT(x,y)),
    'une': z3.fpNEQ,
    'uno': lambda x,y: z3.Or(z3.fpIsNaN(x), z3.fpIsNaN(y)),
    'true': lambda x,y: z3.BoolVal(True),
  }