Skip to content

Commit

Permalink
Add support for wrapping add/sub/mul
Browse files Browse the repository at this point in the history
This adds Int.wrapping_add, Int.wrapping_sub, and Int.wrapping_mul, all
which perform wrapping arithmetic instead of panicking upon overflow.
Other wrapping operations (e.g. wrapping division) aren't supported as
we currently don't have a use for them, and they're not that common in
general (that we know of at least).

Changelog: added
  • Loading branch information
yorickpeterse committed Oct 10, 2022
1 parent d71ffde commit 3956f40
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 0 deletions.
15 changes: 15 additions & 0 deletions bytecode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ pub enum Opcode {
IntBitNot,
IntRotateLeft,
IntRotateRight,
IntWrappingAdd,
IntWrappingSub,
IntWrappingMul,
}

impl Opcode {
Expand Down Expand Up @@ -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)),
};

Expand Down Expand Up @@ -386,6 +392,9 @@ impl Opcode {
Opcode::IntBitNot => 108,
Opcode::IntRotateLeft => 109,
Opcode::IntRotateRight => 110,
Opcode::IntWrappingAdd => 111,
Opcode::IntWrappingSub => 112,
Opcode::IntWrappingMul => 113,
}
}

Expand Down Expand Up @@ -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",
}
}

Expand Down Expand Up @@ -636,6 +648,9 @@ impl Opcode {
Opcode::IntBitNot => 2,
Opcode::IntRotateLeft => 3,
Opcode::IntRotateRight => 3,
Opcode::IntWrappingAdd => 3,
Opcode::IntWrappingSub => 3,
Opcode::IntWrappingMul => 3,
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/type_check/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![
Expand Down
36 changes: 36 additions & 0 deletions libstd/src/std/int.inko
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions libstd/test/std/test_int.inko
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
39 changes: 39 additions & 0 deletions vm/src/instructions/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Pointer, String> {
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,
Expand Down Expand Up @@ -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<Pointer, String> {
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,
Expand All @@ -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<Pointer, String> {
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,
Expand Down
24 changes: 24 additions & 0 deletions vm/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand Down

0 comments on commit 3956f40

Please sign in to comment.