From 551f711d00c97b4423d002f465334be0b088f4c7 Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Tue, 20 Jan 2026 15:49:49 +0000 Subject: [PATCH 1/2] checked arithmetic --- crates/codegen/src/yul/emitter/expr.rs | 330 ++++ crates/codegen/tests/fixtures/alloc.snap | 24 +- .../codegen/tests/fixtures/arg_bindings.snap | 14 +- crates/codegen/tests/fixtures/aug_assign.snap | 62 +- .../tests/fixtures/aug_assign_bit_ops.snap | 84 +- crates/codegen/tests/fixtures/bit_ops.snap | 79 +- .../codegen/tests/fixtures/code_region.snap | 23 +- .../tests/fixtures/comparison_ops.snap | 58 +- .../tests/fixtures/create_contract.snap | 312 ++-- .../tests/fixtures/effect_ptr_domains.snap | 26 +- .../tests/fixtures/enum_variant_contract.snap | 112 +- crates/codegen/tests/fixtures/erc20.snap | 491 ++++-- .../tests/fixtures/erc20_low_level.snap | 130 +- .../codegen/tests/fixtures/full_contract.snap | 101 +- .../codegen/tests/fixtures/function_call.snap | 14 +- .../tests/fixtures/high_level_contract.snap | 172 ++- crates/codegen/tests/fixtures/if_else.snap | 14 +- .../codegen/tests/fixtures/match_struct.snap | 14 +- .../tests/fixtures/match_tuple_binding.snap | 52 +- crates/codegen/tests/fixtures/momo.snap | 14 +- .../tests/fixtures/name_collisions.snap | 103 +- .../newtype_field_mut_method_call.snap | 24 +- .../newtype_storage_byplace_effect_arg.snap | 127 +- ...newtype_storage_field_mut_method_call.snap | 120 +- .../newtype_word_mut_method_call.snap | 18 +- .../codegen/tests/fixtures/range_bounds.snap | 117 +- crates/codegen/tests/fixtures/ret.snap | 51 +- crates/codegen/tests/fixtures/storage.snap | 131 +- .../codegen/tests/fixtures/storage_map.snap | 18 +- .../tests/fixtures/storage_map_contract.snap | 48 +- .../fixtures/test_output/effect_test.snap | 18 +- .../tests/fixtures/tstor_ptr_contract.snap | 128 +- .../tests/fixtures/while_cond_call.snap | 22 +- .../tests/fixtures/wrapping_saturating.fe | 18 + .../tests/fixtures/wrapping_saturating.snap | 295 ++++ .../fe_test/checked_arithmetic_methods.fe | 30 + .../checked_arithmetic_reverts.fe | 47 + .../checked_arithmetic_reverts.snap | 39 + crates/hir/src/analysis/diagnostics.rs | 4 +- crates/hir/src/analysis/ty/const_ty.rs | 363 ++++- crates/hir/test_files/ty_check/ops.fe | 6 +- crates/hir/test_files/ty_check/ops.snap | 354 ++--- crates/mir/src/lower/expr.rs | 198 ++- crates/mir/src/lower/intrinsics.rs | 33 + crates/mir/src/lower/prepass.rs | 14 + crates/mir/tests/fixtures/abi_decode.mir.snap | 56 +- crates/mir/tests/fixtures/echo.mir.snap | 117 +- .../mir/tests/fixtures/for_continue.mir.snap | 51 +- .../mir/tests/fixtures/kitchen_sink.mir.snap | 48 +- .../fixtures/transient_field_effects.mir.snap | 64 +- .../tests/fixtures/while_cond_call.mir.snap | 21 +- .../fixtures/zst_load_index_eval.mir.snap | 24 +- .../const_checked_arithmetic_overflow.fe | 25 + .../const_checked_arithmetic_overflow.snap | 53 + library/core/src/num.fe | 1363 +++++++++++++++-- library/core/src/ops.fe | 45 +- 56 files changed, 5177 insertions(+), 1142 deletions(-) create mode 100644 crates/codegen/tests/fixtures/wrapping_saturating.fe create mode 100644 crates/codegen/tests/fixtures/wrapping_saturating.snap create mode 100644 crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe create mode 100644 crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe create mode 100644 crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap create mode 100644 crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.fe create mode 100644 crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.snap diff --git a/crates/codegen/src/yul/emitter/expr.rs b/crates/codegen/src/yul/emitter/expr.rs index 1ff07c158a..a306de627d 100644 --- a/crates/codegen/src/yul/emitter/expr.rs +++ b/crates/codegen/src/yul/emitter/expr.rs @@ -1,5 +1,6 @@ //! Expression and value lowering helpers shared across the Yul emitter. +use common::ingot::IngotKind; use hir::analysis::ty::simplified_pattern::ConstructorKind; use hir::analysis::ty::ty_def::{PrimTy, TyBase, TyData, TyId}; use hir::hir_def::{ @@ -23,6 +24,331 @@ use super::{ }; impl<'db> FunctionEmitter<'db> { + /// Attempts to lower a call to a core numeric intrinsic directly to inline Yul. + /// + /// This is an optimization that avoids generating separate Yul functions for + /// primitive arithmetic intrinsics like `__add_u8`, `__sub_i32`, `__mul_u256`, etc. + /// Instead, it recognizes the naming pattern `___` and emits the + /// corresponding Yul opcode inline with appropriate bit masking. + /// + /// For types smaller than 256 bits, proper masking is applied: + /// - **Unsigned types**: Results are masked with `and(result, mask)` to truncate + /// overflow bits (e.g., `u8` uses mask `0xff`). + /// - **Signed types**: Results use `signextend(byte, and(value, mask))` to + /// correctly propagate the sign bit. + /// - **256-bit types** (`u256`, `i256`): No masking needed. + /// + /// # Parameters + /// - `call`: The call origin containing the target function and arguments. + /// - `state`: Current block state for lowering argument values. + /// + /// # Returns + /// - `Ok(Some(yul))`: The intrinsic was recognized and lowered to inline Yul. + /// - `Ok(None)`: The call is not a recognized core numeric intrinsic; the caller + /// should fall back to normal function call emission. + /// - `Err(...)`: An error occurred during lowering. + fn try_lower_core_numeric_intrinsic_call( + &self, + call: &CallOrigin<'_>, + state: &BlockState, + ) -> Result, YulError> { + let Some(target) = call.hir_target.as_ref() else { + return Ok(None); + }; + let CallableDef::Func(func) = target.callable_def else { + return Ok(None); + }; + if func.body(self.db).is_some() { + return Ok(None); + } + + match target.callable_def.ingot(self.db).kind(self.db) { + IngotKind::Core | IngotKind::Std => {} + _ => return Ok(None), + } + + let Some(name) = target.callable_def.name(self.db) else { + return Ok(None); + }; + let name = name.data(self.db).as_str(); + if name == "__bitcast" || !name.starts_with("__") { + return Ok(None); + } + + let Some((op, suffix)) = name[2..].rsplit_once('_') else { + return Ok(None); + }; + + let mut lowered_args = Vec::with_capacity(call.args.len()); + for &arg in &call.args { + lowered_args.push(self.lower_value(arg, state)?); + } + if !call.effect_args.is_empty() { + return Err(YulError::Unsupported(format!( + "core numeric intrinsic `{name}` unexpectedly has effect args" + ))); + } + + #[derive(Clone, Copy, Debug)] + enum IntPrim { + Unsigned { + mask: Option<&'static str>, + }, + Signed { + mask: Option<&'static str>, + signextend_byte: Option, + }, + } + + fn int_prim_from_suffix(suffix: &str) -> Option { + Some(match suffix { + "u8" => IntPrim::Unsigned { mask: Some("0xff") }, + "u16" => IntPrim::Unsigned { + mask: Some("0xffff"), + }, + "u32" => IntPrim::Unsigned { + mask: Some("0xffffffff"), + }, + "u64" => IntPrim::Unsigned { + mask: Some("0xffffffffffffffff"), + }, + "u128" => IntPrim::Unsigned { + mask: Some("0xffffffffffffffffffffffffffffffff"), + }, + "u256" | "usize" => IntPrim::Unsigned { mask: None }, + "i8" => IntPrim::Signed { + mask: Some("0xff"), + signextend_byte: Some(0), + }, + "i16" => IntPrim::Signed { + mask: Some("0xffff"), + signextend_byte: Some(1), + }, + "i32" => IntPrim::Signed { + mask: Some("0xffffffff"), + signextend_byte: Some(3), + }, + "i64" => IntPrim::Signed { + mask: Some("0xffffffffffffffff"), + signextend_byte: Some(7), + }, + "i128" => IntPrim::Signed { + mask: Some("0xffffffffffffffffffffffffffffffff"), + signextend_byte: Some(15), + }, + "i256" | "isize" => IntPrim::Signed { + mask: None, + signextend_byte: None, + }, + _ => return None, + }) + } + + fn trunc_bits(value: &str, prim: IntPrim) -> String { + match prim { + IntPrim::Unsigned { mask: Some(mask) } + | IntPrim::Signed { + mask: Some(mask), .. + } => { + format!("and({value}, {mask})") + } + IntPrim::Unsigned { mask: None } | IntPrim::Signed { mask: None, .. } => { + value.to_string() + } + } + } + + fn canonical_unsigned(value: &str, mask: Option<&'static str>) -> String { + match mask { + Some(mask) => format!("and({value}, {mask})"), + None => value.to_string(), + } + } + + fn canonical_signed( + value: &str, + mask: Option<&'static str>, + signextend_byte: Option, + ) -> String { + match (mask, signextend_byte) { + (Some(mask), Some(byte)) => format!("signextend({byte}, and({value}, {mask}))"), + _ => value.to_string(), + } + } + + let lowered = if suffix == "bool" { + let normalize_bool = |value: &str| format!("iszero(iszero({value}))"); + + match (op, lowered_args.as_slice()) { + ("not", [arg]) => Some(format!("iszero({})", normalize_bool(arg))), + ("bitand", [lhs, rhs]) => Some(format!( + "and({}, {})", + normalize_bool(lhs), + normalize_bool(rhs) + )), + ("bitor", [lhs, rhs]) => Some(format!( + "or({}, {})", + normalize_bool(lhs), + normalize_bool(rhs) + )), + ("bitxor", [lhs, rhs]) => Some(format!( + "xor({}, {})", + normalize_bool(lhs), + normalize_bool(rhs) + )), + ("eq", [lhs, rhs]) => Some(format!( + "eq({}, {})", + normalize_bool(lhs), + normalize_bool(rhs) + )), + ("ne", [lhs, rhs]) => Some(format!( + "iszero(eq({}, {}))", + normalize_bool(lhs), + normalize_bool(rhs) + )), + _ => None, + } + } else if let Some(int_prim) = int_prim_from_suffix(suffix) { + let (mask, signextend_byte, signed) = match int_prim { + IntPrim::Unsigned { mask } => (mask, None, false), + IntPrim::Signed { + mask, + signextend_byte, + } => (mask, signextend_byte, true), + }; + + let arg_unsigned = |value: &str| canonical_unsigned(value, mask); + let arg_signed = |value: &str| canonical_signed(value, mask, signextend_byte); + let result_unsigned = |value: String| canonical_unsigned(&value, mask); + let result_signed = |value: String| canonical_signed(&value, mask, signextend_byte); + + match (op, lowered_args.as_slice()) { + ("add", [lhs, rhs]) => Some(if signed { + result_signed(format!("add({}, {})", arg_signed(lhs), arg_signed(rhs))) + } else { + result_unsigned(format!("add({}, {})", arg_unsigned(lhs), arg_unsigned(rhs))) + }), + ("sub", [lhs, rhs]) => Some(if signed { + result_signed(format!("sub({}, {})", arg_signed(lhs), arg_signed(rhs))) + } else { + result_unsigned(format!("sub({}, {})", arg_unsigned(lhs), arg_unsigned(rhs))) + }), + ("mul", [lhs, rhs]) => Some(if signed { + result_signed(format!("mul({}, {})", arg_signed(lhs), arg_signed(rhs))) + } else { + result_unsigned(format!("mul({}, {})", arg_unsigned(lhs), arg_unsigned(rhs))) + }), + ("div", [lhs, rhs]) => Some(if signed { + result_signed(format!("sdiv({}, {})", arg_signed(lhs), arg_signed(rhs))) + } else { + result_unsigned(format!("div({}, {})", arg_unsigned(lhs), arg_unsigned(rhs))) + }), + ("rem", [lhs, rhs]) => Some(if signed { + result_signed(format!("smod({}, {})", arg_signed(lhs), arg_signed(rhs))) + } else { + result_unsigned(format!("mod({}, {})", arg_unsigned(lhs), arg_unsigned(rhs))) + }), + ("pow", [lhs, rhs]) => { + let base_bits = trunc_bits(lhs, int_prim); + let exp_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("exp({base_bits}, {exp_bits})")) + } else { + result_unsigned(format!("exp({base_bits}, {exp_bits})")) + }) + } + ("shl", [lhs, rhs]) => { + let value_bits = trunc_bits(lhs, int_prim); + let shift_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("shl({shift_bits}, {value_bits})")) + } else { + result_unsigned(format!("shl({shift_bits}, {value_bits})")) + }) + } + ("shr", [lhs, rhs]) => { + let shift_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("sar({shift_bits}, {})", arg_signed(lhs))) + } else { + result_unsigned(format!("shr({shift_bits}, {})", arg_unsigned(lhs))) + }) + } + ("bitand", [lhs, rhs]) => { + let lhs_bits = trunc_bits(lhs, int_prim); + let rhs_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("and({lhs_bits}, {rhs_bits})")) + } else { + result_unsigned(format!("and({lhs_bits}, {rhs_bits})")) + }) + } + ("bitor", [lhs, rhs]) => { + let lhs_bits = trunc_bits(lhs, int_prim); + let rhs_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("or({lhs_bits}, {rhs_bits})")) + } else { + result_unsigned(format!("or({lhs_bits}, {rhs_bits})")) + }) + } + ("bitxor", [lhs, rhs]) => { + let lhs_bits = trunc_bits(lhs, int_prim); + let rhs_bits = trunc_bits(rhs, int_prim); + Some(if signed { + result_signed(format!("xor({lhs_bits}, {rhs_bits})")) + } else { + result_unsigned(format!("xor({lhs_bits}, {rhs_bits})")) + }) + } + ("bitnot", [arg]) => { + let arg_bits = trunc_bits(arg, int_prim); + Some(if signed { + result_signed(format!("not({arg_bits})")) + } else { + result_unsigned(format!("not({arg_bits})")) + }) + } + ("neg", [arg]) => Some(result_signed(format!("sub(0, {})", arg_signed(arg)))), + ("eq", [lhs, rhs]) => { + let lhs_bits = trunc_bits(lhs, int_prim); + let rhs_bits = trunc_bits(rhs, int_prim); + Some(format!("eq({lhs_bits}, {rhs_bits})")) + } + ("ne", [lhs, rhs]) => { + let lhs_bits = trunc_bits(lhs, int_prim); + let rhs_bits = trunc_bits(rhs, int_prim); + Some(format!("iszero(eq({lhs_bits}, {rhs_bits}))")) + } + ("lt", [lhs, rhs]) => Some(if signed { + format!("slt({}, {})", arg_signed(lhs), arg_signed(rhs)) + } else { + format!("lt({}, {})", arg_unsigned(lhs), arg_unsigned(rhs)) + }), + ("le", [lhs, rhs]) => Some(if signed { + format!("iszero(sgt({}, {}))", arg_signed(lhs), arg_signed(rhs)) + } else { + format!("iszero(gt({}, {}))", arg_unsigned(lhs), arg_unsigned(rhs)) + }), + ("gt", [lhs, rhs]) => Some(if signed { + format!("sgt({}, {})", arg_signed(lhs), arg_signed(rhs)) + } else { + format!("gt({}, {})", arg_unsigned(lhs), arg_unsigned(rhs)) + }), + ("ge", [lhs, rhs]) => Some(if signed { + format!("iszero(slt({}, {}))", arg_signed(lhs), arg_signed(rhs)) + } else { + format!("iszero(lt({}, {}))", arg_unsigned(lhs), arg_unsigned(rhs)) + }), + _ => None, + } + } else { + None + }; + + Ok(lowered) + } + fn format_hir_expr_context(&self, expr: hir::hir_def::ExprId) -> String { let Some(body) = (match self.mir_func.origin { MirFunctionOrigin::Hir(func) => func.body(self.db), @@ -199,6 +525,10 @@ impl<'db> FunctionEmitter<'db> { )); } + if let Some(intrinsic) = self.try_lower_core_numeric_intrinsic_call(call, state)? { + return Ok(intrinsic); + } + let is_evm_op = match call.hir_target.as_ref() { Some(target) => { matches!( diff --git a/crates/codegen/tests/fixtures/alloc.snap b/crates/codegen/tests/fixtures/alloc.snap index 6a0129b0d9..c65be5b63b 100644 --- a/crates/codegen/tests/fixtures/alloc.snap +++ b/crates/codegen/tests/fixtures/alloc.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/alloc.fe --- @@ -16,11 +15,28 @@ function $bump_const() -> ret { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) + ret := v0 + leave +} +function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave +} +function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/arg_bindings.snap b/crates/codegen/tests/fixtures/arg_bindings.snap index 40aa2f548a..41dba24043 100644 --- a/crates/codegen/tests/fixtures/arg_bindings.snap +++ b/crates/codegen/tests/fixtures/arg_bindings.snap @@ -1,11 +1,21 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/arg_bindings.fe --- function $arg_bindings($x, $y) -> ret { - let v0 := add($x, $y) + let v0 := $u64_add_add($x, $y) + let v1 := v0 + ret := v1 + leave +} +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/aug_assign.snap b/crates/codegen/tests/fixtures/aug_assign.snap index 36a3d8559a..b408a6bffa 100644 --- a/crates/codegen/tests/fixtures/aug_assign.snap +++ b/crates/codegen/tests/fixtures/aug_assign.snap @@ -1,13 +1,67 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/aug_assign.fe --- function $aug_assign($x, $y) -> ret { let v0 := $x - v0 := add(v0, $y) - v0 := mul(v0, 2) - ret := div(v0, 2) + let v1 := $u64_addassign_add_assign(v0, $y) + v0 := v1 + let v2 := $u64_mulassign_mul_assign(v0, 2) + v0 := v2 + let v3 := $u64_div_div(v0, 2) + ret := v3 + leave +} +function $u64_addassign_add_assign($self, $other) -> ret { + let v0 := $u64_add_add($self, $other) + ret := v0 + leave +} +function $u64_mulassign_mul_assign($self, $other) -> ret { + let v0 := $u64_mul_mul($self, $other) + ret := v0 + leave +} +function $u64_div_div($self, $other) -> ret { + let v0 := $u64_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := and(div(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v2 + leave +} +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} +function $u64_mul_mul($self, $other) -> ret { + let v0 := and(mul(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := $u64_eq_ne($self, 0) + let v2 := $u64_div_div(v0, $self) + let v3 := $u64_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 + leave +} +function $u64_eq_eq($self, $other) -> ret { + let v0 := eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} +function $u64_eq_ne($self, $other) -> ret { + let v0 := iszero(eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff))) + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/aug_assign_bit_ops.snap b/crates/codegen/tests/fixtures/aug_assign_bit_ops.snap index 5cd03efd9d..255d9a7384 100644 --- a/crates/codegen/tests/fixtures/aug_assign_bit_ops.snap +++ b/crates/codegen/tests/fixtures/aug_assign_bit_ops.snap @@ -1,12 +1,12 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/aug_assign_bit_ops.fe --- function $aug_assign_bit_ops($x, $y) -> ret { let v0 := $x - v0 := exp(v0, $y) + let v1 := $u256_powassign_pow_assign(v0, $y) + v0 := v1 v0 := shl(3, v0) v0 := shr(1, v0) v0 := and(v0, 255) @@ -15,3 +15,83 @@ function $aug_assign_bit_ops($x, $y) -> ret { ret := v0 leave } +function $u256_powassign_pow_assign($self, $other) -> ret { + let v0 := $u256_pow_pow($self, $other) + ret := v0 + leave +} +function $u256_pow_pow($self, $other) -> ret { + let v0 := $pow_checked__u256__3271ca15373d4483($self, $other) + ret := v0 + leave +} +function $pow_checked__u256__3271ca15373d4483($base, $exp) -> ret { + let v0 := $u256_intword_to_word($exp) + let v1 := and(v0, 115792089237316195423570985008687907853269984665640564039457584007913129639935) + let v2 := $u256_eq_ne(and(v1, 0), 0) + let v3 := and(0, v2) + if v3 { + revert(0, 0) + } + let v4 := $u256_intword_to_word(1) + let v5 := $base + let v6 := v1 + for { let v7 := $u256_ord_gt(v6, 0) } v7 { v7 := $u256_ord_gt(v6, 0) } { + let v8 := $u256_eq_ne(and(v6, 1), 0) + let v9 := v8 + if v9 { + let v10 := $u256_mul_mul(v4, v5) + v4 := v10 + } + v6 := shr(1, v6) + let v11 := $u256_ord_gt(v6, 0) + let v12 := v11 + if v12 { + let v13 := $u256_mul_mul(v5, v5) + v5 := v13 + } + } + ret := v4 + leave +} +function $u256_intword_to_word($self) -> ret { + ret := $self + leave +} +function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave +} +function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave +} +function $u256_mul_mul($self, $other) -> ret { + let v0 := mul($self, $other) + let v1 := $u256_eq_ne($self, 0) + let v2 := $u256_div_div(v0, $self) + let v3 := $u256_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 + leave +} +function $u256_div_div($self, $other) -> ret { + let v0 := $u256_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := div($self, $other) + ret := v2 + leave +} +function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/bit_ops.snap b/crates/codegen/tests/fixtures/bit_ops.snap index 1b6d6b83be..6f3965ee91 100644 --- a/crates/codegen/tests/fixtures/bit_ops.snap +++ b/crates/codegen/tests/fixtures/bit_ops.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/bit_ops.fe --- @@ -24,6 +23,82 @@ function $bit_ops($x, $y) -> ret { leave } function $pow_op($x, $y) -> ret { - ret := exp($x, $y) + let v0 := $u256_pow_pow($x, $y) + ret := v0 + leave +} +function $u256_pow_pow($self, $other) -> ret { + let v0 := $pow_checked__u256__3271ca15373d4483($self, $other) + ret := v0 + leave +} +function $pow_checked__u256__3271ca15373d4483($base, $exp) -> ret { + let v0 := $u256_intword_to_word($exp) + let v1 := and(v0, 115792089237316195423570985008687907853269984665640564039457584007913129639935) + let v2 := $u256_eq_ne(and(v1, 0), 0) + let v3 := and(0, v2) + if v3 { + revert(0, 0) + } + let v4 := $u256_intword_to_word(1) + let v5 := $base + let v6 := v1 + for { let v7 := $u256_ord_gt(v6, 0) } v7 { v7 := $u256_ord_gt(v6, 0) } { + let v8 := $u256_eq_ne(and(v6, 1), 0) + let v9 := v8 + if v9 { + let v10 := $u256_mul_mul(v4, v5) + v4 := v10 + } + v6 := shr(1, v6) + let v11 := $u256_ord_gt(v6, 0) + let v12 := v11 + if v12 { + let v13 := $u256_mul_mul(v5, v5) + v5 := v13 + } + } + ret := v4 + leave +} +function $u256_intword_to_word($self) -> ret { + ret := $self + leave +} +function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave +} +function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave +} +function $u256_mul_mul($self, $other) -> ret { + let v0 := mul($self, $other) + let v1 := $u256_eq_ne($self, 0) + let v2 := $u256_div_div(v0, $self) + let v3 := $u256_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 + leave +} +function $u256_div_div($self, $other) -> ret { + let v0 := $u256_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := div($self, $other) + ret := v2 + leave +} +function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/code_region.snap b/crates/codegen/tests/fixtures/code_region.snap index b3393c5ed2..d03414d3cb 100644 --- a/crates/codegen/tests/fixtures/code_region.snap +++ b/crates/codegen/tests/fixtures/code_region.snap @@ -7,11 +7,13 @@ object "Foo" { code { function $allocate($bytes, $mem) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 96 } - mstore(64, add(v0, $bytes)) + let v3 := $u256_add_add(v0, $bytes) + mstore(64, v3) ret := v0 leave } @@ -35,6 +37,21 @@ object "Foo" { codecopy(v8, v6, v5) return(v8, v5) } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $init__StorPtr_Evm___207f35a85ac4062e() } diff --git a/crates/codegen/tests/fixtures/comparison_ops.snap b/crates/codegen/tests/fixtures/comparison_ops.snap index 16aa1beb6d..dd83399afc 100644 --- a/crates/codegen/tests/fixtures/comparison_ops.snap +++ b/crates/codegen/tests/fixtures/comparison_ops.snap @@ -7,17 +7,53 @@ function $comparison_ops() -> ret { let v0 := 1 let v1 := 2 let v2 := 3 - let v3 := mload(0x40) - if iszero(v3) { - v3 := 0x80 + let v3 := $u256_eq_eq(v0, v0) + let v4 := $u256_eq_ne(v0, v1) + let v5 := $u256_ord_lt(v0, v1) + let v6 := $u256_ord_le(v1, v1) + let v7 := $u256_ord_gt(v2, v1) + let v8 := $u256_ord_ge(v2, v2) + let v9 := mload(0x40) + if iszero(v9) { + v9 := 0x80 } - mstore(0x40, add(v3, 6)) - mstore(v3, iszero(iszero(eq(v0, v0)))) - mstore(add(v3, 1), iszero(iszero(iszero(eq(v0, v1))))) - mstore(add(v3, 2), iszero(iszero(lt(v0, v1)))) - mstore(add(v3, 3), iszero(iszero(iszero(gt(v1, v1))))) - mstore(add(v3, 4), iszero(iszero(gt(v2, v1)))) - mstore(add(v3, 5), iszero(iszero(iszero(lt(v2, v2))))) - ret := v3 + mstore(0x40, add(v9, 6)) + mstore(v9, iszero(iszero(v3))) + mstore(add(v9, 1), iszero(iszero(v4))) + mstore(add(v9, 2), iszero(iszero(v5))) + mstore(add(v9, 3), iszero(iszero(v6))) + mstore(add(v9, 4), iszero(iszero(v7))) + mstore(add(v9, 5), iszero(iszero(v8))) + ret := v9 + leave +} +function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave +} +function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave +} +function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave +} +function $u256_ord_le($self, $other) -> ret { + let v0 := iszero(gt($self, $other)) + ret := v0 + leave +} +function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave +} +function $u256_ord_ge($self, $other) -> ret { + let v0 := iszero(lt($self, $other)) + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/create_contract.snap b/crates/codegen/tests/fixtures/create_contract.snap index 35736079d0..e360ad55b9 100644 --- a/crates/codegen/tests/fixtures/create_contract.snap +++ b/crates/codegen/tests/fixtures/create_contract.snap @@ -93,80 +93,92 @@ object "Factory" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $create2__Evm_Child__64774d714049d5e($self, $value, $args, $salt) -> ret { let v0 := $__Child_init_code_len() let v1 := $__Child_init_code_offset() let v2 := 64 - let v3 := add(v0, v2) - let v4 := $alloc(v3) - codecopy(v4, v1, v0) - let v5 := add(v4, v0) - let v6 := mload(0x40) - if iszero(v6) { - v6 := 0x80 - } - mstore(0x40, add(v6, 96)) - mstore(v6, v5) - mstore(add(v6, 32), v5) - mstore(add(v6, 64), add(v5, v2)) + let v3 := $u256_add_add(v0, v2) + let v4 := v3 + let v5 := $alloc(v4) + codecopy(v5, v1, v0) + let v6 := $u256_add_add(v5, v0) let v7 := v6 - $_t0__t1__encode_encode__Sol_u256_u256_SolEncoder__549b20784d368532($args, v7) - let v8 := $evm_create_create2_raw($self, $value, v4, v3, $salt) - let v9 := eq(v8, 0) - if v9 { - let v10 := returndatasize() - let v11 := $alloc(v10) - returndatacopy(v11, 0, v10) - revert(v11, v10) - } - ret := v8 + let v8 := $u256_add_add(v7, v2) + let v9 := mload(0x40) + if iszero(v9) { + v9 := 0x80 + } + mstore(0x40, add(v9, 96)) + mstore(v9, v7) + mstore(add(v9, 32), v7) + mstore(add(v9, 64), v8) + let v10 := v9 + $_t0__t1__encode_encode__Sol_u256_u256_SolEncoder__549b20784d368532($args, v10) + let v11 := $evm_create_create2_raw($self, $value, v5, v4, $salt) + let v12 := $u256_eq_eq(v11, 0) + let v13 := v12 + if v13 { + let v14 := returndatasize() + let v15 := $alloc(v14) + returndatacopy(v15, 0, v14) + revert(v15, v14) + } + ret := v11 leave } function $create__Evm_Child__64774d714049d5e($self, $value, $args) -> ret { let v0 := $__Child_init_code_len() let v1 := $__Child_init_code_offset() let v2 := 64 - let v3 := add(v0, v2) - let v4 := $alloc(v3) - codecopy(v4, v1, v0) - let v5 := add(v4, v0) - let v6 := mload(0x40) - if iszero(v6) { - v6 := 0x80 - } - mstore(0x40, add(v6, 96)) - mstore(v6, v5) - mstore(add(v6, 32), v5) - mstore(add(v6, 64), add(v5, v2)) + let v3 := $u256_add_add(v0, v2) + let v4 := v3 + let v5 := $alloc(v4) + codecopy(v5, v1, v0) + let v6 := $u256_add_add(v5, v0) let v7 := v6 - $_t0__t1__encode_encode__Sol_u256_u256_SolEncoder__549b20784d368532($args, v7) - let v8 := $evm_create_create_raw($self, $value, v4, v3) - let v9 := eq(v8, 0) - if v9 { - let v10 := returndatasize() - let v11 := $alloc(v10) - returndatacopy(v11, 0, v10) - revert(v11, v10) - } - ret := v8 + let v8 := $u256_add_add(v7, v2) + let v9 := mload(0x40) + if iszero(v9) { + v9 := 0x80 + } + mstore(0x40, add(v9, 96)) + mstore(v9, v7) + mstore(add(v9, 32), v7) + mstore(add(v9, 64), v8) + let v10 := v9 + $_t0__t1__encode_encode__Sol_u256_u256_SolEncoder__549b20784d368532($args, v10) + let v11 := $evm_create_create_raw($self, $value, v5, v4) + let v12 := $u256_eq_eq(v11, 0) + let v13 := v12 + if v13 { + let v14 := returndatasize() + let v15 := $alloc(v14) + returndatacopy(v15, 0, v14) + revert(v15, v14) + } + ret := v11 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -261,13 +273,14 @@ object "Factory" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -295,17 +308,20 @@ object "Factory" { let v0 := mload($self) let v1 := $calldata_byteinput_len(v0) let v2 := mload(add($self, 32)) - let v3 := add(v2, 32) - let v4 := mload(add($self, 32)) - let v5 := or(lt(v3, v4), gt(v3, v1)) - if v5 { + let v3 := $u256_add_add(v2, 32) + let v4 := v3 + let v5 := mload(add($self, 32)) + let v6 := $u256_ord_lt(v4, v5) + let v7 := $u256_ord_gt(v4, v1) + let v8 := or(v6, v7) + if v8 { revert(0, 0) } - let v6 := mload($self) - let v7 := mload(add($self, 32)) - let v8 := $calldata_byteinput_word_at(v6, v7) - mstore(add($self, 32), v3) - ret := v8 + let v9 := mload($self) + let v10 := mload(add($self, 32)) + let v11 := $calldata_byteinput_word_at(v9, v10) + mstore(add($self, 32), v4) + ret := v11 leave } function $soldecoder_i__with_base__CallData__b9ab8dc8a4b2f9e($input, $base) -> ret { @@ -327,14 +343,15 @@ object "Factory" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -346,17 +363,20 @@ object "Factory" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -373,6 +393,16 @@ object "Factory" { ret := v1 leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_CallData___828c6f2e3bda3cab($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e($d) ret := v0 @@ -382,10 +412,35 @@ object "Factory" { $solencoder_abiencoder_write_word($e, $self) leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave @@ -429,11 +484,13 @@ object "Factory" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -471,8 +528,9 @@ object "Factory" { } function $memorybytes_byteinput_word_at($self, $byte_offset) -> ret { let v0 := mload($self) - let v1 := mload(add(v0, $byte_offset)) - ret := v1 + let v1 := $u256_add_add(v0, $byte_offset) + let v2 := mload(v1) + ret := v2 leave } function $sol_abi_decoder_new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -483,16 +541,19 @@ object "Factory" { function $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($self) -> ret { let v0 := $memorybytes_byteinput_len($self) let v1 := mload(add($self, 64)) - let v2 := add(v1, 32) - let v3 := mload(add($self, 64)) - let v4 := or(lt(v2, v3), gt(v2, v0)) - if v4 { + let v2 := $u256_add_add(v1, 32) + let v3 := v2 + let v4 := mload(add($self, 64)) + let v5 := $u256_ord_lt(v3, v4) + let v6 := $u256_ord_gt(v3, v0) + let v7 := or(v5, v6) + if v7 { revert(0, 0) } - let v5 := mload(add($self, 64)) - let v6 := $memorybytes_byteinput_word_at($self, v5) - mstore(add($self, 64), v2) - ret := v6 + let v8 := mload(add($self, 64)) + let v9 := $memorybytes_byteinput_word_at($self, v8) + mstore(add($self, 64), v3) + ret := v9 leave } function $soldecoder_i__new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -520,11 +581,36 @@ object "Factory" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_MemoryBytes___c66d50dfccf37f39($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($d) ret := v0 leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } $__Child_init() } @@ -541,12 +627,14 @@ object "Factory" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -595,13 +683,14 @@ object "Factory" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -645,10 +734,35 @@ object "Factory" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/effect_ptr_domains.snap b/crates/codegen/tests/fixtures/effect_ptr_domains.snap index 8bdbe89d1f..6687fc781c 100644 --- a/crates/codegen/tests/fixtures/effect_ptr_domains.snap +++ b/crates/codegen/tests/fixtures/effect_ptr_domains.snap @@ -5,9 +5,11 @@ input_file: tests/fixtures/effect_ptr_domains.fe --- function $bump__StorPtr_Foo___3698cc3c4b2626e3($foo) { let v0 := sload($foo) - sstore($foo, add(v0, 1)) - let v1 := sload(add($foo, 1)) - sstore(add($foo, 1), add(v1, 2)) + let v1 := $u256_add_add(v0, 1) + sstore($foo, v1) + let v2 := sload(add($foo, 1)) + let v3 := $u256_add_add(v2, 2) + sstore(add($foo, 1), v3) leave } function $test_effect_ptr_domains($st, $mem) { @@ -29,6 +31,16 @@ function $test_effect_ptr_domains($st, $mem) { $bump__MemPtr_Foo___cdd30998d577b6ea(v5) leave } +function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} function $mem_ptr__mem__RawMem_Foo__19292a5189c12f79($self, $addr) -> ret { let v0 := $memptr_t__effectref_from_raw__Foo__668f96d7165c93b($addr) ret := v0 @@ -36,9 +48,11 @@ function $mem_ptr__mem__RawMem_Foo__19292a5189c12f79($self, $addr) -> ret { } function $bump__MemPtr_Foo___cdd30998d577b6ea($foo) { let v0 := mload($foo) - mstore($foo, add(v0, 1)) - let v1 := mload(add($foo, 32)) - mstore(add($foo, 32), add(v1, 2)) + let v1 := $u256_add_add(v0, 1) + mstore($foo, v1) + let v2 := mload(add($foo, 32)) + let v3 := $u256_add_add(v2, 2) + mstore(add($foo, 32), v3) leave } function $stor_ptr__st__RawStorage_Foo__d44156c896232646($self, $slot) -> ret { diff --git a/crates/codegen/tests/fixtures/enum_variant_contract.snap b/crates/codegen/tests/fixtures/enum_variant_contract.snap index 9d94716586..36663c9d75 100644 --- a/crates/codegen/tests/fixtures/enum_variant_contract.snap +++ b/crates/codegen/tests/fixtures/enum_variant_contract.snap @@ -24,86 +24,94 @@ object "EnumContract" { function $runtime__StorPtr_Evm___207f35a85ac4062e() { let v0 := calldataload(0) let v1 := shr(224, v0) - let v2 := eq(v1, 1817627404) - if v2 { - let v3 := calldataload(4) - let v4 := mload(0x40) - if iszero(v4) { - v4 := 0x80 + let v2 := $u256_eq_eq(v1, 1817627404) + let v3 := v2 + if v3 { + let v4 := calldataload(4) + let v5 := mload(0x40) + if iszero(v5) { + v5 := 0x80 } - mstore(0x40, add(v4, 64)) - mstore(v4, 1) - mstore(add(v4, 32), v3) - let v5 := 0 - let v6 := mload(v4) - switch v6 + mstore(0x40, add(v5, 64)) + mstore(v5, 1) + mstore(add(v5, 32), v4) + let v6 := 0 + let v7 := mload(v5) + switch v7 case 1 { - let v7 := mload(add(v4, 32)) - let v8 := v7 - v5 := v8 + let v8 := mload(add(v5, 32)) + let v9 := v8 + v6 := v9 } case 0 { - v5 := 0 + v6 := 0 } default { } - let v9 := v5 - $abi_encode_u256(v9, 0) + let v10 := v6 + $abi_encode_u256(v10, 0) } - if iszero(v2) { - let v10 := eq(v1, 1163776883) - if v10 { - let v11 := mload(0x40) - if iszero(v11) { - v11 := 0x80 + if iszero(v3) { + let v11 := $u256_eq_eq(v1, 1163776883) + let v12 := v11 + if v12 { + let v13 := mload(0x40) + if iszero(v13) { + v13 := 0x80 } - mstore(0x40, add(v11, 64)) - mstore(v11, 0) - let v12 := 0 - let v13 := mload(v11) - switch v13 + mstore(0x40, add(v13, 64)) + mstore(v13, 0) + let v14 := 0 + let v15 := mload(v13) + switch v15 case 1 { - let v14 := mload(add(v11, 32)) - let v15 := v14 - v12 := v15 + let v16 := mload(add(v13, 32)) + let v17 := v16 + v14 := v17 } case 0 { - v12 := 0 + v14 := 0 } default { } - let v16 := v12 - $abi_encode_u256(v16, 0) + let v18 := v14 + $abi_encode_u256(v18, 0) } - if iszero(v10) { - let v17 := eq(v1, 3572425762) - if iszero(v17) { + if iszero(v12) { + let v19 := $u256_eq_eq(v1, 3572425762) + let v20 := v19 + if iszero(v20) { return(0, 0) } - let v18 := calldataload(4) - let v19 := mload(0x40) - if iszero(v19) { - v19 := 0x80 + let v21 := calldataload(4) + let v22 := mload(0x40) + if iszero(v22) { + v22 := 0x80 } - mstore(0x40, add(v19, 64)) - mstore(v19, 1) - mstore(add(v19, 32), v18) - let v20 := 0 - let v21 := mload(v19) - switch v21 + mstore(0x40, add(v22, 64)) + mstore(v22, 1) + mstore(add(v22, 32), v21) + let v23 := 0 + let v24 := mload(v22) + switch v24 case 1 { - v20 := 1 + v23 := 1 } case 0 { - v20 := 0 + v23 := 0 } default { } - let v22 := v20 - $abi_encode_u256(v22, 0) + let v25 := v23 + $abi_encode_u256(v25, 0) } } } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $runtime__StorPtr_Evm___207f35a85ac4062e() return(0, 0) } diff --git a/crates/codegen/tests/fixtures/erc20.snap b/crates/codegen/tests/fixtures/erc20.snap index f74348c71a..cad4177830 100644 --- a/crates/codegen/tests/fixtures/erc20.snap +++ b/crates/codegen/tests/fixtures/erc20.snap @@ -36,8 +36,9 @@ object "CoolCoin" { function $__CoolCoin_init_contract($initial_supply, $owner, $store, $auth, $ctx, $log) { $accesscontrol_grant($auth, 1, $owner) $accesscontrol_grant($auth, 2, $owner) - let v0 := gt($initial_supply, 0) - if v0 { + let v0 := $u256_ord_gt($initial_supply, 0) + let v1 := v0 + if v1 { $mint__StorPtr_TokenStore___6cde5b1e4cc29186($owner, $initial_supply, $store, $log) } leave @@ -48,8 +49,10 @@ object "CoolCoin" { let v2 := mload(add($self, 32)) let v3 := v2 let v4 := $u256_storagekey_write_key($ptr, v1) - let v5 := $address_storagekey_write_key(add($ptr, v4), v3) - ret := add(v4, v5) + let v5 := $u256_add_add($ptr, v4) + let v6 := $address_storagekey_write_key(v5, v3) + let v7 := $u256_add_add(v4, v6) + ret := v7 leave } function $accesscontrol_grant($self, $role, $to) { @@ -68,6 +71,11 @@ object "CoolCoin" { ret := v0 leave } + function $address_eq_eq($self, $other) -> ret { + let v0 := $u256_eq_eq($self, $other) + ret := v0 + leave + } function $address_storagekey_write_key($ptr, $self) -> ret { let v0 := $u256_storagekey_write_key($ptr, $self) ret := v0 @@ -79,11 +87,13 @@ object "CoolCoin" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -156,29 +166,38 @@ object "CoolCoin" { } function $memorybytes_byteinput_word_at($self, $byte_offset) -> ret { let v0 := mload($self) - let v1 := mload(add(v0, $byte_offset)) - ret := v1 + let v1 := $u256_add_add(v0, $byte_offset) + let v2 := mload(v1) + ret := v2 leave } function $mint__StorPtr_TokenStore___6cde5b1e4cc29186($to, $amount, $store, $log) { let v0 := $address_zero() - $assert(iszero(eq($to, v0))) - let v1 := sload($store) - sstore($store, add(v1, $amount)) + let v1 := $ne__Address_Address__397b9a625a435b6f($to, v0) + $assert(v1) + let v2 := sload($store) + let v3 := $u256_addassign_add_assign(v2, $amount) + sstore($store, v3) pop($store) pop($store) - let v2 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, add(v2, $amount)) - let v3 := $address_zero() - let v4 := mload(0x40) - if iszero(v4) { - v4 := 0x80 - } - mstore(0x40, add(v4, 96)) - mstore(v4, v3) - mstore(add(v4, 32), $to) - mstore(add(v4, 64), $amount) - $emit__log__Log_TransferEvent__41b584f23318b0be($log, v4) + let v4 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) + let v5 := $u256_add_add(v4, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, v5) + let v6 := $address_zero() + let v7 := mload(0x40) + if iszero(v7) { + v7 := 0x80 + } + mstore(0x40, add(v7, 96)) + mstore(v7, v6) + mstore(add(v7, 32), $to) + mstore(add(v7, 64), $amount) + $emit__log__Log_TransferEvent__41b584f23318b0be($log, v7) + leave + } + function $ne__Address_Address__397b9a625a435b6f($self, $other) -> ret { + let v0 := $address_eq_eq($self, $other) + ret := iszero(v0) leave } function $sol_abi_decoder_new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -189,16 +208,19 @@ object "CoolCoin" { function $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($self) -> ret { let v0 := $memorybytes_byteinput_len($self) let v1 := mload(add($self, 64)) - let v2 := add(v1, 32) - let v3 := mload(add($self, 64)) - let v4 := or(lt(v2, v3), gt(v2, v0)) - if v4 { + let v2 := $u256_add_add(v1, 32) + let v3 := v2 + let v4 := mload(add($self, 64)) + let v5 := $u256_ord_lt(v3, v4) + let v6 := $u256_ord_gt(v3, v0) + let v7 := or(v5, v6) + if v7 { revert(0, 0) } - let v5 := mload(add($self, 64)) - let v6 := $memorybytes_byteinput_word_at($self, v5) - mstore(add($self, 64), v2) - ret := v6 + let v8 := mload(add($self, 64)) + let v9 := $memorybytes_byteinput_word_at($self, v8) + mstore(add($self, 64), v3) + ret := v9 leave } function $soldecoder_i__new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -262,28 +284,62 @@ object "CoolCoin" { function $storagemap_storage_slot_with_salt__Address__ce8f588742192062($key, $salt) -> ret { let v0 := mload(64) let v1 := $address_storagekey_write_key(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storagemap_storage_slot_with_salt___u256__Address___c7c570db20cdd393($key, $salt) -> ret { let v0 := mload(64) let v1 := $_a__b__storagekey_write_key__u256_Address__1ae3ad2a1b729a8b(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storptr_t__effectref_from_raw__TokenStore__cf27701b626eb0c2($raw) -> ret { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_MemoryBytes___c66d50dfccf37f39($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($d) ret := v0 leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } function $u256_storagekey_write_key($ptr, $self) -> ret { mstore($ptr, $self) ret := 32 @@ -419,7 +475,8 @@ object "CoolCoin" { mstore(v5, v4) mstore(add(v5, 32), v1) let v6 := $storagemap_k__v__const_salt__u256__get___Address__Address__u256_1__12414c27d5bfff1f(0, v5) - $approve__StorPtr_TokenStore___6cde5b1e4cc29186(v4, v1, add(v6, v3), $store, $log) + let v7 := $u256_add_add(v6, v3) + $approve__StorPtr_TokenStore___6cde5b1e4cc29186(v4, v1, v7, $store, $log) ret := 1 leave } @@ -438,8 +495,10 @@ object "CoolCoin" { mstore(v5, v4) mstore(add(v5, 32), v1) let v6 := $storagemap_k__v__const_salt__u256__get___Address__Address__u256_1__12414c27d5bfff1f(0, v5) - $assert(iszero(lt(v6, v3))) - $approve__StorPtr_TokenStore___6cde5b1e4cc29186(v4, v1, sub(v6, v3), $store, $log) + let v7 := $u256_ord_ge(v6, v3) + $assert(v7) + let v8 := $u256_sub_sub(v6, v3) + $approve__StorPtr_TokenStore___6cde5b1e4cc29186(v4, v1, v8, $store, $log) ret := 1 leave } @@ -529,8 +588,10 @@ object "CoolCoin" { let v2 := mload(add($self, 32)) let v3 := v2 let v4 := $address_storagekey_write_key($ptr, v1) - let v5 := $address_storagekey_write_key(add($ptr, v4), v3) - ret := add(v4, v5) + let v5 := $u256_add_add($ptr, v4) + let v6 := $address_storagekey_write_key(v5, v3) + let v7 := $u256_add_add(v4, v6) + ret := v7 leave } function $_a__b__storagekey_write_key__u256_Address__1ae3ad2a1b729a8b($ptr, $self) -> ret { @@ -539,8 +600,10 @@ object "CoolCoin" { let v2 := mload(add($self, 32)) let v3 := v2 let v4 := $u256_storagekey_write_key($ptr, v1) - let v5 := $address_storagekey_write_key(add($ptr, v4), v3) - ret := add(v4, v5) + let v5 := $u256_add_add($ptr, v4) + let v6 := $address_storagekey_write_key(v5, v3) + let v7 := $u256_add_add(v4, v6) + ret := v7 leave } function $accesscontrol_require($self, $role, $ctx) { @@ -553,7 +616,8 @@ object "CoolCoin" { mstore(v1, $role) mstore(add(v1, 32), v0) let v2 := $storagemap_k__v__const_salt__u256__get___u256__Address__bool_2__74418d210da55cc0(0, v1) - $assert(eq(v2, 1)) + let v3 := $bool_eq_eq(v2, 1) + $assert(v3) leave } function $address_decode_decode__Sol_SolDecoder_CallData___828c6f2e3bda3cab($d) -> ret { @@ -561,6 +625,11 @@ object "CoolCoin" { ret := v0 leave } + function $address_eq_eq($self, $other) -> ret { + let v0 := $u256_eq_eq($self, $other) + ret := v0 + leave + } function $address_storagekey_write_key($ptr, $self) -> ret { let v0 := $u256_storagekey_write_key($ptr, $self) ret := v0 @@ -572,11 +641,13 @@ object "CoolCoin" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -595,27 +666,29 @@ object "CoolCoin" { } function $approve__StorPtr_TokenStore___6cde5b1e4cc29186($owner, $spender, $amount, $store, $log) { let v0 := $address_zero() - $assert(iszero(eq($owner, v0))) - let v1 := $address_zero() - $assert(iszero(eq($spender, v1))) + let v1 := $ne__Address_Address__397b9a625a435b6f($owner, v0) + $assert(v1) + let v2 := $address_zero() + let v3 := $ne__Address_Address__397b9a625a435b6f($spender, v2) + $assert(v3) pop($store) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v4 := mload(0x40) + if iszero(v4) { + v4 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, $owner) - mstore(add(v2, 32), $spender) - $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v2, $amount) - let v3 := mload(0x40) - if iszero(v3) { - v3 := 0x80 + mstore(0x40, add(v4, 64)) + mstore(v4, $owner) + mstore(add(v4, 32), $spender) + $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v4, $amount) + let v5 := mload(0x40) + if iszero(v5) { + v5 := 0x80 } - mstore(0x40, add(v3, 96)) - mstore(v3, $owner) - mstore(add(v3, 32), $spender) - mstore(add(v3, 64), $amount) - $emit__log__Log_ApprovalEvent__bbf50d88fed22fda($log, v3) + mstore(0x40, add(v5, 96)) + mstore(v5, $owner) + mstore(add(v5, 32), $spender) + mstore(add(v5, 64), $amount) + $emit__log__Log_ApprovalEvent__bbf50d88fed22fda($log, v5) leave } function $approve_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) -> ret { @@ -653,30 +726,40 @@ object "CoolCoin" { } leave } + function $bool_eq_eq($self, $other) -> ret { + let v0 := eq(iszero(iszero($self)), iszero(iszero($other))) + ret := v0 + leave + } function $bool_wordrepr_from_word($word) -> ret { - ret := iszero(eq($word, 0)) + let v0 := $u256_eq_ne($word, 0) + ret := v0 leave } function $burn__StorPtr_TokenStore___6cde5b1e4cc29186($from, $amount, $store, $log) { let v0 := $address_zero() - $assert(iszero(eq($from, v0))) + let v1 := $ne__Address_Address__397b9a625a435b6f($from, v0) + $assert(v1) pop($store) - let v1 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $from) - $assert(iszero(lt(v1, $amount))) + let v2 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $from) + let v3 := $u256_ord_ge(v2, $amount) + $assert(v3) pop($store) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, sub(v1, $amount)) - let v2 := sload($store) - sstore($store, sub(v2, $amount)) - let v3 := $address_zero() - let v4 := mload(0x40) - if iszero(v4) { - v4 := 0x80 + let v4 := $u256_sub_sub(v2, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, v4) + let v5 := sload($store) + let v6 := $u256_subassign_sub_assign(v5, $amount) + sstore($store, v6) + let v7 := $address_zero() + let v8 := mload(0x40) + if iszero(v8) { + v8 := 0x80 } - mstore(0x40, add(v4, 96)) - mstore(v4, $from) - mstore(add(v4, 32), v3) - mstore(add(v4, 64), $amount) - $emit__log__Log_TransferEvent__41b584f23318b0be($log, v4) + mstore(0x40, add(v8, 96)) + mstore(v8, $from) + mstore(add(v8, 32), v7) + mstore(add(v8, 64), $amount) + $emit__log__Log_TransferEvent__41b584f23318b0be($log, v8) leave } function $burn_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) -> ret { @@ -699,12 +782,14 @@ object "CoolCoin" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -789,23 +874,26 @@ object "CoolCoin" { } function $mint__StorPtr_TokenStore___6cde5b1e4cc29186($to, $amount, $store, $log) { let v0 := $address_zero() - $assert(iszero(eq($to, v0))) - let v1 := sload($store) - sstore($store, add(v1, $amount)) + let v1 := $ne__Address_Address__397b9a625a435b6f($to, v0) + $assert(v1) + let v2 := sload($store) + let v3 := $u256_addassign_add_assign(v2, $amount) + sstore($store, v3) pop($store) pop($store) - let v2 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, add(v2, $amount)) - let v3 := $address_zero() - let v4 := mload(0x40) - if iszero(v4) { - v4 := 0x80 + let v4 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) + let v5 := $u256_add_add(v4, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, v5) + let v6 := $address_zero() + let v7 := mload(0x40) + if iszero(v7) { + v7 := 0x80 } - mstore(0x40, add(v4, 96)) - mstore(v4, v3) - mstore(add(v4, 32), $to) - mstore(add(v4, 64), $amount) - $emit__log__Log_TransferEvent__41b584f23318b0be($log, v4) + mstore(0x40, add(v7, 96)) + mstore(v7, v6) + mstore(add(v7, 32), $to) + mstore(add(v7, 64), $amount) + $emit__log__Log_TransferEvent__41b584f23318b0be($log, v7) leave } function $mint_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) -> ret { @@ -824,6 +912,11 @@ object "CoolCoin" { function $name_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) { leave } + function $ne__Address_Address__397b9a625a435b6f($self, $other) -> ret { + let v0 := $address_eq_eq($self, $other) + ret := iszero(v0) + leave + } function $return_value__Evm_Sol_String_32___fce5fd9d073f47ee($self, $value) { let v0 := $sol_abi_encoder_new() let v1 := $solencoder_abiencoder_reserve_head(v0, 32) @@ -883,13 +976,14 @@ object "CoolCoin" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -917,17 +1011,20 @@ object "CoolCoin" { let v0 := mload($self) let v1 := $calldata_byteinput_len(v0) let v2 := mload(add($self, 32)) - let v3 := add(v2, 32) - let v4 := mload(add($self, 32)) - let v5 := or(lt(v3, v4), gt(v3, v1)) - if v5 { + let v3 := $u256_add_add(v2, 32) + let v4 := v3 + let v5 := mload(add($self, 32)) + let v6 := $u256_ord_lt(v4, v5) + let v7 := $u256_ord_gt(v4, v1) + let v8 := or(v6, v7) + if v8 { revert(0, 0) } - let v6 := mload($self) - let v7 := mload(add($self, 32)) - let v8 := $calldata_byteinput_word_at(v6, v7) - mstore(add($self, 32), v3) - ret := v8 + let v9 := mload($self) + let v10 := mload(add($self, 32)) + let v11 := $calldata_byteinput_word_at(v9, v10) + mstore(add($self, 32), v4) + ret := v11 leave } function $soldecoder_i__with_base__CallData__b9ab8dc8a4b2f9e($input, $base) -> ret { @@ -949,14 +1046,15 @@ object "CoolCoin" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -968,17 +1066,20 @@ object "CoolCoin" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -1005,16 +1106,18 @@ object "CoolCoin" { mstore(v0, $owner) mstore(add(v0, 32), $spender) let v1 := $storagemap_k__v__const_salt__u256__get___Address__Address__u256_1__12414c27d5bfff1f(0, v0) - $assert(iszero(lt(v1, $amount))) + let v2 := $u256_ord_ge(v1, $amount) + $assert(v2) pop($store) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, $owner) - mstore(add(v2, 32), $spender) - $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v2, sub(v1, $amount)) + mstore(0x40, add(v3, 64)) + mstore(v3, $owner) + mstore(add(v3, 32), $spender) + let v4 := $u256_sub_sub(v1, $amount) + $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v3, v4) leave } function $stor_ptr__Evm_AccessControl__9eae422fa78e0dfe($self, $slot) -> ret { @@ -1086,25 +1189,31 @@ object "CoolCoin" { function $storagemap_storage_slot_with_salt__Address__ce8f588742192062($key, $salt) -> ret { let v0 := mload(64) let v1 := $address_storagekey_write_key(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storagemap_storage_slot_with_salt___Address__Address___c32d499da259c78e($key, $salt) -> ret { let v0 := mload(64) let v1 := $_a__b__storagekey_write_key__Address_Address__397b9a625a435b6f(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storagemap_storage_slot_with_salt___u256__Address___c7c570db20cdd393($key, $salt) -> ret { let v0 := mload(64) let v1 := $_a__b__storagekey_write_key__u256_Address__1ae3ad2a1b729a8b(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storptr_t__effectref_from_raw__TokenStore__cf27701b626eb0c2($raw) -> ret { @@ -1124,27 +1233,32 @@ object "CoolCoin" { } function $transfer__StorPtr_TokenStore___6cde5b1e4cc29186($from, $to, $amount, $store, $log) { let v0 := $address_zero() - $assert(iszero(eq($from, v0))) - let v1 := $address_zero() - $assert(iszero(eq($to, v1))) + let v1 := $ne__Address_Address__397b9a625a435b6f($from, v0) + $assert(v1) + let v2 := $address_zero() + let v3 := $ne__Address_Address__397b9a625a435b6f($to, v2) + $assert(v3) pop($store) - let v2 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $from) - $assert(iszero(lt(v2, $amount))) + let v4 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $from) + let v5 := $u256_ord_ge(v4, $amount) + $assert(v5) pop($store) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, sub(v2, $amount)) + let v6 := $u256_sub_sub(v4, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, v6) pop($store) pop($store) - let v3 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, add(v3, $amount)) - let v4 := mload(0x40) - if iszero(v4) { - v4 := 0x80 + let v7 := $storagemap_k__v__const_salt__u256__get__Address_u256_0__b8b2f68065fe2328(0, $to) + let v8 := $u256_add_add(v7, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, v8) + let v9 := mload(0x40) + if iszero(v9) { + v9 := 0x80 } - mstore(0x40, add(v4, 96)) - mstore(v4, $from) - mstore(add(v4, 32), $to) - mstore(add(v4, 64), $amount) - $emit__log__Log_TransferEvent__41b584f23318b0be($log, v4) + mstore(0x40, add(v9, 96)) + mstore(v9, $from) + mstore(add(v9, 32), $to) + mstore(add(v9, 64), $amount) + $emit__log__Log_TransferEvent__41b584f23318b0be($log, v9) leave } function $transfer_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) -> ret { @@ -1175,6 +1289,21 @@ object "CoolCoin" { ret := v3 leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_CallData___828c6f2e3bda3cab($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e($d) ret := v0 @@ -1184,15 +1313,55 @@ object "CoolCoin" { $solencoder_abiencoder_write_word($e, $self) leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_ge($self, $other) -> ret { + let v0 := iszero(lt($self, $other)) + ret := v0 + leave + } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } function $u256_storagekey_write_key($ptr, $self) -> ret { mstore($ptr, $self) ret := 32 leave } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } + function $u256_subassign_sub_assign($self, $other) -> ret { + let v0 := $u256_sub_sub($self, $other) + ret := v0 + leave + } function $u256_wordrepr_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/erc20_low_level.snap b/crates/codegen/tests/fixtures/erc20_low_level.snap index f37a1b18c6..cbba59aa9d 100644 --- a/crates/codegen/tests/fixtures/erc20_low_level.snap +++ b/crates/codegen/tests/fixtures/erc20_low_level.snap @@ -5,6 +5,11 @@ input_file: tests/fixtures/erc20_low_level.fe --- object "Erc20Contract" { code { + function $address_eq_eq($self, $other) -> ret { + let v0 := $u256_eq_eq($self, $other) + ret := v0 + leave + } function $address_zero() -> ret { ret := 0 leave @@ -17,8 +22,9 @@ object "Erc20Contract" { function $erc20_set_owner_once_stor($self, $owner, $ops) { let v0 := sload(add($self, 1)) let v1 := $address_zero() - let v2 := iszero(eq(v0, v1)) - if v2 { + let v2 := $ne__Address_Address__397b9a625a435b6f(v0, v1) + let v3 := v2 + if v3 { revert(0, 0) } sstore(add($self, 1), $owner) @@ -33,6 +39,11 @@ object "Erc20Contract" { codecopy(0, v3, v2) return(0, v2) } + function $ne__Address_Address__397b9a625a435b6f($self, $other) -> ret { + let v0 := $address_eq_eq($self, $other) + ret := iszero(v0) + leave + } function $stor_ptr__Evm_Erc20__c294a0aeae425afd($self, $slot) -> ret { let v0 := $storptr_t__effectref_from_raw__Erc20__88e369469f2b52ef($slot) ret := v0 @@ -42,6 +53,11 @@ object "Erc20Contract" { ret := $raw leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $init__StorPtr_Evm___207f35a85ac4062e() } @@ -53,8 +69,10 @@ object "Erc20Contract" { let v2 := mload(add($self, 32)) let v3 := v2 let v4 := $address_storagekey_write_key($ptr, v1) - let v5 := $address_storagekey_write_key(add($ptr, v4), v3) - ret := add(v4, v5) + let v5 := $u256_add_add($ptr, v4) + let v6 := $address_storagekey_write_key(v5, v3) + let v7 := $u256_add_add(v4, v6) + ret := v7 leave } function $abi_encode_string($word, $len, $ops) { @@ -67,6 +85,11 @@ object "Erc20Contract" { mstore(0, $value) return(0, 32) } + function $address_eq_eq($self, $other) -> ret { + let v0 := $u256_eq_eq($self, $other) + ret := v0 + leave + } function $address_storagekey_write_key($ptr, $self) -> ret { let v0 := $u256_storagekey_write_key($ptr, $self) ret := v0 @@ -131,47 +154,55 @@ object "Erc20Contract" { function $erc20_mint_stor($self, $to, $amount, $ctx, $ops) { let v0 := caller() let v1 := sload(add($self, 1)) - let v2 := iszero(eq(v0, v1)) - if v2 { + let v2 := $ne__Address_Address__397b9a625a435b6f(v0, v1) + let v3 := v2 + if v3 { revert(0, 0) } - let v3 := $erc20_balance_of_stor($self, $to) + let v4 := $erc20_balance_of_stor($self, $to) pop($self) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, add(v3, $amount)) - let v4 := sload($self) - sstore($self, add(v4, $amount)) + let v5 := $u256_add_add(v4, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, v5) + let v6 := sload($self) + let v7 := $u256_add_add(v6, $amount) + sstore($self, v7) leave } function $erc20_transfer_from_stor($self, $owner, $to, $amount, $ctx, $ops) { let v0 := caller() let v1 := $erc20_allowance_stor($self, $owner, v0) - let v2 := lt(v1, $amount) - if v2 { + let v2 := $u256_ord_lt(v1, $amount) + let v3 := v2 + if v3 { revert(0, 0) } $erc20_transfer_stor($self, $owner, $to, $amount, $ops) pop($self) - let v3 := mload(0x40) - if iszero(v3) { - v3 := 0x80 + let v4 := mload(0x40) + if iszero(v4) { + v4 := 0x80 } - mstore(0x40, add(v3, 64)) - mstore(v3, $owner) - mstore(add(v3, 32), v0) - $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v3, sub(v1, $amount)) + mstore(0x40, add(v4, 64)) + mstore(v4, $owner) + mstore(add(v4, 32), v0) + let v5 := $u256_sub_sub(v1, $amount) + $storagemap_k__v__const_salt__u256__set___Address__Address__u256_1__12414c27d5bfff1f(0, v4, v5) leave } function $erc20_transfer_stor($self, $from, $to, $amount, $ops) { let v0 := $erc20_balance_of_stor($self, $from) - let v1 := lt(v0, $amount) - if v1 { + let v1 := $u256_ord_lt(v0, $amount) + let v2 := v1 + if v2 { revert(0, 0) } - let v2 := $erc20_balance_of_stor($self, $to) + let v3 := $erc20_balance_of_stor($self, $to) pop($self) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, sub(v0, $amount)) + let v4 := $u256_sub_sub(v0, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $from, v4) pop($self) - $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, add(v2, $amount)) + let v5 := $u256_add_add(v3, $amount) + $storagemap_k__v__const_salt__u256__set__Address_u256_0__b8b2f68065fe2328(0, $to, v5) leave } function $mint__StorPtr_Erc20___2745299687a147d4($to, $amount, $erc20, $ctx, $ops) -> ret { @@ -179,6 +210,11 @@ object "Erc20Contract" { ret := 1 leave } + function $ne__Address_Address__397b9a625a435b6f($self, $other) -> ret { + let v0 := $address_eq_eq($self, $other) + ret := iszero(v0) + leave + } function $runtime__StorPtr_Evm___207f35a85ac4062e() { let v0 := $stor_ptr__Evm_Erc20__c294a0aeae425afd(0, 0) let v1 := calldataload(0) @@ -294,17 +330,21 @@ object "Erc20Contract" { function $storagemap_storage_slot_with_salt__Address__ce8f588742192062($key, $salt) -> ret { let v0 := mload(64) let v1 := $address_storagekey_write_key(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storagemap_storage_slot_with_salt___Address__Address___c32d499da259c78e($key, $salt) -> ret { let v0 := mload(64) let v1 := $_a__b__storagekey_write_key__Address_Address__397b9a625a435b6f(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $storptr_t__effectref_from_raw__Erc20__88e369469f2b52ef($raw) -> ret { @@ -322,11 +362,41 @@ object "Erc20Contract" { ret := 1 leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } function $u256_storagekey_write_key($ptr, $self) -> ret { mstore($ptr, $self) ret := 32 leave } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u256_wordrepr_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/full_contract.snap b/crates/codegen/tests/fixtures/full_contract.snap index 79450717e1..31ea15e086 100644 --- a/crates/codegen/tests/fixtures/full_contract.snap +++ b/crates/codegen/tests/fixtures/full_contract.snap @@ -24,44 +24,93 @@ object "ShapeDispatcher" { function $dispatch__StorPtr_Evm___207f35a85ac4062e() { let v0 := calldataload(0) let v1 := shr(224, v0) - let v2 := eq(v1, 151146943) - if v2 { - let v3 := calldataload(4) - let v4 := calldataload(36) - let v5 := mload(0x40) - if iszero(v5) { - v5 := 0x80 + let v2 := $u256_eq_eq(v1, 151146943) + let v3 := v2 + if v3 { + let v4 := calldataload(4) + let v5 := calldataload(36) + let v6 := mload(0x40) + if iszero(v6) { + v6 := 0x80 } - mstore(0x40, add(v5, 64)) - mstore(v5, v3) - mstore(add(v5, 32), v4) - let v6 := v5 - let v7 := mload(v6) - mstore(v6, add(v7, 1)) - let v8 := mload(add(v6, 32)) - mstore(add(v6, 32), add(v8, 2)) - let v9 := $point_area_mem(v6) - $abi_encode(v9, 0) + mstore(0x40, add(v6, 64)) + mstore(v6, v4) + mstore(add(v6, 32), v5) + let v7 := v6 + let v8 := mload(v7) + let v9 := $u256_add_add(v8, 1) + mstore(v7, v9) + let v10 := mload(add(v7, 32)) + let v11 := $u256_add_add(v10, 2) + mstore(add(v7, 32), v11) + let v12 := $point_area_mem(v7) + $abi_encode(v12, 0) } - let v10 := eq(v1, 2066295049) - if v10 { - let v11 := calldataload(4) - let v12 := v11 - v12 := add(v12, 3) - let v13 := $square_area(v12) - $abi_encode(v13, 0) + let v13 := $u256_eq_eq(v1, 2066295049) + let v14 := v13 + if v14 { + let v15 := calldataload(4) + let v16 := v15 + let v17 := $u256_add_add(v16, 3) + v16 := v17 + let v18 := $square_area(v16) + $abi_encode(v18, 0) } return(0, 0) } function $point_area_mem($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 32)) - ret := mul(v0, v1) + let v2 := $u256_mul_mul(v0, v1) + ret := v2 leave } function $square_area($self) -> ret { let v0 := $self - ret := mul(v0, v0) + let v1 := $u256_mul_mul(v0, v0) + ret := v1 + leave + } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_div_div($self, $other) -> ret { + let v0 := $u256_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := div($self, $other) + ret := v2 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave + } + function $u256_mul_mul($self, $other) -> ret { + let v0 := mul($self, $other) + let v1 := $u256_eq_ne($self, 0) + let v2 := $u256_div_div(v0, $self) + let v3 := $u256_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 leave } $dispatch__StorPtr_Evm___207f35a85ac4062e() diff --git a/crates/codegen/tests/fixtures/function_call.snap b/crates/codegen/tests/fixtures/function_call.snap index 0ceb3f4f3d..56f5b5bfbb 100644 --- a/crates/codegen/tests/fixtures/function_call.snap +++ b/crates/codegen/tests/fixtures/function_call.snap @@ -1,11 +1,11 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/function_call.fe --- function $add_one($x) -> ret { - ret := add($x, 1) + let v0 := $u64_add_add($x, 1) + ret := v0 leave } function $call_add_one() -> ret { @@ -13,3 +13,13 @@ function $call_add_one() -> ret { ret := v0 leave } +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/high_level_contract.snap b/crates/codegen/tests/fixtures/high_level_contract.snap index 555889973f..56777abcce 100644 --- a/crates/codegen/tests/fixtures/high_level_contract.snap +++ b/crates/codegen/tests/fixtures/high_level_contract.snap @@ -39,11 +39,13 @@ object "EchoContract" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -81,8 +83,9 @@ object "EchoContract" { } function $memorybytes_byteinput_word_at($self, $byte_offset) -> ret { let v0 := mload($self) - let v1 := mload(add(v0, $byte_offset)) - ret := v1 + let v1 := $u256_add_add(v0, $byte_offset) + let v2 := mload(v1) + ret := v2 leave } function $sol_abi_decoder_new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -93,16 +96,19 @@ object "EchoContract" { function $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($self) -> ret { let v0 := $memorybytes_byteinput_len($self) let v1 := mload(add($self, 64)) - let v2 := add(v1, 32) - let v3 := mload(add($self, 64)) - let v4 := or(lt(v2, v3), gt(v2, v0)) - if v4 { + let v2 := $u256_add_add(v1, 32) + let v3 := v2 + let v4 := mload(add($self, 64)) + let v5 := $u256_ord_lt(v3, v4) + let v6 := $u256_ord_gt(v3, v0) + let v7 := or(v5, v6) + if v7 { revert(0, 0) } - let v5 := mload(add($self, 64)) - let v6 := $memorybytes_byteinput_word_at($self, v5) - mstore(add($self, 64), v2) - ret := v6 + let v8 := mload(add($self, 64)) + let v9 := $memorybytes_byteinput_word_at($self, v8) + mstore(add($self, 64), v3) + ret := v9 leave } function $soldecoder_i__new__MemoryBytes__b48f82585f3ed728($input) -> ret { @@ -130,11 +136,36 @@ object "EchoContract" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_MemoryBytes___c66d50dfccf37f39($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728($d) ret := v0 leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } $__EchoContract_init() } @@ -180,11 +211,13 @@ object "EchoContract" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -193,12 +226,14 @@ object "EchoContract" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -268,13 +303,14 @@ object "EchoContract" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -302,17 +338,20 @@ object "EchoContract" { let v0 := mload($self) let v1 := $calldata_byteinput_len(v0) let v2 := mload(add($self, 32)) - let v3 := add(v2, 32) - let v4 := mload(add($self, 32)) - let v5 := or(lt(v3, v4), gt(v3, v1)) - if v5 { + let v3 := $u256_add_add(v2, 32) + let v4 := v3 + let v5 := mload(add($self, 32)) + let v6 := $u256_ord_lt(v4, v5) + let v7 := $u256_ord_gt(v4, v1) + let v8 := or(v6, v7) + if v8 { revert(0, 0) } - let v6 := mload($self) - let v7 := mload(add($self, 32)) - let v8 := $calldata_byteinput_word_at(v6, v7) - mstore(add($self, 32), v3) - ret := v8 + let v9 := mload($self) + let v10 := mload(add($self, 32)) + let v11 := $calldata_byteinput_word_at(v9, v10) + mstore(add($self, 32), v4) + ret := v11 leave } function $soldecoder_i__with_base__CallData__b9ab8dc8a4b2f9e($input, $base) -> ret { @@ -334,14 +373,15 @@ object "EchoContract" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -353,17 +393,20 @@ object "EchoContract" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -389,6 +432,16 @@ object "EchoContract" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } function $u256_decode_decode__Sol_SolDecoder_CallData___828c6f2e3bda3cab($d) -> ret { let v0 := $soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e($d) ret := v0 @@ -398,10 +451,35 @@ object "EchoContract" { $solencoder_abiencoder_write_word($e, $self) leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_gt($self, $other) -> ret { + let v0 := gt($self, $other) + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/if_else.snap b/crates/codegen/tests/fixtures/if_else.snap index bbb146dfff..a879bdae81 100644 --- a/crates/codegen/tests/fixtures/if_else.snap +++ b/crates/codegen/tests/fixtures/if_else.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/if_else.fe --- @@ -23,6 +22,17 @@ function $if_else($cond) -> ret { leave } function $helper($x) -> ret { - ret := add($x, 1) + let v0 := $u64_add_add($x, 1) + ret := v0 + leave +} +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/match_struct.snap b/crates/codegen/tests/fixtures/match_struct.snap index f6bada5d2c..ce8b1c2bc5 100644 --- a/crates/codegen/tests/fixtures/match_struct.snap +++ b/crates/codegen/tests/fixtures/match_struct.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/match_struct.fe --- @@ -33,7 +32,8 @@ function $match_struct_fields($p) -> ret { let v9 := v8 let v10 := and(mload($p), 0xff) let v11 := v10 - v0 := add(v11, v9) + let v12 := $u8_add_add(v11, v9) + v0 := v12 } } ret := v0 @@ -59,3 +59,13 @@ function $match_struct_wildcard($p) -> ret { ret := v0 leave } +function $u8_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xff), and($other, 0xff)), 0xff) + let v1 := lt(and(v0, 0xff), and($self, 0xff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/match_tuple_binding.snap b/crates/codegen/tests/fixtures/match_tuple_binding.snap index dd7a892196..f3cc02a3a3 100644 --- a/crates/codegen/tests/fixtures/match_tuple_binding.snap +++ b/crates/codegen/tests/fixtures/match_tuple_binding.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/match_tuple_binding.fe --- @@ -26,7 +25,8 @@ function $match_tuple_extract($t) -> ret { let v8 := v7 let v9 := and(mload(add($t, 1)), 0xff) let v10 := v9 - v0 := add(v8, v10) + let v11 := $u8_add_add(v8, v10) + v0 := v11 } } ret := v0 @@ -52,35 +52,49 @@ function $match_nested_extract($t) -> ret { let v7 := v6 let v8 := and(mload($t), 0xff) let v9 := v8 - v0 := add(v9, v7) + let v10 := $u8_add_add(v9, v7) + v0 := v10 } default { - let v10 := and(mload($t), 0xff) - let v11 := v10 - let v12 := and(mload(add($t, 2)), 0xff) - let v13 := v12 - v0 := add(v11, v13) + let v11 := and(mload($t), 0xff) + let v12 := v11 + let v13 := and(mload(add($t, 2)), 0xff) + let v14 := v13 + let v15 := $u8_add_add(v12, v14) + v0 := v15 } } } default { - let v14 := and(mload(add($t, 2)), 0xff) - switch v14 + let v16 := and(mload(add($t, 2)), 0xff) + switch v16 case 0 { - let v15 := and(mload(add($t, 1)), 0xff) - let v16 := v15 - let v17 := and(mload($t), 0xff) + let v17 := and(mload(add($t, 1)), 0xff) let v18 := v17 - v0 := add(v18, v16) - } - default { let v19 := and(mload($t), 0xff) let v20 := v19 - let v21 := and(mload(add($t, 2)), 0xff) - let v22 := v21 - v0 := add(v20, v22) + let v21 := $u8_add_add(v20, v18) + v0 := v21 + } + default { + let v22 := and(mload($t), 0xff) + let v23 := v22 + let v24 := and(mload(add($t, 2)), 0xff) + let v25 := v24 + let v26 := $u8_add_add(v23, v25) + v0 := v26 } } ret := v0 leave } +function $u8_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xff), and($other, 0xff)), 0xff) + let v1 := lt(and(v0, 0xff), and($self, 0xff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/momo.snap b/crates/codegen/tests/fixtures/momo.snap index ea1f9283ec..9ad51aa9d6 100644 --- a/crates/codegen/tests/fixtures/momo.snap +++ b/crates/codegen/tests/fixtures/momo.snap @@ -1,12 +1,22 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/momo.fe --- function $read_x($val) -> ret { let v0 := and(mload($val), 0xff) let v1 := and(mload(add($val, 1)), 0xff) - ret := add(v0, v1) + let v2 := $u8_add_add(v0, v1) + ret := v2 + leave +} +function $u8_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xff), and($other, 0xff)), 0xff) + let v1 := lt(and(v0, 0xff), and($self, 0xff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/name_collisions.snap b/crates/codegen/tests/fixtures/name_collisions.snap index 44a219dbab..3456725075 100644 --- a/crates/codegen/tests/fixtures/name_collisions.snap +++ b/crates/codegen/tests/fixtures/name_collisions.snap @@ -14,7 +14,8 @@ function $flattened_fn() -> ret { function $test_module_qualifier_flatten_collision() -> ret { let v0 := $flattened_fn() let v1 := $flattened_fn() - ret := add(v0, v1) + let v2 := $u32_add_add(v0, v1) + ret := v2 leave } function $test_generic_type_collision() -> ret { @@ -28,11 +29,13 @@ function $test_generic_type_collision() -> ret { leave } function $widget_process($self) -> ret { - ret := mul($self, 2) + let v0 := $u32_mul_mul($self, 2) + ret := v0 leave } function $widget_process($self) -> ret { - ret := mul($self, 3) + let v0 := $u64_mul_mul($self, 3) + ret := v0 leave } function $test_impl_method_collision() -> ret { @@ -44,11 +47,13 @@ function $test_impl_method_collision() -> ret { leave } function $multitraitimpl_trait_same_method($self) -> ret { - ret := add($self, 1) + let v0 := $u32_add_add($self, 1) + ret := v0 leave } function $multitraitimpl_trait_same_method($self) -> ret { - ret := add($self, 2) + let v0 := $u32_add_add($self, 2) + ret := v0 leave } function $test_trait_method_collision() -> ret { @@ -56,13 +61,16 @@ function $test_trait_method_collision() -> ret { let v1 := $multitraitimpl_trait_same_method(v0) let v2 := 100 let v3 := $multitraitimpl_trait_same_method(v2) - ret := add(v1, v3) + let v4 := $u32_add_add(v1, v3) + ret := v4 leave } function $param_local_collision($v0) -> ret { - let v0 := add($v0, 1) - let v1 := add(v0, 2) - ret := v1 + let v0 := $u32_add_add($v0, 1) + let v1 := v0 + let v2 := $u32_add_add(v1, 2) + let v3 := v2 + ret := v3 leave } function $test_param_local_collision() -> ret { @@ -81,7 +89,8 @@ function $test_cast_shim_collision() -> ret { leave } function $ret_param_collision($ret) -> ret { - ret := add($ret, 1) + let v0 := $u32_add_add($ret, 1) + ret := v0 leave } function $test_ret_param_collision() -> ret { @@ -118,6 +127,16 @@ function $main() -> ret { ret := 0 leave } +function $u32_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffff), and($other, 0xffffffff)), 0xffffffff) + let v1 := lt(and(v0, 0xffffffff), and($self, 0xffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} function $generic_fn__S__33f7c3322e562590($val) -> ret { ret := $val leave @@ -126,3 +145,67 @@ function $generic_fn__S__33f7c3322e562590($val) -> ret { ret := $val leave } +function $u32_mul_mul($self, $other) -> ret { + let v0 := and(mul(and($self, 0xffffffff), and($other, 0xffffffff)), 0xffffffff) + let v1 := $u32_eq_ne($self, 0) + let v2 := $u32_div_div(v0, $self) + let v3 := $u32_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 + leave +} +function $u64_mul_mul($self, $other) -> ret { + let v0 := and(mul(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := $u64_eq_ne($self, 0) + let v2 := $u64_div_div(v0, $self) + let v3 := $u64_eq_ne(v2, $other) + let v4 := and(v1, v3) + if v4 { + revert(0, 0) + } + ret := v0 + leave +} +function $u32_eq_ne($self, $other) -> ret { + let v0 := iszero(eq(and($self, 0xffffffff), and($other, 0xffffffff))) + ret := v0 + leave +} +function $u32_div_div($self, $other) -> ret { + let v0 := $u32_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := and(div(and($self, 0xffffffff), and($other, 0xffffffff)), 0xffffffff) + ret := v2 + leave +} +function $u64_eq_ne($self, $other) -> ret { + let v0 := iszero(eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff))) + ret := v0 + leave +} +function $u64_div_div($self, $other) -> ret { + let v0 := $u64_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := and(div(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v2 + leave +} +function $u32_eq_eq($self, $other) -> ret { + let v0 := eq(and($self, 0xffffffff), and($other, 0xffffffff)) + ret := v0 + leave +} +function $u64_eq_eq($self, $other) -> ret { + let v0 := eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/newtype_field_mut_method_call.snap b/crates/codegen/tests/fixtures/newtype_field_mut_method_call.snap index 9329c9f9ef..3a22d5044d 100644 --- a/crates/codegen/tests/fixtures/newtype_field_mut_method_call.snap +++ b/crates/codegen/tests/fixtures/newtype_field_mut_method_call.snap @@ -5,13 +5,15 @@ input_file: tests/fixtures/newtype_field_mut_method_call.fe --- function $counter_bump($self) { let v0 := mload($self) - mstore($self, add(v0, 1)) + let v1 := $u256_addassign_add_assign(v0, 1) + mstore($self, v1) leave } function $wrapcounter_bump($self) { let v0 := $self let v1 := mload(v0) - mstore(v0, add(v1, 1)) + let v2 := $u256_addassign_add_assign(v1, 1) + mstore(v0, v2) leave } function $newtype_field_mut_method_call($x) -> ret { @@ -37,9 +39,25 @@ function $newtype_field_mut_method_call($x) -> ret { ret := v4 leave } +function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave +} function $wrapcounter_bump_mem($self) { let v0 := $self let v1 := mload(v0) - mstore(v0, add(v1, 1)) + let v2 := $u256_addassign_add_assign(v1, 1) + mstore(v0, v2) + leave +} +function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 leave } diff --git a/crates/codegen/tests/fixtures/newtype_storage_byplace_effect_arg.snap b/crates/codegen/tests/fixtures/newtype_storage_byplace_effect_arg.snap index 6eeac78a1e..405a68449c 100644 --- a/crates/codegen/tests/fixtures/newtype_storage_byplace_effect_arg.snap +++ b/crates/codegen/tests/fixtures/newtype_storage_byplace_effect_arg.snap @@ -36,11 +36,13 @@ object "NewtypeByPlaceEffectArg" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -116,6 +118,21 @@ object "NewtypeByPlaceEffectArg" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $__NewtypeByPlaceEffectArg_init() } @@ -124,9 +141,10 @@ object "NewtypeByPlaceEffectArg" { function $__NewtypeByPlaceEffectArg_recv_0_0($args, $w, $x) -> ret { $bump__StorPtr_Wrap___5036bb3e02939b91($w) let v0 := sload($x) - sstore($x, add(v0, 1)) - let v1 := sload($x) - ret := v1 + let v1 := $u256_addassign_add_assign(v0, 1) + sstore($x, v1) + let v2 := sload($x) + ret := v2 leave } function $__NewtypeByPlaceEffectArg_runtime() { @@ -146,17 +164,20 @@ object "NewtypeByPlaceEffectArg" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } function $bump__StorPtr_Wrap___5036bb3e02939b91($w) { let v0 := sload($w) - sstore($w, add(v0, 1)) + let v1 := $u256_addassign_add_assign(v0, 1) + sstore($w, v1) leave } function $bump_decode_decode__SolDecoder_CallData___c1e4510fd444b966($d) { @@ -164,12 +185,14 @@ object "NewtypeByPlaceEffectArg" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -236,13 +259,14 @@ object "NewtypeByPlaceEffectArg" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -285,14 +309,15 @@ object "NewtypeByPlaceEffectArg" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -304,17 +329,20 @@ object "NewtypeByPlaceEffectArg" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -345,14 +373,49 @@ object "NewtypeByPlaceEffectArg" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave + } function $u256_encode_encode__Sol_SolEncoder__dce63ad1a1fe4ae2($self, $e) { $solencoder_abiencoder_write_word($e, $self) leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/newtype_storage_field_mut_method_call.snap b/crates/codegen/tests/fixtures/newtype_storage_field_mut_method_call.snap index f94fa10c29..d8086dc034 100644 --- a/crates/codegen/tests/fixtures/newtype_storage_field_mut_method_call.snap +++ b/crates/codegen/tests/fixtures/newtype_storage_field_mut_method_call.snap @@ -36,11 +36,13 @@ object "NewtypeStorageFieldMutMethodCall" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -116,6 +118,21 @@ object "NewtypeStorageFieldMutMethodCall" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $__NewtypeStorageFieldMutMethodCall_init() } @@ -146,11 +163,13 @@ object "NewtypeStorageFieldMutMethodCall" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -159,12 +178,14 @@ object "NewtypeStorageFieldMutMethodCall" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -231,13 +252,14 @@ object "NewtypeStorageFieldMutMethodCall" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -280,14 +302,15 @@ object "NewtypeStorageFieldMutMethodCall" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -299,17 +322,20 @@ object "NewtypeStorageFieldMutMethodCall" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -340,20 +366,56 @@ object "NewtypeStorageFieldMutMethodCall" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave + } function $u256_encode_encode__Sol_SolEncoder__dce63ad1a1fe4ae2($self, $e) { $solencoder_abiencoder_write_word($e, $self) leave } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave } function $wrap_bump($self) -> ret { - $self := add($self, 1) + let v0 := $u256_addassign_add_assign($self, 1) + $self := v0 ret := $self leave } diff --git a/crates/codegen/tests/fixtures/newtype_word_mut_method_call.snap b/crates/codegen/tests/fixtures/newtype_word_mut_method_call.snap index 3ed7f2d31e..a39a380b5a 100644 --- a/crates/codegen/tests/fixtures/newtype_word_mut_method_call.snap +++ b/crates/codegen/tests/fixtures/newtype_word_mut_method_call.snap @@ -4,7 +4,8 @@ expression: output input_file: tests/fixtures/newtype_word_mut_method_call.fe --- function $wrap_bump($self) -> ret { - $self := add($self, 1) + let v0 := $u256_addassign_add_assign($self, 1) + $self := v0 ret := $self leave } @@ -15,3 +16,18 @@ function $newtype_word_mut_method_call($x) -> ret { ret := v0 leave } +function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave +} +function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/range_bounds.snap b/crates/codegen/tests/fixtures/range_bounds.snap index 9c6ce29fe8..b5578e3590 100644 --- a/crates/codegen/tests/fixtures/range_bounds.snap +++ b/crates/codegen/tests/fixtures/range_bounds.snap @@ -10,7 +10,8 @@ function $sum_const() -> ret { for { } lt(v1, v2) { } { let v3 := $range_known_const_s__usize___known_const_e__usize___seq_get__0_4__f9605efabd18106c(0, v1) let v4 := v3 - v0 := add(v0, v4) + let v5 := $usize_add_add(v0, v4) + v0 := v5 v1 := add(v1, 1) } ret := v0 @@ -29,7 +30,8 @@ function $sum_dynamic_end($end) -> ret { for { } lt(v2, v3) { } { let v4 := $range_known_const_s__usize___known_const_e__usize___seq_get__0_4__f9605efabd18106c(v1, v2) let v5 := v4 - v0 := add(v0, v5) + let v6 := $usize_add_add(v0, v5) + v0 := v6 v2 := add(v2, 1) } ret := v0 @@ -48,7 +50,8 @@ function $sum_dynamic_start($start) -> ret { for { } lt(v2, v3) { } { let v4 := $range_unknown__known_const_e__usize___seq_get__4__f248ae0e02044d7e(v1, v2) let v5 := v4 - v0 := add(v0, v5) + let v6 := $usize_add_add(v0, v5) + v0 := v6 v2 := add(v2, 1) } ret := v0 @@ -68,90 +71,130 @@ function $sum_dynamic($start, $end) -> ret { for { } lt(v2, v3) { } { let v4 := $range_unknown__known_const_e__usize___seq_get__4__f248ae0e02044d7e(v1, v2) let v5 := v4 - v0 := add(v0, v5) + let v6 := $usize_add_add(v0, v5) + v0 := v6 v2 := add(v2, 1) } ret := v0 leave } function $range_known_const_s__usize___known_const_e__usize___seq_len__0_4__f9605efabd18106c($self) -> ret { - let v0 := 0 - switch lt(4, 0) + let v0 := $usize_ord_lt(4, 0) + let v1 := 0 + switch v0 case 1 { - v0 := 0 + v1 := 0 } case 0 { - v0 := sub(4, 0) + let v2 := $usize_sub_sub(4, 0) + v1 := v2 } default { - v0 := sub(4, 0) + let v3 := $usize_sub_sub(4, 0) + v1 := v3 } - ret := v0 + ret := v1 leave } function $range_known_const_s__usize___known_const_e__usize___seq_get__0_4__f9605efabd18106c($self, $i) -> ret { - ret := add(0, $i) + let v0 := $usize_add_add(0, $i) + ret := v0 + leave +} +function $usize_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 leave } function $range_known_const_s__usize___unknown__seq_len__0__fc6647014fb554e5($self) -> ret { let v0 := mload($self) - let v1 := 0 - switch lt(v0, 0) + let v1 := $usize_ord_lt(v0, 0) + let v2 := 0 + switch v1 case 1 { - v1 := 0 + v2 := 0 } case 0 { - let v2 := mload($self) - v1 := sub(v2, 0) + let v3 := mload($self) + let v4 := $usize_sub_sub(v3, 0) + v2 := v4 } default { - let v3 := mload($self) - v1 := sub(v3, 0) + let v5 := mload($self) + let v6 := $usize_sub_sub(v5, 0) + v2 := v6 } - ret := v1 + ret := v2 leave } function $range_unknown__known_const_e__usize___seq_len__4__f248ae0e02044d7e($self) -> ret { let v0 := mload($self) - let v1 := 0 - switch lt(4, v0) + let v1 := $usize_ord_lt(4, v0) + let v2 := 0 + switch v1 case 1 { - v1 := 0 + v2 := 0 } case 0 { - let v2 := mload($self) - v1 := sub(4, v2) + let v3 := mload($self) + let v4 := $usize_sub_sub(4, v3) + v2 := v4 } default { - let v3 := mload($self) - v1 := sub(4, v3) + let v5 := mload($self) + let v6 := $usize_sub_sub(4, v5) + v2 := v6 } - ret := v1 + ret := v2 leave } function $range_unknown__known_const_e__usize___seq_get__4__f248ae0e02044d7e($self, $i) -> ret { let v0 := mload($self) - ret := add(v0, $i) + let v1 := $usize_add_add(v0, $i) + ret := v1 leave } function $range_unknown__unknown__seq_len($self) -> ret { let v0 := mload(add($self, 32)) let v1 := mload($self) - let v2 := 0 - switch lt(v0, v1) + let v2 := $usize_ord_lt(v0, v1) + let v3 := 0 + switch v2 case 1 { - v2 := 0 + v3 := 0 } case 0 { - let v3 := mload(add($self, 32)) - let v4 := mload($self) - v2 := sub(v3, v4) + let v4 := mload(add($self, 32)) + let v5 := mload($self) + let v6 := $usize_sub_sub(v4, v5) + v3 := v6 } default { - let v5 := mload(add($self, 32)) - let v6 := mload($self) - v2 := sub(v5, v6) + let v7 := mload(add($self, 32)) + let v8 := mload($self) + let v9 := $usize_sub_sub(v7, v8) + v3 := v9 } + ret := v3 + leave +} +function $usize_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave +} +function $usize_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) ret := v2 leave } diff --git a/crates/codegen/tests/fixtures/ret.snap b/crates/codegen/tests/fixtures/ret.snap index 39c946feec..36ba2a8b5b 100644 --- a/crates/codegen/tests/fixtures/ret.snap +++ b/crates/codegen/tests/fixtures/ret.snap @@ -1,6 +1,5 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/ret.fe --- @@ -11,22 +10,56 @@ function $retfoo($b1, $x) -> ret { leave } if iszero(v0) { - let v1 := lt($x, 5) - if v1 { + let v1 := $u64_ord_lt($x, 5) + let v2 := v1 + if v2 { ret := 1 leave } - if iszero(v1) { - let v2 := sub($x, 1) - let v3 := eq(v2, 42) - if v3 { + if iszero(v2) { + let v3 := $u64_sub_sub($x, 1) + let v4 := v3 + let v5 := $u64_eq_eq(v4, 42) + let v6 := v5 + if v6 { ret := 2 leave } - if iszero(v3) { - ret := add(v2, 1) + if iszero(v6) { + let v7 := $u64_add_add(v4, 1) + ret := v7 leave } } } } +function $u64_ord_lt($self, $other) -> ret { + let v0 := lt(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} +function $u64_sub_sub($self, $other) -> ret { + let v0 := gt(and($other, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := and(sub(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v2 + leave +} +function $u64_eq_eq($self, $other) -> ret { + let v0 := eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/storage.snap b/crates/codegen/tests/fixtures/storage.snap index b0924d254c..ee319864aa 100644 --- a/crates/codegen/tests/fixtures/storage.snap +++ b/crates/codegen/tests/fixtures/storage.snap @@ -37,20 +37,24 @@ object "Coin" { } function $credit_alice__StorPtr_CoinStore__StorPtr_TotalSupply___c407f5b00af9ba78($amount, $store, $supply) -> ret { let v0 := sload($store) - sstore($store, add(v0, $amount)) - let v1 := sload($supply) - sstore($supply, add(v1, $amount)) - let v2 := sload($store) - ret := v2 + let v1 := $u256_add_add(v0, $amount) + sstore($store, v1) + let v2 := sload($supply) + let v3 := $u256_add_add(v2, $amount) + sstore($supply, v3) + let v4 := sload($store) + ret := v4 leave } function $credit_bob__StorPtr_CoinStore__StorPtr_TotalSupply___c407f5b00af9ba78($amount, $store, $supply) -> ret { let v0 := sload(add($store, 1)) - sstore(add($store, 1), add(v0, $amount)) - let v1 := sload($supply) - sstore($supply, add(v1, $amount)) - let v2 := sload(add($store, 1)) - ret := v2 + let v1 := $u256_add_add(v0, $amount) + sstore(add($store, 1), v1) + let v2 := sload($supply) + let v3 := $u256_add_add(v2, $amount) + sstore($supply, v3) + let v4 := sload(add($store, 1)) + ret := v4 leave } function $runtime__StorPtr_Evm___207f35a85ac4062e() { @@ -128,57 +132,63 @@ object "Coin" { } function $transfer_from_alice__StorPtr_CoinStore___ba1c0d0726e89ba2($amount, $store) -> ret { let v0 := sload($store) - let v1 := lt(v0, $amount) - if v1 { - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v1 := $u256_ord_lt(v0, $amount) + let v2 := v1 + if v2 { + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 32)) - mstore(v2, 1) - ret := v2 + mstore(0x40, add(v3, 32)) + mstore(v3, 1) + ret := v3 leave } - if iszero(v1) { - let v3 := sload($store) - sstore($store, sub(v3, $amount)) - let v4 := sload(add($store, 1)) - sstore(add($store, 1), add(v4, $amount)) - let v5 := mload(0x40) - if iszero(v5) { - v5 := 0x80 + if iszero(v2) { + let v4 := sload($store) + let v5 := $u256_sub_sub(v4, $amount) + sstore($store, v5) + let v6 := sload(add($store, 1)) + let v7 := $u256_add_add(v6, $amount) + sstore(add($store, 1), v7) + let v8 := mload(0x40) + if iszero(v8) { + v8 := 0x80 } - mstore(0x40, add(v5, 32)) - mstore(v5, 0) - ret := v5 + mstore(0x40, add(v8, 32)) + mstore(v8, 0) + ret := v8 leave } } function $transfer_from_bob__StorPtr_CoinStore___ba1c0d0726e89ba2($amount, $store) -> ret { let v0 := sload(add($store, 1)) - let v1 := lt(v0, $amount) - if v1 { - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v1 := $u256_ord_lt(v0, $amount) + let v2 := v1 + if v2 { + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 32)) - mstore(v2, 1) - ret := v2 + mstore(0x40, add(v3, 32)) + mstore(v3, 1) + ret := v3 leave } - if iszero(v1) { - let v3 := sload(add($store, 1)) - sstore(add($store, 1), sub(v3, $amount)) - let v4 := sload($store) - sstore($store, add(v4, $amount)) - let v5 := mload(0x40) - if iszero(v5) { - v5 := 0x80 + if iszero(v2) { + let v4 := sload(add($store, 1)) + let v5 := $u256_sub_sub(v4, $amount) + sstore(add($store, 1), v5) + let v6 := sload($store) + let v7 := $u256_add_add(v6, $amount) + sstore($store, v7) + let v8 := mload(0x40) + if iszero(v8) { + v8 := 0x80 } - mstore(0x40, add(v5, 32)) - mstore(v5, 0) - ret := v5 + mstore(0x40, add(v8, 32)) + mstore(v8, 0) + ret := v8 leave } } @@ -197,6 +207,31 @@ object "Coin" { ret := v0 leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } $runtime__StorPtr_Evm___207f35a85ac4062e() return(0, 0) } diff --git a/crates/codegen/tests/fixtures/storage_map.snap b/crates/codegen/tests/fixtures/storage_map.snap index 58466d270d..928a698196 100644 --- a/crates/codegen/tests/fixtures/storage_map.snap +++ b/crates/codegen/tests/fixtures/storage_map.snap @@ -45,9 +45,11 @@ function $storagemap_set_word_with_salt__u256__3271ca15373d4483($key, $salt, $wo function $storagemap_storage_slot_with_salt__u256__3271ca15373d4483($key, $salt) -> ret { let v0 := mload(64) let v1 := $u256_storagekey_write_key(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $u256_storagekey_write_key($ptr, $self) -> ret { @@ -55,3 +57,13 @@ function $u256_storagekey_write_key($ptr, $self) -> ret { ret := 32 leave } +function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave +} diff --git a/crates/codegen/tests/fixtures/storage_map_contract.snap b/crates/codegen/tests/fixtures/storage_map_contract.snap index 98d59462ae..7efc45d224 100644 --- a/crates/codegen/tests/fixtures/storage_map_contract.snap +++ b/crates/codegen/tests/fixtures/storage_map_contract.snap @@ -123,31 +123,61 @@ object "BalanceMap" { function $storagemap_storage_slot_with_salt__u256__3271ca15373d4483($key, $salt) -> ret { let v0 := mload(64) let v1 := $u256_storagekey_write_key(v0, $key) - mstore(add(v0, v1), $salt) - let v2 := keccak256(v0, add(v1, 32)) - ret := v2 + let v2 := $u256_add_add(v0, v1) + mstore(v2, $salt) + let v3 := $u256_add_add(v1, 32) + let v4 := keccak256(v0, v3) + ret := v4 leave } function $transfer__MemPtr_StorageMap_u256__u256__0____3cd7ace0c4584833($from, $to, $amount, $balances) -> ret { let v0 := $storagemap_k__v__const_salt__u256__get__u256_u256_0__6a346ad3c930d971($balances, $from) - let v1 := lt(v0, $amount) - if v1 { + let v1 := $u256_ord_lt(v0, $amount) + let v2 := v1 + if v2 { ret := 1 leave } - if iszero(v1) { - let v2 := $storagemap_k__v__const_salt__u256__get__u256_u256_0__6a346ad3c930d971($balances, $to) - $storagemap_k__v__const_salt__u256__set__u256_u256_0__6a346ad3c930d971($balances, $from, sub(v0, $amount)) - $storagemap_k__v__const_salt__u256__set__u256_u256_0__6a346ad3c930d971($balances, $to, add(v2, $amount)) + if iszero(v2) { + let v3 := $storagemap_k__v__const_salt__u256__get__u256_u256_0__6a346ad3c930d971($balances, $to) + let v4 := $u256_sub_sub(v0, $amount) + $storagemap_k__v__const_salt__u256__set__u256_u256_0__6a346ad3c930d971($balances, $from, v4) + let v5 := $u256_add_add(v3, $amount) + $storagemap_k__v__const_salt__u256__set__u256_u256_0__6a346ad3c930d971($balances, $to, v5) ret := 0 leave } } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } function $u256_storagekey_write_key($ptr, $self) -> ret { mstore($ptr, $self) ret := 32 leave } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u256_wordrepr_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/test_output/effect_test.snap b/crates/codegen/tests/fixtures/test_output/effect_test.snap index f77ccfe823..2670471020 100644 --- a/crates/codegen/tests/fixtures/test_output/effect_test.snap +++ b/crates/codegen/tests/fixtures/test_output/effect_test.snap @@ -13,7 +13,23 @@ object "test_test_effect" { code { function $test_effect__StorPtr_u256___64779554cfbf0358($x) { let v0 := sload($x) - sstore($x, add(v0, 1)) + let v1 := $u256_addassign_add_assign(v0, 1) + sstore($x, v1) + leave + } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 leave } $test_effect__StorPtr_u256___64779554cfbf0358(0) diff --git a/crates/codegen/tests/fixtures/tstor_ptr_contract.snap b/crates/codegen/tests/fixtures/tstor_ptr_contract.snap index 2c5cb2e9ca..bdcac90066 100644 --- a/crates/codegen/tests/fixtures/tstor_ptr_contract.snap +++ b/crates/codegen/tests/fixtures/tstor_ptr_contract.snap @@ -37,11 +37,13 @@ object "GuardContract" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -106,6 +108,21 @@ object "GuardContract" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } $__GuardContract_init() } @@ -113,11 +130,12 @@ object "GuardContract" { code { function $__GuardContract_recv_0_0($args, $block_reentrance, $call_count) -> ret { let v0 := sload($call_count) - sstore($call_count, add(v0, 1)) - let v1 := iszero(eq(tload($block_reentrance), 0)) - let v2 := v1 - tstore($block_reentrance, iszero(iszero(iszero(v2)))) - ret := v2 + let v1 := $u256_addassign_add_assign(v0, 1) + sstore($call_count, v1) + let v2 := iszero(eq(tload($block_reentrance), 0)) + let v3 := v2 + tstore($block_reentrance, iszero(iszero(iszero(v3)))) + ret := v3 leave } function $__GuardContract_runtime() { @@ -137,11 +155,13 @@ object "GuardContract" { } function $alloc($size) -> ret { let v0 := mload(64) - let v1 := eq(v0, 0) - if v1 { + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { v0 := 128 } - mstore(64, add(v0, $size)) + let v3 := $u256_add_add(v0, $size) + mstore(64, v3) ret := v0 leave } @@ -157,12 +177,14 @@ object "GuardContract" { } function $calldata_byteinput_len($self) -> ret { let v0 := calldatasize() - ret := sub(v0, $self) + let v1 := $u256_sub_sub(v0, $self) + ret := v1 leave } function $calldata_byteinput_word_at($self, $byte_offset) -> ret { - let v0 := calldataload(add($self, $byte_offset)) - ret := v0 + let v0 := $u256_add_add($self, $byte_offset) + let v1 := calldataload(v0) + ret := v1 leave } function $cursor_i__fork_mem__CallData__b9ab8dc8a4b2f9e($self, $pos) -> ret { @@ -227,13 +249,14 @@ object "GuardContract" { function $runtime_selector__Evm_Sol__2533f5c49b57a682($self) -> ret { let v0 := $evm_contracthost_input($self) let v1 := $calldata_byteinput_len(v0) - let v2 := lt(v1, 4) - if v2 { + let v2 := $u256_ord_lt(v1, 4) + let v3 := v2 + if v3 { $evm_contracthost_abort($self) } - let v3 := $calldata_byteinput_word_at(v0, 0) - let v4 := $sol_abi_selector_from_prefix(v3) - ret := v4 + let v4 := $calldata_byteinput_word_at(v0, 0) + let v5 := $sol_abi_selector_from_prefix(v4) + ret := v5 leave } function $s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35($self) -> ret { @@ -276,14 +299,15 @@ object "GuardContract" { function $solencoder_abiencoder_finish($self) -> ret { let v0 := mload($self) let v1 := mload(add($self, 64)) - let v2 := mload(0x40) - if iszero(v2) { - v2 := 0x80 + let v2 := $u256_sub_sub(v1, v0) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 } - mstore(0x40, add(v2, 64)) - mstore(v2, v0) - mstore(add(v2, 32), sub(v1, v0)) - ret := v2 + mstore(0x40, add(v3, 64)) + mstore(v3, v0) + mstore(add(v3, 32), v2) + ret := v3 leave } function $solencoder_abiencoder_reserve_head($self, $bytes) -> ret { @@ -295,17 +319,20 @@ object "GuardContract" { function $solencoder_abiencoder_write_word($self, $v) { let v0 := mload(add($self, 32)) mstore(v0, $v) - mstore(add($self, 32), add(v0, 32)) + let v1 := $u256_add_add(v0, 32) + mstore(add($self, 32), v1) leave } function $solencoder_ensure_init_mem($self, $bytes) { let v0 := mload($self) - let v1 := eq(v0, 0) - if v1 { - let v2 := $alloc($bytes) - mstore($self, v2) - mstore(add($self, 32), v2) - mstore(add($self, 64), add(v2, $bytes)) + let v1 := $u256_eq_eq(v0, 0) + let v2 := v1 + if v2 { + let v3 := $alloc($bytes) + mstore($self, v3) + mstore(add($self, 32), v3) + let v4 := $u256_add_add(v3, $bytes) + mstore(add($self, 64), v4) } leave } @@ -335,10 +362,45 @@ object "GuardContract" { ret := $raw leave } + function $u256_add_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := v1 + if v2 { + revert(0, 0) + } + ret := v0 + leave + } + function $u256_addassign_add_assign($self, $other) -> ret { + let v0 := $u256_add_add($self, $other) + ret := v0 + leave + } + function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave + } function $u256_intword_to_word($self) -> ret { ret := $self leave } + function $u256_ord_lt($self, $other) -> ret { + let v0 := lt($self, $other) + ret := v0 + leave + } + function $u256_sub_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := sub($self, $other) + ret := v2 + leave + } function $u32_intword_from_word($word) -> ret { ret := $word leave diff --git a/crates/codegen/tests/fixtures/while_cond_call.snap b/crates/codegen/tests/fixtures/while_cond_call.snap index 93457fb716..1b97ecb3b7 100644 --- a/crates/codegen/tests/fixtures/while_cond_call.snap +++ b/crates/codegen/tests/fixtures/while_cond_call.snap @@ -1,17 +1,33 @@ --- source: crates/codegen/tests/yul.rs -assertion_line: 42 expression: output input_file: tests/fixtures/while_cond_call.fe --- function $lt_ten($value) -> ret { - ret := lt($value, 10) + let v0 := $u64_ord_lt($value, 10) + ret := v0 leave } function $while_cond_call() -> ret { let v0 := 0 for { let v1 := $lt_ten(v0) } v1 { v1 := $lt_ten(v0) } { - v0 := add(v0, 1) + let v2 := $u64_add_add(v0, 1) + v0 := v2 + } + ret := v0 + leave +} +function $u64_ord_lt($self, $other) -> ret { + let v0 := lt(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} +function $u64_add_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := v1 + if v2 { + revert(0, 0) } ret := v0 leave diff --git a/crates/codegen/tests/fixtures/wrapping_saturating.fe b/crates/codegen/tests/fixtures/wrapping_saturating.fe new file mode 100644 index 0000000000..e912044160 --- /dev/null +++ b/crates/codegen/tests/fixtures/wrapping_saturating.fe @@ -0,0 +1,18 @@ +use core::ops::{WrappingAdd, WrappingSub, WrappingMul} +use core::ops::{SaturatingAdd, SaturatingSub, SaturatingMul} + +pub fn wrapping_ops_u64(a: u64, b: u64) -> (u64, u64, u64) { + (a.wrapping_add(b), a.wrapping_sub(b), a.wrapping_mul(b)) +} + +pub fn saturating_ops_u64(a: u64, b: u64) -> (u64, u64, u64) { + (a.saturating_add(b), a.saturating_sub(b), a.saturating_mul(b)) +} + +pub fn wrapping_ops_u256(a: u256, b: u256) -> (u256, u256, u256) { + (a.wrapping_add(b), a.wrapping_sub(b), a.wrapping_mul(b)) +} + +pub fn saturating_ops_u256(a: u256, b: u256) -> (u256, u256, u256) { + (a.saturating_add(b), a.saturating_sub(b), a.saturating_mul(b)) +} diff --git a/crates/codegen/tests/fixtures/wrapping_saturating.snap b/crates/codegen/tests/fixtures/wrapping_saturating.snap new file mode 100644 index 0000000000..8a88d074d3 --- /dev/null +++ b/crates/codegen/tests/fixtures/wrapping_saturating.snap @@ -0,0 +1,295 @@ +--- +source: crates/codegen/tests/yul.rs +expression: output +input_file: tests/fixtures/wrapping_saturating.fe +--- +function $wrapping_ops_u64($a, $b) -> ret { + let v0 := $u64_wrappingadd_wrapping_add($a, $b) + let v1 := $u64_wrappingsub_wrapping_sub($a, $b) + let v2 := $u64_wrappingmul_wrapping_mul($a, $b) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 + } + mstore(0x40, add(v3, 24)) + mstore(v3, and(v0, 0xffffffffffffffff)) + mstore(add(v3, 8), and(v1, 0xffffffffffffffff)) + mstore(add(v3, 16), and(v2, 0xffffffffffffffff)) + ret := v3 + leave +} +function $saturating_ops_u64($a, $b) -> ret { + let v0 := $u64_saturatingadd_saturating_add($a, $b) + let v1 := $u64_saturatingsub_saturating_sub($a, $b) + let v2 := $u64_saturatingmul_saturating_mul($a, $b) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 + } + mstore(0x40, add(v3, 24)) + mstore(v3, and(v0, 0xffffffffffffffff)) + mstore(add(v3, 8), and(v1, 0xffffffffffffffff)) + mstore(add(v3, 16), and(v2, 0xffffffffffffffff)) + ret := v3 + leave +} +function $wrapping_ops_u256($a, $b) -> ret { + let v0 := $u256_wrappingadd_wrapping_add($a, $b) + let v1 := $u256_wrappingsub_wrapping_sub($a, $b) + let v2 := $u256_wrappingmul_wrapping_mul($a, $b) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 + } + mstore(0x40, add(v3, 96)) + mstore(v3, v0) + mstore(add(v3, 32), v1) + mstore(add(v3, 64), v2) + ret := v3 + leave +} +function $saturating_ops_u256($a, $b) -> ret { + let v0 := $u256_saturatingadd_saturating_add($a, $b) + let v1 := $u256_saturatingsub_saturating_sub($a, $b) + let v2 := $u256_saturatingmul_saturating_mul($a, $b) + let v3 := mload(0x40) + if iszero(v3) { + v3 := 0x80 + } + mstore(0x40, add(v3, 96)) + mstore(v3, v0) + mstore(add(v3, 32), v1) + mstore(add(v3, 64), v2) + ret := v3 + leave +} +function $u64_wrappingadd_wrapping_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v0 + leave +} +function $u64_wrappingsub_wrapping_sub($self, $other) -> ret { + let v0 := and(sub(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v0 + leave +} +function $u64_wrappingmul_wrapping_mul($self, $other) -> ret { + let v0 := and(mul(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v0 + leave +} +function $u64_saturatingadd_saturating_add($self, $other) -> ret { + let v0 := and(add(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v1 := lt(and(v0, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v2 := 0 + switch v1 + case 1 { + v2 := 18446744073709551615 + } + case 0 { + v2 := v0 + } + default { + v2 := v0 + } + ret := v2 + leave +} +function $u64_saturatingsub_saturating_sub($self, $other) -> ret { + let v0 := gt(and($other, 0xffffffffffffffff), and($self, 0xffffffffffffffff)) + let v1 := 0 + switch v0 + case 1 { + v1 := 0 + } + case 0 { + let v2 := and(sub(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + v1 := v2 + } + default { + let v3 := and(sub(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + v1 := v3 + } + ret := v1 + leave +} +function $u64_saturatingmul_saturating_mul($self, $other) -> ret { + let v0 := $u64_eq_eq($self, 0) + let v1 := $u64_eq_eq($other, 0) + let v2 := 0 + switch or(v0, v1) + case 1 { + v2 := 0 + } + case 0 { + let v3 := and(mul(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v4 := $u64_div_div(v3, $self) + let v5 := $u64_eq_ne(v4, $other) + let v6 := 0 + switch v5 + case 1 { + v6 := 18446744073709551615 + } + case 0 { + v6 := v3 + } + default { + v6 := v3 + } + v2 := v6 + } + default { + let v7 := and(mul(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + let v8 := $u64_div_div(v7, $self) + let v9 := $u64_eq_ne(v8, $other) + let v10 := 0 + switch v9 + case 1 { + v10 := 18446744073709551615 + } + case 0 { + v10 := v7 + } + default { + v10 := v7 + } + v2 := v10 + } + ret := v2 + leave +} +function $u256_wrappingadd_wrapping_add($self, $other) -> ret { + let v0 := add($self, $other) + ret := v0 + leave +} +function $u256_wrappingsub_wrapping_sub($self, $other) -> ret { + let v0 := sub($self, $other) + ret := v0 + leave +} +function $u256_wrappingmul_wrapping_mul($self, $other) -> ret { + let v0 := mul($self, $other) + ret := v0 + leave +} +function $u256_saturatingadd_saturating_add($self, $other) -> ret { + let v0 := add($self, $other) + let v1 := lt(v0, $self) + let v2 := 0 + switch v1 + case 1 { + v2 := 115792089237316195423570985008687907853269984665640564039457584007913129639935 + } + case 0 { + v2 := v0 + } + default { + v2 := v0 + } + ret := v2 + leave +} +function $u256_saturatingsub_saturating_sub($self, $other) -> ret { + let v0 := gt($other, $self) + let v1 := 0 + switch v0 + case 1 { + v1 := 0 + } + case 0 { + let v2 := sub($self, $other) + v1 := v2 + } + default { + let v3 := sub($self, $other) + v1 := v3 + } + ret := v1 + leave +} +function $u256_saturatingmul_saturating_mul($self, $other) -> ret { + let v0 := $u256_eq_eq($self, 0) + let v1 := $u256_eq_eq($other, 0) + let v2 := 0 + switch or(v0, v1) + case 1 { + v2 := 0 + } + case 0 { + let v3 := mul($self, $other) + let v4 := $u256_div_div(v3, $self) + let v5 := $u256_eq_ne(v4, $other) + let v6 := 0 + switch v5 + case 1 { + v6 := 115792089237316195423570985008687907853269984665640564039457584007913129639935 + } + case 0 { + v6 := v3 + } + default { + v6 := v3 + } + v2 := v6 + } + default { + let v7 := mul($self, $other) + let v8 := $u256_div_div(v7, $self) + let v9 := $u256_eq_ne(v8, $other) + let v10 := 0 + switch v9 + case 1 { + v10 := 115792089237316195423570985008687907853269984665640564039457584007913129639935 + } + case 0 { + v10 := v7 + } + default { + v10 := v7 + } + v2 := v10 + } + ret := v2 + leave +} +function $u64_eq_eq($self, $other) -> ret { + let v0 := eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)) + ret := v0 + leave +} +function $u64_div_div($self, $other) -> ret { + let v0 := $u64_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := and(div(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff)), 0xffffffffffffffff) + ret := v2 + leave +} +function $u64_eq_ne($self, $other) -> ret { + let v0 := iszero(eq(and($self, 0xffffffffffffffff), and($other, 0xffffffffffffffff))) + ret := v0 + leave +} +function $u256_eq_eq($self, $other) -> ret { + let v0 := eq($self, $other) + ret := v0 + leave +} +function $u256_div_div($self, $other) -> ret { + let v0 := $u256_eq_eq($other, 0) + let v1 := v0 + if v1 { + revert(0, 0) + } + let v2 := div($self, $other) + ret := v2 + leave +} +function $u256_eq_ne($self, $other) -> ret { + let v0 := iszero(eq($self, $other)) + ret := v0 + leave +} diff --git a/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe b/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe new file mode 100644 index 0000000000..fc48f9c3cb --- /dev/null +++ b/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe @@ -0,0 +1,30 @@ +use std::evm::effects::assert +use core::ops::{ + SaturatingAdd, SaturatingMul, SaturatingSub, WrappingAdd, WrappingMul, WrappingSub, +} + +#[test] +fn test_checked_arithmetic_no_overflow() { + let a: u8 = 1 + let b: u8 = 2 + assert(a + b == 3) + assert(a * b == 2) + assert(b - a == 1) +} + +#[test] +fn test_wrapping_and_saturating_u8() { + let max: u8 = 255 + let zero: u8 = 0 + let sixteen: u8 = as_u8(16) + + assert(max.wrapping_add(1) == 0) + assert(zero.wrapping_sub(1) == 255) + assert(sixteen.wrapping_mul(sixteen) == 0) + + assert(max.saturating_add(1) == 255) + assert(zero.saturating_sub(1) == 0) + assert(sixteen.saturating_mul(sixteen) == 255) +} + +fn as_u8(_ x: u8) -> u8 { x } diff --git a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe new file mode 100644 index 0000000000..8022ab3ac4 --- /dev/null +++ b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe @@ -0,0 +1,47 @@ +#[test] +fn test_add_overflow_u8() { + let x: u8 = 255 + let y: u8 = x + 1 +} + +#[test] +fn test_aug_assign_overflow_u8() { + let mut x: u8 = 255 + x += 1 +} + +#[test] +fn test_div_by_zero_u8() { + let x: u8 = 1 + let y: u8 = x / 0 +} + +#[test] +fn test_rem_by_zero_u8() { + let x: u8 = 1 + let y: u8 = x % 0 +} + +#[test] +fn test_pow_overflow_u8() { + let x: u8 = 2 + let y: u8 = x ** 8 +} + +#[test] +fn test_neg_overflow_i8() { + let x: i8 = -128 + let y: i8 = -x +} + +#[test] +fn test_div_overflow_i8() { + let x: i8 = -128 + let y: i8 = x / -1 +} + +#[test] +fn test_pow_negative_exp_i8() { + let x: i8 = 2 + let y: i8 = x ** -1 +} diff --git a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap new file mode 100644 index 0000000000..44f9892dd6 --- /dev/null +++ b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap @@ -0,0 +1,39 @@ +--- +source: crates/fe/tests/cli_output.rs +assertion_line: 173 +expression: output +input_file: tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe +--- +=== STDOUT === +test test_add_overflow_u8 ... FAILED +test test_aug_assign_overflow_u8 ... FAILED +test test_div_by_zero_u8 ... FAILED +test test_rem_by_zero_u8 ... FAILED +test test_pow_overflow_u8 ... FAILED +test test_neg_overflow_i8 ... FAILED +test test_div_overflow_i8 ... FAILED +test test_pow_negative_exp_i8 ... FAILED + +test result: FAILED. 0 passed; 8 failed + +failures: + test_add_overflow_u8 + test_aug_assign_overflow_u8 + test_div_by_zero_u8 + test_rem_by_zero_u8 + test_pow_overflow_u8 + test_neg_overflow_i8 + test_div_overflow_i8 + test_pow_negative_exp_i8 + +=== STDERR === + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + Test reverted: 0x + +=== EXIT CODE: 1 === diff --git a/crates/hir/src/analysis/diagnostics.rs b/crates/hir/src/analysis/diagnostics.rs index d5498f9837..75a6a8b8e4 100644 --- a/crates/hir/src/analysis/diagnostics.rs +++ b/crates/hir/src/analysis/diagnostics.rs @@ -1477,10 +1477,10 @@ impl DiagnosticVoucher for TyLowerDiag<'_> { Self::InvalidConstTyExpr(span) => CompleteDiagnostic { severity: Severity::Error, - message: "the expression is not supported yet in a const type context".to_string(), + message: "failed to evaluate const expression".to_string(), sub_diagnostics: vec![SubDiagnostic { style: LabelStyle::Primary, - message: "only literal expression is supported".to_string(), + message: "const evaluation failed".to_string(), span: span.resolve(db), }], notes: vec![], diff --git a/crates/hir/src/analysis/ty/const_ty.rs b/crates/hir/src/analysis/ty/const_ty.rs index 9cd1ac7a31..8660eb443a 100644 --- a/crates/hir/src/analysis/ty/const_ty.rs +++ b/crates/hir/src/analysis/ty/const_ty.rs @@ -1,4 +1,6 @@ -use crate::core::hir_def::{Body, Const, Expr, IdentId, IntegerId, LitKind, Partial}; +use crate::core::hir_def::{Body, Const, Expr, IdentId, IntegerId, LitKind, Partial, Stmt}; +use num_bigint::{BigInt, BigUint, Sign}; +use num_traits::{One, Zero}; use super::{ trait_def::TraitInstId, @@ -8,7 +10,7 @@ use super::{ use crate::analysis::{ HirAnalysisDb, name_resolution::{PathRes, resolve_path}, - ty::ty_def::{Kind, TyBase, TyData, TyVarSort}, + ty::ty_def::{Kind, PrimTy, TyBase, TyData, TyVarSort}, ty::{trait_def::assoc_const_body_for_trait_inst, trait_resolution::PredicateListId}, }; @@ -60,6 +62,333 @@ pub(crate) fn evaluate_const_ty<'db>( let expr = expr.clone(); + #[derive(Clone, Copy, Debug)] + struct CheckedIntTy { + bits: u16, + signed: bool, + } + + fn checked_int_ty_from_ty<'db>( + db: &'db dyn HirAnalysisDb, + expected: Option>, + ) -> Option { + let expected = expected?; + let base_ty = expected.base_ty(db); + let TyData::TyBase(TyBase::Prim(prim)) = base_ty.data(db) else { + return None; + }; + Some(match prim { + // unsigned + PrimTy::U8 => CheckedIntTy { + bits: 8, + signed: false, + }, + PrimTy::U16 => CheckedIntTy { + bits: 16, + signed: false, + }, + PrimTy::U32 => CheckedIntTy { + bits: 32, + signed: false, + }, + PrimTy::U64 => CheckedIntTy { + bits: 64, + signed: false, + }, + PrimTy::U128 => CheckedIntTy { + bits: 128, + signed: false, + }, + PrimTy::U256 | PrimTy::Usize => CheckedIntTy { + bits: 256, + signed: false, + }, + // signed + PrimTy::I8 => CheckedIntTy { + bits: 8, + signed: true, + }, + PrimTy::I16 => CheckedIntTy { + bits: 16, + signed: true, + }, + PrimTy::I32 => CheckedIntTy { + bits: 32, + signed: true, + }, + PrimTy::I64 => CheckedIntTy { + bits: 64, + signed: true, + }, + PrimTy::I128 => CheckedIntTy { + bits: 128, + signed: true, + }, + PrimTy::I256 | PrimTy::Isize => CheckedIntTy { + bits: 256, + signed: true, + }, + _ => return None, + }) + } + + fn u256_modulus() -> BigUint { + BigUint::one() << 256usize + } + + fn signed_bounds(ty: CheckedIntTy) -> (BigInt, BigInt) { + debug_assert!(ty.signed); + let half = BigInt::one() << ((ty.bits - 1) as usize); + let min = -half.clone(); + let max = half - BigInt::one(); + (min, max) + } + + fn unsigned_max(ty: CheckedIntTy) -> BigInt { + debug_assert!(!ty.signed); + (BigInt::one() << (ty.bits as usize)) - BigInt::one() + } + + fn in_range(value: &BigInt, ty: CheckedIntTy) -> bool { + if ty.signed { + let (min, max) = signed_bounds(ty); + value >= &min && value <= &max + } else { + value >= &BigInt::zero() && value <= &unsigned_max(ty) + } + } + + fn bigint_to_u256_word(value: &BigInt) -> Option { + let modulus = u256_modulus(); + match value.sign() { + Sign::Minus => { + let abs = value.magnitude(); + if abs > &modulus { + return None; + } + if abs.is_zero() { + Some(BigUint::zero()) + } else { + Some(&modulus - abs) + } + } + _ => value.to_biguint().and_then(|v| (v < modulus).then_some(v)), + } + } + + fn u256_word_to_bigint(word: &BigUint, ty: CheckedIntTy) -> BigInt { + if !ty.signed { + return BigInt::from(word.clone()); + } + + let bits = ty.bits as usize; + let mask = if bits == 256 { + (BigUint::one() << 256usize) - BigUint::one() + } else { + (BigUint::one() << (ty.bits as usize)) - BigUint::one() + }; + let value_bits = word & mask; + let sign_bit = BigUint::one() << ((ty.bits - 1) as usize); + if (value_bits.clone() & sign_bit).is_zero() { + BigInt::from(value_bits) + } else { + BigInt::from(value_bits) - (BigInt::one() << (ty.bits as usize)) + } + } + + #[derive(Clone, Copy, Debug)] + enum ConstIntError { + Overflow, + DivisionByZero, + NegativeExponent, + } + + fn eval_int_expr<'db>( + db: &'db dyn HirAnalysisDb, + body: Body<'db>, + expr: &Expr<'db>, + expected: Option, + ) -> Result { + match expr { + Expr::Block(stmts) => { + let Some(last) = stmts.last() else { + return Err(ConstIntError::Overflow); + }; + let Partial::Present(stmt) = last.data(db, body) else { + return Err(ConstIntError::Overflow); + }; + let Stmt::Expr(expr_id) = stmt else { + return Err(ConstIntError::Overflow); + }; + let Partial::Present(inner) = expr_id.data(db, body) else { + return Err(ConstIntError::Overflow); + }; + eval_int_expr(db, body, inner, expected) + } + + Expr::Lit(LitKind::Int(i)) => Ok(BigInt::from(i.data(db).clone())), + + Expr::Un(inner, op) => { + let Partial::Present(inner) = inner.data(db, body) else { + return Err(ConstIntError::Overflow); + }; + let value = eval_int_expr(db, body, inner, expected)?; + match op { + crate::core::hir_def::expr::UnOp::Minus => { + let Some(expected) = expected else { + return Err(ConstIntError::Overflow); + }; + let neg = -value; + if !in_range(&neg, expected) { + return Err(ConstIntError::Overflow); + } + Ok(neg) + } + crate::core::hir_def::expr::UnOp::Plus => Ok(value), + _ => Err(ConstIntError::Overflow), + } + } + + Expr::Bin(lhs_id, rhs_id, op) => { + let Partial::Present(lhs) = lhs_id.data(db, body) else { + return Err(ConstIntError::Overflow); + }; + let Partial::Present(rhs) = rhs_id.data(db, body) else { + return Err(ConstIntError::Overflow); + }; + let expected = expected.unwrap_or(CheckedIntTy { + bits: 256, + signed: false, + }); + + let lhs = eval_int_expr(db, body, lhs, Some(expected))?; + let rhs = eval_int_expr(db, body, rhs, Some(expected))?; + + match op { + crate::core::hir_def::expr::BinOp::Arith(op) => match op { + crate::core::hir_def::expr::ArithBinOp::Add => { + let result = lhs + rhs; + if !in_range(&result, expected) { + Err(ConstIntError::Overflow) + } else { + Ok(result) + } + } + crate::core::hir_def::expr::ArithBinOp::Sub => { + let result = lhs - rhs; + if !in_range(&result, expected) { + Err(ConstIntError::Overflow) + } else { + Ok(result) + } + } + crate::core::hir_def::expr::ArithBinOp::Mul => { + let result = lhs * rhs; + if !in_range(&result, expected) { + Err(ConstIntError::Overflow) + } else { + Ok(result) + } + } + crate::core::hir_def::expr::ArithBinOp::Div => { + if rhs.is_zero() { + return Err(ConstIntError::DivisionByZero); + } + if expected.signed { + let (min, _) = signed_bounds(expected); + if lhs == min && rhs == -BigInt::one() { + return Err(ConstIntError::Overflow); + } + } + let result = lhs / rhs; + if !in_range(&result, expected) { + Err(ConstIntError::Overflow) + } else { + Ok(result) + } + } + crate::core::hir_def::expr::ArithBinOp::Rem => { + if rhs.is_zero() { + return Err(ConstIntError::DivisionByZero); + } + let result = lhs % rhs; + if !in_range(&result, expected) { + Err(ConstIntError::Overflow) + } else { + Ok(result) + } + } + crate::core::hir_def::expr::ArithBinOp::Pow => { + if rhs.sign() == Sign::Minus { + return Err(ConstIntError::NegativeExponent); + } + let Some(exp) = rhs.to_biguint() else { + return Err(ConstIntError::NegativeExponent); + }; + let mut acc = BigInt::one(); + let mut base = lhs; + let mut exp = exp; + while !exp.is_zero() { + if (&exp & BigUint::one()) == BigUint::one() { + acc *= base.clone(); + if !in_range(&acc, expected) { + return Err(ConstIntError::Overflow); + } + } + exp >>= 1usize; + if exp.is_zero() { + break; + } + base = base.clone() * base; + if !in_range(&base, expected) { + return Err(ConstIntError::Overflow); + } + } + Ok(acc) + } + _ => Err(ConstIntError::Overflow), + }, + _ => Err(ConstIntError::Overflow), + } + } + + Expr::Path(path) => { + let Some(path) = path.to_opt() else { + return Err(ConstIntError::Overflow); + }; + let assumptions = PredicateListId::empty_list(db); + let resolved = resolve_path(db, path, body.scope(), assumptions, true) + .map_err(|_| ConstIntError::Overflow)?; + + let const_ty = match resolved { + PathRes::Const(const_def, declared_ty) => { + let body = const_def.body(db).to_opt().ok_or(ConstIntError::Overflow)?; + ConstTyId::from_body(db, body, Some(declared_ty), Some(const_def)) + } + PathRes::TraitConst(_recv_ty, inst, name) => { + const_ty_from_trait_const(db, inst, name).ok_or(ConstIntError::Overflow)? + } + _ => return Err(ConstIntError::Overflow), + }; + + let evaluated = const_ty.evaluate(db, None); + match evaluated.data(db) { + ConstTyData::Evaluated(EvaluatedConstTy::LitInt(i), _) => { + let word = i.data(db); + let expected_for_interpretation = expected.unwrap_or(CheckedIntTy { + bits: 256, + signed: false, + }); + Ok(u256_word_to_bigint(word, expected_for_interpretation)) + } + _ => Err(ConstIntError::Overflow), + } + } + + _ => Err(ConstIntError::Overflow), + } + } + if let Expr::Path(path) = &expr { let Some(path) = path.to_opt() else { return ConstTyId::new( @@ -124,6 +453,36 @@ pub(crate) fn evaluate_const_ty<'db>( table.new_var(TyVarSort::Integral, &Kind::Star), ), + Expr::Block(..) | Expr::Un(..) | Expr::Bin(..) => { + let expected_int_ty = checked_int_ty_from_ty(db, expected_ty); + match eval_int_expr(db, body, &expr, expected_int_ty) { + Ok(value) => { + let Some(word) = bigint_to_u256_word(&value) else { + return ConstTyId::new( + db, + ConstTyData::Evaluated( + EvaluatedConstTy::Invalid, + TyId::invalid(db, InvalidCause::InvalidConstTyExpr { body }), + ), + ); + }; + ( + EvaluatedConstTy::LitInt(IntegerId::new(db, word)), + table.new_var(TyVarSort::Integral, &Kind::Star), + ) + } + Err(_) => { + return ConstTyId::new( + db, + ConstTyData::Evaluated( + EvaluatedConstTy::Invalid, + TyId::invalid(db, InvalidCause::InvalidConstTyExpr { body }), + ), + ); + } + } + } + _ => { return ConstTyId::new( db, diff --git a/crates/hir/test_files/ty_check/ops.fe b/crates/hir/test_files/ty_check/ops.fe index 01a8dcce16..80e6da07ae 100644 --- a/crates/hir/test_files/ty_check/ops.fe +++ b/crates/hir/test_files/ty_check/ops.fe @@ -31,9 +31,11 @@ impl Eq for Ok { } impl AddAssign for Ok { - fn add_assign(mut self, _ other: Ok) { + fn add_assign(self, _ other: Ok) -> Ok { if other == Ok::Yes { - self = Ok::Yes + Ok::Yes + } else { + self } } } diff --git a/crates/hir/test_files/ty_check/ops.snap b/crates/hir/test_files/ty_check/ops.snap index e97ce96911..3a7be47d61 100644 --- a/crates/hir/test_files/ty_check/ops.snap +++ b/crates/hir/test_files/ty_check/ops.snap @@ -1,5 +1,6 @@ --- -source: crates/hir-analysis/tests/ty_check.rs +source: crates/hir/tests/ty_check.rs +assertion_line: 40 expression: res input_file: test_files/ty_check/ops.fe --- @@ -208,23 +209,27 @@ note: │ ^^^^^ Ok note: - ┌─ ops.fe:34:42 + ┌─ ops.fe:34:44 │ -34 │ fn add_assign(mut self, _ other: Ok) { - │ ╭──────────────────────────────────────────^ +34 │ fn add_assign(self, _ other: Ok) -> Ok { + │ ╭────────────────────────────────────────────^ 35 │ │ if other == Ok::Yes { -36 │ │ self = Ok::Yes -37 │ │ } -38 │ │ } - │ ╰─────^ () +36 │ │ Ok::Yes +37 │ │ } else { +38 │ │ self +39 │ │ } +40 │ │ } + │ ╰─────^ Ok note: ┌─ ops.fe:35:9 │ 35 │ ╭ if other == Ok::Yes { -36 │ │ self = Ok::Yes -37 │ │ } - │ ╰─────────^ () +36 │ │ Ok::Yes +37 │ │ } else { +38 │ │ self +39 │ │ } + │ ╰─────────^ Ok note: ┌─ ops.fe:35:12 @@ -249,414 +254,417 @@ note: │ 35 │ if other == Ok::Yes { │ ╭─────────────────────────────^ -36 │ │ self = Ok::Yes -37 │ │ } - │ ╰─────────^ () +36 │ │ Ok::Yes +37 │ │ } else { + │ ╰─────────^ Ok note: ┌─ ops.fe:36:13 │ -36 │ self = Ok::Yes - │ ^^^^ Ok +36 │ Ok::Yes + │ ^^^^^^^ Ok note: - ┌─ ops.fe:36:13 - │ -36 │ self = Ok::Yes - │ ^^^^^^^^^^^^^^ () + ┌─ ops.fe:37:16 + │ +37 │ } else { + │ ╭────────────────^ +38 │ │ self +39 │ │ } + │ ╰─────────^ Ok note: - ┌─ ops.fe:36:20 + ┌─ ops.fe:38:13 │ -36 │ self = Ok::Yes - │ ^^^^^^^ Ok +38 │ self + │ ^^^^ Ok note: - ┌─ ops.fe:47:39 + ┌─ ops.fe:49:39 │ -47 │ fn add(self, _ p: Point) -> Point { +49 │ fn add(self, _ p: Point) -> Point { │ ╭───────────────────────────────────────^ -48 │ │ Point { -49 │ │ x: self.x + p.x, -50 │ │ y: self.y + p.y, -51 │ │ } -52 │ │ } +50 │ │ Point { +51 │ │ x: self.x + p.x, +52 │ │ y: self.y + p.y, +53 │ │ } +54 │ │ } │ ╰─────^ Point note: - ┌─ ops.fe:48:9 + ┌─ ops.fe:50:9 │ -48 │ ╭ Point { -49 │ │ x: self.x + p.x, -50 │ │ y: self.y + p.y, -51 │ │ } +50 │ ╭ Point { +51 │ │ x: self.x + p.x, +52 │ │ y: self.y + p.y, +53 │ │ } │ ╰─────────^ Point note: - ┌─ ops.fe:49:16 + ┌─ ops.fe:51:16 │ -49 │ x: self.x + p.x, +51 │ x: self.x + p.x, │ ^^^^ Point note: - ┌─ ops.fe:49:16 + ┌─ ops.fe:51:16 │ -49 │ x: self.x + p.x, +51 │ x: self.x + p.x, │ ^^^^^^ i32 note: - ┌─ ops.fe:49:16 + ┌─ ops.fe:51:16 │ -49 │ x: self.x + p.x, +51 │ x: self.x + p.x, │ ^^^^^^^^^^^^ i32 note: - ┌─ ops.fe:49:25 + ┌─ ops.fe:51:25 │ -49 │ x: self.x + p.x, +51 │ x: self.x + p.x, │ ^ Point note: - ┌─ ops.fe:49:25 + ┌─ ops.fe:51:25 │ -49 │ x: self.x + p.x, +51 │ x: self.x + p.x, │ ^^^ i32 note: - ┌─ ops.fe:50:16 + ┌─ ops.fe:52:16 │ -50 │ y: self.y + p.y, +52 │ y: self.y + p.y, │ ^^^^ Point note: - ┌─ ops.fe:50:16 + ┌─ ops.fe:52:16 │ -50 │ y: self.y + p.y, +52 │ y: self.y + p.y, │ ^^^^^^ i32 note: - ┌─ ops.fe:50:16 + ┌─ ops.fe:52:16 │ -50 │ y: self.y + p.y, +52 │ y: self.y + p.y, │ ^^^^^^^^^^^^ i32 note: - ┌─ ops.fe:50:25 + ┌─ ops.fe:52:25 │ -50 │ y: self.y + p.y, +52 │ y: self.y + p.y, │ ^ Point note: - ┌─ ops.fe:50:25 + ┌─ ops.fe:52:25 │ -50 │ y: self.y + p.y, +52 │ y: self.y + p.y, │ ^^^ i32 note: - ┌─ ops.fe:56:37 + ┌─ ops.fe:58:37 │ -56 │ fn add(self, _ n: i32) -> Point { +58 │ fn add(self, _ n: i32) -> Point { │ ╭─────────────────────────────────────^ -57 │ │ Point { -58 │ │ x: self.x + n, -59 │ │ y: self.y + n, -60 │ │ } -61 │ │ } +59 │ │ Point { +60 │ │ x: self.x + n, +61 │ │ y: self.y + n, +62 │ │ } +63 │ │ } │ ╰─────^ Point note: - ┌─ ops.fe:57:9 + ┌─ ops.fe:59:9 │ -57 │ ╭ Point { -58 │ │ x: self.x + n, -59 │ │ y: self.y + n, -60 │ │ } +59 │ ╭ Point { +60 │ │ x: self.x + n, +61 │ │ y: self.y + n, +62 │ │ } │ ╰─────────^ Point note: - ┌─ ops.fe:58:16 + ┌─ ops.fe:60:16 │ -58 │ x: self.x + n, +60 │ x: self.x + n, │ ^^^^ Point note: - ┌─ ops.fe:58:16 + ┌─ ops.fe:60:16 │ -58 │ x: self.x + n, +60 │ x: self.x + n, │ ^^^^^^ i32 note: - ┌─ ops.fe:58:16 + ┌─ ops.fe:60:16 │ -58 │ x: self.x + n, +60 │ x: self.x + n, │ ^^^^^^^^^^ i32 note: - ┌─ ops.fe:58:25 + ┌─ ops.fe:60:25 │ -58 │ x: self.x + n, +60 │ x: self.x + n, │ ^ i32 note: - ┌─ ops.fe:59:16 + ┌─ ops.fe:61:16 │ -59 │ y: self.y + n, +61 │ y: self.y + n, │ ^^^^ Point note: - ┌─ ops.fe:59:16 + ┌─ ops.fe:61:16 │ -59 │ y: self.y + n, +61 │ y: self.y + n, │ ^^^^^^ i32 note: - ┌─ ops.fe:59:16 + ┌─ ops.fe:61:16 │ -59 │ y: self.y + n, +61 │ y: self.y + n, │ ^^^^^^^^^^ i32 note: - ┌─ ops.fe:59:25 + ┌─ ops.fe:61:25 │ -59 │ y: self.y + n, +61 │ y: self.y + n, │ ^ i32 note: - ┌─ ops.fe:66:47 + ┌─ ops.fe:68:47 │ -66 │ fn index(self, _ i: usize) -> Option { +68 │ fn index(self, _ i: usize) -> Option { │ ╭───────────────────────────────────────────────^ -67 │ │ match i { -68 │ │ 0 => Some(self.x) -69 │ │ 1 => Some(self.y) -70 │ │ _ => None -71 │ │ } -72 │ │ } +69 │ │ match i { +70 │ │ 0 => Some(self.x) +71 │ │ 1 => Some(self.y) +72 │ │ _ => None +73 │ │ } +74 │ │ } │ ╰─────^ Option note: - ┌─ ops.fe:67:9 + ┌─ ops.fe:69:9 │ -67 │ ╭ match i { -68 │ │ 0 => Some(self.x) -69 │ │ 1 => Some(self.y) -70 │ │ _ => None -71 │ │ } +69 │ ╭ match i { +70 │ │ 0 => Some(self.x) +71 │ │ 1 => Some(self.y) +72 │ │ _ => None +73 │ │ } │ ╰─────────^ Option note: - ┌─ ops.fe:67:15 + ┌─ ops.fe:69:15 │ -67 │ match i { +69 │ match i { │ ^ usize note: - ┌─ ops.fe:68:13 + ┌─ ops.fe:70:13 │ -68 │ 0 => Some(self.x) +70 │ 0 => Some(self.x) │ ^ usize note: - ┌─ ops.fe:68:18 + ┌─ ops.fe:70:18 │ -68 │ 0 => Some(self.x) +70 │ 0 => Some(self.x) │ ^^^^ fn Some note: - ┌─ ops.fe:68:18 + ┌─ ops.fe:70:18 │ -68 │ 0 => Some(self.x) +70 │ 0 => Some(self.x) │ ^^^^^^^^^^^^ Option note: - ┌─ ops.fe:68:23 + ┌─ ops.fe:70:23 │ -68 │ 0 => Some(self.x) +70 │ 0 => Some(self.x) │ ^^^^ Point note: - ┌─ ops.fe:68:23 + ┌─ ops.fe:70:23 │ -68 │ 0 => Some(self.x) +70 │ 0 => Some(self.x) │ ^^^^^^ i32 note: - ┌─ ops.fe:69:13 + ┌─ ops.fe:71:13 │ -69 │ 1 => Some(self.y) +71 │ 1 => Some(self.y) │ ^ usize note: - ┌─ ops.fe:69:18 + ┌─ ops.fe:71:18 │ -69 │ 1 => Some(self.y) +71 │ 1 => Some(self.y) │ ^^^^ fn Some note: - ┌─ ops.fe:69:18 + ┌─ ops.fe:71:18 │ -69 │ 1 => Some(self.y) +71 │ 1 => Some(self.y) │ ^^^^^^^^^^^^ Option note: - ┌─ ops.fe:69:23 + ┌─ ops.fe:71:23 │ -69 │ 1 => Some(self.y) +71 │ 1 => Some(self.y) │ ^^^^ Point note: - ┌─ ops.fe:69:23 + ┌─ ops.fe:71:23 │ -69 │ 1 => Some(self.y) +71 │ 1 => Some(self.y) │ ^^^^^^ i32 note: - ┌─ ops.fe:70:13 + ┌─ ops.fe:72:13 │ -70 │ _ => None +72 │ _ => None │ ^ usize note: - ┌─ ops.fe:70:18 + ┌─ ops.fe:72:18 │ -70 │ _ => None +72 │ _ => None │ ^^^^ Option note: - ┌─ ops.fe:75:26 + ┌─ ops.fe:77:26 │ -75 │ fn f(a: Point, b: Point) { +77 │ fn f(a: Point, b: Point) { │ ╭──────────────────────────^ -76 │ │ let c = a + b -77 │ │ let c2 = Add::add(a, b) -78 │ │ // let c3 = Add::add(a, 100) TODO: fix `type annotation is needed` -79 │ │ let d = c + as_i32(100) -80 │ │ let x = d[0] -81 │ │ } +78 │ │ let c = a + b +79 │ │ let c2 = Add::add(a, b) +80 │ │ // let c3 = Add::add(a, 100) TODO: fix `type annotation is needed` +81 │ │ let d = c + as_i32(100) +82 │ │ let x = d[0] +83 │ │ } │ ╰─^ () note: - ┌─ ops.fe:76:9 + ┌─ ops.fe:78:9 │ -76 │ let c = a + b +78 │ let c = a + b │ ^ Point note: - ┌─ ops.fe:76:13 + ┌─ ops.fe:78:13 │ -76 │ let c = a + b +78 │ let c = a + b │ ^ Point note: - ┌─ ops.fe:76:13 + ┌─ ops.fe:78:13 │ -76 │ let c = a + b +78 │ let c = a + b │ ^^^^^ Point note: - ┌─ ops.fe:76:17 + ┌─ ops.fe:78:17 │ -76 │ let c = a + b +78 │ let c = a + b │ ^ Point note: - ┌─ ops.fe:77:9 + ┌─ ops.fe:79:9 │ -77 │ let c2 = Add::add(a, b) +79 │ let c2 = Add::add(a, b) │ ^^ Point note: - ┌─ ops.fe:77:14 + ┌─ ops.fe:79:14 │ -77 │ let c2 = Add::add(a, b) +79 │ let c2 = Add::add(a, b) │ ^^^^^^^^ fn add note: - ┌─ ops.fe:77:14 + ┌─ ops.fe:79:14 │ -77 │ let c2 = Add::add(a, b) +79 │ let c2 = Add::add(a, b) │ ^^^^^^^^^^^^^^ Point note: - ┌─ ops.fe:77:23 + ┌─ ops.fe:79:23 │ -77 │ let c2 = Add::add(a, b) +79 │ let c2 = Add::add(a, b) │ ^ Point note: - ┌─ ops.fe:77:26 + ┌─ ops.fe:79:26 │ -77 │ let c2 = Add::add(a, b) +79 │ let c2 = Add::add(a, b) │ ^ Point note: - ┌─ ops.fe:79:9 + ┌─ ops.fe:81:9 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^ Point note: - ┌─ ops.fe:79:13 + ┌─ ops.fe:81:13 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^ Point note: - ┌─ ops.fe:79:13 + ┌─ ops.fe:81:13 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^^^^^^^^^^^^^^^ Point note: - ┌─ ops.fe:79:17 + ┌─ ops.fe:81:17 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^^^^^^ fn as_i32 note: - ┌─ ops.fe:79:17 + ┌─ ops.fe:81:17 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^^^^^^^^^^^ i32 note: - ┌─ ops.fe:79:24 + ┌─ ops.fe:81:24 │ -79 │ let d = c + as_i32(100) +81 │ let d = c + as_i32(100) │ ^^^ i32 note: - ┌─ ops.fe:80:9 + ┌─ ops.fe:82:9 │ -80 │ let x = d[0] +82 │ let x = d[0] │ ^ Option note: - ┌─ ops.fe:80:13 + ┌─ ops.fe:82:13 │ -80 │ let x = d[0] +82 │ let x = d[0] │ ^ Point note: - ┌─ ops.fe:80:13 + ┌─ ops.fe:82:13 │ -80 │ let x = d[0] +82 │ let x = d[0] │ ^^^^ Option note: - ┌─ ops.fe:80:15 + ┌─ ops.fe:82:15 │ -80 │ let x = d[0] +82 │ let x = d[0] │ ^ usize note: - ┌─ ops.fe:83:28 + ┌─ ops.fe:85:28 │ -83 │ fn as_i32(_ x: i32) -> i32 { x } +85 │ fn as_i32(_ x: i32) -> i32 { x } │ ^^^^^ i32 note: - ┌─ ops.fe:83:30 + ┌─ ops.fe:85:30 │ -83 │ fn as_i32(_ x: i32) -> i32 { x } +85 │ fn as_i32(_ x: i32) -> i32 { x } │ ^ i32 diff --git a/crates/mir/src/lower/expr.rs b/crates/mir/src/lower/expr.rs index ebde1fe998..a6705fd3ec 100644 --- a/crates/mir/src/lower/expr.rs +++ b/crates/mir/src/lower/expr.rs @@ -22,7 +22,7 @@ use hir::analysis::{ place::PlaceBase, ty::ty_check::{EffectArg, EffectPassMode}, }; -use hir::hir_def::expr::{ArithBinOp, BinOp}; +use hir::hir_def::expr::{ArithBinOp, BinOp, UnOp}; enum RootLvalue<'db> { Place(Place<'db>), @@ -221,9 +221,17 @@ impl<'db, 'a> MirBuilder<'db, 'a> { let _ = call_args; self.lower_call_expr(expr) } - Partial::Present(Expr::Un(inner, _)) => { - let _ = self.lower_expr(*inner); - self.ensure_value(expr) + Partial::Present(Expr::Un(inner, op)) => { + let _ = inner; + match op { + UnOp::Minus if self.typed_body.callable_expr(expr).is_some() => { + self.lower_call_expr(expr) + } + _ => { + let _ = self.lower_expr(*inner); + self.ensure_value(expr) + } + } } Partial::Present(Expr::Cast(inner, _)) => { let _ = self.lower_expr(*inner); @@ -236,11 +244,26 @@ impl<'db, 'a> MirBuilder<'db, 'a> { // Desugar range expression `start..end` into Range struct construction self.lower_range_expr(expr, *lhs, *rhs) } - Partial::Present(Expr::Bin(lhs, rhs, _)) => { - let _ = self.lower_expr(*lhs); - let _ = self.lower_expr(*rhs); - self.ensure_value(expr) - } + Partial::Present(Expr::Bin(lhs, rhs, op)) => match op { + BinOp::Arith( + ArithBinOp::Add + | ArithBinOp::Sub + | ArithBinOp::Mul + | ArithBinOp::Div + | ArithBinOp::Rem + | ArithBinOp::Pow, + ) + | BinOp::Comp(..) + if self.typed_body.callable_expr(expr).is_some() => + { + self.lower_call_expr(expr) + } + _ => { + let _ = self.lower_expr(*lhs); + let _ = self.lower_expr(*rhs); + self.ensure_value(expr) + } + }, Partial::Present(Expr::Field(lhs, field_index)) => { self.lower_field_expr(expr, *lhs, *field_index) } @@ -424,7 +447,7 @@ impl<'db, 'a> MirBuilder<'db, 'a> { } let mut receiver_space = None; - if self.is_method_call(expr) && !args.is_empty() { + if !args.is_empty() { let needs_space = callable .callable_def .receiver_ty(self.db) @@ -1350,6 +1373,155 @@ impl<'db, 'a> MirBuilder<'db, 'a> { self.store_to_root_lvalue(Some(stmt_id), root_lvalue, stored); } + fn lower_arith_aug_assign_via_trait_call( + &mut self, + stmt_id: StmtId, + expr: ExprId, + target: ExprId, + rhs_value: ValueId, + op: hir::hir_def::expr::ArithBinOp, + ) -> bool { + use hir::hir_def::expr::ArithBinOp; + + if !matches!( + op, + ArithBinOp::Add + | ArithBinOp::Sub + | ArithBinOp::Mul + | ArithBinOp::Div + | ArithBinOp::Rem + | ArithBinOp::Pow + ) { + return false; + } + + let Some(mut callable) = self.typed_body.callable_expr(expr).cloned() else { + return false; + }; + + let peeled_target = self.peel_transparent_newtype_field0_lvalue(target); + let is_peeled = peeled_target != target; + + let root_expr = if is_peeled { peeled_target } else { target }; + let root_ty = self.typed_body.expr_ty(self.db, root_expr); + let lhs_ty = self.typed_body.expr_ty(self.db, target); + + let Some(root_lvalue) = self.root_lvalue_for_expr(root_expr) else { + return true; + }; + + let lhs_value = match &root_lvalue { + RootLvalue::Place(place) => { + let loaded_local = self.alloc_temp_local(lhs_ty, false, "load"); + self.builder.body.locals[loaded_local.index()].address_space = + self.expr_address_space(target); + self.assign( + None, + Some(loaded_local), + Rvalue::Load { + place: place.clone(), + }, + ); + self.alloc_value(lhs_ty, ValueOrigin::Local(loaded_local), ValueRepr::Word) + } + RootLvalue::Local(local) => { + if is_peeled { + let base_value = self.alloc_value( + root_ty, + ValueOrigin::Local(*local), + self.value_repr_for_ty( + root_ty, + self.builder.body.local(*local).address_space, + ), + ); + self.alloc_value( + lhs_ty, + ValueOrigin::TransparentCast { value: base_value }, + ValueRepr::Word, + ) + } else { + self.alloc_value(lhs_ty, ValueOrigin::Local(*local), ValueRepr::Word) + } + } + }; + + let args = vec![lhs_value, rhs_value]; + + let mut receiver_space = None; + if !args.is_empty() { + let needs_space = callable + .callable_def + .receiver_ty(self.db) + .is_some_and(|binder| { + let ty = binder.instantiate_identity(); + self.value_repr_for_ty(ty, AddressSpaceKind::Memory) + .address_space() + .is_some() + }); + if needs_space { + receiver_space = Some(self.value_address_space(args[0])); + } + } + + let mut effect_args = Vec::new(); + let mut effect_writebacks: Vec<(LocalId, Place<'db>)> = Vec::new(); + if let CallableDef::Func(func_def) = callable.callable_def + && func_def.has_effects(self.db) + && extract_contract_function(self.db, func_def).is_none() + && let Some(resolved) = self.typed_body.call_effect_args(expr) + { + self.finalize_place_effect_provider_args_for_call(func_def, &mut callable, resolved); + for resolved_arg in resolved { + let value = self.lower_effect_arg(resolved_arg, &mut effect_writebacks); + effect_args.push(value); + } + } + + let result_space = self + .effect_provider_space_for_provider_ty(lhs_ty) + .unwrap_or_else(|| self.expr_address_space(target)); + let dest = self.alloc_temp_local(lhs_ty, false, "aug"); + self.builder.body.locals[dest.index()].address_space = result_space; + + let hir_target = crate::ir::HirCallTarget { + callable_def: callable.callable_def, + generic_args: callable.generic_args().to_vec(), + trait_inst: callable.trait_inst(), + }; + let call_origin = CallOrigin { + expr: Some(expr), + hir_target: Some(hir_target), + args, + effect_args, + resolved_name: None, + receiver_space, + }; + + self.assign(Some(stmt_id), Some(dest), Rvalue::Call(call_origin)); + for (writeback_local, place) in effect_writebacks { + self.assign(None, Some(writeback_local), Rvalue::Load { place }); + } + + let updated = self.alloc_value( + lhs_ty, + ValueOrigin::Local(dest), + self.value_repr_for_ty(lhs_ty, result_space), + ); + + let stored = if is_peeled { + self.alloc_value( + root_ty, + ValueOrigin::TransparentCast { value: updated }, + self.value_repr_for_expr(root_expr, root_ty), + ) + } else { + updated + }; + + self.store_to_root_lvalue(Some(stmt_id), root_lvalue, stored); + true + } + /// Lowers a statement in the current block. /// /// # Parameters @@ -2228,7 +2400,11 @@ impl<'db, 'a> MirBuilder<'db, 'a> { } } - self.lower_aug_assign_to_lvalue(stmt_id, *target, value_id, *op); + if !self + .lower_arith_aug_assign_via_trait_call(stmt_id, expr, *target, value_id, *op) + { + self.lower_aug_assign_to_lvalue(stmt_id, *target, value_id, *op); + } } _ => { self.move_to_block(block); diff --git a/crates/mir/src/lower/intrinsics.rs b/crates/mir/src/lower/intrinsics.rs index ee24924066..128511e800 100644 --- a/crates/mir/src/lower/intrinsics.rs +++ b/crates/mir/src/lower/intrinsics.rs @@ -8,6 +8,23 @@ impl<'db, 'a> MirBuilder<'db, 'a> { Some(self.typed_body.callable_expr(expr)?.callable_def) } + fn is_panic_like_call(&self, func_def: CallableDef<'db>) -> bool { + match func_def.ingot(self.db).kind(self.db) { + IngotKind::Core | IngotKind::Std => {} + _ => return false, + } + let CallableDef::Func(func) = func_def else { + return false; + }; + if func.body(self.db).is_some() { + return false; + } + let Some(name) = func_def.name(self.db) else { + return false; + }; + matches!(name.data(self.db).as_str(), "panic" | "todo") + } + /// Attempts to lower a statement-only intrinsic call (`mstore`, `codecopy`, etc.). /// /// # Parameters @@ -19,6 +36,22 @@ impl<'db, 'a> MirBuilder<'db, 'a> { if self.current_block().is_none() { return Some(self.ensure_value(expr)); } + + // `core::panic()` / `core::todo()` are modeled as terminating calls in MIR. + let callable_def = self.callable_def_for_call_expr(expr)?; + if self.is_panic_like_call(callable_def) { + let value_id = self.ensure_value(expr); + let u256_ty = TyId::new(self.db, TyData::TyBase(TyBase::Prim(PrimTy::U256))); + let zero = self.alloc_synthetic_value(u256_ty, SyntheticValue::Int(BigUint::from(0u8))); + self.set_current_terminator(Terminator::TerminatingCall( + crate::ir::TerminatingCall::Intrinsic { + op: IntrinsicOp::Revert, + args: vec![zero, zero], + }, + )); + return Some(value_id); + } + let (op, args) = self.intrinsic_stmt_args(expr)?; let value_id = self.ensure_value(expr); if matches!(op, IntrinsicOp::ReturnData | IntrinsicOp::Revert) { diff --git a/crates/mir/src/lower/prepass.rs b/crates/mir/src/lower/prepass.rs index 7321209e9a..af05d9a8d3 100644 --- a/crates/mir/src/lower/prepass.rs +++ b/crates/mir/src/lower/prepass.rs @@ -213,6 +213,20 @@ impl<'db, 'a> MirBuilder<'db, 'a> { } Some((args, arg_exprs)) } + Expr::Bin(lhs, rhs, _) => { + let mut args = Vec::with_capacity(2); + let mut arg_exprs = Vec::with_capacity(2); + arg_exprs.push(*lhs); + args.push(self.lower_expr(*lhs)); + arg_exprs.push(*rhs); + args.push(self.lower_expr(*rhs)); + Some((args, arg_exprs)) + } + Expr::Un(inner, _) => { + let arg_exprs = vec![*inner]; + let args = vec![self.lower_expr(*inner)]; + Some((args, arg_exprs)) + } _ => None, } } diff --git a/crates/mir/tests/fixtures/abi_decode.mir.snap b/crates/mir/tests/fixtures/abi_decode.mir.snap index 068498bee7..074261ea09 100644 --- a/crates/mir/tests/fixtures/abi_decode.mir.snap +++ b/crates/mir/tests/fixtures/abi_decode.mir.snap @@ -1,6 +1,5 @@ --- source: crates/mir/tests/lowering_snapshots.rs -assertion_line: 26 expression: mir_output --- fn decode_u256() -> u256: @@ -34,24 +33,59 @@ fn soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e(v0: SolDecoder< v2: CallData = load mem[&mem[v0].0].0 v1: u256 = calldata_byteinput_len(v2) v4: u256 = load mem[&mem[v0].0].1 - v3: u256 = (v4 + 32) - v5: u256 = load mem[&mem[v0].0].1 - br ((v3 < v5) || (v3 > v1)) bb1 bb2 + v5: u256 = u256_add_add(v4, 32) + v3: u256 = v5 + v6: u256 = load mem[&mem[v0].0].1 + v7: bool = u256_ord_lt(v3, v6) + v8: bool = u256_ord_gt(v3, v1) + br (v7 || v8) bb1 bb2 bb1: terminate revert(0, 0) bb2: - v7: CallData = load mem[&mem[v0].0].0 - v8: u256 = load mem[&mem[v0].0].1 - v6: u256 = calldata_byteinput_word_at(v7, v8) + v10: CallData = load mem[&mem[v0].0].0 + v11: u256 = load mem[&mem[v0].0].1 + v9: u256 = calldata_byteinput_word_at(v10, v11) store mem[&mem[v0].0].1 = v3 - ret v6 + ret v9 fn calldata_byteinput_len(v0: CallData) -> u256: bb0: v1: u256 = calldatasize() - ret (v1 - v0) + v2: u256 = u256_sub_sub(v1, v0) + ret v2 -fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: +fn u256_add_add(v0: u256, v1: u256) -> u256: + bb0: + v2: u256 = __add_u256(v0, v1) + v3: bool = __lt_u256(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + +fn u256_ord_lt(v0: u256, v1: u256) -> bool: bb0: - v2: u256 = calldataload((v0 + v1)) + v2: bool = __lt_u256(v0, v1) ret v2 + +fn u256_ord_gt(v0: u256, v1: u256) -> bool: + bb0: + v2: bool = __gt_u256(v0, v1) + ret v2 + +fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: + bb0: + v2: u256 = u256_add_add(v0, v1) + v3: u256 = calldataload(v2) + ret v3 + +fn u256_sub_sub(v0: u256, v1: u256) -> u256: + bb0: + v2: bool = __gt_u256(v1, v0) + br v2 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + v3: u256 = __sub_u256(v0, v1) + ret v3 diff --git a/crates/mir/tests/fixtures/echo.mir.snap b/crates/mir/tests/fixtures/echo.mir.snap index 0c65329508..71080b0f4e 100644 --- a/crates/mir/tests/fixtures/echo.mir.snap +++ b/crates/mir/tests/fixtures/echo.mir.snap @@ -90,12 +90,14 @@ fn evm_contracthost_abort(v0: Evm) -> !: fn alloc(v0: u256) -> u256: bb0: v1: u256 = mload(64) - br (v1 == 0) bb1 bb2 + v2: bool = u256_eq_eq(v1, 0) + br v2 bb1 bb2 bb1: v1 = 128 jmp bb2 bb2: - eval mstore(64, (v1 + v0)) + v3: u256 = u256_add_add(v1, v0) + eval mstore(64, v3) ret v1 fn sol_abi_decoder_new__MemoryBytes__b48f82585f3ed728(v0: I) -> SolDecoder: @@ -117,13 +119,14 @@ fn runtime_selector__Evm_Sol__2533f5c49b57a682(v0: Self) -> u32: bb0: v1: CallData = evm_contracthost_input(v0) v2: u256 = calldata_byteinput_len(v1) - br (v2 < 4) bb1 bb2 + v3: bool = u256_ord_lt(v2, 4) + br v3 bb1 bb2 bb1: terminate evm_contracthost_abort(v0) bb2: - v3: u256 = calldata_byteinput_word_at(v1, 0) - v4: u32 = sol_abi_selector_from_prefix(v3) - ret v4 + v4: u256 = calldata_byteinput_word_at(v1, 0) + v5: u32 = sol_abi_selector_from_prefix(v4) + ret v5 fn runtime_decoder__Evm_Sol__2533f5c49b57a682(v0: Self) -> SolDecoder: bb0: @@ -155,6 +158,21 @@ fn getx_decode_decode__SolDecoder_CallData___c1e4510fd444b966(v0: D) -> GetX: bb0: ret +fn u256_eq_eq(v0: u256, v1: u256) -> bool: + bb0: + v2: bool = __eq_u256(v0, v1) + ret v2 + +fn u256_add_add(v0: u256, v1: u256) -> u256: + bb0: + v2: u256 = __add_u256(v0, v1) + v3: bool = __lt_u256(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + fn soldecoder_i__new__MemoryBytes__b48f82585f3ed728(v0: I) -> SolDecoder: bb0: v1: Cursor = cursor_i__new__MemoryBytes__b48f82585f3ed728(v0) @@ -167,16 +185,19 @@ fn soldecoder_i__abidecoder_read_word__MemoryBytes__b48f82585f3ed728(v0: SolDeco bb0: v1: u256 = memorybytes_byteinput_len(&mem[&mem[v0].0].0) v3: u256 = load mem[&mem[v0].0].1 - v2: u256 = (v3 + 32) - v4: u256 = load mem[&mem[v0].0].1 - br ((v2 < v4) || (v2 > v1)) bb1 bb2 + v4: u256 = u256_add_add(v3, 32) + v2: u256 = v4 + v5: u256 = load mem[&mem[v0].0].1 + v6: bool = u256_ord_lt(v2, v5) + v7: bool = u256_ord_gt(v2, v1) + br (v6 || v7) bb1 bb2 bb1: terminate revert(0, 0) bb2: - v6: u256 = load mem[&mem[v0].0].1 - v5: u256 = memorybytes_byteinput_word_at(&mem[&mem[v0].0].0, v6) + v9: u256 = load mem[&mem[v0].0].1 + v8: u256 = memorybytes_byteinput_word_at(&mem[&mem[v0].0].0, v9) store mem[&mem[v0].0].1 = v2 - ret v5 + ret v8 fn stor_ptr__Evm_Foo__c3089d776807df8e(v0: Self, v1: u256) -> StorPtr: bb0: @@ -190,13 +211,20 @@ fn evm_contracthost_input(v0: Evm) -> CallData: fn calldata_byteinput_len(v0: CallData) -> u256: bb0: v1: u256 = calldatasize() - ret (v1 - v0) + v2: u256 = u256_sub_sub(v1, v0) + ret v2 -fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: +fn u256_ord_lt(v0: u256, v1: u256) -> bool: bb0: - v2: u256 = calldataload((v0 + v1)) + v2: bool = __lt_u256(v0, v1) ret v2 +fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: + bb0: + v2: u256 = u256_add_add(v0, v1) + v3: u256 = calldataload(v2) + ret v3 + fn sol_abi_selector_from_prefix(v0: u256) -> u32: bb0: v1: u32 = s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35((v0 >> 224)) @@ -227,9 +255,10 @@ fn solencoder_abiencoder_finish(v0: SolEncoder) -> (u256, u256): bb0: v1: u256 = load mem[v0].0 v2: u256 = load mem[v0].2 - v3: (u256, u256) = alloc mem - init mem[v3] { .0 = v1, .1 = (v2 - v1) } - ret v3 + v3: u256 = u256_sub_sub(v2, v1) + v4: (u256, u256) = alloc mem + init mem[v4] { .0 = v1, .1 = v3 } + ret v4 fn evm_contracthost_return_bytes(v0: Evm, v1: u256, v2: u256) -> !: bb0: @@ -252,16 +281,32 @@ fn memorybytes_byteinput_len(v0: MemoryBytes) -> u256: v1: u256 = load mem[v0].1 ret v1 +fn u256_ord_gt(v0: u256, v1: u256) -> bool: + bb0: + v2: bool = __gt_u256(v0, v1) + ret v2 + fn memorybytes_byteinput_word_at(v0: MemoryBytes, v1: u256) -> u256: bb0: v2: u256 = load mem[v0].0 - v3: u256 = mload((v2 + v1)) - ret v3 + v3: u256 = u256_add_add(v2, v1) + v4: u256 = mload(v3) + ret v4 fn storptr_t__effectref_from_raw__Foo__668f96d7165c93b(v0: u256) -> StorPtr: bb0: ret v0 +fn u256_sub_sub(v0: u256, v1: u256) -> u256: + bb0: + v2: bool = __gt_u256(v1, v0) + br v2 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + v3: u256 = __sub_u256(v0, v1) + ret v3 + fn s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35(v0: S) -> u32: bb0: v1: u256 = u256_intword_to_word(v0) @@ -287,12 +332,14 @@ fn solencoder_new() -> SolEncoder: fn solencoder_ensure_init_mem(v0: SolEncoder, v1: u256) -> (): bb0: v2: u256 = load mem[v0].0 - br (v2 == 0) bb1 bb2 + v3: bool = u256_eq_eq(v2, 0) + br v3 bb1 bb2 bb1: - v3: u256 = alloc(v1) - store mem[v0].0 = v3 - store mem[v0].1 = v3 - store mem[v0].2 = (v3 + v1) + v4: u256 = alloc(v1) + store mem[v0].0 = v4 + store mem[v0].1 = v4 + v5: u256 = u256_add_add(v4, v1) + store mem[v0].2 = v5 jmp bb2 bb2: ret @@ -301,7 +348,8 @@ fn solencoder_abiencoder_write_word(v0: SolEncoder, v1: u256) -> (): bb0: v2: u256 = load mem[v0].1 eval mstore(v2, v1) - store mem[v0].1 = (v2 + 32) + v3: u256 = u256_add_add(v2, 32) + store mem[v0].1 = v3 ret fn soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e(v0: SolDecoder) -> u256: @@ -309,17 +357,20 @@ fn soldecoder_i__abidecoder_read_word__CallData__b9ab8dc8a4b2f9e(v0: SolDecoder< v2: CallData = load mem[&mem[v0].0].0 v1: u256 = calldata_byteinput_len(v2) v4: u256 = load mem[&mem[v0].0].1 - v3: u256 = (v4 + 32) - v5: u256 = load mem[&mem[v0].0].1 - br ((v3 < v5) || (v3 > v1)) bb1 bb2 + v5: u256 = u256_add_add(v4, 32) + v3: u256 = v5 + v6: u256 = load mem[&mem[v0].0].1 + v7: bool = u256_ord_lt(v3, v6) + v8: bool = u256_ord_gt(v3, v1) + br (v7 || v8) bb1 bb2 bb1: terminate revert(0, 0) bb2: - v7: CallData = load mem[&mem[v0].0].0 - v8: u256 = load mem[&mem[v0].0].1 - v6: u256 = calldata_byteinput_word_at(v7, v8) + v10: CallData = load mem[&mem[v0].0].0 + v11: u256 = load mem[&mem[v0].0].1 + v9: u256 = calldata_byteinput_word_at(v10, v11) store mem[&mem[v0].0].1 = v3 - ret v6 + ret v9 fn u256_intword_to_word(v0: u256) -> u256: bb0: diff --git a/crates/mir/tests/fixtures/for_continue.mir.snap b/crates/mir/tests/fixtures/for_continue.mir.snap index 99279d2dc6..d09ee3f83b 100644 --- a/crates/mir/tests/fixtures/for_continue.mir.snap +++ b/crates/mir/tests/fixtures/for_continue.mir.snap @@ -13,7 +13,8 @@ fn for_continue() -> usize: bb2: v3: usize = range_known_const_s__usize___known_const_e__usize___seq_get__0_4__f9605efabd18106c((), v1) v4: usize = v3 - br (v4 == 2) bb5 bb6 + v5: bool = usize_eq_eq(v4, 2) + br v5 bb5 bb6 bb3: v1 = (v1 + 1) jmp bb1 @@ -22,22 +23,56 @@ fn for_continue() -> usize: bb5: jmp bb3 bb6: - v0 = (v0 + 1) + v6: usize = usize_add_add(v0, 1) + v0 = v6 jmp bb3 fn range_known_const_s__usize___known_const_e__usize___seq_len__0_4__f9605efabd18106c(v0: Range, Known>) -> usize: bb0: - v1: usize = 0 - switch (4 < 0) [true => bb1, false => bb2] else bb2 + v1: bool = usize_ord_lt(4, 0) + v2: usize = 0 + switch v1 [true => bb1, false => bb2] else bb2 bb1: - v1 = 0 + v2 = 0 jmp bb3 bb2: - v1 = (4 - 0) + v3: usize = usize_sub_sub(4, 0) + v2 = v3 jmp bb3 bb3: - ret v1 + ret v2 fn range_known_const_s__usize___known_const_e__usize___seq_get__0_4__f9605efabd18106c(v0: Range, Known>, v1: usize) -> usize: bb0: - ret (0 + v1) + v2: usize = usize_add_add(0, v1) + ret v2 + +fn usize_eq_eq(v0: usize, v1: usize) -> bool: + bb0: + v2: bool = __eq_usize(v0, v1) + ret v2 + +fn usize_add_add(v0: usize, v1: usize) -> usize: + bb0: + v2: usize = __add_usize(v0, v1) + v3: bool = __lt_usize(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + +fn usize_ord_lt(v0: usize, v1: usize) -> bool: + bb0: + v2: bool = __lt_usize(v0, v1) + ret v2 + +fn usize_sub_sub(v0: usize, v1: usize) -> usize: + bb0: + v2: bool = __gt_usize(v1, v0) + br v2 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + v3: usize = __sub_usize(v0, v1) + ret v3 diff --git a/crates/mir/tests/fixtures/kitchen_sink.mir.snap b/crates/mir/tests/fixtures/kitchen_sink.mir.snap index 4e0676c046..16ed5beb37 100644 --- a/crates/mir/tests/fixtures/kitchen_sink.mir.snap +++ b/crates/mir/tests/fixtures/kitchen_sink.mir.snap @@ -1,14 +1,15 @@ --- source: crates/mir/tests/lowering_snapshots.rs -assertion_line: 26 expression: mir_output --- fn bump__StorPtr_Foo___3698cc3c4b2626e3(v0: u256) -> (): bb0: v1: u256 = load stor[v0].0 - store stor[v0].0 = (v1 + 1) - v2: u256 = load stor[v0].1 - store stor[v0].1 = (v2 + 2) + v2: u256 = u256_add_add(v1, 1) + store stor[v0].0 = v2 + v3: u256 = load stor[v0].1 + v4: u256 = u256_add_add(v3, 2) + store stor[v0].1 = v4 ret fn maybe_inc(v0: Opt) -> u64: @@ -37,7 +38,8 @@ fn maybe_inc(v0: Opt) -> u64: v6 = (v6 + 1) jmp bb6 bb8: - ret (v1 + v6) + v7: u64 = u64_add_add(v1, v6) + ret v7 fn test_effects_and_aggregates(v0: u256, v1: u256) -> (): bb0: @@ -88,6 +90,26 @@ fn selector_match(v0: u256) -> u256: bb5: switch v1 [151146943 => bb1, 2066295049 => bb3] else bb4 +fn u256_add_add(v0: u256, v1: u256) -> u256: + bb0: + v2: u256 = __add_u256(v0, v1) + v3: bool = __lt_u256(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + +fn u64_add_add(v0: u64, v1: u64) -> u64: + bb0: + v2: u64 = __add_u64(v0, v1) + v3: bool = __lt_u64(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + fn mem_ptr__mem__RawMem_Foo__19292a5189c12f79(v0: Self, v1: u256) -> MemPtr: bb0: v2: MemPtr = memptr_t__effectref_from_raw__Foo__668f96d7165c93b(v1) @@ -96,9 +118,11 @@ fn mem_ptr__mem__RawMem_Foo__19292a5189c12f79(v0: Self, v1: u256) -> MemPtr fn bump__MemPtr_Foo___cdd30998d577b6ea(v0: u256) -> (): bb0: v1: u256 = load mem[v0].0 - store mem[v0].0 = (v1 + 1) - v2: u256 = load mem[v0].1 - store mem[v0].1 = (v2 + 2) + v2: u256 = u256_add_add(v1, 1) + store mem[v0].0 = v2 + v3: u256 = load mem[v0].1 + v4: u256 = u256_add_add(v3, 2) + store mem[v0].1 = v4 ret fn stor_ptr__sto__RawStorage_Foo__b1b82e9d4c608c02(v0: Self, v1: u256) -> StorPtr: @@ -155,9 +179,11 @@ fn storagemap_storage_slot_with_salt__u256__3271ca15373d4483(v0: K, v1: u256) -> bb0: v2: u256 = mload(64) v3: u256 = u256_storagekey_write_key(v2, v0) - eval mstore((v2 + v3), v1) - v4: u256 = keccak256(v2, (v3 + 32)) - ret v4 + v4: u256 = u256_add_add(v2, v3) + eval mstore(v4, v1) + v5: u256 = u256_add_add(v3, 32) + v6: u256 = keccak256(v2, v5) + ret v6 fn u256_storagekey_write_key(v0: u256, v1: u256) -> u256: bb0: diff --git a/crates/mir/tests/fixtures/transient_field_effects.mir.snap b/crates/mir/tests/fixtures/transient_field_effects.mir.snap index b8535084b8..b371517106 100644 --- a/crates/mir/tests/fixtures/transient_field_effects.mir.snap +++ b/crates/mir/tests/fixtures/transient_field_effects.mir.snap @@ -5,7 +5,8 @@ expression: mir_output fn bump_lock__StorPtr_u256___64779554cfbf0358(v0: u256) -> (): bb0: v1: u256 = load stor[v0] - store stor[v0] = (v1 + 1) + v2: u256 = u256_addassign_add_assign(v1, 1) + store stor[v0] = v2 ret fn __Guard_init_contract(v0: u256) -> (): @@ -64,10 +65,16 @@ fn __Guard_init_code_len() -> u256: v0: u256 = code_region_len(func_item(__Guard_init)) ret v0 +fn u256_addassign_add_assign(v0: u256, v1: u256) -> u256: + bb0: + v2: u256 = u256_add_add(v0, v1) + ret v2 + fn bump_lock__TStorPtr_u256___e67d09080c0f1393(v0: u256) -> (): bb0: v1: u256 = load tstor[v0] - store tstor[v0] = (v1 + 1) + v2: u256 = u256_addassign_add_assign(v1, 1) + store tstor[v0] = v2 ret fn tstorptr_t__effectref_from_raw__u256__3271ca15373d4483(v0: u256) -> TStorPtr: @@ -81,12 +88,14 @@ fn evm_contracthost_abort(v0: Evm) -> !: fn alloc(v0: u256) -> u256: bb0: v1: u256 = mload(64) - br (v1 == 0) bb1 bb2 + v2: bool = u256_eq_eq(v1, 0) + br v2 bb1 bb2 bb1: v1 = 128 jmp bb2 bb2: - eval mstore(64, (v1 + v0)) + v3: u256 = u256_add_add(v1, v0) + eval mstore(64, v3) ret v1 fn sol_abi_decoder_new__MemoryBytes__b48f82585f3ed728(v0: I) -> SolDecoder: @@ -98,13 +107,14 @@ fn runtime_selector__Evm_Sol__2533f5c49b57a682(v0: Self) -> u32: bb0: v1: CallData = evm_contracthost_input(v0) v2: u256 = calldata_byteinput_len(v1) - br (v2 < 4) bb1 bb2 + v3: bool = u256_ord_lt(v2, 4) + br v3 bb1 bb2 bb1: terminate evm_contracthost_abort(v0) bb2: - v3: u256 = calldata_byteinput_word_at(v1, 0) - v4: u32 = sol_abi_selector_from_prefix(v3) - ret v4 + v4: u256 = calldata_byteinput_word_at(v1, 0) + v5: u32 = sol_abi_selector_from_prefix(v4) + ret v5 fn runtime_decoder__Evm_Sol__2533f5c49b57a682(v0: Self) -> SolDecoder: bb0: @@ -120,6 +130,21 @@ fn return_unit__Evm__1572a3ac6cae1070(v0: Self) -> !: bb0: terminate evm_contracthost_return_bytes(v0, 0, 0) +fn u256_add_add(v0: u256, v1: u256) -> u256: + bb0: + v2: u256 = __add_u256(v0, v1) + v3: bool = __lt_u256(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + +fn u256_eq_eq(v0: u256, v1: u256) -> bool: + bb0: + v2: bool = __eq_u256(v0, v1) + ret v2 + fn soldecoder_i__new__MemoryBytes__b48f82585f3ed728(v0: I) -> SolDecoder: bb0: v1: Cursor = cursor_i__new__MemoryBytes__b48f82585f3ed728(v0) @@ -135,13 +160,20 @@ fn evm_contracthost_input(v0: Evm) -> CallData: fn calldata_byteinput_len(v0: CallData) -> u256: bb0: v1: u256 = calldatasize() - ret (v1 - v0) + v2: u256 = u256_sub_sub(v1, v0) + ret v2 -fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: +fn u256_ord_lt(v0: u256, v1: u256) -> bool: bb0: - v2: u256 = calldataload((v0 + v1)) + v2: bool = __lt_u256(v0, v1) ret v2 +fn calldata_byteinput_word_at(v0: CallData, v1: u256) -> u256: + bb0: + v2: u256 = u256_add_add(v0, v1) + v3: u256 = calldataload(v2) + ret v3 + fn sol_abi_selector_from_prefix(v0: u256) -> u32: bb0: v1: u32 = s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35((v0 >> 224)) @@ -163,6 +195,16 @@ fn cursor_i__new__MemoryBytes__b48f82585f3ed728(v0: I) -> Cursor: v1: Cursor = v2 ret v1 +fn u256_sub_sub(v0: u256, v1: u256) -> u256: + bb0: + v2: bool = __gt_u256(v1, v0) + br v2 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + v3: u256 = __sub_u256(v0, v1) + ret v3 + fn s_intdowncast_downcast_unchecked__u256_u32__6e3637cd3e911b35(v0: S) -> u32: bb0: v1: u256 = u256_intword_to_word(v0) diff --git a/crates/mir/tests/fixtures/while_cond_call.mir.snap b/crates/mir/tests/fixtures/while_cond_call.mir.snap index a98f49d3c7..4c62d5dcdf 100644 --- a/crates/mir/tests/fixtures/while_cond_call.mir.snap +++ b/crates/mir/tests/fixtures/while_cond_call.mir.snap @@ -4,7 +4,8 @@ expression: mir_output --- fn lt_ten(v0: u64) -> bool: bb0: - ret (v0 < 10) + v1: bool = u64_ord_lt(v0, 10) + ret v1 fn while_cond_call() -> u64: bb0: @@ -14,7 +15,23 @@ fn while_cond_call() -> u64: v1: bool = lt_ten(v0) br v1 bb2 bb3 bb2: - v0 = (v0 + 1) + v2: u64 = u64_add_add(v0, 1) + v0 = v2 jmp bb1 bb3: ret v0 + +fn u64_ord_lt(v0: u64, v1: u64) -> bool: + bb0: + v2: bool = __lt_u64(v0, v1) + ret v2 + +fn u64_add_add(v0: u64, v1: u64) -> u64: + bb0: + v2: u64 = __add_u64(v0, v1) + v3: bool = __lt_u64(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 diff --git a/crates/mir/tests/fixtures/zst_load_index_eval.mir.snap b/crates/mir/tests/fixtures/zst_load_index_eval.mir.snap index f2b5533e69..92ab8871fa 100644 --- a/crates/mir/tests/fixtures/zst_load_index_eval.mir.snap +++ b/crates/mir/tests/fixtures/zst_load_index_eval.mir.snap @@ -5,9 +5,10 @@ expression: mir_output fn next_idx__StorPtr_Counter___3ec10ccd701e934a(v0: u256) -> usize: bb0: v1: Counter = load stor[v0] - store stor[v0] = (v1 + 1) - v2: Counter = load stor[v0] - ret v2 + v2: usize = usize_add_add(v1, 1) + store stor[v0] = v2 + v3: Counter = load stor[v0] + ret v3 fn zst_load_index_eval() -> usize: bb0: @@ -20,9 +21,20 @@ fn zst_load_index_eval() -> usize: eval v6 ret v3 +fn usize_add_add(v0: usize, v1: usize) -> usize: + bb0: + v2: usize = __add_usize(v0, v1) + v3: bool = __lt_usize(v2, v0) + br v3 bb1 bb2 + bb1: + terminate revert(0, 0) + bb2: + ret v2 + fn next_idx__MemPtr_Counter___f160915c54d975dc(v0: u256) -> usize: bb0: v1: Counter = load mem[v0] - store mem[v0] = (v1 + 1) - v2: Counter = load mem[v0] - ret v2 + v2: usize = usize_add_add(v1, 1) + store mem[v0] = v2 + v3: Counter = load mem[v0] + ret v3 diff --git a/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.fe b/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.fe new file mode 100644 index 0000000000..04b7e3bf36 --- /dev/null +++ b/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.fe @@ -0,0 +1,25 @@ +const OK: u8 = 200 + 1 +const ADD_OVERFLOW: u8 = 255 + 1 +const SUB_UNDERFLOW: u8 = 0 - 1 +const DIV_BY_ZERO: u8 = 1 / 0 +const REM_BY_ZERO: u8 = 1 % 0 +const POW_OVERFLOW: u8 = 2 ** 8 + +const POW_NEGATIVE_EXP: i8 = 2 ** -1 +const NEG_OVERFLOW: i8 = -(-128) +const DIV_OVERFLOW: i8 = (-128) / (-1) + +struct U8 {} +struct I8 {} + +fn ok(_ x: U8) {} + +fn add_overflow_u8(_ x: U8) {} +fn sub_underflow_u8(_ x: U8) {} +fn div_by_zero_u8(_ x: U8) {} +fn rem_by_zero_u8(_ x: U8) {} +fn pow_overflow_u8(_ x: U8) {} + +fn pow_negative_exp_i8(_ x: I8) {} +fn neg_overflow_i8(_ x: I8) {} +fn div_overflow_i8(_ x: I8) {} diff --git a/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.snap b/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.snap new file mode 100644 index 0000000000..72b56f8735 --- /dev/null +++ b/crates/uitest/fixtures/ty_check/const_checked_arithmetic_overflow.snap @@ -0,0 +1,53 @@ +--- +source: crates/uitest/tests/ty_check.rs +assertion_line: 27 +expression: diags +input_file: fixtures/ty_check/const_checked_arithmetic_overflow.fe +--- +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:2:26 + │ +2 │ const ADD_OVERFLOW: u8 = 255 + 1 + │ ^^^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:3:27 + │ +3 │ const SUB_UNDERFLOW: u8 = 0 - 1 + │ ^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:4:25 + │ +4 │ const DIV_BY_ZERO: u8 = 1 / 0 + │ ^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:5:25 + │ +5 │ const REM_BY_ZERO: u8 = 1 % 0 + │ ^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:6:26 + │ +6 │ const POW_OVERFLOW: u8 = 2 ** 8 + │ ^^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:8:30 + │ +8 │ const POW_NEGATIVE_EXP: i8 = 2 ** -1 + │ ^^^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:9:26 + │ +9 │ const NEG_OVERFLOW: i8 = -(-128) + │ ^^^^^^^ const evaluation failed + +error[3-0015]: failed to evaluate const expression + ┌─ const_checked_arithmetic_overflow.fe:10:26 + │ +10 │ const DIV_OVERFLOW: i8 = (-128) / (-1) + │ ^^^^^^^^^^^^^ const evaluation failed diff --git a/library/core/src/num.fe b/library/core/src/num.fe index 5052d80a80..d7bb493807 100644 --- a/library/core/src/num.fe +++ b/library/core/src/num.fe @@ -1,6 +1,7 @@ use super::ops::* use super::default::Default use super::option::Option::{self, *} +use super::panic // Generic bitcast intrinsic - reinterprets bits between any two primitive integer types extern { @@ -618,15 +619,90 @@ fn sign_extend(word: u256, _ mask: u256, _ sign_bit: u256) -> u256 { } } +fn pow_checked(base: T, exp: T) -> T + where T: IntWord, T: Mul +{ + let exp_word = exp.to_word() & T::MASK + if T::IS_SIGNED && (exp_word & T::SIGN_BIT != 0) { + panic() + } + + let mut acc = T::from_word(1) + let mut base = base + let mut exp_word = exp_word + + while exp_word > 0 { + if (exp_word & 1) != 0 { + acc = acc * base + } + exp_word = exp_word >> 1 + if exp_word > 0 { + base = base * base + } + } + acc +} + // u8 impls impl BitNot for u8 { fn bit_not(self) -> u8 { __bitnot_u8(self) } } impl Not for u8 { fn not(self) -> u8 { __bitnot_u8(self) } } -impl Add for u8 { fn add(self, _ other: u8) -> u8 { __add_u8(self, other) } } -impl Sub for u8 { fn sub(self, _ other: u8) -> u8 { __sub_u8(self, other) } } -impl Mul for u8 { fn mul(self, _ other: u8) -> u8 { __mul_u8(self, other) } } -impl Div for u8 { fn div(self, _ other: u8) -> u8 { __div_u8(self, other) } } -impl Rem for u8 { fn rem(self, _ other: u8) -> u8 { __rem_u8(self, other) } } -impl Pow for u8 { fn pow(self, _ other: u8) -> u8 { __pow_u8(self, other) } } +impl Add for u8 { + fn add(self, _ other: u8) -> u8 { + let result = __add_u8(self, other) + if __lt_u8(result, self) { panic() } + result + } +} +impl Sub for u8 { + fn sub(self, _ other: u8) -> u8 { + if __gt_u8(other, self) { panic() } + __sub_u8(self, other) + } +} +impl Mul for u8 { + fn mul(self, _ other: u8) -> u8 { + let result = __mul_u8(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u8 { + fn div(self, _ other: u8) -> u8 { + if other == 0 { panic() } + __div_u8(self, other) + } +} +impl Rem for u8 { + fn rem(self, _ other: u8) -> u8 { + if other == 0 { panic() } + __rem_u8(self, other) + } +} +impl Pow for u8 { fn pow(self, _ other: u8) -> u8 { pow_checked(self, other) } } +impl WrappingAdd for u8 { fn wrapping_add(self, _ other: u8) -> u8 { __add_u8(self, other) } } +impl WrappingSub for u8 { fn wrapping_sub(self, _ other: u8) -> u8 { __sub_u8(self, other) } } +impl WrappingMul for u8 { fn wrapping_mul(self, _ other: u8) -> u8 { __mul_u8(self, other) } } +impl SaturatingAdd for u8 { + fn saturating_add(self, _ other: u8) -> u8 { + let result = __add_u8(self, other) + if __lt_u8(result, self) { __bitcast(U8_MASK) } else { result } + } +} +impl SaturatingSub for u8 { + fn saturating_sub(self, _ other: u8) -> u8 { + if __gt_u8(other, self) { 0 } else { __sub_u8(self, other) } + } +} +impl SaturatingMul for u8 { + fn saturating_mul(self, _ other: u8) -> u8 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u8(self, other) + if result / self != other { __bitcast(U8_MASK) } else { result } + } + } +} impl Shl for u8 { fn shl(self, _ other: u8) -> u8 { __shl_u8(self, other) } } impl Shr for u8 { fn shr(self, _ other: u8) -> u8 { __shr_u8(self, other) } } impl BitAnd for u8 { fn bitand(self, _ other: u8) -> u8 { __bitand_u8(self, other) } } @@ -642,12 +718,12 @@ impl Ord for u8 { fn gt(self, _ other: u8) -> bool { __gt_u8(self, other) } fn ge(self, _ other: u8) -> bool { __ge_u8(self, other) } } -impl AddAssign for u8 { fn add_assign(mut self, _ other: u8) { self = self + other } } -impl SubAssign for u8 { fn sub_assign(mut self, _ other: u8) { self = self - other } } -impl MulAssign for u8 { fn mul_assign(mut self, _ other: u8) { self = self * other } } -impl DivAssign for u8 { fn div_assign(mut self, _ other: u8) { self = self / other } } -impl RemAssign for u8 { fn rem_assign(mut self, _ other: u8) { self = self % other } } -impl PowAssign for u8 { fn pow_assign(mut self, _ other: u8) { self = self ** other } } +impl AddAssign for u8 { fn add_assign(self, _ other: u8) -> u8 { self + other } } +impl SubAssign for u8 { fn sub_assign(self, _ other: u8) -> u8 { self - other } } +impl MulAssign for u8 { fn mul_assign(self, _ other: u8) -> u8 { self * other } } +impl DivAssign for u8 { fn div_assign(self, _ other: u8) -> u8 { self / other } } +impl RemAssign for u8 { fn rem_assign(self, _ other: u8) -> u8 { self % other } } +impl PowAssign for u8 { fn pow_assign(self, _ other: u8) -> u8 { self ** other } } impl ShlAssign for u8 { fn shl_assign(mut self, _ other: u8) { self = self << other } } impl ShrAssign for u8 { fn shr_assign(mut self, _ other: u8) { self = self >> other } } impl BitAndAssign for u8 { fn bitand_assign(mut self, _ other: u8) { self = self & other } } @@ -656,15 +732,106 @@ impl BitXorAssign for u8 { fn bitxor_assign(mut self, _ other: u8) { self = self impl Default for u8 { fn default() -> Self { 0 } } // i8 impls -impl Neg for i8 { fn neg(self) -> i8 { __neg_i8(self) } } +impl Neg for i8 { + fn neg(self) -> i8 { + let min: i8 = __bitcast(I8_SIGN_BIT) + if self == min { panic() } + __neg_i8(self) + } +} impl BitNot for i8 { fn bit_not(self) -> i8 { __bitnot_i8(self) } } impl Not for i8 { fn not(self) -> i8 { __bitnot_i8(self) } } -impl Add for i8 { fn add(self, _ other: i8) -> i8 { __add_i8(self, other) } } -impl Sub for i8 { fn sub(self, _ other: i8) -> i8 { __sub_i8(self, other) } } -impl Mul for i8 { fn mul(self, _ other: i8) -> i8 { __mul_i8(self, other) } } -impl Div for i8 { fn div(self, _ other: i8) -> i8 { __div_i8(self, other) } } -impl Rem for i8 { fn rem(self, _ other: i8) -> i8 { __rem_i8(self, other) } } -impl Pow for i8 { fn pow(self, _ other: i8) -> i8 { __pow_i8(self, other) } } +impl Add for i8 { + fn add(self, _ other: i8) -> i8 { + let result = __add_i8(self, other) + let self_sign = (self.to_word() & I8_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I8_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I8_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i8 { + fn sub(self, _ other: i8) -> i8 { + let result = __sub_i8(self, other) + let self_sign = (self.to_word() & I8_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I8_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I8_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i8 { + fn mul(self, _ other: i8) -> i8 { + let result = __mul_i8(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i8 { + fn div(self, _ other: i8) -> i8 { + if other == 0 { panic() } + let min: i8 = __bitcast(I8_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i8(self, other) + } +} +impl Rem for i8 { + fn rem(self, _ other: i8) -> i8 { + if other == 0 { panic() } + __rem_i8(self, other) + } +} +impl Pow for i8 { fn pow(self, _ other: i8) -> i8 { pow_checked(self, other) } } +impl WrappingAdd for i8 { fn wrapping_add(self, _ other: i8) -> i8 { __add_i8(self, other) } } +impl WrappingSub for i8 { fn wrapping_sub(self, _ other: i8) -> i8 { __sub_i8(self, other) } } +impl WrappingMul for i8 { fn wrapping_mul(self, _ other: i8) -> i8 { __mul_i8(self, other) } } +impl SaturatingAdd for i8 { + fn saturating_add(self, _ other: i8) -> i8 { + let result = __add_i8(self, other) + let self_sign = (self.to_word() & I8_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I8_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I8_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I8_SIGN_BIT) } else { __bitcast(I8_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i8 { + fn saturating_sub(self, _ other: i8) -> i8 { + let result = __sub_i8(self, other) + let self_sign = (self.to_word() & I8_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I8_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I8_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I8_SIGN_BIT) } else { __bitcast(I8_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i8 { + fn saturating_mul(self, _ other: i8) -> i8 { + if self == 0 || other == 0 { + 0 + } else { + let min: i8 = __bitcast(I8_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I8_SIGN_BIT - 1) + } else { + let result = __mul_i8(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I8_SIGN_BIT) != 0 + if negative { __bitcast(I8_SIGN_BIT) } else { __bitcast(I8_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i8 { fn shl(self, _ other: i8) -> i8 { __shl_i8(self, other) } } impl Shr for i8 { fn shr(self, _ other: i8) -> i8 { __shr_i8(self, other) } } impl BitAnd for i8 { fn bitand(self, _ other: i8) -> i8 { __bitand_i8(self, other) } } @@ -680,12 +847,12 @@ impl Ord for i8 { fn gt(self, _ other: i8) -> bool { __gt_i8(self, other) } fn ge(self, _ other: i8) -> bool { __ge_i8(self, other) } } -impl AddAssign for i8 { fn add_assign(mut self, _ other: i8) { self = self + other } } -impl SubAssign for i8 { fn sub_assign(mut self, _ other: i8) { self = self - other } } -impl MulAssign for i8 { fn mul_assign(mut self, _ other: i8) { self = self * other } } -impl DivAssign for i8 { fn div_assign(mut self, _ other: i8) { self = self / other } } -impl RemAssign for i8 { fn rem_assign(mut self, _ other: i8) { self = self % other } } -impl PowAssign for i8 { fn pow_assign(mut self, _ other: i8) { self = self ** other } } +impl AddAssign for i8 { fn add_assign(self, _ other: i8) -> i8 { self + other } } +impl SubAssign for i8 { fn sub_assign(self, _ other: i8) -> i8 { self - other } } +impl MulAssign for i8 { fn mul_assign(self, _ other: i8) -> i8 { self * other } } +impl DivAssign for i8 { fn div_assign(self, _ other: i8) -> i8 { self / other } } +impl RemAssign for i8 { fn rem_assign(self, _ other: i8) -> i8 { self % other } } +impl PowAssign for i8 { fn pow_assign(self, _ other: i8) -> i8 { self ** other } } impl ShlAssign for i8 { fn shl_assign(mut self, _ other: i8) { self = self << other } } impl ShrAssign for i8 { fn shr_assign(mut self, _ other: i8) { self = self >> other } } impl BitAndAssign for i8 { fn bitand_assign(mut self, _ other: i8) { self = self & other } } @@ -694,15 +861,106 @@ impl BitXorAssign for i8 { fn bitxor_assign(mut self, _ other: i8) { self = self impl Default for i8 { fn default() -> Self { 0 } } // i16 impls -impl Neg for i16 { fn neg(self) -> i16 { __neg_i16(self) } } +impl Neg for i16 { + fn neg(self) -> i16 { + let min: i16 = __bitcast(I16_SIGN_BIT) + if self == min { panic() } + __neg_i16(self) + } +} impl BitNot for i16 { fn bit_not(self) -> i16 { __bitnot_i16(self) } } impl Not for i16 { fn not(self) -> i16 { __bitnot_i16(self) } } -impl Add for i16 { fn add(self, _ other: i16) -> i16 { __add_i16(self, other) } } -impl Sub for i16 { fn sub(self, _ other: i16) -> i16 { __sub_i16(self, other) } } -impl Mul for i16 { fn mul(self, _ other: i16) -> i16 { __mul_i16(self, other) } } -impl Div for i16 { fn div(self, _ other: i16) -> i16 { __div_i16(self, other) } } -impl Rem for i16 { fn rem(self, _ other: i16) -> i16 { __rem_i16(self, other) } } -impl Pow for i16 { fn pow(self, _ other: i16) -> i16 { __pow_i16(self, other) } } +impl Add for i16 { + fn add(self, _ other: i16) -> i16 { + let result = __add_i16(self, other) + let self_sign = (self.to_word() & I16_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I16_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I16_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i16 { + fn sub(self, _ other: i16) -> i16 { + let result = __sub_i16(self, other) + let self_sign = (self.to_word() & I16_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I16_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I16_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i16 { + fn mul(self, _ other: i16) -> i16 { + let result = __mul_i16(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i16 { + fn div(self, _ other: i16) -> i16 { + if other == 0 { panic() } + let min: i16 = __bitcast(I16_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i16(self, other) + } +} +impl Rem for i16 { + fn rem(self, _ other: i16) -> i16 { + if other == 0 { panic() } + __rem_i16(self, other) + } +} +impl Pow for i16 { fn pow(self, _ other: i16) -> i16 { pow_checked(self, other) } } +impl WrappingAdd for i16 { fn wrapping_add(self, _ other: i16) -> i16 { __add_i16(self, other) } } +impl WrappingSub for i16 { fn wrapping_sub(self, _ other: i16) -> i16 { __sub_i16(self, other) } } +impl WrappingMul for i16 { fn wrapping_mul(self, _ other: i16) -> i16 { __mul_i16(self, other) } } +impl SaturatingAdd for i16 { + fn saturating_add(self, _ other: i16) -> i16 { + let result = __add_i16(self, other) + let self_sign = (self.to_word() & I16_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I16_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I16_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I16_SIGN_BIT) } else { __bitcast(I16_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i16 { + fn saturating_sub(self, _ other: i16) -> i16 { + let result = __sub_i16(self, other) + let self_sign = (self.to_word() & I16_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I16_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I16_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I16_SIGN_BIT) } else { __bitcast(I16_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i16 { + fn saturating_mul(self, _ other: i16) -> i16 { + if self == 0 || other == 0 { + 0 + } else { + let min: i16 = __bitcast(I16_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I16_SIGN_BIT - 1) + } else { + let result = __mul_i16(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I16_SIGN_BIT) != 0 + if negative { __bitcast(I16_SIGN_BIT) } else { __bitcast(I16_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i16 { fn shl(self, _ other: i16) -> i16 { __shl_i16(self, other) } } impl Shr for i16 { fn shr(self, _ other: i16) -> i16 { __shr_i16(self, other) } } impl BitAnd for i16 { fn bitand(self, _ other: i16) -> i16 { __bitand_i16(self, other) } } @@ -718,12 +976,12 @@ impl Ord for i16 { fn gt(self, _ other: i16) -> bool { __gt_i16(self, other) } fn ge(self, _ other: i16) -> bool { __ge_i16(self, other) } } -impl AddAssign for i16 { fn add_assign(mut self, _ other: i16) { self = self + other } } -impl SubAssign for i16 { fn sub_assign(mut self, _ other: i16) { self = self - other } } -impl MulAssign for i16 { fn mul_assign(mut self, _ other: i16) { self = self * other } } -impl DivAssign for i16 { fn div_assign(mut self, _ other: i16) { self = self / other } } -impl RemAssign for i16 { fn rem_assign(mut self, _ other: i16) { self = self % other } } -impl PowAssign for i16 { fn pow_assign(mut self, _ other: i16) { self = self ** other } } +impl AddAssign for i16 { fn add_assign(self, _ other: i16) -> i16 { self + other } } +impl SubAssign for i16 { fn sub_assign(self, _ other: i16) -> i16 { self - other } } +impl MulAssign for i16 { fn mul_assign(self, _ other: i16) -> i16 { self * other } } +impl DivAssign for i16 { fn div_assign(self, _ other: i16) -> i16 { self / other } } +impl RemAssign for i16 { fn rem_assign(self, _ other: i16) -> i16 { self % other } } +impl PowAssign for i16 { fn pow_assign(self, _ other: i16) -> i16 { self ** other } } impl ShlAssign for i16 { fn shl_assign(mut self, _ other: i16) { self = self << other } } impl ShrAssign for i16 { fn shr_assign(mut self, _ other: i16) { self = self >> other } } impl BitAndAssign for i16 { fn bitand_assign(mut self, _ other: i16) { self = self & other } } @@ -734,12 +992,63 @@ impl Default for i16 { fn default() -> Self { 0 } } // u16 impls impl BitNot for u16 { fn bit_not(self) -> u16 { __bitnot_u16(self) } } impl Not for u16 { fn not(self) -> u16 { __bitnot_u16(self) } } -impl Add for u16 { fn add(self, _ other: u16) -> u16 { __add_u16(self, other) } } -impl Sub for u16 { fn sub(self, _ other: u16) -> u16 { __sub_u16(self, other) } } -impl Mul for u16 { fn mul(self, _ other: u16) -> u16 { __mul_u16(self, other) } } -impl Div for u16 { fn div(self, _ other: u16) -> u16 { __div_u16(self, other) } } -impl Rem for u16 { fn rem(self, _ other: u16) -> u16 { __rem_u16(self, other) } } -impl Pow for u16 { fn pow(self, _ other: u16) -> u16 { __pow_u16(self, other) } } +impl Add for u16 { + fn add(self, _ other: u16) -> u16 { + let result = __add_u16(self, other) + if __lt_u16(result, self) { panic() } + result + } +} +impl Sub for u16 { + fn sub(self, _ other: u16) -> u16 { + if __gt_u16(other, self) { panic() } + __sub_u16(self, other) + } +} +impl Mul for u16 { + fn mul(self, _ other: u16) -> u16 { + let result = __mul_u16(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u16 { + fn div(self, _ other: u16) -> u16 { + if other == 0 { panic() } + __div_u16(self, other) + } +} +impl Rem for u16 { + fn rem(self, _ other: u16) -> u16 { + if other == 0 { panic() } + __rem_u16(self, other) + } +} +impl Pow for u16 { fn pow(self, _ other: u16) -> u16 { pow_checked(self, other) } } +impl WrappingAdd for u16 { fn wrapping_add(self, _ other: u16) -> u16 { __add_u16(self, other) } } +impl WrappingSub for u16 { fn wrapping_sub(self, _ other: u16) -> u16 { __sub_u16(self, other) } } +impl WrappingMul for u16 { fn wrapping_mul(self, _ other: u16) -> u16 { __mul_u16(self, other) } } +impl SaturatingAdd for u16 { + fn saturating_add(self, _ other: u16) -> u16 { + let result = __add_u16(self, other) + if __lt_u16(result, self) { __bitcast(U16_MASK) } else { result } + } +} +impl SaturatingSub for u16 { + fn saturating_sub(self, _ other: u16) -> u16 { + if __gt_u16(other, self) { 0 } else { __sub_u16(self, other) } + } +} +impl SaturatingMul for u16 { + fn saturating_mul(self, _ other: u16) -> u16 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u16(self, other) + if result / self != other { __bitcast(U16_MASK) } else { result } + } + } +} impl Shl for u16 { fn shl(self, _ other: u16) -> u16 { __shl_u16(self, other) } } impl Shr for u16 { fn shr(self, _ other: u16) -> u16 { __shr_u16(self, other) } } impl BitAnd for u16 { fn bitand(self, _ other: u16) -> u16 { __bitand_u16(self, other) } } @@ -755,12 +1064,12 @@ impl Ord for u16 { fn gt(self, _ other: u16) -> bool { __gt_u16(self, other) } fn ge(self, _ other: u16) -> bool { __ge_u16(self, other) } } -impl AddAssign for u16 { fn add_assign(mut self, _ other: u16) { self = self + other } } -impl SubAssign for u16 { fn sub_assign(mut self, _ other: u16) { self = self - other } } -impl MulAssign for u16 { fn mul_assign(mut self, _ other: u16) { self = self * other } } -impl DivAssign for u16 { fn div_assign(mut self, _ other: u16) { self = self / other } } -impl RemAssign for u16 { fn rem_assign(mut self, _ other: u16) { self = self % other } } -impl PowAssign for u16 { fn pow_assign(mut self, _ other: u16) { self = self ** other } } +impl AddAssign for u16 { fn add_assign(self, _ other: u16) -> u16 { self + other } } +impl SubAssign for u16 { fn sub_assign(self, _ other: u16) -> u16 { self - other } } +impl MulAssign for u16 { fn mul_assign(self, _ other: u16) -> u16 { self * other } } +impl DivAssign for u16 { fn div_assign(self, _ other: u16) -> u16 { self / other } } +impl RemAssign for u16 { fn rem_assign(self, _ other: u16) -> u16 { self % other } } +impl PowAssign for u16 { fn pow_assign(self, _ other: u16) -> u16 { self ** other } } impl ShlAssign for u16 { fn shl_assign(mut self, _ other: u16) { self = self << other } } impl ShrAssign for u16 { fn shr_assign(mut self, _ other: u16) { self = self >> other } } impl BitAndAssign for u16 { fn bitand_assign(mut self, _ other: u16) { self = self & other } } @@ -770,15 +1079,106 @@ impl Default for u16 { fn default() -> Self { 0 } } // i32 impls -impl Neg for i32 { fn neg(self) -> i32 { __neg_i32(self) } } +impl Neg for i32 { + fn neg(self) -> i32 { + let min: i32 = __bitcast(I32_SIGN_BIT) + if self == min { panic() } + __neg_i32(self) + } +} impl BitNot for i32 { fn bit_not(self) -> i32 { __bitnot_i32(self) } } impl Not for i32 { fn not(self) -> i32 { __bitnot_i32(self) } } -impl Add for i32 { fn add(self, _ other: i32) -> i32 { __add_i32(self, other) } } -impl Sub for i32 { fn sub(self, _ other: i32) -> i32 { __sub_i32(self, other) } } -impl Mul for i32 { fn mul(self, _ other: i32) -> i32 { __mul_i32(self, other) } } -impl Div for i32 { fn div(self, _ other: i32) -> i32 { __div_i32(self, other) } } -impl Rem for i32 { fn rem(self, _ other: i32) -> i32 { __rem_i32(self, other) } } -impl Pow for i32 { fn pow(self, _ other: i32) -> i32 { __pow_i32(self, other) } } +impl Add for i32 { + fn add(self, _ other: i32) -> i32 { + let result = __add_i32(self, other) + let self_sign = (self.to_word() & I32_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I32_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I32_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i32 { + fn sub(self, _ other: i32) -> i32 { + let result = __sub_i32(self, other) + let self_sign = (self.to_word() & I32_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I32_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I32_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i32 { + fn mul(self, _ other: i32) -> i32 { + let result = __mul_i32(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i32 { + fn div(self, _ other: i32) -> i32 { + if other == 0 { panic() } + let min: i32 = __bitcast(I32_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i32(self, other) + } +} +impl Rem for i32 { + fn rem(self, _ other: i32) -> i32 { + if other == 0 { panic() } + __rem_i32(self, other) + } +} +impl Pow for i32 { fn pow(self, _ other: i32) -> i32 { pow_checked(self, other) } } +impl WrappingAdd for i32 { fn wrapping_add(self, _ other: i32) -> i32 { __add_i32(self, other) } } +impl WrappingSub for i32 { fn wrapping_sub(self, _ other: i32) -> i32 { __sub_i32(self, other) } } +impl WrappingMul for i32 { fn wrapping_mul(self, _ other: i32) -> i32 { __mul_i32(self, other) } } +impl SaturatingAdd for i32 { + fn saturating_add(self, _ other: i32) -> i32 { + let result = __add_i32(self, other) + let self_sign = (self.to_word() & I32_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I32_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I32_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I32_SIGN_BIT) } else { __bitcast(I32_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i32 { + fn saturating_sub(self, _ other: i32) -> i32 { + let result = __sub_i32(self, other) + let self_sign = (self.to_word() & I32_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I32_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I32_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I32_SIGN_BIT) } else { __bitcast(I32_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i32 { + fn saturating_mul(self, _ other: i32) -> i32 { + if self == 0 || other == 0 { + 0 + } else { + let min: i32 = __bitcast(I32_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I32_SIGN_BIT - 1) + } else { + let result = __mul_i32(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I32_SIGN_BIT) != 0 + if negative { __bitcast(I32_SIGN_BIT) } else { __bitcast(I32_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i32 { fn shl(self, _ other: i32) -> i32 { __shl_i32(self, other) } } impl Shr for i32 { fn shr(self, _ other: i32) -> i32 { __shr_i32(self, other) } } impl BitAnd for i32 { fn bitand(self, _ other: i32) -> i32 { __bitand_i32(self, other) } } @@ -794,12 +1194,12 @@ impl Ord for i32 { fn gt(self, _ other: i32) -> bool { __gt_i32(self, other) } fn ge(self, _ other: i32) -> bool { __ge_i32(self, other) } } -impl AddAssign for i32 { fn add_assign(mut self, _ other: i32) { self = self + other } } -impl SubAssign for i32 { fn sub_assign(mut self, _ other: i32) { self = self - other } } -impl MulAssign for i32 { fn mul_assign(mut self, _ other: i32) { self = self * other } } -impl DivAssign for i32 { fn div_assign(mut self, _ other: i32) { self = self / other } } -impl RemAssign for i32 { fn rem_assign(mut self, _ other: i32) { self = self % other } } -impl PowAssign for i32 { fn pow_assign(mut self, _ other: i32) { self = self ** other } } +impl AddAssign for i32 { fn add_assign(self, _ other: i32) -> i32 { self + other } } +impl SubAssign for i32 { fn sub_assign(self, _ other: i32) -> i32 { self - other } } +impl MulAssign for i32 { fn mul_assign(self, _ other: i32) -> i32 { self * other } } +impl DivAssign for i32 { fn div_assign(self, _ other: i32) -> i32 { self / other } } +impl RemAssign for i32 { fn rem_assign(self, _ other: i32) -> i32 { self % other } } +impl PowAssign for i32 { fn pow_assign(self, _ other: i32) -> i32 { self ** other } } impl ShlAssign for i32 { fn shl_assign(mut self, _ other: i32) { self = self << other } } impl ShrAssign for i32 { fn shr_assign(mut self, _ other: i32) { self = self >> other } } impl BitAndAssign for i32 { fn bitand_assign(mut self, _ other: i32) { self = self & other } } @@ -810,12 +1210,63 @@ impl Default for i32 { fn default() -> Self { 0 } } // u32 impls impl BitNot for u32 { fn bit_not(self) -> u32 { __bitnot_u32(self) } } impl Not for u32 { fn not(self) -> u32 { __bitnot_u32(self) } } -impl Add for u32 { fn add(self, _ other: u32) -> u32 { __add_u32(self, other) } } -impl Sub for u32 { fn sub(self, _ other: u32) -> u32 { __sub_u32(self, other) } } -impl Mul for u32 { fn mul(self, _ other: u32) -> u32 { __mul_u32(self, other) } } -impl Div for u32 { fn div(self, _ other: u32) -> u32 { __div_u32(self, other) } } -impl Rem for u32 { fn rem(self, _ other: u32) -> u32 { __rem_u32(self, other) } } -impl Pow for u32 { fn pow(self, _ other: u32) -> u32 { __pow_u32(self, other) } } +impl Add for u32 { + fn add(self, _ other: u32) -> u32 { + let result = __add_u32(self, other) + if __lt_u32(result, self) { panic() } + result + } +} +impl Sub for u32 { + fn sub(self, _ other: u32) -> u32 { + if __gt_u32(other, self) { panic() } + __sub_u32(self, other) + } +} +impl Mul for u32 { + fn mul(self, _ other: u32) -> u32 { + let result = __mul_u32(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u32 { + fn div(self, _ other: u32) -> u32 { + if other == 0 { panic() } + __div_u32(self, other) + } +} +impl Rem for u32 { + fn rem(self, _ other: u32) -> u32 { + if other == 0 { panic() } + __rem_u32(self, other) + } +} +impl Pow for u32 { fn pow(self, _ other: u32) -> u32 { pow_checked(self, other) } } +impl WrappingAdd for u32 { fn wrapping_add(self, _ other: u32) -> u32 { __add_u32(self, other) } } +impl WrappingSub for u32 { fn wrapping_sub(self, _ other: u32) -> u32 { __sub_u32(self, other) } } +impl WrappingMul for u32 { fn wrapping_mul(self, _ other: u32) -> u32 { __mul_u32(self, other) } } +impl SaturatingAdd for u32 { + fn saturating_add(self, _ other: u32) -> u32 { + let result = __add_u32(self, other) + if __lt_u32(result, self) { __bitcast(U32_MASK) } else { result } + } +} +impl SaturatingSub for u32 { + fn saturating_sub(self, _ other: u32) -> u32 { + if __gt_u32(other, self) { 0 } else { __sub_u32(self, other) } + } +} +impl SaturatingMul for u32 { + fn saturating_mul(self, _ other: u32) -> u32 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u32(self, other) + if result / self != other { __bitcast(U32_MASK) } else { result } + } + } +} impl Shl for u32 { fn shl(self, _ other: u32) -> u32 { __shl_u32(self, other) } } impl Shr for u32 { fn shr(self, _ other: u32) -> u32 { __shr_u32(self, other) } } impl BitAnd for u32 { fn bitand(self, _ other: u32) -> u32 { __bitand_u32(self, other) } } @@ -831,12 +1282,12 @@ impl Ord for u32 { fn gt(self, _ other: u32) -> bool { __gt_u32(self, other) } fn ge(self, _ other: u32) -> bool { __ge_u32(self, other) } } -impl AddAssign for u32 { fn add_assign(mut self, _ other: u32) { self = self + other } } -impl SubAssign for u32 { fn sub_assign(mut self, _ other: u32) { self = self - other } } -impl MulAssign for u32 { fn mul_assign(mut self, _ other: u32) { self = self * other } } -impl DivAssign for u32 { fn div_assign(mut self, _ other: u32) { self = self / other } } -impl RemAssign for u32 { fn rem_assign(mut self, _ other: u32) { self = self % other } } -impl PowAssign for u32 { fn pow_assign(mut self, _ other: u32) { self = self ** other } } +impl AddAssign for u32 { fn add_assign(self, _ other: u32) -> u32 { self + other } } +impl SubAssign for u32 { fn sub_assign(self, _ other: u32) -> u32 { self - other } } +impl MulAssign for u32 { fn mul_assign(self, _ other: u32) -> u32 { self * other } } +impl DivAssign for u32 { fn div_assign(self, _ other: u32) -> u32 { self / other } } +impl RemAssign for u32 { fn rem_assign(self, _ other: u32) -> u32 { self % other } } +impl PowAssign for u32 { fn pow_assign(self, _ other: u32) -> u32 { self ** other } } impl ShlAssign for u32 { fn shl_assign(mut self, _ other: u32) { self = self << other } } impl ShrAssign for u32 { fn shr_assign(mut self, _ other: u32) { self = self >> other } } impl BitAndAssign for u32 { fn bitand_assign(mut self, _ other: u32) { self = self & other } } @@ -845,15 +1296,106 @@ impl BitXorAssign for u32 { fn bitxor_assign(mut self, _ other: u32) { self = se impl Default for u32 { fn default() -> Self { 0 } } // i64 impls -impl Neg for i64 { fn neg(self) -> i64 { __neg_i64(self) } } +impl Neg for i64 { + fn neg(self) -> i64 { + let min: i64 = __bitcast(I64_SIGN_BIT) + if self == min { panic() } + __neg_i64(self) + } +} impl BitNot for i64 { fn bit_not(self) -> i64 { __bitnot_i64(self) } } impl Not for i64 { fn not(self) -> i64 { __bitnot_i64(self) } } -impl Add for i64 { fn add(self, _ other: i64) -> i64 { __add_i64(self, other) } } -impl Sub for i64 { fn sub(self, _ other: i64) -> i64 { __sub_i64(self, other) } } -impl Mul for i64 { fn mul(self, _ other: i64) -> i64 { __mul_i64(self, other) } } -impl Div for i64 { fn div(self, _ other: i64) -> i64 { __div_i64(self, other) } } -impl Rem for i64 { fn rem(self, _ other: i64) -> i64 { __rem_i64(self, other) } } -impl Pow for i64 { fn pow(self, _ other: i64) -> i64 { __pow_i64(self, other) } } +impl Add for i64 { + fn add(self, _ other: i64) -> i64 { + let result = __add_i64(self, other) + let self_sign = (self.to_word() & I64_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I64_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I64_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i64 { + fn sub(self, _ other: i64) -> i64 { + let result = __sub_i64(self, other) + let self_sign = (self.to_word() & I64_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I64_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I64_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i64 { + fn mul(self, _ other: i64) -> i64 { + let result = __mul_i64(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i64 { + fn div(self, _ other: i64) -> i64 { + if other == 0 { panic() } + let min: i64 = __bitcast(I64_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i64(self, other) + } +} +impl Rem for i64 { + fn rem(self, _ other: i64) -> i64 { + if other == 0 { panic() } + __rem_i64(self, other) + } +} +impl Pow for i64 { fn pow(self, _ other: i64) -> i64 { pow_checked(self, other) } } +impl WrappingAdd for i64 { fn wrapping_add(self, _ other: i64) -> i64 { __add_i64(self, other) } } +impl WrappingSub for i64 { fn wrapping_sub(self, _ other: i64) -> i64 { __sub_i64(self, other) } } +impl WrappingMul for i64 { fn wrapping_mul(self, _ other: i64) -> i64 { __mul_i64(self, other) } } +impl SaturatingAdd for i64 { + fn saturating_add(self, _ other: i64) -> i64 { + let result = __add_i64(self, other) + let self_sign = (self.to_word() & I64_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I64_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I64_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I64_SIGN_BIT) } else { __bitcast(I64_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i64 { + fn saturating_sub(self, _ other: i64) -> i64 { + let result = __sub_i64(self, other) + let self_sign = (self.to_word() & I64_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I64_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I64_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I64_SIGN_BIT) } else { __bitcast(I64_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i64 { + fn saturating_mul(self, _ other: i64) -> i64 { + if self == 0 || other == 0 { + 0 + } else { + let min: i64 = __bitcast(I64_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I64_SIGN_BIT - 1) + } else { + let result = __mul_i64(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I64_SIGN_BIT) != 0 + if negative { __bitcast(I64_SIGN_BIT) } else { __bitcast(I64_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i64 { fn shl(self, _ other: i64) -> i64 { __shl_i64(self, other) } } impl Shr for i64 { fn shr(self, _ other: i64) -> i64 { __shr_i64(self, other) } } impl BitAnd for i64 { fn bitand(self, _ other: i64) -> i64 { __bitand_i64(self, other) } } @@ -869,12 +1411,12 @@ impl Ord for i64 { fn gt(self, _ other: i64) -> bool { __gt_i64(self, other) } fn ge(self, _ other: i64) -> bool { __ge_i64(self, other) } } -impl AddAssign for i64 { fn add_assign(mut self, _ other: i64) { self = self + other } } -impl SubAssign for i64 { fn sub_assign(mut self, _ other: i64) { self = self - other } } -impl MulAssign for i64 { fn mul_assign(mut self, _ other: i64) { self = self * other } } -impl DivAssign for i64 { fn div_assign(mut self, _ other: i64) { self = self / other } } -impl RemAssign for i64 { fn rem_assign(mut self, _ other: i64) { self = self % other } } -impl PowAssign for i64 { fn pow_assign(mut self, _ other: i64) { self = self ** other } } +impl AddAssign for i64 { fn add_assign(self, _ other: i64) -> i64 { self + other } } +impl SubAssign for i64 { fn sub_assign(self, _ other: i64) -> i64 { self - other } } +impl MulAssign for i64 { fn mul_assign(self, _ other: i64) -> i64 { self * other } } +impl DivAssign for i64 { fn div_assign(self, _ other: i64) -> i64 { self / other } } +impl RemAssign for i64 { fn rem_assign(self, _ other: i64) -> i64 { self % other } } +impl PowAssign for i64 { fn pow_assign(self, _ other: i64) -> i64 { self ** other } } impl ShlAssign for i64 { fn shl_assign(mut self, _ other: i64) { self = self << other } } impl ShrAssign for i64 { fn shr_assign(mut self, _ other: i64) { self = self >> other } } impl BitAndAssign for i64 { fn bitand_assign(mut self, _ other: i64) { self = self & other } } @@ -883,15 +1425,106 @@ impl BitXorAssign for i64 { fn bitxor_assign(mut self, _ other: i64) { self = se impl Default for i64 { fn default() -> Self { 0 } } // i128 impls -impl Neg for i128 { fn neg(self) -> i128 { __neg_i128(self) } } +impl Neg for i128 { + fn neg(self) -> i128 { + let min: i128 = __bitcast(I128_SIGN_BIT) + if self == min { panic() } + __neg_i128(self) + } +} impl BitNot for i128 { fn bit_not(self) -> i128 { __bitnot_i128(self) } } impl Not for i128 { fn not(self) -> i128 { __bitnot_i128(self) } } -impl Add for i128 { fn add(self, _ other: i128) -> i128 { __add_i128(self, other) } } -impl Sub for i128 { fn sub(self, _ other: i128) -> i128 { __sub_i128(self, other) } } -impl Mul for i128 { fn mul(self, _ other: i128) -> i128 { __mul_i128(self, other) } } -impl Div for i128 { fn div(self, _ other: i128) -> i128 { __div_i128(self, other) } } -impl Rem for i128 { fn rem(self, _ other: i128) -> i128 { __rem_i128(self, other) } } -impl Pow for i128 { fn pow(self, _ other: i128) -> i128 { __pow_i128(self, other) } } +impl Add for i128 { + fn add(self, _ other: i128) -> i128 { + let result = __add_i128(self, other) + let self_sign = (self.to_word() & I128_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I128_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I128_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i128 { + fn sub(self, _ other: i128) -> i128 { + let result = __sub_i128(self, other) + let self_sign = (self.to_word() & I128_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I128_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I128_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i128 { + fn mul(self, _ other: i128) -> i128 { + let result = __mul_i128(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i128 { + fn div(self, _ other: i128) -> i128 { + if other == 0 { panic() } + let min: i128 = __bitcast(I128_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i128(self, other) + } +} +impl Rem for i128 { + fn rem(self, _ other: i128) -> i128 { + if other == 0 { panic() } + __rem_i128(self, other) + } +} +impl Pow for i128 { fn pow(self, _ other: i128) -> i128 { pow_checked(self, other) } } +impl WrappingAdd for i128 { fn wrapping_add(self, _ other: i128) -> i128 { __add_i128(self, other) } } +impl WrappingSub for i128 { fn wrapping_sub(self, _ other: i128) -> i128 { __sub_i128(self, other) } } +impl WrappingMul for i128 { fn wrapping_mul(self, _ other: i128) -> i128 { __mul_i128(self, other) } } +impl SaturatingAdd for i128 { + fn saturating_add(self, _ other: i128) -> i128 { + let result = __add_i128(self, other) + let self_sign = (self.to_word() & I128_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I128_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I128_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I128_SIGN_BIT) } else { __bitcast(I128_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i128 { + fn saturating_sub(self, _ other: i128) -> i128 { + let result = __sub_i128(self, other) + let self_sign = (self.to_word() & I128_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I128_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I128_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I128_SIGN_BIT) } else { __bitcast(I128_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i128 { + fn saturating_mul(self, _ other: i128) -> i128 { + if self == 0 || other == 0 { + 0 + } else { + let min: i128 = __bitcast(I128_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I128_SIGN_BIT - 1) + } else { + let result = __mul_i128(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I128_SIGN_BIT) != 0 + if negative { __bitcast(I128_SIGN_BIT) } else { __bitcast(I128_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i128 { fn shl(self, _ other: i128) -> i128 { __shl_i128(self, other) } } impl Shr for i128 { fn shr(self, _ other: i128) -> i128 { __shr_i128(self, other) } } impl BitAnd for i128 { fn bitand(self, _ other: i128) -> i128 { __bitand_i128(self, other) } } @@ -907,12 +1540,12 @@ impl Ord for i128 { fn gt(self, _ other: i128) -> bool { __gt_i128(self, other) } fn ge(self, _ other: i128) -> bool { __ge_i128(self, other) } } -impl AddAssign for i128 { fn add_assign(mut self, _ other: i128) { self = self + other } } -impl SubAssign for i128 { fn sub_assign(mut self, _ other: i128) { self = self - other } } -impl MulAssign for i128 { fn mul_assign(mut self, _ other: i128) { self = self * other } } -impl DivAssign for i128 { fn div_assign(mut self, _ other: i128) { self = self / other } } -impl RemAssign for i128 { fn rem_assign(mut self, _ other: i128) { self = self % other } } -impl PowAssign for i128 { fn pow_assign(mut self, _ other: i128) { self = self ** other } } +impl AddAssign for i128 { fn add_assign(self, _ other: i128) -> i128 { self + other } } +impl SubAssign for i128 { fn sub_assign(self, _ other: i128) -> i128 { self - other } } +impl MulAssign for i128 { fn mul_assign(self, _ other: i128) -> i128 { self * other } } +impl DivAssign for i128 { fn div_assign(self, _ other: i128) -> i128 { self / other } } +impl RemAssign for i128 { fn rem_assign(self, _ other: i128) -> i128 { self % other } } +impl PowAssign for i128 { fn pow_assign(self, _ other: i128) -> i128 { self ** other } } impl ShlAssign for i128 { fn shl_assign(mut self, _ other: i128) { self = self << other } } impl ShrAssign for i128 { fn shr_assign(mut self, _ other: i128) { self = self >> other } } impl BitAndAssign for i128 { fn bitand_assign(mut self, _ other: i128) { self = self & other } } @@ -923,12 +1556,63 @@ impl Default for i128 { fn default() -> Self { 0 } } // u64 impls impl BitNot for u64 { fn bit_not(self) -> u64 { __bitnot_u64(self) } } impl Not for u64 { fn not(self) -> u64 { __bitnot_u64(self) } } -impl Add for u64 { fn add(self, _ other: u64) -> u64 { __add_u64(self, other) } } -impl Sub for u64 { fn sub(self, _ other: u64) -> u64 { __sub_u64(self, other) } } -impl Mul for u64 { fn mul(self, _ other: u64) -> u64 { __mul_u64(self, other) } } -impl Div for u64 { fn div(self, _ other: u64) -> u64 { __div_u64(self, other) } } -impl Rem for u64 { fn rem(self, _ other: u64) -> u64 { __rem_u64(self, other) } } -impl Pow for u64 { fn pow(self, _ other: u64) -> u64 { __pow_u64(self, other) } } +impl Add for u64 { + fn add(self, _ other: u64) -> u64 { + let result = __add_u64(self, other) + if __lt_u64(result, self) { panic() } + result + } +} +impl Sub for u64 { + fn sub(self, _ other: u64) -> u64 { + if __gt_u64(other, self) { panic() } + __sub_u64(self, other) + } +} +impl Mul for u64 { + fn mul(self, _ other: u64) -> u64 { + let result = __mul_u64(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u64 { + fn div(self, _ other: u64) -> u64 { + if other == 0 { panic() } + __div_u64(self, other) + } +} +impl Rem for u64 { + fn rem(self, _ other: u64) -> u64 { + if other == 0 { panic() } + __rem_u64(self, other) + } +} +impl Pow for u64 { fn pow(self, _ other: u64) -> u64 { pow_checked(self, other) } } +impl WrappingAdd for u64 { fn wrapping_add(self, _ other: u64) -> u64 { __add_u64(self, other) } } +impl WrappingSub for u64 { fn wrapping_sub(self, _ other: u64) -> u64 { __sub_u64(self, other) } } +impl WrappingMul for u64 { fn wrapping_mul(self, _ other: u64) -> u64 { __mul_u64(self, other) } } +impl SaturatingAdd for u64 { + fn saturating_add(self, _ other: u64) -> u64 { + let result = __add_u64(self, other) + if __lt_u64(result, self) { __bitcast(U64_MASK) } else { result } + } +} +impl SaturatingSub for u64 { + fn saturating_sub(self, _ other: u64) -> u64 { + if __gt_u64(other, self) { 0 } else { __sub_u64(self, other) } + } +} +impl SaturatingMul for u64 { + fn saturating_mul(self, _ other: u64) -> u64 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u64(self, other) + if result / self != other { __bitcast(U64_MASK) } else { result } + } + } +} impl Shl for u64 { fn shl(self, _ other: u64) -> u64 { __shl_u64(self, other) } } impl Shr for u64 { fn shr(self, _ other: u64) -> u64 { __shr_u64(self, other) } } impl BitAnd for u64 { fn bitand(self, _ other: u64) -> u64 { __bitand_u64(self, other) } } @@ -944,12 +1628,12 @@ impl Ord for u64 { fn gt(self, _ other: u64) -> bool { __gt_u64(self, other) } fn ge(self, _ other: u64) -> bool { __ge_u64(self, other) } } -impl AddAssign for u64 { fn add_assign(mut self, _ other: u64) { self = self + other } } -impl SubAssign for u64 { fn sub_assign(mut self, _ other: u64) { self = self - other } } -impl MulAssign for u64 { fn mul_assign(mut self, _ other: u64) { self = self * other } } -impl DivAssign for u64 { fn div_assign(mut self, _ other: u64) { self = self / other } } -impl RemAssign for u64 { fn rem_assign(mut self, _ other: u64) { self = self % other } } -impl PowAssign for u64 { fn pow_assign(mut self, _ other: u64) { self = self ** other } } +impl AddAssign for u64 { fn add_assign(self, _ other: u64) -> u64 { self + other } } +impl SubAssign for u64 { fn sub_assign(self, _ other: u64) -> u64 { self - other } } +impl MulAssign for u64 { fn mul_assign(self, _ other: u64) -> u64 { self * other } } +impl DivAssign for u64 { fn div_assign(self, _ other: u64) -> u64 { self / other } } +impl RemAssign for u64 { fn rem_assign(self, _ other: u64) -> u64 { self % other } } +impl PowAssign for u64 { fn pow_assign(self, _ other: u64) -> u64 { self ** other } } impl ShlAssign for u64 { fn shl_assign(mut self, _ other: u64) { self = self << other } } impl ShrAssign for u64 { fn shr_assign(mut self, _ other: u64) { self = self >> other } } impl BitAndAssign for u64 { fn bitand_assign(mut self, _ other: u64) { self = self & other } } @@ -960,12 +1644,63 @@ impl Default for u64 { fn default() -> Self { 0 } } // u128 impls impl BitNot for u128 { fn bit_not(self) -> u128 { __bitnot_u128(self) } } impl Not for u128 { fn not(self) -> u128 { __bitnot_u128(self) } } -impl Add for u128 { fn add(self, _ other: u128) -> u128 { __add_u128(self, other) } } -impl Sub for u128 { fn sub(self, _ other: u128) -> u128 { __sub_u128(self, other) } } -impl Mul for u128 { fn mul(self, _ other: u128) -> u128 { __mul_u128(self, other) } } -impl Div for u128 { fn div(self, _ other: u128) -> u128 { __div_u128(self, other) } } -impl Rem for u128 { fn rem(self, _ other: u128) -> u128 { __rem_u128(self, other) } } -impl Pow for u128 { fn pow(self, _ other: u128) -> u128 { __pow_u128(self, other) } } +impl Add for u128 { + fn add(self, _ other: u128) -> u128 { + let result = __add_u128(self, other) + if __lt_u128(result, self) { panic() } + result + } +} +impl Sub for u128 { + fn sub(self, _ other: u128) -> u128 { + if __gt_u128(other, self) { panic() } + __sub_u128(self, other) + } +} +impl Mul for u128 { + fn mul(self, _ other: u128) -> u128 { + let result = __mul_u128(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u128 { + fn div(self, _ other: u128) -> u128 { + if other == 0 { panic() } + __div_u128(self, other) + } +} +impl Rem for u128 { + fn rem(self, _ other: u128) -> u128 { + if other == 0 { panic() } + __rem_u128(self, other) + } +} +impl Pow for u128 { fn pow(self, _ other: u128) -> u128 { pow_checked(self, other) } } +impl WrappingAdd for u128 { fn wrapping_add(self, _ other: u128) -> u128 { __add_u128(self, other) } } +impl WrappingSub for u128 { fn wrapping_sub(self, _ other: u128) -> u128 { __sub_u128(self, other) } } +impl WrappingMul for u128 { fn wrapping_mul(self, _ other: u128) -> u128 { __mul_u128(self, other) } } +impl SaturatingAdd for u128 { + fn saturating_add(self, _ other: u128) -> u128 { + let result = __add_u128(self, other) + if __lt_u128(result, self) { __bitcast(U128_MASK) } else { result } + } +} +impl SaturatingSub for u128 { + fn saturating_sub(self, _ other: u128) -> u128 { + if __gt_u128(other, self) { 0 } else { __sub_u128(self, other) } + } +} +impl SaturatingMul for u128 { + fn saturating_mul(self, _ other: u128) -> u128 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u128(self, other) + if result / self != other { __bitcast(U128_MASK) } else { result } + } + } +} impl Shl for u128 { fn shl(self, _ other: u128) -> u128 { __shl_u128(self, other) } } impl Shr for u128 { fn shr(self, _ other: u128) -> u128 { __shr_u128(self, other) } } impl BitAnd for u128 { fn bitand(self, _ other: u128) -> u128 { __bitand_u128(self, other) } } @@ -981,12 +1716,12 @@ impl Ord for u128 { fn gt(self, _ other: u128) -> bool { __gt_u128(self, other) } fn ge(self, _ other: u128) -> bool { __ge_u128(self, other) } } -impl AddAssign for u128 { fn add_assign(mut self, _ other: u128) { self = self + other } } -impl SubAssign for u128 { fn sub_assign(mut self, _ other: u128) { self = self - other } } -impl MulAssign for u128 { fn mul_assign(mut self, _ other: u128) { self = self * other } } -impl DivAssign for u128 { fn div_assign(mut self, _ other: u128) { self = self / other } } -impl RemAssign for u128 { fn rem_assign(mut self, _ other: u128) { self = self % other } } -impl PowAssign for u128 { fn pow_assign(mut self, _ other: u128) { self = self ** other } } +impl AddAssign for u128 { fn add_assign(self, _ other: u128) -> u128 { self + other } } +impl SubAssign for u128 { fn sub_assign(self, _ other: u128) -> u128 { self - other } } +impl MulAssign for u128 { fn mul_assign(self, _ other: u128) -> u128 { self * other } } +impl DivAssign for u128 { fn div_assign(self, _ other: u128) -> u128 { self / other } } +impl RemAssign for u128 { fn rem_assign(self, _ other: u128) -> u128 { self % other } } +impl PowAssign for u128 { fn pow_assign(self, _ other: u128) -> u128 { self ** other } } impl ShlAssign for u128 { fn shl_assign(mut self, _ other: u128) { self = self << other } } impl ShrAssign for u128 { fn shr_assign(mut self, _ other: u128) { self = self >> other } } impl BitAndAssign for u128 { fn bitand_assign(mut self, _ other: u128) { self = self & other } } @@ -997,12 +1732,63 @@ impl Default for u128 { fn default() -> Self { 0 } } // usize impls impl BitNot for usize { fn bit_not(self) -> usize { __bitnot_usize(self) } } impl Not for usize { fn not(self) -> usize { __bitnot_usize(self) } } -impl Add for usize { fn add(self, _ other: usize) -> usize { __add_usize(self, other) } } -impl Sub for usize { fn sub(self, _ other: usize) -> usize { __sub_usize(self, other) } } -impl Mul for usize { fn mul(self, _ other: usize) -> usize { __mul_usize(self, other) } } -impl Div for usize { fn div(self, _ other: usize) -> usize { __div_usize(self, other) } } -impl Rem for usize { fn rem(self, _ other: usize) -> usize { __rem_usize(self, other) } } -impl Pow for usize { fn pow(self, _ other: usize) -> usize { __pow_usize(self, other) } } +impl Add for usize { + fn add(self, _ other: usize) -> usize { + let result = __add_usize(self, other) + if __lt_usize(result, self) { panic() } + result + } +} +impl Sub for usize { + fn sub(self, _ other: usize) -> usize { + if __gt_usize(other, self) { panic() } + __sub_usize(self, other) + } +} +impl Mul for usize { + fn mul(self, _ other: usize) -> usize { + let result = __mul_usize(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for usize { + fn div(self, _ other: usize) -> usize { + if other == 0 { panic() } + __div_usize(self, other) + } +} +impl Rem for usize { + fn rem(self, _ other: usize) -> usize { + if other == 0 { panic() } + __rem_usize(self, other) + } +} +impl Pow for usize { fn pow(self, _ other: usize) -> usize { pow_checked(self, other) } } +impl WrappingAdd for usize { fn wrapping_add(self, _ other: usize) -> usize { __add_usize(self, other) } } +impl WrappingSub for usize { fn wrapping_sub(self, _ other: usize) -> usize { __sub_usize(self, other) } } +impl WrappingMul for usize { fn wrapping_mul(self, _ other: usize) -> usize { __mul_usize(self, other) } } +impl SaturatingAdd for usize { + fn saturating_add(self, _ other: usize) -> usize { + let result = __add_usize(self, other) + if __lt_usize(result, self) { __bitcast(U256_MASK) } else { result } + } +} +impl SaturatingSub for usize { + fn saturating_sub(self, _ other: usize) -> usize { + if __gt_usize(other, self) { 0 } else { __sub_usize(self, other) } + } +} +impl SaturatingMul for usize { + fn saturating_mul(self, _ other: usize) -> usize { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_usize(self, other) + if result / self != other { __bitcast(U256_MASK) } else { result } + } + } +} impl Shl for usize { fn shl(self, _ other: usize) -> usize { __shl_usize(self, other) } } impl Shr for usize { fn shr(self, _ other: usize) -> usize { __shr_usize(self, other) } } impl BitAnd for usize { fn bitand(self, _ other: usize) -> usize { __bitand_usize(self, other) } } @@ -1018,18 +1804,115 @@ impl Ord for usize { fn gt(self, _ other: usize) -> bool { __gt_usize(self, other) } fn ge(self, _ other: usize) -> bool { __ge_usize(self, other) } } +impl AddAssign for usize { fn add_assign(self, _ other: usize) -> usize { self + other } } +impl SubAssign for usize { fn sub_assign(self, _ other: usize) -> usize { self - other } } +impl MulAssign for usize { fn mul_assign(self, _ other: usize) -> usize { self * other } } +impl DivAssign for usize { fn div_assign(self, _ other: usize) -> usize { self / other } } +impl RemAssign for usize { fn rem_assign(self, _ other: usize) -> usize { self % other } } +impl PowAssign for usize { fn pow_assign(self, _ other: usize) -> usize { self ** other } } impl Default for usize { fn default() -> Self { 0 } } // i256 impls -impl Neg for i256 { fn neg(self) -> i256 { __neg_i256(self) } } +impl Neg for i256 { + fn neg(self) -> i256 { + let min: i256 = __bitcast(I256_SIGN_BIT) + if self == min { panic() } + __neg_i256(self) + } +} impl BitNot for i256 { fn bit_not(self) -> i256 { __bitnot_i256(self) } } impl Not for i256 { fn not(self) -> i256 { __bitnot_i256(self) } } -impl Add for i256 { fn add(self, _ other: i256) -> i256 { __add_i256(self, other) } } -impl Sub for i256 { fn sub(self, _ other: i256) -> i256 { __sub_i256(self, other) } } -impl Mul for i256 { fn mul(self, _ other: i256) -> i256 { __mul_i256(self, other) } } -impl Div for i256 { fn div(self, _ other: i256) -> i256 { __div_i256(self, other) } } -impl Rem for i256 { fn rem(self, _ other: i256) -> i256 { __rem_i256(self, other) } } -impl Pow for i256 { fn pow(self, _ other: i256) -> i256 { __pow_i256(self, other) } } +impl Add for i256 { + fn add(self, _ other: i256) -> i256 { + let result = __add_i256(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for i256 { + fn sub(self, _ other: i256) -> i256 { + let result = __sub_i256(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for i256 { + fn mul(self, _ other: i256) -> i256 { + let result = __mul_i256(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for i256 { + fn div(self, _ other: i256) -> i256 { + if other == 0 { panic() } + let min: i256 = __bitcast(I256_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_i256(self, other) + } +} +impl Rem for i256 { + fn rem(self, _ other: i256) -> i256 { + if other == 0 { panic() } + __rem_i256(self, other) + } +} +impl Pow for i256 { fn pow(self, _ other: i256) -> i256 { pow_checked(self, other) } } +impl WrappingAdd for i256 { fn wrapping_add(self, _ other: i256) -> i256 { __add_i256(self, other) } } +impl WrappingSub for i256 { fn wrapping_sub(self, _ other: i256) -> i256 { __sub_i256(self, other) } } +impl WrappingMul for i256 { fn wrapping_mul(self, _ other: i256) -> i256 { __mul_i256(self, other) } } +impl SaturatingAdd for i256 { + fn saturating_add(self, _ other: i256) -> i256 { + let result = __add_i256(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for i256 { + fn saturating_sub(self, _ other: i256) -> i256 { + let result = __sub_i256(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for i256 { + fn saturating_mul(self, _ other: i256) -> i256 { + if self == 0 || other == 0 { + 0 + } else { + let min: i256 = __bitcast(I256_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I256_SIGN_BIT - 1) + } else { + let result = __mul_i256(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I256_SIGN_BIT) != 0 + if negative { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for i256 { fn shl(self, _ other: i256) -> i256 { __shl_i256(self, other) } } impl Shr for i256 { fn shr(self, _ other: i256) -> i256 { __shr_i256(self, other) } } impl BitAnd for i256 { fn bitand(self, _ other: i256) -> i256 { __bitand_i256(self, other) } } @@ -1045,12 +1928,12 @@ impl Ord for i256 { fn gt(self, _ other: i256) -> bool { __gt_i256(self, other) } fn ge(self, _ other: i256) -> bool { __ge_i256(self, other) } } -impl AddAssign for i256 { fn add_assign(mut self, _ other: i256) { self = self + other } } -impl SubAssign for i256 { fn sub_assign(mut self, _ other: i256) { self = self - other } } -impl MulAssign for i256 { fn mul_assign(mut self, _ other: i256) { self = self * other } } -impl DivAssign for i256 { fn div_assign(mut self, _ other: i256) { self = self / other } } -impl RemAssign for i256 { fn rem_assign(mut self, _ other: i256) { self = self % other } } -impl PowAssign for i256 { fn pow_assign(mut self, _ other: i256) { self = self ** other } } +impl AddAssign for i256 { fn add_assign(self, _ other: i256) -> i256 { self + other } } +impl SubAssign for i256 { fn sub_assign(self, _ other: i256) -> i256 { self - other } } +impl MulAssign for i256 { fn mul_assign(self, _ other: i256) -> i256 { self * other } } +impl DivAssign for i256 { fn div_assign(self, _ other: i256) -> i256 { self / other } } +impl RemAssign for i256 { fn rem_assign(self, _ other: i256) -> i256 { self % other } } +impl PowAssign for i256 { fn pow_assign(self, _ other: i256) -> i256 { self ** other } } impl ShlAssign for i256 { fn shl_assign(mut self, _ other: i256) { self = self << other } } impl ShrAssign for i256 { fn shr_assign(mut self, _ other: i256) { self = self >> other } } impl BitAndAssign for i256 { fn bitand_assign(mut self, _ other: i256) { self = self & other } } @@ -1061,12 +1944,63 @@ impl Default for i256 { fn default() -> Self { 0 } } // u256 impls impl BitNot for u256 { fn bit_not(self) -> u256 { __bitnot_u256(self) } } impl Not for u256 { fn not(self) -> u256 { __bitnot_u256(self) } } -impl Add for u256 { fn add(self, _ other: u256) -> u256 { __add_u256(self, other) } } -impl Sub for u256 { fn sub(self, _ other: u256) -> u256 { __sub_u256(self, other) } } -impl Mul for u256 { fn mul(self, _ other: u256) -> u256 { __mul_u256(self, other) } } -impl Div for u256 { fn div(self, _ other: u256) -> u256 { __div_u256(self, other) } } -impl Rem for u256 { fn rem(self, _ other: u256) -> u256 { __rem_u256(self, other) } } -impl Pow for u256 { fn pow(self, _ other: u256) -> u256 { __pow_u256(self, other) } } +impl Add for u256 { + fn add(self, _ other: u256) -> u256 { + let result = __add_u256(self, other) + if __lt_u256(result, self) { panic() } + result + } +} +impl Sub for u256 { + fn sub(self, _ other: u256) -> u256 { + if __gt_u256(other, self) { panic() } + __sub_u256(self, other) + } +} +impl Mul for u256 { + fn mul(self, _ other: u256) -> u256 { + let result = __mul_u256(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for u256 { + fn div(self, _ other: u256) -> u256 { + if other == 0 { panic() } + __div_u256(self, other) + } +} +impl Rem for u256 { + fn rem(self, _ other: u256) -> u256 { + if other == 0 { panic() } + __rem_u256(self, other) + } +} +impl Pow for u256 { fn pow(self, _ other: u256) -> u256 { pow_checked(self, other) } } +impl WrappingAdd for u256 { fn wrapping_add(self, _ other: u256) -> u256 { __add_u256(self, other) } } +impl WrappingSub for u256 { fn wrapping_sub(self, _ other: u256) -> u256 { __sub_u256(self, other) } } +impl WrappingMul for u256 { fn wrapping_mul(self, _ other: u256) -> u256 { __mul_u256(self, other) } } +impl SaturatingAdd for u256 { + fn saturating_add(self, _ other: u256) -> u256 { + let result = __add_u256(self, other) + if __lt_u256(result, self) { U256_MASK } else { result } + } +} +impl SaturatingSub for u256 { + fn saturating_sub(self, _ other: u256) -> u256 { + if __gt_u256(other, self) { 0 } else { __sub_u256(self, other) } + } +} +impl SaturatingMul for u256 { + fn saturating_mul(self, _ other: u256) -> u256 { + if self == 0 || other == 0 { + 0 + } else { + let result = __mul_u256(self, other) + if result / self != other { U256_MASK } else { result } + } + } +} impl Shl for u256 { fn shl(self, _ other: u256) -> u256 { __shl_u256(self, other) } } impl Shr for u256 { fn shr(self, _ other: u256) -> u256 { __shr_u256(self, other) } } impl BitAnd for u256 { fn bitand(self, _ other: u256) -> u256 { __bitand_u256(self, other) } } @@ -1082,12 +2016,12 @@ impl Ord for u256 { fn gt(self, _ other: u256) -> bool { __gt_u256(self, other) } fn ge(self, _ other: u256) -> bool { __ge_u256(self, other) } } -impl AddAssign for u256 { fn add_assign(mut self, _ other: u256) { self = self + other } } -impl SubAssign for u256 { fn sub_assign(mut self, _ other: u256) { self = self - other } } -impl MulAssign for u256 { fn mul_assign(mut self, _ other: u256) { self = self * other } } -impl DivAssign for u256 { fn div_assign(mut self, _ other: u256) { self = self / other } } -impl RemAssign for u256 { fn rem_assign(mut self, _ other: u256) { self = self % other } } -impl PowAssign for u256 { fn pow_assign(mut self, _ other: u256) { self = self ** other } } +impl AddAssign for u256 { fn add_assign(self, _ other: u256) -> u256 { self + other } } +impl SubAssign for u256 { fn sub_assign(self, _ other: u256) -> u256 { self - other } } +impl MulAssign for u256 { fn mul_assign(self, _ other: u256) -> u256 { self * other } } +impl DivAssign for u256 { fn div_assign(self, _ other: u256) -> u256 { self / other } } +impl RemAssign for u256 { fn rem_assign(self, _ other: u256) -> u256 { self % other } } +impl PowAssign for u256 { fn pow_assign(self, _ other: u256) -> u256 { self ** other } } impl ShlAssign for u256 { fn shl_assign(mut self, _ other: u256) { self = self << other } } impl ShrAssign for u256 { fn shr_assign(mut self, _ other: u256) { self = self >> other } } impl BitAndAssign for u256 { fn bitand_assign(mut self, _ other: u256) { self = self & other } } @@ -1096,15 +2030,106 @@ impl BitXorAssign for u256 { fn bitxor_assign(mut self, _ other: u256) { self = impl Default for u256 { fn default() -> Self { 0 } } // isize impls -impl Neg for isize { fn neg(self) -> isize { __neg_isize(self) } } +impl Neg for isize { + fn neg(self) -> isize { + let min: isize = __bitcast(I256_SIGN_BIT) + if self == min { panic() } + __neg_isize(self) + } +} impl BitNot for isize { fn bit_not(self) -> isize { __bitnot_isize(self) } } impl Not for isize { fn not(self) -> isize { __bitnot_isize(self) } } -impl Add for isize { fn add(self, _ other: isize) -> isize { __add_isize(self, other) } } -impl Sub for isize { fn sub(self, _ other: isize) -> isize { __sub_isize(self, other) } } -impl Mul for isize { fn mul(self, _ other: isize) -> isize { __mul_isize(self, other) } } -impl Div for isize { fn div(self, _ other: isize) -> isize { __div_isize(self, other) } } -impl Rem for isize { fn rem(self, _ other: isize) -> isize { __rem_isize(self, other) } } -impl Pow for isize { fn pow(self, _ other: isize) -> isize { __pow_isize(self, other) } } +impl Add for isize { + fn add(self, _ other: isize) -> isize { + let result = __add_isize(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { panic() } + result + } +} +impl Sub for isize { + fn sub(self, _ other: isize) -> isize { + let result = __sub_isize(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { panic() } + result + } +} +impl Mul for isize { + fn mul(self, _ other: isize) -> isize { + let result = __mul_isize(self, other) + if self != 0 && result / self != other { panic() } + result + } +} +impl Div for isize { + fn div(self, _ other: isize) -> isize { + if other == 0 { panic() } + let min: isize = __bitcast(I256_SIGN_BIT) + if self == min && other == -1 { panic() } + __div_isize(self, other) + } +} +impl Rem for isize { + fn rem(self, _ other: isize) -> isize { + if other == 0 { panic() } + __rem_isize(self, other) + } +} +impl Pow for isize { fn pow(self, _ other: isize) -> isize { pow_checked(self, other) } } +impl WrappingAdd for isize { fn wrapping_add(self, _ other: isize) -> isize { __add_isize(self, other) } } +impl WrappingSub for isize { fn wrapping_sub(self, _ other: isize) -> isize { __sub_isize(self, other) } } +impl WrappingMul for isize { fn wrapping_mul(self, _ other: isize) -> isize { __mul_isize(self, other) } } +impl SaturatingAdd for isize { + fn saturating_add(self, _ other: isize) -> isize { + let result = __add_isize(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign == other_sign && self_sign != result_sign { + if self_sign { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingSub for isize { + fn saturating_sub(self, _ other: isize) -> isize { + let result = __sub_isize(self, other) + let self_sign = (self.to_word() & I256_SIGN_BIT) != 0 + let other_sign = (other.to_word() & I256_SIGN_BIT) != 0 + let result_sign = (result.to_word() & I256_SIGN_BIT) != 0 + if self_sign != other_sign && self_sign != result_sign { + if self_sign { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } +} +impl SaturatingMul for isize { + fn saturating_mul(self, _ other: isize) -> isize { + if self == 0 || other == 0 { + 0 + } else { + let min: isize = __bitcast(I256_SIGN_BIT) + if (self == min && other == -1) || (other == min && self == -1) { + __bitcast(I256_SIGN_BIT - 1) + } else { + let result = __mul_isize(self, other) + if result / self != other { + let negative = ((self.to_word() ^ other.to_word()) & I256_SIGN_BIT) != 0 + if negative { __bitcast(I256_SIGN_BIT) } else { __bitcast(I256_SIGN_BIT - 1) } + } else { + result + } + } + } + } +} impl Shl for isize { fn shl(self, _ other: isize) -> isize { __shl_isize(self, other) } } impl Shr for isize { fn shr(self, _ other: isize) -> isize { __shr_isize(self, other) } } impl BitAnd for isize { fn bitand(self, _ other: isize) -> isize { __bitand_isize(self, other) } } @@ -1120,12 +2145,12 @@ impl Ord for isize { fn gt(self, _ other: isize) -> bool { __gt_isize(self, other) } fn ge(self, _ other: isize) -> bool { __ge_isize(self, other) } } -impl AddAssign for isize { fn add_assign(mut self, _ other: isize) { self = self + other } } -impl SubAssign for isize { fn sub_assign(mut self, _ other: isize) { self = self - other } } -impl MulAssign for isize { fn mul_assign(mut self, _ other: isize) { self = self * other } } -impl DivAssign for isize { fn div_assign(mut self, _ other: isize) { self = self / other } } -impl RemAssign for isize { fn rem_assign(mut self, _ other: isize) { self = self % other } } -impl PowAssign for isize { fn pow_assign(mut self, _ other: isize) { self = self ** other } } +impl AddAssign for isize { fn add_assign(self, _ other: isize) -> isize { self + other } } +impl SubAssign for isize { fn sub_assign(self, _ other: isize) -> isize { self - other } } +impl MulAssign for isize { fn mul_assign(self, _ other: isize) -> isize { self * other } } +impl DivAssign for isize { fn div_assign(self, _ other: isize) -> isize { self / other } } +impl RemAssign for isize { fn rem_assign(self, _ other: isize) -> isize { self % other } } +impl PowAssign for isize { fn pow_assign(self, _ other: isize) -> isize { self ** other } } impl ShlAssign for isize { fn shl_assign(mut self, _ other: isize) { self = self << other } } impl ShrAssign for isize { fn shr_assign(mut self, _ other: isize) { self = self >> other } } impl BitAndAssign for isize { fn bitand_assign(mut self, _ other: isize) { self = self & other } } diff --git a/library/core/src/ops.fe b/library/core/src/ops.fe index 2ebed4f1ed..7ecb314349 100644 --- a/library/core/src/ops.fe +++ b/library/core/src/ops.fe @@ -34,6 +34,37 @@ pub trait Pow { fn pow(self, _ other: T) -> Self::Output } +// Explicit overflow behavior (opt-in) +pub trait WrappingAdd { + type Output = Self + fn wrapping_add(self, _ other: T) -> Self::Output +} + +pub trait WrappingSub { + type Output = Self + fn wrapping_sub(self, _ other: T) -> Self::Output +} + +pub trait WrappingMul { + type Output = Self + fn wrapping_mul(self, _ other: T) -> Self::Output +} + +pub trait SaturatingAdd { + type Output = Self + fn saturating_add(self, _ other: T) -> Self::Output +} + +pub trait SaturatingSub { + type Output = Self + fn saturating_sub(self, _ other: T) -> Self::Output +} + +pub trait SaturatingMul { + type Output = Self + fn saturating_mul(self, _ other: T) -> Self::Output +} + // Bitwise binary operators pub trait BitAnd { type Output = Self @@ -106,29 +137,29 @@ pub trait Index { fn index(self, _ index: I) -> Self::Output } -// Augmented assignment operators (return unit) +// Augmented assignment operators (return updated value) pub trait AddAssign { - fn add_assign(mut self, _ other: T) + fn add_assign(mut self, _ other: T) -> Self } pub trait SubAssign { - fn sub_assign(mut self, _ other: T) + fn sub_assign(mut self, _ other: T) -> Self } pub trait MulAssign { - fn mul_assign(mut self, _ other: T) + fn mul_assign(mut self, _ other: T) -> Self } pub trait DivAssign { - fn div_assign(mut self, _ other: T) + fn div_assign(mut self, _ other: T) -> Self } pub trait RemAssign { - fn rem_assign(mut self, _ other: T) + fn rem_assign(mut self, _ other: T) -> Self } pub trait PowAssign { - fn pow_assign(mut self, _ other: T) + fn pow_assign(mut self, _ other: T) -> Self } pub trait ShlAssign { From 3f68050d298b4f3941e0ce8302903eb133318ba0 Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Wed, 21 Jan 2026 12:44:00 +0000 Subject: [PATCH 2/2] Implement should_revert for in test runner --- crates/codegen/src/lib.rs | 2 +- crates/codegen/src/yul/emitter/mod.rs | 2 +- crates/codegen/src/yul/emitter/module.rs | 31 +++++++++--- crates/codegen/src/yul/mod.rs | 4 +- crates/fe/src/test.rs | 46 ++++++++++++++--- .../fe_test/checked_arithmetic_methods.fe | 50 +++++++++++++++++++ .../checked_arithmetic_reverts.fe | 47 ----------------- .../checked_arithmetic_reverts.snap | 39 --------------- crates/hir/src/core/hir_def/attr.rs | 31 ++++++++++++ 9 files changed, 150 insertions(+), 102 deletions(-) delete mode 100644 crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe delete mode 100644 crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index 0ed029fa69..fe671a40f7 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1,6 +1,6 @@ mod yul; pub use yul::{ - EmitModuleError, TestMetadata, TestModuleOutput, YulError, emit_module_yul, + EmitModuleError, ExpectedRevert, TestMetadata, TestModuleOutput, YulError, emit_module_yul, emit_module_yul_with_layout, emit_test_module_yul, emit_test_module_yul_with_layout, }; diff --git a/crates/codegen/src/yul/emitter/mod.rs b/crates/codegen/src/yul/emitter/mod.rs index e854c74b4c..0b279b1b74 100644 --- a/crates/codegen/src/yul/emitter/mod.rs +++ b/crates/codegen/src/yul/emitter/mod.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::yul::errors::YulError; pub use module::{ - TestMetadata, TestModuleOutput, emit_module_yul, emit_module_yul_with_layout, + ExpectedRevert, TestMetadata, TestModuleOutput, emit_module_yul, emit_module_yul_with_layout, emit_test_module_yul, emit_test_module_yul_with_layout, }; diff --git a/crates/codegen/src/yul/emitter/module.rs b/crates/codegen/src/yul/emitter/module.rs index a7e7826b21..cfb0e408b6 100644 --- a/crates/codegen/src/yul/emitter/module.rs +++ b/crates/codegen/src/yul/emitter/module.rs @@ -36,6 +36,17 @@ pub struct TestMetadata { pub yul: String, pub value_param_count: usize, pub effect_param_count: usize, + pub expected_revert: Option, +} + +/// Describes the expected revert behavior for a test. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ExpectedRevert { + /// Test should revert with any data. + Any, + // Future phases: + // ExactData(Vec), + // Selector([u8; 4]), } /// Output returned by `emit_test_module_yul`. @@ -333,6 +344,7 @@ pub fn emit_test_module_yul_with_layout( yul, value_param_count: test.value_param_count, effect_param_count: test.effect_param_count, + expected_revert: test.expected_revert, }); } @@ -427,6 +439,7 @@ struct TestInfo { object_name: String, value_param_count: usize, effect_param_count: usize, + expected_revert: Option, } /// Dependency set required to emit a single test object. @@ -448,12 +461,15 @@ fn collect_test_infos(db: &dyn HirDb, functions: &[MirFunction<'_>]) -> Vec]) -> Vec TestOutcome { }; // Execute the test bytecode in revm - let (result, logs) = execute_test(&case.display_name, &bytecode, show_logs); + let (result, logs) = execute_test( + &case.display_name, + &bytecode, + show_logs, + case.expected_revert.as_ref(), + ); TestOutcome { result, logs } } /// Deploys and executes compiled test bytecode in revm. /// /// The test passes if the function returns normally, fails if it reverts. +/// When `expected_revert` is set, the logic is inverted: the test passes if it reverts. /// /// * `name` - Display name used for reporting. /// * `bytecode_hex` - Hex-encoded init bytecode for the test object. /// * `show_logs` - Whether to execute with log collection enabled. +/// * `expected_revert` - If set, the test is expected to revert. /// /// Returns the test result and any emitted logs. -fn execute_test(name: &str, bytecode_hex: &str, show_logs: bool) -> (TestResult, Vec) { +fn execute_test( + name: &str, + bytecode_hex: &str, + show_logs: bool, + expected_revert: Option<&ExpectedRevert>, +) -> (TestResult, Vec) { // Deploy the test contract let mut instance = match RuntimeInstance::deploy(bytecode_hex) { Ok(instance) => instance, @@ -337,8 +349,9 @@ fn execute_test(name: &str, bytecode_hex: &str, show_logs: bool) -> (TestResult, instance.call_raw(&[], options).map(|_| Vec::new()) }; - match call_result { - Ok(logs) => ( + match (call_result, expected_revert) { + // Normal test: execution succeeded + (Ok(logs), None) => ( TestResult { name: name.to_string(), passed: true, @@ -346,7 +359,8 @@ fn execute_test(name: &str, bytecode_hex: &str, show_logs: bool) -> (TestResult, }, logs, ), - Err(err) => ( + // Normal test: execution reverted (failure) + (Err(err), None) => ( TestResult { name: name.to_string(), passed: false, @@ -354,6 +368,26 @@ fn execute_test(name: &str, bytecode_hex: &str, show_logs: bool) -> (TestResult, }, Vec::new(), ), + // Expected revert: execution succeeded (failure - should have reverted) + (Ok(_), Some(_)) => ( + TestResult { + name: name.to_string(), + passed: false, + error_message: Some("Expected test to revert, but it succeeded".to_string()), + }, + Vec::new(), + ), + // Expected revert: execution reverted (success) + (Err(_err), Some(ExpectedRevert::Any)) => ( + TestResult { + name: name.to_string(), + passed: true, + error_message: None, + }, + Vec::new(), + ), + // Future: match specific revert data + // (Err(HarnessError::Revert(data)), Some(ExpectedRevert::ExactData(expected))) => { ... } } } diff --git a/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe b/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe index fc48f9c3cb..fbb366a6d6 100644 --- a/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe +++ b/crates/fe/tests/fixtures/fe_test/checked_arithmetic_methods.fe @@ -28,3 +28,53 @@ fn test_wrapping_and_saturating_u8() { } fn as_u8(_ x: u8) -> u8 { x } + +// Tests that verify overflow/underflow causes reverts + +#[test(should_revert)] +fn test_add_overflow_u8() { + let x: u8 = 255 + let y: u8 = x + 1 +} + +#[test(should_revert)] +fn test_aug_assign_overflow_u8() { + let mut x: u8 = 255 + x += 1 +} + +#[test(should_revert)] +fn test_div_by_zero_u8() { + let x: u8 = 1 + let y: u8 = x / 0 +} + +#[test(should_revert)] +fn test_rem_by_zero_u8() { + let x: u8 = 1 + let y: u8 = x % 0 +} + +#[test(should_revert)] +fn test_pow_overflow_u8() { + let x: u8 = 2 + let y: u8 = x ** 8 +} + +#[test(should_revert)] +fn test_neg_overflow_i8() { + let x: i8 = -128 + let y: i8 = -x +} + +#[test(should_revert)] +fn test_div_overflow_i8() { + let x: i8 = -128 + let y: i8 = x / -1 +} + +#[test(should_revert)] +fn test_pow_negative_exp_i8() { + let x: i8 = 2 + let y: i8 = x ** -1 +} diff --git a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe deleted file mode 100644 index 8022ab3ac4..0000000000 --- a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe +++ /dev/null @@ -1,47 +0,0 @@ -#[test] -fn test_add_overflow_u8() { - let x: u8 = 255 - let y: u8 = x + 1 -} - -#[test] -fn test_aug_assign_overflow_u8() { - let mut x: u8 = 255 - x += 1 -} - -#[test] -fn test_div_by_zero_u8() { - let x: u8 = 1 - let y: u8 = x / 0 -} - -#[test] -fn test_rem_by_zero_u8() { - let x: u8 = 1 - let y: u8 = x % 0 -} - -#[test] -fn test_pow_overflow_u8() { - let x: u8 = 2 - let y: u8 = x ** 8 -} - -#[test] -fn test_neg_overflow_i8() { - let x: i8 = -128 - let y: i8 = -x -} - -#[test] -fn test_div_overflow_i8() { - let x: i8 = -128 - let y: i8 = x / -1 -} - -#[test] -fn test_pow_negative_exp_i8() { - let x: i8 = 2 - let y: i8 = x ** -1 -} diff --git a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap b/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap deleted file mode 100644 index 44f9892dd6..0000000000 --- a/crates/fe/tests/fixtures/fe_test_runner/checked_arithmetic_reverts.snap +++ /dev/null @@ -1,39 +0,0 @@ ---- -source: crates/fe/tests/cli_output.rs -assertion_line: 173 -expression: output -input_file: tests/fixtures/fe_test_runner/checked_arithmetic_reverts.fe ---- -=== STDOUT === -test test_add_overflow_u8 ... FAILED -test test_aug_assign_overflow_u8 ... FAILED -test test_div_by_zero_u8 ... FAILED -test test_rem_by_zero_u8 ... FAILED -test test_pow_overflow_u8 ... FAILED -test test_neg_overflow_i8 ... FAILED -test test_div_overflow_i8 ... FAILED -test test_pow_negative_exp_i8 ... FAILED - -test result: FAILED. 0 passed; 8 failed - -failures: - test_add_overflow_u8 - test_aug_assign_overflow_u8 - test_div_by_zero_u8 - test_rem_by_zero_u8 - test_pow_overflow_u8 - test_neg_overflow_i8 - test_div_overflow_i8 - test_pow_negative_exp_i8 - -=== STDERR === - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - Test reverted: 0x - -=== EXIT CODE: 1 === diff --git a/crates/hir/src/core/hir_def/attr.rs b/crates/hir/src/core/hir_def/attr.rs index 097d2da605..e3ed78bda6 100644 --- a/crates/hir/src/core/hir_def/attr.rs +++ b/crates/hir/src/core/hir_def/attr.rs @@ -25,6 +25,37 @@ impl<'db> AttrListId<'db> { } }) } + + /// Returns the attribute with the given name, if present. + pub fn get_attr(self, db: &'db dyn HirDb, name: &str) -> Option<&'db NormalAttr<'db>> { + self.data(db).iter().find_map(|attr| { + if let Attr::Normal(normal_attr) = attr + && let Some(path) = normal_attr.path.to_opt() + && let Some(ident) = path.as_ident(db) + && ident.data(db) == name + { + Some(normal_attr) + } else { + None + } + }) + } +} + +impl<'db> NormalAttr<'db> { + /// Returns true if this attribute has an argument with the given key (no value). + /// + /// For example, `#[test(should_revert)]` has the argument `should_revert`. + pub fn has_arg(&self, db: &'db dyn HirDb, key: &str) -> bool { + self.args.iter().any(|arg| { + arg.value.is_none() + && arg + .key + .to_opt() + .and_then(|p| p.as_ident(db)) + .is_some_and(|ident| ident.data(db) == key) + }) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From)]