def pysmt2bv(ps): """Convert a pySMT type to a bit-vector type. Currently, only conversion from `pysmt.shortcuts.BV` and `pysmt.shortcuts.Symbol` is supported. >>> from pysmt import shortcuts, typing # pySMT shortcuts and typing modules >>> from arxpy.smt.types import pysmt2bv >>> env = shortcuts.reset_env() >>> pysmt2bv(env.formula_manager.Symbol("x", env.type_manager.BVType(8))).vrepr() "Variable('x', width=8)" >>> pysmt2bv(env.formula_manager.BV(1, 8)).vrepr() 'Constant(0b00000001, width=8)' """ class_name = type(ps).__name__ msg = "unknown conversion of {} ({} {}) to a bit-vector type".format( ps, ps.get_type(), class_name) if ps.is_symbol(): if str(ps.get_type()) == "Bool": return core.Variable(ps.symbol_name(), 1) else: return core.Variable(ps.symbol_name(), ps.bv_width()) elif ps.is_bv_constant(): return core.Constant(int(ps.constant_value()), ps.bv_width()) elif ps.is_false(): return core.Constant(0, 1) elif ps.is_true(): return core.Constant(1, 1) else: raise NotImplementedError(msg)
def eval(cls, x, y): def doit(x, y, width): """Modular subtraction when both operands are integers.""" return (x - y) % (2**width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y), x.width), x.width) zero = core.Constant(0, x.width) if x == zero: return BvNeg(y) elif y == zero: return x elif x == y: return zero elif isinstance(x, BvAdd): # (x0 + x1) - y if x.args[0] == y: return x.args[1] elif x.args[1] == y: return x.args[0] elif isinstance(y, BvAdd): # x - (y0 + y1) if y.args[0] == x: return BvNeg(y.args[1]) elif y.args[1] == x: return BvNeg(y.args[0])
def eval(cls, x, y): zero = core.Constant(0, 1) one = core.Constant(1, 1) if x is y: return one elif isinstance(x, core.Constant) and isinstance(y, core.Constant): return one if x.val == y.val else zero
def eval(cls, x, y, z): # Source: Hacker's Delight if x.width < 4: # the HW of a 1-bit/2-bit vector requires 1-bit/2-bit (HW(0b1)=0b1, HW(0b11)=0b10) # thus, the sum of three HW of these sizes require an extra bit (3*0b1=0b11, 3*0b10=0b110) # the HW of a 3-bit vector requires 2-bit (Hw(0b111)=0b11) # but the sum of three HW of 3-bit require two extra bit (3*0b11 = 0b1001) offset = 1 if x.width == 3: offset = 2 x = operation.ZeroExtend(PopCount(x), offset) y = operation.ZeroExtend(PopCount(y), offset) z = operation.ZeroExtend(PopCount(z), offset) return x + y + z elif x.width > 32: width = cls.output_width(x, y, z) x = PopCount(x) x = operation.ZeroExtend(x, width - x.width) y = PopCount(y) y = operation.ZeroExtend(y, width - y.width) z = PopCount(z) z = operation.ZeroExtend(z, width - z.width) return x + y + z orig_x, orig_y, orig_z = x, y, z while (x.width & (x.width - 1)) != 0: x = operation.ZeroExtend(x, 1) while (y.width & (y.width - 1)) != 0: y = operation.ZeroExtend(y, 1) while (z.width & (z.width - 1)) != 0: z = operation.ZeroExtend(z, 1) width_log2 = x.width.bit_length() - 1 m_ctes = [] for i in range(width_log2): m_ctes.append(repeat_pattern(pattern01(2 ** i), x.width)) bv = core.Constant(0, x.width) for i, m in enumerate(m_ctes): if i == 0: x = x - ((x >> core.Constant(1, bv.width)) & m) y = y - ((y >> core.Constant(1, bv.width)) & m) z = z - ((z >> core.Constant(1, bv.width)) & m) bv = x + y + z elif i == 1: x = (x & m) + ((x >> core.Constant(2 ** i, bv.width)) & m) # generic case y = (y & m) + ((y >> core.Constant(2 ** i, bv.width)) & m) z = (z & m) + ((z >> core.Constant(2 ** i, bv.width)) & m) bv = x + y + z elif i == 2: bv = (bv & m) + ((bv >> core.Constant(4, bv.width)) & m) elif i == 3: bv = bv + (bv >> core.Constant(8, bv.width)) elif i == 4: bv = bv + (bv >> core.Constant(16, bv.width)) return bv[cls.output_width(orig_x, orig_y, orig_z) - 1:]
def is_possible(self, output_diff): """Return whether the given output `RXDiff` is possible. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.differential.difference import RXDiff >>> from arxpy.differential.derivative import RXDA >>> alpha = RXDiff(Constant(0, 4)), RXDiff(Constant(0, 4)) >>> f = RXDA(alpha) >>> beta = RXDiff(Constant(0, 4)) >>> f.is_possible(beta) 0b1 >>> a0, a1, b = Variable("a0", 4), Variable("a1", 4), Variable("b", 4) >>> alpha = RXDiff(a0), RXDiff(a1) >>> f = RXDA(alpha) >>> beta = RXDiff(b) >>> result = f.is_possible(beta) >>> result # doctest:+NORMALIZE_WHITESPACE 0b11 == (~(((((a0[:1]) ^ (a1[:1]) ^ (b[:1])) << 0b001) ^ (a0[:1]) ^ (a1[:1]) ^ (b[:1]))[:1]) | ((((a0[:1]) ^ (b[:1])) | ((a1[:1]) ^ (b[:1])))[1:])) >>> result.xreplace({a0: Constant(0, 4), a1: Constant(0, 4), b: Constant(0, 4)}) 0b1 >>> a1 = Constant(0, 4) >>> alpha = RXDiff(a0), RXDiff(a1) >>> f = RXDA(alpha) >>> beta = RXDiff(b) >>> result = f.is_possible(beta) >>> result # doctest:+NORMALIZE_WHITESPACE 0b11 == (~(((((a0[:1]) ^ (b[:1])) << 0b001) ^ (a0[:1]) ^ (b[:1]))[:1]) | ((((a0[:1]) ^ (b[:1])) | (b[:1]))[1:])) See `Derivative.is_possible` for more information. """ # (I ^ SHL)(da ^ db ^ dc)[1:] <= SHL((da ^ dc) OR (db ^ dc))[1:] # one = core.Constant(1, self.input_diff[0].val.width) # alt v1 alpha, beta = [d.val for d in self.input_diff] gamma = output_diff.val # da, db, dc = alpha >> one, beta >> one, gamma >> one # alt v1 da, db, dc = alpha[:1], beta[:1], gamma[:1] # ignore LSB one = core.Constant(1, da.width) # lhs = (da ^ db ^ dc) ^ ((da ^ db ^ dc) << one) # alt v1 # rhs = ((da ^ dc) | (db ^ dc)) << one lhs = ((da ^ db ^ dc) ^ ((da ^ db ^ dc) << one))[:1] rhs = (((da ^ dc) | (db ^ dc)) << one)[:1] def bitwise_implication(x, y): return (~x) | y # alt v1 # n = lhs.width # return operation.BvComp( # bitwise_implication(lhs[n - 2:1], rhs[n - 2:1]), # ignore MSB, LSB # ~ core.Constant(0, n - 2)) return operation.BvComp(bitwise_implication(lhs, rhs), ~core.Constant(0, lhs.width)) # alt v1
def is_possible(self, output_diff): """Return whether the given output `XorDiff` is possible. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.differential.difference import XorDiff >>> from arxpy.differential.derivative import XDA >>> alpha = XorDiff(Constant(0, 4)), XorDiff(Constant(0, 4)) >>> f = XDA(alpha) >>> beta = XorDiff(Constant(0, 4)) >>> f.is_possible(beta) 0b1 >>> a0, a1, b = Variable("a0", 4), Variable("a1", 4), Variable("b", 4) >>> alpha = XorDiff(a0), XorDiff(a1) >>> f = XDA(alpha) >>> beta = XorDiff(b) >>> result = f.is_possible(beta) >>> result 0x0 == ((~(a0 << 0x1) ^ (a1 << 0x1)) & (~(a0 << 0x1) ^ (b << 0x1)) & ((a0 << 0x1) ^ b ^ a0 ^ a1)) >>> result.xreplace({a0: Constant(0, 4), a1: Constant(0, 4), b: Constant(0, 4)}) 0b1 >>> a1 = Constant(0, 4) >>> alpha = XorDiff(a0), XorDiff(a1) >>> f = XDA(alpha) >>> beta = XorDiff(b) >>> result = f.is_possible(beta) >>> result 0x0 == (~(a0 << 0x1) & ~(b << 0x1) & (a0 ^ b)) See `Derivative.is_possible` for more information. """ a, b = [d.val for d in self.input_diff] c = output_diff.val one = core.Constant(1, a.width) def eq(x, y, z): if not isinstance(x, core.Constant): if isinstance(y, core.Constant): return eq(y, x, z) elif isinstance(z, core.Constant): return eq(z, x, y) return (~x ^ y) & (~x ^ z) def xor_shift(x, y, z): if not isinstance(x, core.Constant): if isinstance(y, core.Constant): return xor_shift(y, x, z) elif isinstance(z, core.Constant): return xor_shift(z, x, y) return (x ^ y ^ z ^ (x << one)) return operation.BvComp( eq(a << one, b << one, c << one) & xor_shift(a, b, c), core.Constant(0, a.width))
def _is_possible(self, output_diff, debug=False): u = self.input_diff[0].val v = output_diff.val a = self.op.constant n = a.width one = core.Constant(1, n) assert self._effective_width == n - 1 def carry(x, y): # carry[i] = Maj(x[i-1], y[i-1], carry[i-1]) return (x + y) ^ x ^ y # # S_0 well defined case11_ = (u << one) & (v << one) # i-bit is True if S_i = 11* case00_ = (~(u << one)) & (~(v << one)) # i-bit is True if S_i = 00* case__1 = u ^ v a = a << one # i-bit holds a[i-1] local_eq_a = ~(a ^ (a << one)) # i-bit is True if a[i-1] == a[i-2] case00_prev = case00_ << one # i-bit is True if S_{i-1} = 00* c = carry(local_eq_a & case00_, (~case00_prev)) case001 = case00_ & case__1 bad_case11_ = case11_ & ~(case__1 ^ local_eq_a) & (c | ~case00_prev) bad_events = (case001 | bad_case11_) is_valid = operation.BvComp(bad_events, core.Constant(0, bad_events.width)) if debug: print("\n\n ~~ ") print("u: ", u.bin()) print("v: ", v.bin()) print("a: ", a.bin()) print("case11_: ", case11_.bin()) print("case00_: ", case00_.bin()) print("case__1: ", case__1.bin()) print("case001: ", case001.bin()) print("case00_prev: ", case00_prev.bin()) print("local_eq_a: ", local_eq_a.bin()) print("c: ", c.bin()) print("bad_case11_: ", bad_case11_.bin()) print("bad_events: ", bad_events.bin()) print("is_valid :", is_valid.bin()) print("\n\n") return is_valid
def eval(cls, x, y): def doit(x, y): """Remainder operation when both operands are int.""" assert y != 0 return x % y zero = core.Constant(0, x.width) one = core.Constant(1, x.width) assert y != zero if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y)), x.width) elif x == y or x == zero or y == one: return zero
def has_probability_one(self, output_diff): """Return whether the input diff propagates to the output diff with probability one. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.differential.difference import XorDiff >>> from arxpy.differential.derivative import XDA >>> alpha = XorDiff(Constant(0, 4)), XorDiff(Constant(0, 4)) >>> f = XDA(alpha) >>> f.has_probability_one(XorDiff(Constant(0, 4))) 0b1 """ is_possible = self.is_possible(output_diff) a, b = [d.val for d in self.input_diff] c = output_diff.val n = a.width def eq(x, y, z): if not isinstance(x, core.Constant): if isinstance(y, core.Constant): return eq(y, x, z) elif isinstance(z, core.Constant): return eq(z, x, y) return (~x ^ y) & (~x ^ z) # not optimized return is_possible & operation.BvComp( (~eq(a, b, c))[n-2:], core.Constant(0, n - 1) )
def eval(cls, x, i, j): def doit(x, i, j): """Extract from x[i] down x[j] from a constant x.""" bin_repr = x.bin() prefix, value = bin_repr[:2], bin_repr[2:] n = x.width value = value[n - 1 - i:n - j] # e.g.: i=n-1, j=0 return int(prefix + value, 2) if isinstance(x, core.Constant): return core.Constant(doit(x, i, j), cls.output_width(x, i, j)) elif i == x.width - 1 and j == 0: return x elif isinstance(x, Extract): # x[3:1][2] = (x3 x2 x1)[2] = x3 = x[3] offset = x.args[2] return Extract(x.args[0], i + offset, j + offset) elif isinstance(x, Concat): if i <= x.args[1].width - 1: # 4-bit x, y: concat(x, y)[3:] = y[3:] return Extract(x.args[1], i, j) elif j >= x.args[1].width: # 4-bit x, y: concat(x, y)[:5] = x[:1] offset = x.args[1].width return Extract(x.args[0], i - offset, j - offset) elif isinstance(x, (BvShl, RotateLeft)) and \ isinstance(x.args[1], (int, core.Constant)) and x.args[1] <= j: # (x << 1)[:2] = x[n-2: 1] offset = int(x.args[1]) return Extract(x.args[0], i - offset, j - offset) elif isinstance(x, (BvLshr, RotateRight)) and \ isinstance(x.args[1], (int, core.Constant)) and i < x.width - x.args[1]: # (x >> 1)[n-3:] = x[n-2: 1] offset = int(x.args[1]) return Extract(x.args[0], i + offset, j + offset)
def is_valid(self): """Return the bv expression for non-zero propagation probability. >>> from arxpy.bitvector.core import Constant >>> from arxpy.diffcrypt.difference import DiffVar >>> from arxpy.diffcrypt.differential import RXDBvAdd >>> a, b, c = DiffVar("a", 8), DiffVar("b", 8), DiffVar("c", 8) >>> rxda = RXDBvAdd([a, b], c) >>> rxda.is_valid() # doctest: +ELLIPSIS 0b111111 == (~(((((c >> 0x01) ^ ((a >> 0x01) ^ (b >> 0x01))) ... >>> zero = Constant(0, 8) >>> rxda.is_valid().xreplace({a: zero, b: zero, c: zero}) 0b1 """ # (I ^ SHL)(da ^ db ^ dc)[1:] <= SHL((da ^ dc) OR (db ^ dc))[1:] alpha, beta = self.input_diff gamma = self.output_diff # da, db, dc = alpha[:1], beta[:1], gamma[:1] # boolector error da, db, dc = alpha >> 1, beta >> 1, gamma >> 1 # ignore LSB lhs = (da ^ db ^ dc) ^ ((da ^ db ^ dc) << 1) rhs = ((da ^ dc) | (db ^ dc)) << 1 def bitwise_implication(x, y): return (~x) | y n = lhs.width return operation.BvComp( bitwise_implication(lhs[n - 2:1], rhs[n - 2:1]), # ignore MSB, LSB ~core.Constant(0, n - 2))
def eval(cls, x, y): def doit(x, y, width): """Modular addition when both operands are integers.""" return (x + y) % (2 ** width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y), x.width), x.width) zero = core.Constant(0, x.width) if x == zero: return y elif y == zero: return x elif x == BvNeg(y): return zero
def eval(cls, x, y): def doit(x, y, width): """Modular multiplication when both operands are int.""" return (x * y) % (2**width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y), x.width), x.width) zero = core.Constant(0, x.width) one = core.Constant(1, x.width) if x == zero or y == zero: return zero elif x == one: return y elif y == one: return x
def eval(cls, x, y): def doit(x, y): """Logical right shift operation when both operands are int.""" return x >> y zero = core.Constant(0, x.width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y)), x.width) elif isinstance(y, core.Constant) and y >= x.width: return zero elif x == zero or y == zero: return x elif isinstance(x, BvLshr) and isinstance(x.args[1], core.Constant) \ and isinstance(y, core.Constant): r = min(x.args[0].width, int(x.args[1]) + int(y)) return BvLshr(x.args[0], r)
def eval(cls, x): def doit(x, width): """Unary minus operation when the operand is int.""" return ((2**width) - x) % (2**width) if isinstance(x, core.Constant): return core.Constant(doit(int(x), x.width), x.width) elif isinstance(x, BvNeg): return x.args[0]
def eval(cls, x): def doit(x, width): """NOT operation when the operand is int.""" return ~x % (2**width) if isinstance(x, core.Constant): return core.Constant(doit(int(x), x.width), x.width) elif isinstance(x, BvNot): return x.args[0]
def eval(cls, x, y): def doit(x, y, width): """Shift left operation when both operands are int.""" return (x << y) % (2**width) zero = core.Constant(0, x.width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y), x.width), x.width) elif isinstance(y, core.Constant) and y >= x.width: return zero elif x == zero or y == zero: return x elif isinstance(x, BvShl) and isinstance(x.args[1], core.Constant) \ and isinstance(y, core.Constant): # prevent out of bound r = min(x.args[0].width, int(x.args[1]) + int(y)) return BvShl(x.args[0], r)
def exact_weight(self, output_diff): """Return the weight without rounding to the closest integer. When the input/output differences have width smaller than 16, the exact weight does not coincide with the actual real weight. For example, for width=8, the error can be as high as 1.2. Moreover, in this case the exact weight is not always smaller than the weight. """ alpha, beta = [d.val for d in self.input_diff] gamma = output_diff.val da, db, dc = alpha[:1], beta[:1], gamma[:1] one = core.Constant(1, da.width) rhs = ((da ^ dc) | (db ^ dc)) << one # rhs = ((da ^ dc) | (db ^ dc))[da.width-2:] # alt v1 hw = extraop.PopCount(rhs) result = int(hw) def bitwise_implication(x, y): return (~x) | y n = alpha.width w_rotational_pair = -(math.log2((1 + 2**(1 - n) + 0.5 + 2**(-n))) - 2) if bitwise_implication(da[0] ^ db[0] ^ dc[0], core.Constant(0, 1)) == core.Constant(1, 1): result += w_rotational_pair else: result += 3 # if debug: # print("\n\n ~~ ") # print("da: ", da.bin()) # print("db: ", db.bin()) # print("dc: ", dc.bin()) # print("rhs: ", rhs.bin()) # print("hw: ", int(hw)) # print("bw_implies: ", bitwise_implication(da[0] ^ db[0] ^ dc[0], core.Constant(0, 1)).bin()) # print("result: ", result) # print("\n\n") return result
def eval(cls, x, y): def doit(x, y): """Division operation (truncated) when both operands are int.""" assert y != 0 return x // y zero = core.Constant(0, x.width) one = core.Constant(1, x.width) assert y != zero if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y)), x.width) elif x == y: return one elif x == zero: return zero elif y == one: return x
def weight(self, output_diff, prefix=None, debug=False, version=2): """Return the weight of a possible output `XorDiff`. If the output difference is symbolic, a pair ``(weight, assertions)`` is returned, where ``assertions`` is a tuple of equalities fixing some temporary variables. If the output difference is a constant value, only the value of the weight is returned. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.bitvector.context import NotEvaluation >>> from arxpy.bitvector.printing import BvWrapPrinter >>> from arxpy.bitvector.extraop import PopCount, PopCountSum2, PopCountSum3, PopCountDiff, Reverse, LeadingZeros >>> from arxpy.differential.difference import XorDiff >>> from arxpy.differential.derivative import XDCA >>> alpha, cte = XorDiff(Constant(0, 4)), Constant(1, 4) >>> f = XDCA(alpha, cte) >>> f.weight(XorDiff(Constant(0, 4))) 0x0 >>> alpha = XorDiff(Variable("u", 4)) >>> f = XDCA(alpha, cte) >>> beta = XorDiff(Variable("v", 4)) >>> with NotEvaluation([Reverse, PopCount, PopCountDiff, PopCountSum2, PopCountSum3, LeadingZeros]): ... weight_value, assertions = f.weight(beta, prefix="") >>> print(BvWrapPrinter().doprint(weight_value)) -(::(PopCountDiff((~_0lz & ((~u & ~v) << 0x1)) | ((u ^ v) << 0x1), (_1rev & _4rev) ^ (0x1 + _1rev) ^ (0x1 + _1rev + (_1rev & _4rev)), 0b0, ::(0b0, PopCount(&(_4rev & ((_1rev & _4rev) ^ (0x1 + _1rev) ^ (0x1 + _1rev + (_1rev & _4rev))), ~(((_1rev & _4rev) ^ (0x1 + _1rev) ^ (0x1 + _1rev + (_1rev & _4rev))) << 0x1) >>> assertions # doctest: +NORMALIZE_WHITESPACE [_0lz == LeadingZeros(~((~u & ~v) << 0x1)), _1rev == Reverse((~u & ~v) << 0x1), _2rev == Reverse(~(((~u & ~v) << 0x1) >> 0x1) & ((~u & ~v) << 0x1) & (~(0x2 ^ u ^ v) >> 0x1)), _3rev == Reverse(_2rev ^ _1rev ^ (_1rev + _2rev)), _4rev == Reverse((((0x1 & (((~u & ~v) << 0x1) >> 0x1)) + (0x2 & (((~u & ~v) << 0x1) >> 0x1) & ~((~u & ~v) << 0x1))) & ~(_3rev | (~(((~u & ~v) << 0x1) >> 0x1) & ((~u & ~v) << 0x1) & (~(0x2 ^ u ^ v) >> 0x1)))) | ((~(((~u & ~v) << 0x1) >> 0x1) & ((~u & ~v) << 0x1) & (~(0x2 ^ u ^ v) >> 0x1)) - (((0x1 & (((~u & ~v) << 0x1) >> 0x1)) + (0x2 & (((~u & ~v) << 0x1) >> 0x1) & ~((~u & ~v) << 0x1))) & (_3rev | (~(((~u & ~v) << 0x1) >> 0x1) & ((~u & ~v) << 0x1) & (~(0x2 ^ u ^ v) >> 0x1))))))] See `Derivative.weight` for more information. """ u = self.input_diff[0].val v = output_diff.val a = self.op.constant effective_width = self._effective_width index_one = self._index_first_one if effective_width <= 1: return core.Constant(0, 1) else: u = difference.XorDiff(u[:index_one]) v = difference.XorDiff(v[:index_one]) der = type(self)([u], a[:index_one]) return der._weight(v, prefix, debug, version)
def eval(cls, x, y): def doit(x, y): """OR operation when both operands are int.""" return x | y zero = core.Constant(0, x.width) allones = BvNot(zero) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y)), x.width) elif x == allones or y == allones: return allones elif x == zero: return y elif y == zero: return x elif x == y: return x elif x == BvNot(y): return allones
def weight(self): """Return the weight of the differential. >>> from arxpy.bitvector.core import Constant >>> from arxpy.diffcrypt.difference import DiffVar >>> from arxpy.diffcrypt.differential import RXDBvAdd >>> a, b, c = DiffVar("a", 8), DiffVar("b", 8), DiffVar("c", 8) >>> rxda = RXDBvAdd([a, b], c) >>> rxda.weight() # doctest: +ELLIPSIS (0b00010 * (0b00 ∘ (((0x0f & ((0x33 & ((0x55 & ((0b00 ... >>> zero = Constant(0, 8) >>> rxda.weight().xreplace({a: zero, b: zero, c: zero}) 0b00011 """ alpha, beta = self.input_diff gamma = self.output_diff da, db, dc = alpha[:1], beta[:1], gamma[:1] # da, db, dc = alpha >> 1, beta >> 1, gamma >> 1 rhs = ((da ^ dc) | (db ^ dc)) << 1 hw = _HammingWeight(rhs[:1]) # ignore LSB max_hw = rhs.width - 1 weight_width = max((2 * max_hw + 6).bit_length(), hw.width, 3) # 0b110 # let lhs = LSB(lhs) = da ^ db ^ dc # rhs = LSB(rhs) = 0 # case A (2 * 1.415): lhs => rhs # case B (2 * 3): lhs ^ 1 => rhs def bitwise_implication(x, y): return (~x) | y cte_part = operation.Ite( bitwise_implication(da[0] ^ db[0] ^ dc[0], core.Constant(0, 1)), core.Constant(3, weight_width), core.Constant(6, weight_width)) hw_extend = operation.ZeroExtend(hw, weight_width - hw.width) return 2 * hw_extend + cte_part
def eval(cls, bv): # Source: Hacker's Delight if bv.width < 4: w = cls.output_width(bv) return sum([operation.ZeroExtend(bv[i], w-1) for i in range(bv.width)]) # extend the bv until power of 2 length original_width = bv.width while (bv.width & (bv.width - 1)) != 0: bv = operation.ZeroExtend(bv, 1) width_log2 = bv.width.bit_length() - 1 m_ctes = [] for i in range(width_log2): m_ctes.append(repeat_pattern(pattern01(2 ** i), bv.width)) if bv.width > 32: for i, m in enumerate(m_ctes): bv = (bv & m) + ((bv >> core.Constant(2 ** i, bv.width)) & m) return bv[original_width.bit_length() - 1:] for i, m in enumerate(m_ctes): if i == 0: bv = bv - ((bv >> core.Constant(1, bv.width)) & m) elif i == 1: bv = (bv & m) + ((bv >> core.Constant(2 ** i, bv.width)) & m) # generic case elif i == 2: bv = (bv + (bv >> core.Constant(4, bv.width))) & m elif i == 3: bv = bv + (bv >> core.Constant(8, bv.width)) elif i == 4: bv = bv + (bv >> core.Constant(16, bv.width)) return bv[original_width.bit_length() - 1:]
def eval(cls, bv): # Source: Hacker's Delight if bv.width == 1: return bv elif bv.width == 2: return operation.RotateLeft(bv, 1) elif bv.width == 3: return operation.Concat(operation.Concat(bv[0], bv[1]), bv[2]) original_width = bv.width while (bv.width & (bv.width - 1)) != 0: bv = operation.ZeroExtend(bv, 1) width_log2 = bv.width.bit_length() - 1 m_ctes = [] for i in range(width_log2): m_ctes.append(repeat_pattern(pattern01(2 ** i), bv.width)) if bv.width > 32: for i, m in list(enumerate(m_ctes)): bv = ((bv & m) << core.Constant(2 ** i, bv.width)) | ((bv >> core.Constant(2 ** i, bv.width)) & m) return bv[:bv.width - original_width] for i, m in list(enumerate(m_ctes))[:3]: bv = ((bv & m) << core.Constant(2 ** i, bv.width)) | ((bv >> core.Constant(2 ** i, bv.width)) & m) # generic case if len(m_ctes) == 4: bv = ((bv & m_ctes[3]) << core.Constant(8, bv.width)) | ((bv >> core.Constant(8, bv.width)) & m_ctes[3]) elif len(m_ctes) == 5: rol = operation.RotateLeft ror = operation.RotateRight bv = ror(bv & m_ctes[3], 8) | (rol(bv, 8) & m_ctes[3]) return bv[:bv.width - original_width]
def eval(cls, x, y): def doit(x, y, width): """Modular addition when both operands are integers.""" return (x + y) % (2**width) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(int(x), int(y), x.width), x.width) zero = core.Constant(0, x.width) if x == zero: return y elif y == zero: return x elif x == BvNeg(y): return zero elif isinstance(x, BvSub): # (x0 - x1) + y if x.args[1] == y: return x.args[0] elif isinstance(y, BvSub): # x + (y0 - y1) if y.args[1] == x: return y.args[0]
def eval(cls, bv): # Source: Hacker's Delight if bv.width == 1: return ~bv original_width = bv.width while (bv.width & (bv.width - 1)) != 0: bv = operation.ZeroExtend(bv, 1) width_log2 = bv.width.bit_length() - 1 for i in range(width_log2): bv = bv | (bv >> core.Constant(2 ** i, bv.width)) return ~bv[original_width - 1:]
def eval(cls, x, y): # Source: Hacker's Delight if x.width < 4: # the HW of a 1-bit/2-bit vector requires 1-bit/2-bit (HW(0b1)=0b1, HW(0b11)=0b10) # thus, the sum of two HW of these sizes require an extra bit # the HW of a 3-bit vector requires 2-bit (Hw(0b111)=0b11) # and the sum of two HW of 3-bit also require an extra bit return operation.ZeroExtend(PopCount(x), 1) + operation.ZeroExtend(PopCount(y), 1) elif x.width > 32: width = cls.output_width(x, y) x = PopCount(x) x = operation.ZeroExtend(x, width - x.width) y = PopCount(y) y = operation.ZeroExtend(y, width - y.width) return x + y orig_x, orig_y = x, y while (x.width & (x.width - 1)) != 0: x = operation.ZeroExtend(x, 1) while (y.width & (y.width - 1)) != 0: y = operation.ZeroExtend(y, 1) width_log2 = x.width.bit_length() - 1 m_ctes = [] for i in range(width_log2): m_ctes.append(repeat_pattern(pattern01(2 ** i), x.width)) bv = core.Constant(0, x.width) for i, m in enumerate(m_ctes): if i == 0: x = x - ((x >> core.Constant(1, bv.width)) & m) y = y - ((y >> core.Constant(1, bv.width)) & m) bv = x + y elif i == 1: x = (x & m) + ((x >> core.Constant(2 ** i, bv.width)) & m) # generic case y = (y & m) + ((y >> core.Constant(2 ** i, bv.width)) & m) bv = x + y elif i == 2: bv = (bv & m) + ((bv >> core.Constant(4, bv.width)) & m) elif i == 3: bv = bv + (bv >> core.Constant(8, bv.width)) elif i == 4: bv = bv + (bv >> core.Constant(16, bv.width)) return bv[cls.output_width(orig_x, orig_y) - 1:]
def eval(cls, x, r): def doit(val, r, width): """Right cyclic rotation operation when both operands are int.""" mask = 2**width - 1 r = r % width return ((val & mask) >> r) | (val << (width - r) & mask) if isinstance(x, core.Constant): return core.Constant(doit(int(x), r, x.width), x.width) elif r == 0: return x elif isinstance(x, RotateRight): return RotateRight(x.args[0], (x.args[1] + r) % x.args[0].width) elif isinstance(x, RotateLeft): return RotateLeft(x.args[0], (x.args[1] - r) % x.args[0].width)
def eval(cls, x, y): def doit(x, y): """Concatenation when both operands are int.""" return int(x.bin() + y.bin()[2:], 2) if isinstance(x, core.Constant) and isinstance(y, core.Constant): return core.Constant(doit(x, y), cls.output_width(x, y)) elif isinstance(x, core.Constant) and isinstance(y, Concat) and \ isinstance(y.args[0], core.Constant): return Concat(Concat(x, y.args[0]), y.args[1]) elif isinstance(y, core.Constant) and isinstance(x, Concat) and \ isinstance(x.args[1], core.Constant): return Concat(x.args[0], Concat(x.args[1], y)) elif isinstance(x, Extract) and isinstance(y, Extract): # x[5:4] concat x[3:2] = x[5:2] if x.args[0] == y.args[0] and x.args[2] == y.args[1] + 1: return Extract(x.args[0], x.args[1], y.args[2])
def _has_probability_one(self, output_diff): u = self.input_diff[0].val v = output_diff.val a = self.op.constant n = a.width assert self._effective_width == n - 1 one = core.Constant(1, n) def all_ones(width): return ~ core.Constant(0, width) case00_ = (~(u << one)) & (~(v << one)) # i-bit is True if S_i = 00* case__1 = u ^ v case000 = case00_ & (~case__1) return operation.BvComp(case000, all_ones(n))