def to_address(expr, arg, out_typ): # question: should this be allowed? if is_integer_type(arg.typ): if arg.typ._int_info.is_signed: _FAIL(arg.typ, out_typ, expr) # TODO once we introduce uint160, we can just check that arg # is unsigned, and then call to_int(expr, arg, uint160) because # the logic is equivalent. # disallow casting from Bytes[N>20] _check_bytes(expr, arg, out_typ, 32) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if arg_typ.maxlen > 20: arg = int_clamp(arg, 160, signed=False) if is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if info.m > 20: arg = int_clamp(arg, 160, signed=False) elif is_integer_type(arg.typ): arg_info = arg.typ._int_info if arg_info.bits > 160 or arg_info.is_signed: arg = int_clamp(arg, 160, signed=False) return IRnode.from_list(arg, typ=out_typ)
def clamp_basetype(ir_node): t = ir_node.typ if not isinstance(t, BaseType): raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest # copy of the input ir_node = unwrap_location(ir_node) if isinstance(t, EnumType): bits = len(t.members) # assert x >> bits == 0 ret = int_clamp(ir_node, bits, signed=False) elif is_integer_type(t) or is_decimal_type(t): if t._num_info.bits == 256: ret = ir_node else: ret = int_clamp(ir_node, t._num_info.bits, signed=t._num_info.is_signed) elif is_bytes_m_type(t): if t._bytes_info.m == 32: ret = ir_node # special case, no clamp. else: ret = bytes_clamp(ir_node, t._bytes_info.m) elif t.typ in ("address", ): ret = int_clamp(ir_node, 160) elif t.typ in ("bool", ): ret = int_clamp(ir_node, 1) else: # pragma: nocover raise CompilerPanic(f"{t} passed to clamp_basetype") return IRnode.from_list(ret, typ=ir_node.typ)
def safe_pow(x, y): num_info = x.typ._num_info if not is_integer_type(x.typ): # type checker should have caught this raise TypeCheckFailure("non-integer pow") if x.is_literal: # cannot pass 1 or 0 to `calculate_largest_power` if x.value == 1: return IRnode.from_list([1]) if x.value == 0: return IRnode.from_list(["iszero", y]) upper_bound = calculate_largest_power(x.value, num_info.bits, num_info.is_signed) + 1 # for signed integers, this also prevents negative values ok = ["lt", y, upper_bound] elif y.is_literal: upper_bound = calculate_largest_base(y.value, num_info.bits, num_info.is_signed) + 1 if num_info.is_signed: ok = ["and", ["slt", x, upper_bound], ["sgt", x, -upper_bound]] else: ok = ["lt", x, upper_bound] else: # `a ** b` where neither `a` or `b` are known # TODO this is currently unreachable, once we implement a way to do it safely # remove the check in `vyper/context/types/value/numeric.py` return return IRnode.from_list(["seq", ["assert", ok], ["exp", x, y]])
def to_address(expr, arg, out_typ): # question: should this be allowed? if is_integer_type(arg.typ): if arg.typ._int_info.is_signed: _FAIL(arg.typ, out_typ, expr) return to_int(expr, arg, out_typ)
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) ret = b.resolve(ret) return IRnode.from_list(ret)
def to_int(expr, arg, out_typ): int_info = out_typ._int_info assert int_info.bits % 8 == 0 _check_bytes(expr, arg, out_typ, 32) if isinstance(expr, vy_ast.Constant): return _literal_int(expr, arg.typ, out_typ) elif isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_typ.maxlen * 8 > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_info.m_bits > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_decimal_type(arg.typ): arg = _fixed_to_int(arg, out_typ) elif is_integer_type(arg.typ): arg = _int_to_int(arg, out_typ) elif is_base_type(arg.typ, "address"): if int_info.is_signed: # TODO if possible, refactor to move this validation close to the entry of the function _FAIL(arg.typ, out_typ, expr) if int_info.bits < 160: arg = int_clamp(arg, int_info.bits, signed=False) return IRnode.from_list(arg, typ=out_typ)
def clamp_basetype(ir_node): t = ir_node.typ if not isinstance(t, BaseType): raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest # copy of the input ir_node = unwrap_location(ir_node) if is_integer_type(t) or is_decimal_type(t): if t._num_info.bits == 256: return ir_node else: return int_clamp(ir_node, t._num_info.bits, signed=t._num_info.is_signed) if is_bytes_m_type(t): if t._bytes_info.m == 32: return ir_node # special case, no clamp. else: return bytes_clamp(ir_node, t._bytes_info.m) if t.typ in ("address", ): return int_clamp(ir_node, 160) if t.typ in ("bool", ): return int_clamp(ir_node, 1) raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest
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 _type_class_of(typ): if is_integer_type(typ): return "int" if is_bytes_m_type(typ): return "bytes_m" if is_decimal_type(typ): return "decimal" if isinstance(typ, BaseType): return typ.typ # e.g., "bool" if isinstance(typ, ByteArrayType): return "bytes" if isinstance(typ, StringType): return "string"
def to_decimal(expr, arg, out_typ): # question: is converting from Bytes to decimal allowed? _check_bytes(expr, arg, out_typ, max_bytes_allowed=16) if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if arg_typ.maxlen * 8 > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if info.m_bits > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): int_info = arg.typ._int_info arg = _int_to_fixed(arg, out_typ) out_info = out_typ._decimal_info if int_info.bits > out_info.bits: # TODO: _num_clamp probably not necessary bc already # clamped in _int_to_fixed arg = _num_clamp(arg, out_info, int_info) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest
def to_bytes_m(expr, arg, out_typ): out_info = out_typ._bytes_info _check_bytes(expr, arg, out_typ, max_bytes_allowed=out_info.m) if isinstance(arg.typ, ByteArrayType): bytes_val = LOAD(bytes_data_ptr(arg)) # zero out any dirty bytes (which can happen in the last # word of a bytearray) len_ = get_bytearray_length(arg) num_zero_bits = IRnode.from_list(["mul", ["sub", 32, len_], 8]) with num_zero_bits.cache_when_complex("bits") as (b, num_zero_bits): arg = shl(num_zero_bits, shr(num_zero_bits, bytes_val)) arg = b.resolve(arg) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info # clamp if it's a downcast if arg_info.m > out_info.m: arg = bytes_clamp(arg, out_info.m) elif is_integer_type(arg.typ) or is_base_type(arg.typ, "address"): int_bits = arg.typ._int_info.bits if out_info.m_bits < int_bits: # question: allow with runtime clamp? # arg = int_clamp(m_bits, signed=int_info.signed) _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) elif is_decimal_type(arg.typ): if out_info.m_bits < arg.typ._decimal_info.bits: _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) else: # bool arg = shl(256 - out_info.m_bits, arg) return IRnode.from_list(arg, typ=out_typ)
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node original_arg = arg out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif isinstance(out_typ, EnumType): ret = to_enum(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) # test if arg actually changed. if not, we do not need to use # unwrap_location (this can reduce memory traffic for downstream # operations which are in-place, like the returndata routine) test_arg = IRnode.from_list(arg, typ=out_typ) if test_arg == ret: original_arg.typ = out_typ return original_arg return IRnode.from_list(b.resolve(ret))
def to_decimal(expr, arg, out_typ): _check_bytes(expr, arg, out_typ, 32) out_info = out_typ._decimal_info if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, arg.typ, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) if arg_typ.maxlen * 8 > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) if info.m_bits > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): # TODO: consider adding _int_info to bool so we can use _int_to_fixed arg = ["mul", arg, 10**out_info.decimals] return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest
def _get_element_ptr_array(parent, key, array_bounds_check): assert isinstance(parent.typ, ArrayLike) if not is_integer_type(key.typ): raise TypeCheckFailure(f"{key.typ} used as array index") subtype = parent.typ.subtype if parent.value == "~empty": if array_bounds_check: # this case was previously missing a bounds check. codegen # is a bit complicated when bounds check is required, so # block it. there is no reason to index into a literal empty # array anyways! raise TypeCheckFailure("indexing into zero array not allowed") return IRnode.from_list("~empty", subtype) if parent.value == "multi": assert isinstance(key.value, int) return parent.args[key.value] ix = unwrap_location(key) if array_bounds_check: is_darray = isinstance(parent.typ, DArrayType) bound = get_dyn_array_count(parent) if is_darray else parent.typ.count # uclamplt works, even for signed ints. since two's-complement # is used, if the index is negative, (unsigned) LT will interpret # it as a very large number, larger than any practical value for # an array index, and the clamp will throw an error. # NOTE: there are optimization rules for this when ix or bound is literal ix = clamp("lt", ix, bound) if parent.encoding == Encoding.ABI: if parent.location == STORAGE: raise CompilerPanic("storage variables should not be abi encoded" ) # pragma: notest member_abi_t = subtype.abi_type ofst = _mul(ix, member_abi_t.embedded_static_size()) return _getelemptr_abi_helper(parent, subtype, ofst) if parent.location.word_addressable: element_size = subtype.storage_size_in_words elif parent.location.byte_addressable: element_size = subtype.memory_bytes_required else: raise CompilerPanic("unreachable") # pragma: notest ofst = _mul(ix, element_size) if has_length_word(parent.typ): data_ptr = add_ofst( parent, parent.location.word_scale * DYNAMIC_ARRAY_OVERHEAD) else: data_ptr = parent return IRnode.from_list(add_ofst(data_ptr, ofst), typ=subtype, location=parent.location)