Skip to content

Commit

Permalink
Add Int.rotate_left and Int.rotate_right
Browse files Browse the repository at this point in the history
These methods will be needed to (somewhat) efficiently implement
cryptographic hash functions (amongst other things).

As these methods are likely to be used in performance critical code
they're implemented as instructions instead of mimicking them using bit
shifting in Inko.

Changelog: added
  • Loading branch information
yorickpeterse committed Oct 10, 2022
1 parent 743f8a3 commit d71ffde
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 0 deletions.
10 changes: 10 additions & 0 deletions bytecode/src/lib.rs
Expand Up @@ -148,6 +148,8 @@ pub enum Opcode {
Pop,
FuturePoll,
IntBitNot,
IntRotateLeft,
IntRotateRight,
}

impl Opcode {
Expand Down Expand Up @@ -262,6 +264,8 @@ impl Opcode {
106 => Opcode::Pop,
107 => Opcode::FuturePoll,
108 => Opcode::IntBitNot,
109 => Opcode::IntRotateLeft,
110 => Opcode::IntRotateRight,
_ => return Err(format!("The opcode {} is invalid", byte)),
};

Expand Down Expand Up @@ -380,6 +384,8 @@ impl Opcode {
Opcode::Pop => 106,
Opcode::FuturePoll => 107,
Opcode::IntBitNot => 108,
Opcode::IntRotateLeft => 109,
Opcode::IntRotateRight => 110,
}
}

Expand Down Expand Up @@ -494,6 +500,8 @@ impl Opcode {
Opcode::Pop => "pop",
Opcode::FuturePoll => "future_poll",
Opcode::IntBitNot => "int_bit_not",
Opcode::IntRotateLeft => "int_rotate_left",
Opcode::IntRotateRight => "int_rotate_right",
}
}

Expand Down Expand Up @@ -626,6 +634,8 @@ impl Opcode {
Opcode::Pop => 1,
Opcode::FuturePoll => 2,
Opcode::IntBitNot => 2,
Opcode::IntRotateLeft => 3,
Opcode::IntRotateRight => 3,
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/type_check/methods.rs
Expand Up @@ -1518,6 +1518,8 @@ pub(crate) fn define_builtin_functions(state: &mut State) -> bool {
(Opcode::ProcessSuspend, nil),
(Opcode::FuturePoll, any),
(Opcode::IntBitNot, int),
(Opcode::IntRotateLeft, int),
(Opcode::IntRotateRight, int),
];

let macros = vec![
Expand Down
20 changes: 20 additions & 0 deletions libstd/src/std/int.inko
Expand Up @@ -231,6 +231,26 @@ class builtin Int {
fn pub not -> Int {
_INKO.int_bit_not(self)
}

# Shifts the bits to the left, wrapping the truncated bits to the end of the
# resulting integer.
#
# # Examples
#
# 0xaa00000000006e1.rotate_left(12) # => 0x6e10aa
fn pub rotate_left(amount: Int) -> Int {
_INKO.int_rotate_left(self, amount)
}

# Shifts the bits to the right, wrapping the truncated bits to the end of the
# resulting integer.
#
# # Examples
#
# 0x6e10aa.rotate_right(12) # => 0xaa00000000006e1
fn pub rotate_right(amount: Int) -> Int {
_INKO.int_rotate_right(self, amount)
}
}

impl ToInt for Int {
Expand Down
8 changes: 8 additions & 0 deletions libstd/test/std/test_int.inko
Expand Up @@ -242,4 +242,12 @@ fn pub tests(t: mut Tests) {
t.equal(MAX.not, MIN)
t.equal(MIN.not, MAX)
}

t.test('Int.rotate_left') fn (t) {
t.equal(0xAA00000000006E1.rotate_left(12), 0x6E10AA)
}

t.test('Int.rotate_right') fn (t) {
t.equal(0x6E10AA.rotate_right(12), 0xAA00000000006E1)
}
}
26 changes: 26 additions & 0 deletions vm/src/instructions/integer.rs
Expand Up @@ -162,6 +162,32 @@ pub(crate) fn not(state: &State, ptr: Pointer) -> Pointer {
Int::alloc(state.permanent_space.int_class(), value)
}

#[inline(always)]
pub(crate) fn rotate_left(
state: &State,
left: Pointer,
right: Pointer,
) -> Pointer {
let lhs = unsafe { Int::read(left) };
let rhs = unsafe { Int::read(right) as u32 };
let value = lhs.rotate_left(rhs);

Int::alloc(state.permanent_space.int_class(), value)
}

#[inline(always)]
pub(crate) fn rotate_right(
state: &State,
left: Pointer,
right: Pointer,
) -> Pointer {
let lhs = unsafe { Int::read(left) };
let rhs = unsafe { Int::read(right) as u32 };
let value = lhs.rotate_right(rhs);

Int::alloc(state.permanent_space.int_class(), value)
}

#[inline(always)]
pub(crate) fn shl(
state: &State,
Expand Down
16 changes: 16 additions & 0 deletions vm/src/machine.rs
Expand Up @@ -278,6 +278,22 @@ impl<'a> Machine<'a> {

state.context.set_register(reg, res);
}
Opcode::IntRotateLeft => {
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::rotate_left(self.state, a, b);

state.context.set_register(reg, res);
}
Opcode::IntRotateRight => {
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::rotate_right(self.state, a, b);

state.context.set_register(reg, res);
}
Opcode::IntShl => {
let reg = ins.arg(0);
let a = state.context.get_register(ins.arg(1));
Expand Down

0 comments on commit d71ffde

Please sign in to comment.