def parse_UnaryOp(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, vy_ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool": return IRnode.from_list(["iszero", operand], typ="bool") if isinstance(self.expr.op, vy_ast.Invert): if isinstance(operand.typ, EnumType): n_members = len(operand.typ.members) # use (xor 0b11..1 operand) to flip all the bits in # `operand`. `mask` could be a very large constant and # hurt codesize, but most user enums will likely have few # enough members that the mask will not be large. mask = (2**n_members) - 1 return IRnode.from_list(["xor", mask, operand], typ=operand.typ) if is_base_type(operand.typ, "uint256"): return IRnode.from_list(["not", operand], typ=operand.typ) # block `~` for all other integer types, since reasoning # about dirty bits is not entirely trivial. maybe revisit # this at a later date. raise UnimplementedException( f"~ is not supported for {operand.typ}", self.expr) if isinstance(self.expr.op, vy_ast.USub) and is_numeric_type( operand.typ): assert operand.typ._num_info.is_signed # Clamp on minimum signed integer value as we cannot negate that # value (all other integer values are fine) min_int_val, _ = operand.typ._num_info.bounds return IRnode.from_list( ["sub", 0, clamp("sgt", operand, min_int_val)], typ=operand.typ)
def _clamp_numeric_convert(arg, arg_bounds, out_bounds, arg_is_signed): arg_lo, arg_hi = arg_bounds out_lo, out_hi = out_bounds if arg_lo < out_lo: # if not arg_is_signed, arg_lo is 0, so this branch cannot be hit assert arg_is_signed, "bad assumption in numeric convert" arg = clamp("sge", arg, out_lo) if arg_hi > out_hi: # out_hi must be smaller than MAX_UINT256, so clample makes sense. # add an assertion, just in case this assumption ever changes. assert out_hi < 2**256 - 1, "bad assumption in numeric convert" CLAMP_OP = "sle" if arg_is_signed else "le" arg = clamp(CLAMP_OP, arg, out_hi) return arg
def safe_div(x, y): num_info = x.typ._num_info typ = x.typ ok = [1] # true if is_decimal_type(x.typ): lo, hi = num_info.bounds if max(abs(lo), abs(hi)) * num_info.divisor > 2**256 - 1: # stub to prevent us from adding fixed point numbers we don't know # how to deal with raise UnimplementedException( "safe_mul for decimal{num_info.bits}x{num_info.decimals}") x = ["mul", x, num_info.divisor] DIV = "sdiv" if num_info.is_signed else "div" res = IRnode.from_list([DIV, x, clamp("gt", y, 0)], typ=typ) with res.cache_when_complex("res") as (b1, res): # TODO: refactor this condition / push some things into the optimizer if num_info.is_signed and num_info.bits == 256: if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) if not x.is_literal and not y.typ.is_literal: ok = ["or", ["ne", y, ["not", 0]], ["ne", x, upper_bound]] # TODO push these rules into the optimizer elif x.is_literal and x.value == -(2**255): ok = ["ne", y, ["not", 0]] elif y.is_literal and y.value == -1: ok = ["ne", x, upper_bound] else: # x or y is a literal, and not an evil value. pass elif num_info.is_signed and is_integer_type(typ): lo, hi = num_info.bounds # we need to throw on min_value(typ) / -1, # but we can skip if one of the operands is a literal and not # the evil value can_skip_clamp = (x.is_literal and x.value != lo) or (y.is_literal and y.value != -1) if not can_skip_clamp: # clamp_basetype has fewer ops than the int256 rule. res = clamp_basetype(res) elif is_decimal_type(typ): # always clamp decimals, since decimal division can actually # result in something larger than either operand (e.g. 1.0 / 0.1) # TODO maybe use safe_mul res = clamp_basetype(res) check = IRnode.from_list(["assert", ok], error_msg="safemul") return IRnode.from_list(b1.resolve(["seq", check, res]))
def _int_to_int(arg, out_typ): arg_info = arg.typ._int_info out_info = out_typ._int_info # do the same thing as # _clamp_numeric_convert(arg, arg_info.bounds, out_info.bounds, arg_info.is_signed) # but with better code size and gas. if arg_info.is_signed and not out_info.is_signed: # e.g. (clample (clampge arg 0) (2**128 - 1)) # note that when out_info.bits == 256, # (clample arg 2**256 - 1) does not make sense. # see similar assertion in _clamp_numeric_convert. if out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # note: because of the usage of signed=False, and the fact # that out_bits < 256 in this branch, below implies # not only (clample arg 2**128 - 1) but also (clampge arg 0). arg = int_clamp(arg, out_info.bits, signed=False) else: # note: this also works for out_bits == 256. arg = clamp("sge", arg, 0) elif not arg_info.is_signed and out_info.is_signed: # e.g. (uclample (uclampge arg 0) (2**127 - 1)) # (note that (uclampge arg 0) always evaluates to true.) arg = int_clamp(arg, out_info.bits - 1, signed=False) elif out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # narrowing conversion, signs are the same. # we can just use regular int clampers. arg = int_clamp(arg, out_info.bits, out_info.is_signed) else: # widening conversion, signs are the same. # we do not have to do any clamps. assert arg_info.is_signed == out_info.is_signed and out_info.bits >= arg_info.bits return IRnode.from_list(arg, typ=out_typ)
def safe_mod(x, y): num_info = x.typ._num_info MOD = "smod" if num_info.is_signed else "mod" return IRnode.from_list([MOD, x, clamp("gt", y, 0)])