Skip to content

Commit

Permalink
feat(wasm): Add bitset for detecting value change (diff)
Browse files Browse the repository at this point in the history
  • Loading branch information
DrSensor committed Oct 17, 2023
1 parent e862b05 commit cdd4d38
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 32 deletions.
22 changes: 21 additions & 1 deletion core/wasm/accessor/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod index;
mod types;

use index::current as index;
use types::{number::Type, JSNumber, Number, C};
use types::{number::Type, Change, JSNumber, Number, C};

type Getter = unsafe fn(Number) -> JSNumber;
type Setter = unsafe fn(Number, JSNumber);
Expand Down Expand Up @@ -40,6 +40,20 @@ fn c_accessor(ty: Type) -> C::Tuple<Getter, Setter> {
C::Tuple::from((getter, setter))
}

#[export_name = "num.xchg"]
unsafe fn exchange(
get: fn(Number) -> JSNumber,
set: fn(Number, JSNumber),
diff: Change,
this: Number,
val: JSNumber,
) {
if get(this) != val {
set_diff(diff);
set(this, val)
}
}

#[export_name = "num.set"]
fn set(set: fn(Number, JSNumber), this: Number, val: JSNumber) {
set(this, val)
Expand All @@ -50,6 +64,12 @@ fn get(get: fn(Number) -> JSNumber, this: Number) -> JSNumber {
get(this)
}

unsafe fn set_diff(diff: Change) {
let diff_offset = (index() as f32 / isize::BITS as f32).floor() as usize;
let diff_ptr = (diff.addr as *mut isize).add(diff_offset);
*diff_ptr |= 1 << index();
}

unsafe fn ptr<T>(this: Number) -> *mut T {
// TODO(unstable): refactor `this.addr as PointerLike` so it can be casted as `*const T` too
(this.addr as *mut T).offset(index())
Expand Down
4 changes: 2 additions & 2 deletions core/wasm/instance/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod scope;
mod types;

use core::arch::wasm32::{memory_grow, memory_size};
use types::{number::Type, Null, Number, C, PAGE};
use types::{number::Type, BitSet, Change, Null, Number, C, PAGE};

extern "C" {
// WARN: Currently `&str` transformed into `param i32 i32` but in the future it might be `stringref`
Expand Down Expand Up @@ -48,7 +48,7 @@ unsafe fn array_new(ty: Type, len: u16, nullable: bool) -> Number {
#[export_name = "num.allocateAUTO"]
unsafe fn array_construct(ty: Type, nullable: bool) -> (Number, u16) {
let len = scope::size();
let addr = if nullable { Null::byte(len) } else { 0 } + offset::get();
let addr = Change::byte(len) + if nullable { Null::byte(len) } else { 0 } + offset::get();
(allocate_at(addr, ty, len), len)
}

Expand Down
25 changes: 18 additions & 7 deletions core/wasm/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ macro_rules! convert_ptr {
}
pub(crate) use convert_ptr;

pub trait BitSet {
/// length of null count in byte
fn byte(len: u16) -> usize {
(len as f32 / byte::BITS as f32).ceil() as usize
}
/// minimum bits (can be used to determine alignment)
const BITS: u32 = u8::BITS;
}

#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Buffer {
Expand All @@ -46,14 +55,16 @@ pub struct Null {
pub addr: usize,
}
convert_ptr!(Null);
impl Null {
/// length of null count in byte
pub fn byte(len: u16) -> usize {
(len as f32 / byte::BITS as f32).ceil() as usize
}
/// minimum bits (can be used to determine alignment)
pub const BITS: u32 = u8::BITS;
impl BitSet for Null {}

#[repr(transparent)]
#[derive(Clone, Copy)]
/// Bits that represent which accessor where the value has changed
pub struct Change {
pub addr: usize,
}
convert_ptr!(Change);
impl BitSet for Change {}

pub trait Layout
where
Expand Down
32 changes: 21 additions & 11 deletions libs/rust/src/datatype/null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ impl<T: Build> Build for self::Null<T> {
unsafe fn accessor() -> Self::Accessor {
T::accessor()
}
fn diff(addr: usize, len: host::Len) -> usize {
addr - types::Null::byte(len) - types::Change::byte(len)
}
unsafe fn allocate(len: host::Len) -> usize {
let ptr = host::num::allocate(T::TYPE_ID, len, true); // BUG: `host::num`? What if `T` is `str`
ptr.addr - types::Null::byte(len)
Expand All @@ -28,36 +31,43 @@ impl<T: Build> Build for self::Null<T> {
let addr = ptr.addr - types::Null::byte(len);
(addr, len)
}
unsafe fn build(len: host::Len, addr: usize, accessor: Self::Accessor) -> Self {
unsafe fn build(
len: host::Len,
addr: usize,
accessor: Self::Accessor,
diff_addr: Option<usize>,
) -> Self {
host::null::noop();
let data = T::build(len, addr + types::Null::byte(len), accessor);
let ptr = types::Null { addr };
self::Null { ptr, data }
self::Null {
ptr: types::Null {
addr: addr - types::Null::byte(len),
},
data: T::build(len, addr + types::Null::byte(len), accessor, diff_addr),
}
}
}

impl<T: Build> Default for Null<T> {
fn default() -> Self {
unsafe {
let (addr, len) = Self::auto_allocate();
Self::build(len, addr, T::accessor())
Self::build(len, addr, Self::accessor(), Some(Self::diff(addr, len)))
}
}
}

impl<T: Build> self::Null<T> {
pub fn prop_name(name: impl Into<&'static str>) -> Self {
unsafe {
let (addr, len) = Self::prop_allocate(name.into());
Self::build(len, addr, Self::accessor())
Self::build(len, addr, Self::accessor(), Some(Self::diff(addr, len)))
}
}
pub fn new(len: host::Len) -> Self {
unsafe { Self::build(len, Self::allocate(len), T::accessor()) }
unsafe { Self::build(len, Self::allocate(len), Self::accessor(), None) }
}
}

impl<T: Series + Build> Series for self::Null<T> {
impl<T: Build + Series> Series for self::Null<T> {
const TYPE_ID: i8 = <T as Series>::TYPE_ID;
fn addr(&self) -> usize {
self.data.addr()
Expand All @@ -70,9 +80,9 @@ impl<T: Series + Build> Series for self::Null<T> {
impl<T: Build + Accessor> Accessor for self::Null<T> {
type Type = Option<T::Type>;
fn set(&self, value: Self::Type) {
if let Some(val) = value {
if let Some(value) = value {
unsafe { host::null::clr(self.ptr) };
self.data.set(val);
self.data.set(value)
} else {
unsafe { host::null::set(self.ptr) };
}
Expand Down
37 changes: 28 additions & 9 deletions libs/rust/src/datatype/number.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
extern crate alloc;
use crate::{host, types, Accessor, Build, Series};
use core::primitive;
use types::number::{JSNumber, Type};
use types::BitSet;

macro_rules! bridge {
($ty:ident, $Ty:ident) => {
#[allow(non_camel_case_types)]
pub struct $ty {
len: host::Len,
ptr: types::Number,
diff: Option<types::Change>, // WARN: Unfortunaly the compiler can't optimize this even when initialize as None
accr: (host::num::Getter, host::num::Setter),
}

Expand All @@ -18,6 +21,9 @@ macro_rules! bridge {
host::num::accessor_noop();
host::num::accessor(Type::$Ty as primitive::i8).into()
}
fn diff(addr: usize, len: host::Len) -> usize {
addr - types::Change::byte(len)
}
unsafe fn allocate(len: host::Len) -> usize {
host::num::alloc_noop();
let ptr = host::num::allocate(Type::$Ty as primitive::i8, len, false);
Expand All @@ -34,30 +40,38 @@ macro_rules! bridge {
host::num::allocatePROP(prop_name, Type::$Ty as primitive::i8, false).into();
(ptr.addr, len)
}
unsafe fn build(len: host::Len, addr: usize, accr: Self::Accessor) -> Self {
let ptr = types::Number { addr };
$ty { len, ptr, accr }
unsafe fn build(
len: host::Len,
addr: usize,
accr: Self::Accessor,
diff_addr: Option<usize>,
) -> Self {
$ty {
len,
ptr: types::Number { addr },
diff: diff_addr.map(|addr| types::Change { addr }),
accr,
}
}
}

impl Default for $ty {
fn default() -> Self {
unsafe {
let (addr, len) = Self::auto_allocate();
Self::build(len, addr, Self::accessor())
Self::build(len, addr, Self::accessor(), Some(Self::diff(addr, len)))
}
}
}

impl self::$ty {
pub fn prop_name(name: impl Into<&'static str>) -> Self {
unsafe {
let (addr, len) = Self::prop_allocate(name.into());
Self::build(len, addr, Self::accessor())
Self::build(len, addr, Self::accessor(), Some(Self::diff(addr, len)))
}
}
pub fn new(len: host::Len) -> Self {
unsafe { Self::build(len, Self::allocate(len), Self::accessor()) }
unsafe { Self::build(len, Self::allocate(len), Self::accessor(), None) }
}
}

Expand All @@ -74,8 +88,13 @@ macro_rules! bridge {
impl Accessor for self::$ty {
type Type = primitive::$ty;
fn set(&self, value: Self::Type) {
let (_, setter) = self.accr;
unsafe { host::num::set(setter, self.ptr, value as JSNumber) }
let value = value as JSNumber;
let (getter, setter) = self.accr;
if let Some(diff) = self.diff {
unsafe { host::num::exchange(getter, setter, diff, self.ptr, value) }
} else {
unsafe { host::num::set(setter, self.ptr, value) }
}
}
fn get(&self) -> Self::Type {
let (getter, _) = self.accr;
Expand Down
8 changes: 7 additions & 1 deletion libs/rust/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(improper_ctypes)]
#![allow(dead_code)]

pub type Len = u16;
pub type TypeId = i8;
Expand All @@ -13,6 +14,8 @@ pub mod null {

#[link_name = "null.set"]
pub fn set(ptr: Null);
#[link_name = "null.tgl"]
pub fn tgl(ptr: Null);
#[link_name = "null.clr"]
pub fn clr(ptr: Null);
#[link_name = "null.chk"]
Expand All @@ -25,7 +28,7 @@ C::Item_primitive!(Len);

pub mod num {
use super::{Len, TypeId};
use crate::types::{JSNumber, Number, C};
use crate::types::{Change, JSNumber, Number, C};

pub type Setter = extern "C" fn(Number, JSNumber);
pub type Getter = extern "C" fn(Number) -> JSNumber;
Expand Down Expand Up @@ -71,6 +74,9 @@ pub mod num {
#[link_name = "num.cABIaccessor"]
pub fn accessor(ty: TypeId) -> C::Tuple<Getter, Setter>;

#[link_name = "num.xchg"]
pub fn exchange(getter: Getter, setter: Setter, diff: Change, ptr: Number, value: JSNumber);

#[link_name = "num.set"]
pub fn set(setter: Setter, ptr: Number, value: JSNumber);
#[link_name = "num.get"]
Expand Down
8 changes: 7 additions & 1 deletion libs/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ pub trait Build {
const TYPE_ID: primitive::i8;
type Accessor;
unsafe fn accessor() -> Self::Accessor;
fn diff(addr: usize, len: host::Len) -> usize;
unsafe fn allocate(len: host::Len) -> usize;
unsafe fn auto_allocate() -> (usize, host::Len);
unsafe fn prop_allocate(prop_name: &str) -> (usize, host::Len);
unsafe fn build(len: host::Len, addr: usize, accessor: Self::Accessor) -> Self;
unsafe fn build(
len: host::Len,
addr: usize,
accessor: Self::Accessor,
diff_addr: Option<usize>,
) -> Self;
}

0 comments on commit cdd4d38

Please sign in to comment.