Skip to content

Commit

Permalink
feat(wasm): Simplify registering accessor via prop_name("…")
Browse files Browse the repository at this point in the history
* Introduce `[comp].allocatePROP`

* Imports `env:cABIprop` and `env:cache` in instance/[comp].wasm
  • Loading branch information
DrSensor committed Oct 13, 2023
1 parent 7b82e1a commit e862b05
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 66 deletions.
53 changes: 39 additions & 14 deletions core/js/registry.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { builtinSet } from "./bind/accessor.js";
import type { Descriptors, NullValue, Prototype } from "./types.d.ts";
import type { Descriptors, Module, NullValue, Prototype } from "./types.d.ts";

export type Cache<P extends Prototype = Prototype> = [
descriptors: Descriptors<P>,
Expand All @@ -10,33 +10,58 @@ type Targets = Array<
Node | [target: Element, attrName: string, builtin?: typeof builtinSet]
>;

export type Binder = AccessorBinder;
export type AccessorBinder = {
databank_: unknown[];
export interface Binder {
targets_: Targets[];
dedupeRender_?: VoidFunction;
};
}

export type Registry<P extends Prototype> = WeakMap<P, Cache<P>>;
export interface AccessorBinder extends Binder {
/** @todo rename it as `dataframe_` */
databank_: unknown[];
}
export interface WasmBinder extends Binder {
series_: [addr: number, len: number][];
}

/////////////////////////////////////////////////////////////////////
export interface PropCache {
[propName: string]: WasmBinder;
}

declare const registry: Registry<Prototype>; // TODO: consider using Attr or target Element or host Element as a key (maybe 🤔)
export default registry;
export type Registry<Key extends WeakKey> = WeakMap<
Key,
Key extends Prototype ? Cache<Key> : PropCache
>;

/////////////////////////////////////////////////////////////////////

/** `instance[index]` can be used to get instance ID number */
export declare const index: unique symbol;
export const index: unique symbol;

/** Read current event
Use {@link setCurrentEvent} to set this */
export let event: Event | NullValue;
/** Setter function to set current {@link event} */
export function setCurrentEvent(event_: typeof event): void;

/** Read current value
Use {@link setCurrentValue} to set this */
export let value: unknown;
/** Setter function to set current {@link value} */
export function setCurrentValue(value_: typeof value): void;

/** Setter function to set current {@link event} */
export function setCurrentEvent(event_: typeof event): void;
// TODO: consider using Attr or target Element or host Element as a key (maybe 🤔)
declare const registry: Registry<Prototype | typeof module>;
export default registry;

/** Setter function to set current {@link value} */
export function setCurrentValue(value_: unknown): void;
/** Read current module Object
Use {@link setCurrentModule} to set this */
export let module: Module | WebAssembly.Exports;
/** Setter function to set current {@link module} */
export function setCurrentModule(module_: typeof module): void;

/** Map prop_name to wasm memory address */
export function cABIregister(
propNameAddr: number,
propNameLen: number,
memAddr: number,
): void;
15 changes: 7 additions & 8 deletions core/js/registry.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
/// <reference types="./registry.d.ts" />
/** @typedef {import("./registry.js")} $ */

/** @type $["event"] */ export let event;
/** @type $["setCurrentEvent"] */
export const setCurrentEvent = (event_) => {
event = event_;
};

/** @type $["event"] */
export let event;

/** @type $["value"] */ export let value;
/** @type $["setCurrentValue"] */
export const setCurrentValue = (value_) => {
value = value_;
};

/** @type $["value"] */
export let value;
/** @type $["module"] */ export let module;
/** @type $["setCurrentModule"] */
export const setCurrentmodule = (module_) => {
module = module_;
};

/** @type $["index"] */
export const index = Symbol();

/** @type $["default"] */
export default new WeakMap();
6 changes: 0 additions & 6 deletions core/js/wasm/globals.js

This file was deleted.

16 changes: 16 additions & 0 deletions core/js/wasm/imports.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { PropCache, WasmBinder } from "../registry.js";

export const memory: WebAssembly.Memory;
export const offset: WebAssembly.Global,
index: WebAssembly.Global,
scopeSize: WebAssembly.Global;

export function prop(
propNameAddr: number,
propNameLen: number,
): [addr: number, len: number];

export function cache(addr: number, len: number): void;

declare type MayCache = PropCache | undefined;
declare type Series = WasmBinder["series_"];
29 changes: 29 additions & 0 deletions core/js/wasm/imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// <reference types="./imports.d.ts" />
/** @typedef {import("./imports.js")} $ */
/** @typedef {import("./imports.js").MayCache} MayCache */
/** @typedef {import("./imports.js").Series} Series */
import registry, { module } from "../registry.js";

export const memory = new WebAssembly.Memory({ initial: 1 });

const muti32 = () => new WebAssembly.Global({ mutable: true, value: "i32" });
export const offset = muti32(),
index = muti32(),
scopeSize = muti32();

const utf8 = new TextDecoder();

let /** @type string|undefined */ propName;
let /** @type Series|undefined */ series_;

/** @type $["prop"] */
export function prop(propNameAddr, propNameLen) {
propName = utf8.decode(memory.buffer.slice(propNameAddr, propNameLen));
series_ = /** @type MayCache */ (registry.get(module))?.[propName].series_;
return series_?.[index.value] ?? [0, -1];
}

/** @type $["cache"] */
export function cache(addr, len) {
/** @type Series */ (series_)[index.value] = [addr, len];
}
5 changes: 3 additions & 2 deletions core/js/wasm/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
/** @typedef {import("./number.js")} $ */

import * as load from "../utils/load.js";
import { index, memory, offset, scopeSize } from "./globals.js";
import { index, memory, offset, scopeSize } from "./imports.js"; // wasm globals
import { cache, prop } from "./imports.js"; // js function

export const instance = /** @type $["instance"] */ (
load.wasm.instantiate(
new URL("../../wasm/instance/number.wasm", import.meta.url),
{ env: { offset, scopeSize, memory } },
{ env: { offset, scopeSize, memory, prop, cache } },
)
);

Expand Down
3 changes: 3 additions & 0 deletions core/wasm/Knitfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ opt := $opt -Oz
opt := $opt $(concat(prefix(feats.binaryen, "--enable-"), " "))
if cli.debug > 0 then
opt := $opt -g
if cli.debug < 2 then
opt := $opt --strip-dwarf
end
end
--

Expand Down
48 changes: 39 additions & 9 deletions core/wasm/instance/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ mod types;
use core::arch::wasm32::{memory_grow, memory_size};
use types::{number::Type, Null, Number, C, PAGE};

extern "C" {
// WARN: Currently `&str` transformed into `param i32 i32` but in the future it might be `stringref`
#[allow(improper_ctypes)]
fn prop(prop_name: &str) -> (Number, i32);
fn cache(addr: Number, len: u16);
}

#[export_name = "num.arr.noop"]
fn noop() {}

#[export_name = "num.allocate"]
#[inline(never)]
unsafe fn array_of(ty: Type, len: u16, nullable: bool) -> Number {
let addr = offset::get() + if nullable { Null::byte(len) } else { 0 };

unsafe fn allocate_at(addr: usize, ty: Type, len: u16) -> Number {
let ty = ty as i8;
let byte = if ty > -64 {
ty.unsigned_abs()
Expand All @@ -35,16 +38,43 @@ unsafe fn array_of(ty: Type, len: u16, nullable: bool) -> Number {
Number { addr }
}

#[export_name = "num.allocate"]
#[inline(never)]
unsafe fn array_new(ty: Type, len: u16, nullable: bool) -> Number {
let addr = if nullable { Null::byte(len) } else { 0 } + offset::get();
allocate_at(addr, ty, len)
}

#[export_name = "num.allocateAUTO"]
unsafe fn array_new(ty: Type, nullable: bool) -> (Number, u16) {
let size = scope::size();
(array_of(ty, size, nullable), size)
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();
(allocate_at(addr, ty, len), len)
}

#[export_name = "num.allocatePROP"]
unsafe fn array_of(prop_name: &str, ty: Type, nullable: bool) -> (Number, u16) {
let (addr, len) = match prop(prop_name) {
(addr, len) if len != -1 => (addr, len as u16),
_ => {
let (addr, len) = array_construct(ty, nullable);
cache(addr, len);
(addr, len)
}
};
(addr, len)
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
#[export_name = "num.cABIallocatePROP"]
unsafe fn c_array_of(prop_name: &str, ty: Type, nullable: bool) -> C::Tuple<Number, u16> {
C::Tuple::from(array_of(prop_name, ty, nullable))
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
#[export_name = "num.cABIallocateAUTO"]
unsafe fn c_array_new(ty: Type, nullable: bool) -> C::Tuple<Number, u16> {
C::Tuple::from(array_new(ty, nullable))
C::Tuple::from(array_construct(ty, nullable))
}

C::primitive_Item!(u16);
33 changes: 7 additions & 26 deletions examples/rust/cargo/no-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@
use libnusa::{number, Accessor};
use std::sync::OnceLock;

#[derive(Default)]
struct This {
count: number::u8,
}
static THIS: OnceLock<This> = OnceLock::new();

#[no_mangle]
fn constructor() {
let _ = THIS.set(This::default());
}

#[no_mangle]
fn set_count(value: u8) {
if let Some(this) = THIS.get() {
this.count.set(value);
}
}

#[no_mangle]
fn get_count() -> u8 {
if let Some(this) = THIS.get() {
this.count.get()
} else {
unreachable!()
}
thread_local! {
static THIS: This = This {
count: number::u8::prop_name("count"),
};
}

#[no_mangle]
fn increment() {
if let Some(this) = THIS.get() {
this.count.set(this.count.get() + 1)
} else {
unreachable!()
}
THIS.with(|this| {
this.count.set(this.count.get() + 1);
})
}
16 changes: 15 additions & 1 deletion libs/rust/src/datatype/null.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{host, types, Accessor, Build, Series};
use types::BitSet;

pub struct Null<T: Build> {
ptr: types::Null, // TODO: pass this on every *fastops/number.wasm* functions
ptr: types::Null,
data: T,
}

Expand All @@ -16,10 +17,17 @@ impl<T: Build> Build for self::Null<T> {
ptr.addr - types::Null::byte(len)
}
unsafe fn auto_allocate() -> (usize, host::Len) {
host::null::noop();
let (ptr, len) = host::num::allocateAUTO(T::TYPE_ID, true).into(); // BUG: `host::num`? What if `T` is `str`
let addr = ptr.addr - types::Null::byte(len);
(addr, len)
}
unsafe fn prop_allocate(prop_name: &str) -> (usize, host::Len) {
host::num::alloc_noop();
let (ptr, len) = host::num::allocatePROP(prop_name, T::TYPE_ID, false).into();
let addr = ptr.addr - types::Null::byte(len);
(addr, len)
}
unsafe fn build(len: host::Len, addr: usize, accessor: Self::Accessor) -> Self {
host::null::noop();
let data = T::build(len, addr + types::Null::byte(len), accessor);
Expand All @@ -38,6 +46,12 @@ impl<T: Build> Default for Null<T> {
}

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())
}
}
pub fn new(len: host::Len) -> Self {
unsafe { Self::build(len, Self::allocate(len), T::accessor()) }
}
Expand Down
12 changes: 12 additions & 0 deletions libs/rust/src/datatype/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ macro_rules! bridge {
let (ptr, len) = host::num::allocateAUTO(Type::$Ty as primitive::i8, false).into();
(ptr.addr, len)
}
unsafe fn prop_allocate(prop_name: &str) -> (usize, host::Len) {
host::num::alloc_noop();
let (ptr, len) =
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 }
Expand All @@ -44,6 +50,12 @@ macro_rules! bridge {
}

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())
}
}
pub fn new(len: host::Len) -> Self {
unsafe { Self::build(len, Self::allocate(len), Self::accessor()) }
}
Expand Down
10 changes: 10 additions & 0 deletions libs/rust/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(improper_ctypes)]

pub type Len = u16;
pub type TypeId = i8;

Expand Down Expand Up @@ -46,6 +48,14 @@ pub mod num {
#[cfg(not(target_feature = "multivalue"))]
#[link_name = "num.cABIallocateAUTO"]
pub fn allocateAUTO(ty: TypeId, nullable: bool) -> C::Tuple<Number, Len>;

#[cfg(target_feature = "multivalue")]
#[link_name = "num.allocatePROP"]
pub fn allocatePROP(prop_name: &str, ty: TypeId, nullable: bool) -> (Number, Len);
// WARN: Currently `&str` transformed into `param i32 i32` but in the future it might be `stringref`
#[cfg(not(target_feature = "multivalue"))]
#[link_name = "num.cABIallocatePROP"]
pub fn allocatePROP(prop_name: &str, ty: TypeId, nullable: bool) -> C::Tuple<Number, Len>;
}

#[link(wasm_import_module = "nusa")]
Expand Down
Loading

0 comments on commit e862b05

Please sign in to comment.