diff --git a/bytecode/src/lib.rs b/bytecode/src/lib.rs index 816381235..eaacedf27 100644 --- a/bytecode/src/lib.rs +++ b/bytecode/src/lib.rs @@ -150,6 +150,9 @@ pub enum Opcode { IntBitNot, IntRotateLeft, IntRotateRight, + IntWrappingAdd, + IntWrappingSub, + IntWrappingMul, } impl Opcode { @@ -266,6 +269,9 @@ impl Opcode { 108 => Opcode::IntBitNot, 109 => Opcode::IntRotateLeft, 110 => Opcode::IntRotateRight, + 111 => Opcode::IntWrappingAdd, + 112 => Opcode::IntWrappingSub, + 113 => Opcode::IntWrappingMul, _ => return Err(format!("The opcode {} is invalid", byte)), }; @@ -386,6 +392,9 @@ impl Opcode { Opcode::IntBitNot => 108, Opcode::IntRotateLeft => 109, Opcode::IntRotateRight => 110, + Opcode::IntWrappingAdd => 111, + Opcode::IntWrappingSub => 112, + Opcode::IntWrappingMul => 113, } } @@ -502,6 +511,9 @@ impl Opcode { Opcode::IntBitNot => "int_bit_not", Opcode::IntRotateLeft => "int_rotate_left", Opcode::IntRotateRight => "int_rotate_right", + Opcode::IntWrappingAdd => "int_wrapping_add", + Opcode::IntWrappingSub => "int_wrapping_sub", + Opcode::IntWrappingMul => "int_wrapping_mul", } } @@ -636,6 +648,9 @@ impl Opcode { Opcode::IntBitNot => 2, Opcode::IntRotateLeft => 3, Opcode::IntRotateRight => 3, + Opcode::IntWrappingAdd => 3, + Opcode::IntWrappingSub => 3, + Opcode::IntWrappingMul => 3, } } diff --git a/compiler/src/type_check/methods.rs b/compiler/src/type_check/methods.rs index 97ea75ee6..a376727cd 100644 --- a/compiler/src/type_check/methods.rs +++ b/compiler/src/type_check/methods.rs @@ -1520,6 +1520,9 @@ pub(crate) fn define_builtin_functions(state: &mut State) -> bool { (Opcode::IntBitNot, int), (Opcode::IntRotateLeft, int), (Opcode::IntRotateRight, int), + (Opcode::IntWrappingAdd, int), + (Opcode::IntWrappingSub, int), + (Opcode::IntWrappingMul, int), ]; let macros = vec![ diff --git a/libstd/src/std/int.inko b/libstd/src/std/int.inko index 2f1664ea0..72e6c2a42 100644 --- a/libstd/src/std/int.inko +++ b/libstd/src/std/int.inko @@ -251,6 +251,42 @@ class builtin Int { fn pub rotate_right(amount: Int) -> Int { _INKO.int_rotate_right(self, amount) } + + # Adds `other` to `self`, wrapping around when overflowing. + # + # # Examples + # + # import std::int::(MAX, MIN) + # + # 1.wrapping_add(1) # => 2 + # MAX.wrapping_add(1) # => MAX + fn pub wrapping_add(other: Int) -> Int { + _INKO.int_wrapping_add(self, other) + } + + # Subtracts `other` from `self`, wrapping around when overflowing. + # + # # Examples + # + # import std::int::(MAX, MIN) + # + # 1.wrapping_sub(1) # => 0 + # MIN.wrapping_sub(1) # => MAX + fn pub wrapping_sub(other: Int) -> Int { + _INKO.int_wrapping_sub(self, other) + } + + # Multiplies `other` with `self`, wrapping around when overflowing. + # + # # Examples + # + # import std::int::(MAX, MIN) + # + # 1.wrapping_mul(2) # => 2 + # MAX.wrapping_mul(2) # => -2 + fn pub wrapping_mul(other: Int) -> Int { + _INKO.int_wrapping_mul(self, other) + } } impl ToInt for Int { diff --git a/libstd/test/std/test_int.inko b/libstd/test/std/test_int.inko index 1a0abe502..bd2cda1dc 100644 --- a/libstd/test/std/test_int.inko +++ b/libstd/test/std/test_int.inko @@ -250,4 +250,19 @@ fn pub tests(t: mut Tests) { t.test('Int.rotate_right') fn (t) { t.equal(0x6E10AA.rotate_right(12), 0xAA00000000006E1) } + + t.test('Int.wrapping_add') fn (t) { + t.equal(MAX.wrapping_add(1), MIN) + t.equal(0.wrapping_add(1), 1) + } + + t.test('Int.wrapping_sub') fn (t) { + t.equal(MIN.wrapping_sub(1), MAX) + t.equal(1.wrapping_sub(1), 0) + } + + t.test('Int.wrapping_mul') fn (t) { + t.equal(MAX.wrapping_mul(2), -2) + t.equal(1.wrapping_mul(2), 2) + } } diff --git a/vm/src/instructions/integer.rs b/vm/src/instructions/integer.rs index 976fe8335..c4a0f00e9 100644 --- a/vm/src/instructions/integer.rs +++ b/vm/src/instructions/integer.rs @@ -73,6 +73,19 @@ pub(crate) fn add( Ok(Int::alloc(state.permanent_space.int_class(), value)) } +#[inline(always)] +pub(crate) fn wrapping_add( + state: &State, + left: Pointer, + right: Pointer, +) -> Result { + let lhs = unsafe { Int::read(left) }; + let rhs = unsafe { Int::read(right) }; + let value = lhs.wrapping_add(rhs); + + Ok(Int::alloc(state.permanent_space.int_class(), value)) +} + #[inline(always)] pub(crate) fn div( state: &State, @@ -112,6 +125,19 @@ pub(crate) fn mul( Ok(Int::alloc(state.permanent_space.int_class(), value)) } +#[inline(always)] +pub(crate) fn wrapping_mul( + state: &State, + left: Pointer, + right: Pointer, +) -> Result { + let lhs = unsafe { Int::read(left) }; + let rhs = unsafe { Int::read(right) }; + let value = lhs.wrapping_mul(rhs); + + Ok(Int::alloc(state.permanent_space.int_class(), value)) +} + #[inline(always)] pub(crate) fn sub( state: &State, @@ -123,6 +149,19 @@ pub(crate) fn sub( Ok(Int::alloc(state.permanent_space.int_class(), value)) } +#[inline(always)] +pub(crate) fn wrapping_sub( + state: &State, + left: Pointer, + right: Pointer, +) -> Result { + let lhs = unsafe { Int::read(left) }; + let rhs = unsafe { Int::read(right) }; + let value = lhs.wrapping_sub(rhs); + + Ok(Int::alloc(state.permanent_space.int_class(), value)) +} + #[inline(always)] pub(crate) fn modulo( state: &State, diff --git a/vm/src/machine.rs b/vm/src/machine.rs index db4ffd8b9..34622c102 100644 --- a/vm/src/machine.rs +++ b/vm/src/machine.rs @@ -215,6 +215,14 @@ impl<'a> Machine<'a> { state.context.set_register(reg, res); } + Opcode::IntWrappingAdd => { + let reg = ins.arg(0); + let a = state.context.get_register(ins.arg(1)); + let b = state.context.get_register(ins.arg(2)); + let res = integer::wrapping_add(self.state, a, b)?; + + state.context.set_register(reg, res); + } Opcode::IntDiv => { let reg = ins.arg(0); let a = state.context.get_register(ins.arg(1)); @@ -231,6 +239,14 @@ impl<'a> Machine<'a> { state.context.set_register(reg, res); } + Opcode::IntWrappingMul => { + let reg = ins.arg(0); + let a = state.context.get_register(ins.arg(1)); + let b = state.context.get_register(ins.arg(2)); + let res = integer::wrapping_mul(self.state, a, b)?; + + state.context.set_register(reg, res); + } Opcode::IntSub => { let reg = ins.arg(0); let a = state.context.get_register(ins.arg(1)); @@ -239,6 +255,14 @@ impl<'a> Machine<'a> { state.context.set_register(reg, res); } + Opcode::IntWrappingSub => { + let reg = ins.arg(0); + let a = state.context.get_register(ins.arg(1)); + let b = state.context.get_register(ins.arg(2)); + let res = integer::wrapping_sub(self.state, a, b)?; + + state.context.set_register(reg, res); + } Opcode::IntMod => { let reg = ins.arg(0); let a = state.context.get_register(ins.arg(1));