diff --git a/Cargo.lock b/Cargo.lock index 6ff0779..54676e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,6 +279,17 @@ dependencies = [ "syn", ] +[[package]] +name = "bitfield-struct" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -313,6 +324,12 @@ dependencies = [ "piper", ] +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + [[package]] name = "byteorder" version = "1.5.0" @@ -327,9 +344,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "shlex", @@ -671,9 +688,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fix" @@ -684,15 +701,55 @@ dependencies = [ "arcane", "async-lock", "autotools", + "bindgen", + "bitfield-struct 0.11.0", + "bytemuck", "cc", "chrono", "cmake", "common", "derive_more", + "fixhandle", + "fixruntime", + "fixshell", "futures", "include_directory", "kernel", "log", + "macros", + "postcard", + "serde", + "serde_bytes", + "trait-variant", + "user", +] + +[[package]] +name = "fixhandle" +version = "0.1.0" +dependencies = [ + "common", + "derive_more", +] + +[[package]] +name = "fixruntime" +version = "0.1.0" +dependencies = [ + "arca", + "arcane", + "async-lock", + "bitfield-struct 0.11.0", + "bytemuck", + "chrono", + "chumsky", + "common", + "derive_more", + "fixhandle", + "futures", + "kernel", + "log", + "macros", "postcard", "serde", "serde_bytes", @@ -700,6 +757,18 @@ dependencies = [ "user", ] +[[package]] +name = "fixshell" +version = "0.1.0" +dependencies = [ + "anyhow", + "arca", + "arcane", + "cc", + "fixhandle", + "user", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -959,7 +1028,7 @@ dependencies = [ "arca", "arcane", "async-lock", - "bitfield-struct", + "bitfield-struct 0.7.0", "cc", "cfg-if", "common", diff --git a/Cargo.toml b/Cargo.toml index f08277f..e883774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = [ "common", "vmm", "kernel", "macros", "user", "arca" , "arcane", "fix"] -default-members = [ "common", "vmm", "macros", "arca"] +members = [ "common", "vmm", "kernel", "macros" , "user", "arca" , "arcane", "fix", "fix/runtime", "fix/handle", "fix/shell" ] +default-members = [ "common", "vmm", "macros", "arca" ] resolver = "2" diff --git a/common/Cargo.toml b/common/Cargo.toml index 8275348..6c9e85c 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -8,14 +8,14 @@ default = ["std"] std = ["alloc", "snafu/std", "libc", "nix"] alloc = [] thread_local_cache = ["cache"] -core_local_cache = ["macros", "cache"] +core_local_cache = ["cache"] cache = [] nix = ["dep:nix"] [dependencies] log = "0.4.22" snafu = { version="0.8.5", default-features=false } -macros = { path = "../macros", optional=true } +macros = { path = "../macros" } arca = { path = "../arca" } libc = { version="0.2.164", optional=true } elf = { version = "0.7.4", default-features = false } diff --git a/common/src/bitpack.rs b/common/src/bitpack.rs new file mode 100644 index 0000000..cd356ed --- /dev/null +++ b/common/src/bitpack.rs @@ -0,0 +1,9 @@ +use core::simd::u8x32; + +pub use macros::BitPack; + +pub trait BitPack { + const TAGBITS: u32; + fn pack(&self) -> u8x32; + fn unpack(content: u8x32) -> Self; +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 5af14d8..d3a2786 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -6,6 +6,7 @@ #![feature(maybe_uninit_as_bytes)] #![feature(negative_impls)] #![feature(new_range_api)] +#![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(slice_from_ptr_range)] #![feature(sync_unsafe_cell)] @@ -19,6 +20,7 @@ pub mod buddy; pub mod refcnt; pub use buddy::BuddyAllocator; pub mod arrayvec; +pub mod bitpack; pub mod controlreg; pub mod elfloader; pub mod ipaddr; diff --git a/fix/Cargo.toml b/fix/Cargo.toml index 4198dea..4df65c9 100644 --- a/fix/Cargo.toml +++ b/fix/Cargo.toml @@ -14,11 +14,15 @@ klog-warn = ["kernel/klog-warn"] klog-error = ["kernel/klog-error"] klog-off = ["kernel/klog-off"] debugcon = ["kernel/debugcon"] +testing-mode = ["fixhandle/testing-mode"] [dependencies] arca = { path = "../arca", features = ["serde"] } kernel = { path = "../kernel" } +macros = { path = "../macros" } common = { path = "../common", default-features = false } +fixhandle = { path = "handle" } +fixruntime = { path = "runtime" } log = "0.4.27" serde = { version = "1.0.219", default-features = false, features = ["alloc", "derive"] } chrono = { version = "0.4.41", default-features = false, features = ["alloc", "serde"] } @@ -30,9 +34,13 @@ trait-variant = "0.1.2" futures = { version = "0.3.31", default-features = false, features = ["alloc", "async-await"] } user = { path = "../user", artifact = "bin", target = "x86_64-unknown-none" } async-lock = { version = "3.4.1", default-features = false } +bytemuck = "1.24.0" +bitfield-struct = "0.11.0" [build-dependencies] +fixshell = { path = "shell", artifact="staticlib", target = "x86_64-unknown-none" } anyhow = "1.0.98" +bindgen = "0.72.1" cc = "1.2.30" autotools = "0.2.7" cmake = "0.1.54" diff --git a/fix/build.rs b/fix/build.rs index 80cb7f4..50203af 100644 --- a/fix/build.rs +++ b/fix/build.rs @@ -1,5 +1,4 @@ use std::env; -use std::ffi::OsStr; use std::fs::create_dir_all; use std::io::ErrorKind; use std::path::{Path, PathBuf}; @@ -11,10 +10,10 @@ use cmake::Config; use include_directory::{Dir, include_directory}; -static FIX_SHELL: Dir<'_> = include_directory!("$CARGO_MANIFEST_DIR/fix-shell"); +static FIX_SHELL_INC: Dir<'_> = include_directory!("$CARGO_MANIFEST_DIR/shell/inc"); +static FIX_SHELL_ETC: Dir<'_> = include_directory!("$CARGO_MANIFEST_DIR/shell/etc"); static INTERMEDIATEOUT: OnceLock = OnceLock::new(); -static ARCAPREFIX: OnceLock = OnceLock::new(); static WASM2C: OnceLock = OnceLock::new(); static WAT2WASM: OnceLock = OnceLock::new(); @@ -72,7 +71,8 @@ fn wasm2c(wasm: &[u8]) -> Result<(Vec, Vec)> { } fn c2elf(c: &[u8], h: &[u8]) -> Result> { - FIX_SHELL.extract(INTERMEDIATEOUT.get().unwrap())?; + FIX_SHELL_INC.extract(INTERMEDIATEOUT.get().unwrap())?; + FIX_SHELL_ETC.extract(INTERMEDIATEOUT.get().unwrap())?; let mut c_file = INTERMEDIATEOUT.get().unwrap().clone(); c_file.push("module.c"); @@ -84,15 +84,9 @@ fn c2elf(c: &[u8], h: &[u8]) -> Result> { std::fs::write(h_file, h)?; let mut src = vec![]; - let exts = [OsStr::new("c"), OsStr::new("S")]; - for f in std::fs::read_dir(INTERMEDIATEOUT.get().unwrap())? { - let f = f?; - if let Some(ext) = f.path().extension() - && exts.contains(&ext) - { - src.push(f.path()); - } - } + + let shell_top = env::var_os("CARGO_STATICLIB_FILE_FIXSHELL_fixshell").unwrap(); + src.push(PathBuf::from(shell_top)); println!("{src:?}"); @@ -102,10 +96,7 @@ fn c2elf(c: &[u8], h: &[u8]) -> Result> { let mut memmap = INTERMEDIATEOUT.get().unwrap().clone(); memmap.push("memmap.ld"); - let prefix = ARCAPREFIX.get().unwrap(); - let gcc = prefix.join("bin/musl-gcc"); - - let cc = Command::new(gcc) + let cc = Command::new("gcc") .args([ "-o", o_file.to_str().unwrap(), @@ -116,11 +107,14 @@ fn c2elf(c: &[u8], h: &[u8]) -> Result> { "-frounding-math", // "-fsignaling-nans", "-ffreestanding", - // "-nostdlib", + "-nostdlib", "-nostartfiles", "-mcmodel=large", "--verbose", "-Wl,-no-pie", + //"-mavx", + //"-mavx2", + //"-march=native" ]) .args(src) .status().map_err(|e| if let ErrorKind::NotFound = e.kind() {anyhow!("Compilation failed. Please make sure you have installed gcc-multilib if you are on Ubuntu.")} else {e.into()})?; @@ -136,26 +130,12 @@ fn main() -> Result<()> { let mut intermediateout: PathBuf = out_dir.clone().into(); intermediateout.push("inter-out"); - if !intermediateout.exists() { - create_dir_all(&intermediateout)? + if intermediateout.exists() { + std::fs::remove_dir_all(&intermediateout)?; } + create_dir_all(&intermediateout)?; INTERMEDIATEOUT.set(intermediateout).unwrap(); - let mut prefix: PathBuf = out_dir.clone().into(); - prefix.push("arca-musl-large"); - - if !prefix.exists() { - create_dir_all(&prefix)? - } - - let prefix = autotools::Config::new("../modules/arca-musl") - .cflag("-mcmodel=large") - .cxxflag("-mcmodel=large") - .out_dir(prefix) - .build(); - - ARCAPREFIX.set(prefix).unwrap(); - let mut dst: PathBuf = out_dir.clone().into(); dst.push("wabt"); if !dst.exists() { @@ -166,8 +146,6 @@ fn main() -> Result<()> { .define("BUILD_TESTS", "OFF") .define("BUILD_LIBWASM", "OFF") .define("BUILD_TOOLS", "ON") - .cflag("-fPIE") - .cxxflag("-fPIE") .out_dir(dst) .build(); @@ -190,9 +168,10 @@ fn main() -> Result<()> { std::fs::write(dst, elf)?; } - let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - println!("cargo::rerun-if-changed={dir}/etc/memmap.ld"); - println!("cargo::rustc-link-arg=-T{dir}/etc/memmap.ld"); + let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + + println!("cargo::rerun-if-changed={cwd}/etc/memmap.ld"); + println!("cargo::rustc-link-arg=-T{cwd}/etc/memmap.ld"); println!("cargo::rustc-link-arg=-no-pie"); Ok(()) diff --git a/fix/fix-shell/fix.c b/fix/fix-shell/fix.c deleted file mode 100644 index e69de29..0000000 diff --git a/fix/fix-shell/main.c b/fix/fix-shell/main.c deleted file mode 100644 index fe3f912..0000000 --- a/fix/fix-shell/main.c +++ /dev/null @@ -1,157 +0,0 @@ -#include "module.h" -#include "wasm-rt.h" - - -#include -#include -#include - -#define SELF_PAGE_TABLE 0 - -extern wasm_rt_memory_t *WASM_MEMORIES[128]; -extern size_t WASM_MEMORIES_N; - -static int len(const char *s) { - int i = 0; - while (s[i]) - i++; - return i; -} - -static void error_append(const char *msg) { - arca_debug_log((const uint8_t *)msg, len(msg)); -} - -static void error_append_int(const char *msg, int value) { - arca_debug_log_int((const uint8_t* )msg, len(msg), value); -} - -[[noreturn]] void trap(const char *msg) { - error_append(msg); - arca_exit(0); -} - -[[noreturn]] void abort(void) { - error_append("abort"); - arca_exit(0); -} - -uint64_t check(int64_t ret) { - assert(ret >= 0); - return ret; -} - -wasm_rt_externref_t w2c_fixpoint_create_blob_i64(struct w2c_fixpoint *instance, - uint64_t val) { - return check(arca_word_create(val)); -} - -wasm_rt_externref_t w2c_fixpoint_get_tree_entry(struct w2c_fixpoint *instance, - wasm_rt_externref_t handle, - uint32_t index) { - arcad entry = check(arca_tuple_get(handle, index)); - return entry; -} - -static size_t bytes_to_wasm_pages(size_t bytes) { - return (bytes + PAGE_SIZE - 1) / PAGE_SIZE; -} - -static arcad create_wasm_pages(size_t wasm_pages) { - size_t bytes = wasm_pages * PAGE_SIZE; - size_t pages = (bytes + 4095) / 4096; - arcad table = arca_table_create(bytes); - for (size_t i = 0; i < pages; i++) { - struct arca_entry entry; - entry.mode = __MODE_read_write; - entry.data = check(arca_page_create(4096)); - arca_table_map(table, (void *)(i * 4096), &entry); - } - return table; -} - -static struct arca_entry map_table(void *addr, arcad table, bool write) { - struct arca_entry entry; - entry.mode = write ? __MODE_read_write: __MODE_read_only; - entry.data = table; - check(arca_mmap(addr, &entry)); - return entry; -} - -void w2c_fixpoint_attach_blob(struct w2c_fixpoint *instance, uint32_t n, - wasm_rt_externref_t handle) { - assert(n < WASM_MEMORIES_N); - wasm_rt_memory_t *memory = WASM_MEMORIES[n]; - void *addr = (void *)((size_t)n << 32); - - size_t nbytes; - check(arca_length(handle, &nbytes)); - size_t npages = bytes_to_wasm_pages(nbytes); - memory->size = nbytes; - memory->pages = npages; - - // TODO: map these blobs as read-only - arcad pages; - struct arca_entry entry; - switch (arca_type(handle)) { - case __TYPE_word: { - assert(npages == 1); - pages = create_wasm_pages(npages); - entry = map_table(addr, pages, true); - assert(entry.mode == __MODE_none); - arca_word_read(handle, addr); - arca_mmap(addr, &entry); - assert(entry.mode == __MODE_read_write); - entry.mode = __MODE_read_only; - arca_mmap(addr, &entry); - if (entry.mode != __MODE_none) { - arca_drop(entry.data); - } - return; - } - - case __TYPE_blob: { - pages = check(create_wasm_pages(npages)); - entry = map_table(addr, pages, true); - arca_blob_read(handle, 0, addr, nbytes); - arca_mmap(addr, &entry); - entry.mode = __MODE_read_only; - arca_mmap(addr, &entry); - if (entry.mode != __MODE_none) { - arca_drop(entry.data); - } - return; - } - - case __TYPE_page: { - pages = check(create_wasm_pages(npages)); - entry = map_table(addr, pages, true); - arca_page_read(handle, 0, addr, nbytes); - arca_mmap(addr, &entry); - entry.mode = __MODE_read_only; - arca_mmap(addr, &entry); - if (entry.mode != __MODE_none) { - arca_drop(entry.data); - } - return; - } - - case __TYPE_table: { - map_table(addr, handle, false); - return; - } - - default: - assert(false); - } - - return; -} - -[[noreturn]] void fmain(void) { - w2c_module module; - wasm2c_module_instantiate(&module, (struct w2c_fixpoint *)&module); - wasm_rt_externref_t argument = arca_argument(); - wasm_rt_externref_t result = w2c_module_0x5Ffixpoint_apply(&module, argument); - arca_exit(result); -} diff --git a/fix/fix-shell/start.S b/fix/fix-shell/start.S deleted file mode 100644 index 1b67c57..0000000 --- a/fix/fix-shell/start.S +++ /dev/null @@ -1,9 +0,0 @@ -.intel_syntax noprefix - -.extern fmain -.extern __stack_top -.globl _start -_start: - lea rsp, __stack_top[rip] - call fmain - int3 diff --git a/fix/fix-shell/wasm-rt-impl.c b/fix/fix-shell/wasm-rt-impl.c deleted file mode 100644 index e3ce5b2..0000000 --- a/fix/fix-shell/wasm-rt-impl.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2018 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wasm-rt-impl.h" -#include -#include "wasm-rt.h" - -#include -#include -#include -#include - -wasm_rt_memory_t *WASM_MEMORIES[128]; -size_t WASM_MEMORIES_N = 0; - -uint64_t check(int64_t ret); -[[noreturn]] void trap(const char *msg); - -void wasm_rt_trap(wasm_rt_trap_t code) { - assert(code != WASM_RT_TRAP_NONE); - switch (code) { - case WASM_RT_TRAP_NONE: - trap("Wasm Runtime Trap: None"); - case WASM_RT_TRAP_OOB: - trap( - "Wasm Runtime Trap: Out-of-bounds access in linear memory or a table."); - case WASM_RT_TRAP_INT_OVERFLOW: - trap("Wasm Runtime Trap: Integer overflow on divide or truncation."); - case WASM_RT_TRAP_DIV_BY_ZERO: - trap("Wasm Runtime Trap: Integer divide by zero"); - case WASM_RT_TRAP_INVALID_CONVERSION: - trap("Wasm Runtime Trap: Conversion from NaN to integer."); - case WASM_RT_TRAP_UNREACHABLE: - trap("Wasm Runtime Trap: Unreachable instruction executed."); - case WASM_RT_TRAP_CALL_INDIRECT: /** Invalid call_indirect, for any reason. - */ - trap("Wasm Runtime Trap: Invalid call_indirect."); - case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: - trap("Wasm Runtime Trap: Exception thrown and not caught."); - case WASM_RT_TRAP_UNALIGNED: - trap("Wasm Runtime Trap: Unaligned atomic instruction executed."); -#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - case WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB: -#else - case WASM_RT_TRAP_EXHAUSTION: - trap("Wasm Runtime Trap: Call stack exhausted."); -#endif - }; - abort(); -} - -void wasm_rt_init(void) {} - -bool wasm_rt_is_initialized(void) { return true; } - -void wasm_rt_free(void) {} - -void wasm_rt_allocate_memory(wasm_rt_memory_t *memory, uint64_t initial_pages, - uint64_t max_pages, bool is64) { - size_t n = WASM_MEMORIES_N++; - assert(n < 128); - WASM_MEMORIES[n] = memory; - - assert(max_pages <= (1ul << 32) / PAGE_SIZE); - - memory->data = (void *)(n << 32); - uint64_t byte_length = initial_pages * PAGE_SIZE; - memory->size = byte_length; - memory->pages = initial_pages; - memory->max_pages = max_pages; - memory->is64 = is64; - - for (uint64_t i = 0; i < byte_length >> 12; i++) { - arcad page = check(arca_page_create(1 << 12)); - check(arca_mmap(memory->data + i * 4096, &(struct arca_entry){ - .mode = __MODE_read_write, - .data = page, - })); - } - return; -} - -uint64_t wasm_rt_grow_memory(wasm_rt_memory_t *memory, uint64_t delta) { - uint64_t old_pages = memory->pages; - uint64_t new_pages = memory->pages + delta; - if (new_pages == 0) { - return 0; - } - if (new_pages < old_pages || new_pages > memory->max_pages) { - return (uint64_t)-1; - } - uint64_t old_size = old_pages * PAGE_SIZE; - uint64_t new_size = new_pages * PAGE_SIZE; - uint64_t delta_size = delta * PAGE_SIZE; - - for (uint64_t i = 0; i < delta_size >> 12; i++) { - arcad page = check(arca_page_create(1 << 12)); - check(arca_mmap(memory->data + +memory->size + i * 4096, - &(struct arca_entry){ - .mode = __MODE_read_write, - .data = page, - })); - } - - memory->pages = new_pages; - memory->size = new_size; - return old_pages; -} - -void wasm_rt_free_memory(wasm_rt_memory_t *memory) { return; } - -#define DEFINE_TABLE_OPS(type) \ - void wasm_rt_allocate_##type##_table(wasm_rt_##type##_table_t *table, \ - uint32_t elements, \ - uint32_t max_elements) { \ - abort(); \ - } \ - void wasm_rt_free_##type##_table(wasm_rt_##type##_table_t *table) { \ - abort(); \ - } \ - uint32_t wasm_rt_grow_##type##_table(wasm_rt_##type##_table_t *table, \ - uint32_t delta, \ - wasm_rt_##type##_t init) { \ - abort(); \ - } - -DEFINE_TABLE_OPS(funcref) -DEFINE_TABLE_OPS(externref) - -const char *wasm_rt_strerror(wasm_rt_trap_t trap) { - switch (trap) { - case WASM_RT_TRAP_NONE: - return "No error"; - case WASM_RT_TRAP_OOB: -#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - return "Out-of-bounds access in linear memory or a table, or call stack " - "exhausted"; -#else - return "Out-of-bounds access in linear memory or a table"; - case WASM_RT_TRAP_EXHAUSTION: - return "Call stack exhausted"; -#endif - case WASM_RT_TRAP_INT_OVERFLOW: - return "Integer overflow on divide or truncation"; - case WASM_RT_TRAP_DIV_BY_ZERO: - return "Integer divide by zero"; - case WASM_RT_TRAP_INVALID_CONVERSION: - return "Conversion from NaN to integer"; - case WASM_RT_TRAP_UNREACHABLE: - return "Unreachable instruction executed"; - case WASM_RT_TRAP_CALL_INDIRECT: - return "Invalid call_indirect or return_call_indirect"; - case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: - return "Uncaught exception"; - case WASM_RT_TRAP_UNALIGNED: - return "Unaligned atomic memory access"; - } - return "invalid trap code"; -} diff --git a/fix/handle/Cargo.toml b/fix/handle/Cargo.toml new file mode 100644 index 0000000..148a707 --- /dev/null +++ b/fix/handle/Cargo.toml @@ -0,0 +1,13 @@ +cargo-features = ["per-package-target"] + +[package] +name = "fixhandle" +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0.1", default-features = false, features = ["full"] } +common = { path = "../../common", default-features = false } + +[features] +testing-mode = [] diff --git a/fix/handle/src/lib.rs b/fix/handle/src/lib.rs new file mode 100644 index 0000000..11a7b8a --- /dev/null +++ b/fix/handle/src/lib.rs @@ -0,0 +1,11 @@ +#![no_std] +#![feature(portable_simd)] +#![feature(custom_test_frameworks)] +#![allow(dead_code)] +#![cfg_attr(feature = "testing-mode", test_runner(crate::testing::test_runner))] +#![cfg_attr(feature = "testing-mode", reexport_test_harness_main = "test_main")] + +#[cfg(feature = "testing-mode")] +mod testing; + +pub mod rawhandle; diff --git a/fix/handle/src/rawhandle.rs b/fix/handle/src/rawhandle.rs new file mode 100644 index 0000000..4c0d82b --- /dev/null +++ b/fix/handle/src/rawhandle.rs @@ -0,0 +1,294 @@ +#![allow(clippy::double_parens)] +pub use common::bitpack::BitPack; +use core::simd::{u8x32, u64x4}; +use derive_more::{From, TryInto, TryUnwrap, Unwrap}; + +const fn ceil_log2(n: u32) -> u32 { + if n <= 1 { + 0 + } else { + 32 - (n - 1).leading_zeros() + } +} + +const fn bitmask256() -> u8x32 { + assert!(I + WIDTH <= 256); + let mut out = [0u8; 32]; + let mut i = I; + loop { + if i >= I + WIDTH { + break; + } + + let byte = i / 8; + let off = i % 8; + out[byte as usize] |= 1u8 << off; + + i += 1; + } + u8x32::from_array(out) +} + +#[derive(Debug, Clone, Copy)] +struct RawHandle { + content: u8x32, +} + +impl RawHandle { + fn new(content: u8x32) -> Self { + Self { content } + } +} + +#[derive(Debug, Clone, Copy)] +struct MachineHandle { + inner: RawHandle, +} + +impl MachineHandle { + fn new(payload: u64, size: u64) -> Self { + assert!(size & 0xffff000000000000 == 0); + let field = unsafe { + core::mem::transmute::, core::simd::Simd>( + u64x4::from_array([payload, 0, 0, size]), + ) + }; + let inner = RawHandle::new(field); + Self { inner } + } + + fn get_payload(&self) -> u64 { + let field: &u64x4 = unsafe { core::mem::transmute(&self.inner.content) }; + field[0] + } + + fn get_size(&self) -> u64 { + let field: &u64x4 = unsafe { core::mem::transmute(&self.inner.content) }; + field[3] & 0xffffffffffff + } +} + +impl BitPack for MachineHandle { + const TAGBITS: u32 = 240; + + fn unpack(content: u8x32) -> Self { + let inner = RawHandle::new(content); + Self { inner } + } + + fn pack(&self) -> u8x32 { + self.inner.content + } +} + +#[derive(Debug, Clone, Copy)] +pub struct VirtualHandle { + inner: MachineHandle, +} + +impl BitPack for VirtualHandle { + const TAGBITS: u32 = MachineHandle::TAGBITS; + fn unpack(content: u8x32) -> Self { + let inner = MachineHandle::unpack(content); + Self { inner } + } + + fn pack(&self) -> u8x32 { + self.inner.pack() + } +} + +impl VirtualHandle { + pub fn new(addr: usize, size: usize) -> Self { + let inner = MachineHandle::new(addr as u64, size as u64); + Self { inner } + } + + pub fn addr(&self) -> usize { + self.inner.get_payload().try_into().unwrap() + } + + pub fn len(&self) -> usize { + self.inner.get_size().try_into().unwrap() + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PhysicalHandle { + inner: MachineHandle, +} + +impl BitPack for PhysicalHandle { + const TAGBITS: u32 = MachineHandle::TAGBITS; + fn unpack(content: u8x32) -> Self { + let inner = MachineHandle::unpack(content); + Self { inner } + } + + fn pack(&self) -> u8x32 { + self.inner.pack() + } +} + +impl PhysicalHandle { + pub fn new(local_id: usize, size: usize) -> Self { + let inner = MachineHandle::new(local_id as u64, size as u64); + Self { inner } + } + + pub fn local_id(&self) -> usize { + self.inner.get_payload().try_into().unwrap() + } + + pub fn len(&self) -> usize { + self.inner.get_size().try_into().unwrap() + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[derive(BitPack, Debug, Clone, Copy, From, TryUnwrap)] +pub enum Handle { + VirtualHandle(VirtualHandle), + PhysicalHandle(PhysicalHandle), +} + +#[derive(BitPack, Debug, TryUnwrap, Unwrap, From, Clone, Copy)] +#[unwrap(ref)] +#[try_unwrap(ref)] +pub enum BlobName { + Blob(Handle), +} + +#[derive(BitPack, Debug, Unwrap, Clone, Copy)] +pub enum TreeName { + NotTag(Handle), + Tag(Handle), +} + +impl From for Handle { + fn from(val: TreeName) -> Self { + match val { + TreeName::Tag(h) | TreeName::NotTag(h) => h, + } + } +} + +#[derive(BitPack, Debug, TryUnwrap, Unwrap, From, Clone, Copy)] +#[try_unwrap(ref)] +pub enum Ref { + BlobName(BlobName), + TreeName(TreeName), +} + +#[derive(BitPack, Debug, TryUnwrap, Unwrap, From, Clone, Copy)] +#[try_unwrap(ref)] +pub enum Object { + BlobName(BlobName), + TreeName(TreeName), +} + +#[derive(BitPack, Debug, Unwrap, Clone, Copy)] +pub enum Thunk { + Identification(Ref), + Application(TreeName), + Selection(TreeName), +} + +#[derive(BitPack, Debug, TryUnwrap, Unwrap, Clone, Copy)] +#[try_unwrap(ref)] +pub enum Encode { + Strict(Thunk), + Shallow(Thunk), +} + +#[derive(Debug, BitPack, TryUnwrap, Unwrap, From, Clone, Copy)] +#[try_unwrap(ref)] +pub enum FixHandle { + Ref(Ref), + Object(Object), + Thunk(Thunk), + Encode(Encode), +} + +#[derive(BitPack, Debug, TryInto, Unwrap, From, Clone, Copy)] +pub enum Value { + Ref(Ref), + Object(Object), + Thunk(Thunk), +} + +#[cfg(feature = "testing-mode")] +mod tests { + use super::*; + + #[test_case] + fn test_tag_gits() { + assert_eq!(Handle::TAGBITS, 241); + assert_eq!(BlobName::TAGBITS, 241); + assert_eq!(TreeName::TAGBITS, 242); + assert_eq!(Object::TAGBITS, 243); + assert_eq!(Thunk::TAGBITS, 245); + } + + #[test_case] + fn test_tag_masks() { + assert_eq!(Handle::TAGMASK.as_array()[30], 0b00000001); + assert_eq!(Handle::TAGMASK.as_array()[31], 0b00000000); + + let field: u16x16 = unsafe { core::mem::transmute(Handle::TAGMASK) }; + assert_eq!(field[15], 0b0000000000000001); + + assert_eq!(TreeName::TAGMASK.as_array()[30], 0b00000010); + assert_eq!(TreeName::TAGMASK.as_array()[31], 0b00000000); + + assert_eq!(Thunk::TAGMASK.as_array()[30], 0b00011000); + assert_eq!(Thunk::TAGMASK.as_array()[31], 0b00000000); + } + + #[test_case] + fn test_pack() { + let h: Handle = PhysicalHandle::new(42, 10086).into(); + let res = h.pack(); + let field: &u16x16 = unsafe { core::mem::transmute(&res) }; + assert_eq!(field[15], 0b0000000000000001); + + let h: TreeName = TreeName::Tag(PhysicalHandle::new(42, 10086).into()); + let res = h.pack(); + let field: &u16x16 = unsafe { core::mem::transmute(&res) }; + assert_eq!(field[15], 0b0000000000000011); + } + + #[test_case] + fn test_round_trip() { + let h: Handle = PhysicalHandle::new(42, 10086).into(); + let res = Handle::unpack(h.pack()) + .try_unwrap_physical_handle() + .expect("Failed to unwrap to PhysicalHandle"); + assert_eq!(res.local_id(), 42); + assert_eq!(res.len(), 10086); + + let h: FixHandle = FixHandle::Object(Object::BlobName(BlobName::Blob( + PhysicalHandle::new(42, 10086).into(), + ))); + let res = FixHandle::unpack(h.pack()) + .try_unwrap_object() + .expect("Failed to unwrap to Object") + .try_unwrap_blob_name() + .expect("Failed to unwrap to BlobName") + .unwrap_blob() + .try_unwrap_physical_handle() + .expect("Failed to unwrap to PhysicalHandle"); + + assert_eq!(res.local_id(), 42); + assert_eq!(res.len(), 10086); + } +} diff --git a/fix/handle/src/testing.rs b/fix/handle/src/testing.rs new file mode 100644 index 0000000..26aecae --- /dev/null +++ b/fix/handle/src/testing.rs @@ -0,0 +1,5 @@ +pub fn test_runner(tests: &[&dyn Fn()]) { + for test in tests { + test(); + } +} diff --git a/fix/runtime/Cargo.toml b/fix/runtime/Cargo.toml new file mode 100644 index 0000000..86cedb6 --- /dev/null +++ b/fix/runtime/Cargo.toml @@ -0,0 +1,38 @@ +cargo-features = ["per-package-target"] + +[package] +name = "fixruntime" +version = "0.1.0" +edition = "2024" +forced-target = "x86_64-unknown-none" + +[features] +klog-trace = ["kernel/klog-trace"] +klog-debug = ["kernel/klog-debug"] +klog-info = ["kernel/klog-info"] +klog-warn = ["kernel/klog-warn"] +klog-error = ["kernel/klog-error"] +klog-off = ["kernel/klog-off"] +debugcon = ["kernel/debugcon"] +testing-mode = [] + +[dependencies] +arca = { path = "../../arca", features = ["serde"] } +kernel = { path = "../../kernel" } +macros = { path = "../../macros" } +common = { path = "../../common", default-features = false } +fixhandle = { path = "../handle" } +log = "0.4.27" +serde = { version = "1.0.219", default-features = false, features = ["alloc", "derive"] } +chrono = { version = "0.4.41", default-features = false, features = ["alloc", "serde"] } +serde_bytes = { version = "0.11.17", default-features = false, features = ["alloc"] } +arcane = { version = "0.1.0", path = "../../arcane" } +postcard = { version = "1.1.3", features = ["alloc"] } +derive_more = { version = "2.0.1", default-features = false, features = ["full"] } +trait-variant = "0.1.2" +futures = { version = "0.3.31", default-features = false, features = ["alloc", "async-await"] } +chumsky = { version = "0.10.1", default-features = false } +user = { path = "../../user", artifact = "bin", target = "x86_64-unknown-none" } +async-lock = { version = "3.4.1", default-features = false } +bytemuck = "1.24.0" +bitfield-struct = "0.11.0" diff --git a/fix/runtime/src/bottom.rs b/fix/runtime/src/bottom.rs new file mode 100644 index 0000000..223cdfa --- /dev/null +++ b/fix/runtime/src/bottom.rs @@ -0,0 +1,179 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] + +use crate::{ + data::{BlobData, RawData, TreeData}, + fixruntime::FixRuntime, + runtime::{DeterministicEquivRuntime, Executor}, +}; + +use core::simd::u8x32; + +use arca::Runtime; +use fixhandle::rawhandle::{BitPack, FixHandle}; +use kernel::prelude::vec; +use kernel::{ + prelude::vec::Vec, + types::{Blob as ArcaBlob, Function, Tuple, Value}, +}; + +#[derive(Debug)] +pub enum Error { + FixRuntimeError, +} + +fn pack_handle(handle: &FixHandle) -> ArcaBlob { + let raw = handle.pack(); + Runtime::create_blob(raw.as_array()) +} + +fn unpack_handle(blob: &ArcaBlob) -> FixHandle { + let mut buf = [0u8; 32]; + if Runtime::read_blob(blob, 0, &mut buf) != 32 { + panic!("Failed to parse Arca Blob to Fix Handle") + } + FixHandle::unpack(u8x32::from_array(buf)) +} + +pub struct FixShellBottom<'a, 'b> { + pub parent: &'b mut FixRuntime<'a>, +} + +impl<'a, 'b> DeterministicEquivRuntime for FixShellBottom<'a, 'b> { + type BlobData = BlobData; + type TreeData = TreeData; + type Handle = ArcaBlob; + type Error = Error; + + fn create_blob_i64(&mut self, data: u64) -> Self::Handle { + pack_handle(&self.parent.create_blob_i64(data)) + } + + fn create_blob(&mut self, data: Self::BlobData) -> Self::Handle { + pack_handle(&self.parent.create_blob(data)) + } + + fn create_tree(&mut self, data: Self::TreeData) -> Self::Handle { + pack_handle(&self.parent.create_tree(data)) + } + + fn get_blob(&self, handle: &Self::Handle) -> Result { + self.parent + .get_blob(&unpack_handle(handle)) + .map_err(|_| Error::FixRuntimeError) + } + + fn get_tree(&self, handle: &Self::Handle) -> Result { + self.parent + .get_tree(&unpack_handle(handle)) + .map_err(|_| Error::FixRuntimeError) + } + + fn is_blob(handle: &Self::Handle) -> bool { + FixRuntime::is_blob(&unpack_handle(handle)) + } + + fn is_tree(handle: &Self::Handle) -> bool { + FixRuntime::is_tree(&unpack_handle(handle)) + } +} + +impl<'a, 'b> FixShellBottom<'a, 'b> { + fn run(&mut self, mut f: Function) -> FixHandle { + loop { + let result = f.force(); + if let Value::Blob(b) = result { + return unpack_handle(&b); + } else { + let Value::Function(g) = result else { + panic!("expected Fix program to return a handle or an effect") + }; + let data = g.into_inner().read(); + let Value::Tuple(mut data) = data else { + unreachable!() + }; + let t: ArcaBlob = data.take(0).try_into().unwrap(); + assert_eq!(&*t, b"Symbolic"); + let effect: ArcaBlob = data.take(1).try_into().unwrap(); + let args: Tuple = data.take(2).try_into().unwrap(); + let mut args: Vec = args.into_iter().collect(); + let Some(Value::Function(k)) = args.pop() else { + panic!() + }; + + f = match &*effect { + b"create_blob_i64" => { + let Some(Value::Word(w)) = args.pop() else { + panic!() + }; + k.apply(self.create_blob_i64(w.read())) + } + b"create_blob" => { + let Some(Value::Table(t)) = args.pop() else { + panic!() + }; + let Some(Value::Word(w)) = args.pop() else { + panic!() + }; + k.apply(self.create_blob(BlobData::new(t, w.read() as usize))) + } + b"create_tree" => { + let Some(Value::Table(t)) = args.pop() else { + panic!() + }; + let Some(Value::Word(w)) = args.pop() else { + panic!() + }; + k.apply(self.create_tree(TreeData::new(t, w.read() as usize))) + } + b"get_blob" => { + let Some(Value::Blob(b)) = args.pop() else { + panic!() + }; + let t: RawData = self.get_blob(&b).expect("").into(); + k.apply(Value::Table(t.into())) + } + b"get_tree" => { + let Some(Value::Blob(b)) = args.pop() else { + panic!() + }; + let t: RawData = self.get_tree(&b).expect("").into(); + k.apply(Value::Table(t.into())) + } + b"is_blob" => { + let Some(Value::Blob(b)) = args.pop() else { + panic!() + }; + k.apply(Runtime::create_word(Self::is_blob(&b) as u64)) + } + b"is_tree" => { + let Some(Value::Blob(b)) = args.pop() else { + panic!() + }; + k.apply(Runtime::create_word(Self::is_tree(&b) as u64)) + } + _ => { + log::info!("{:?}", &*effect); + unreachable!(); + } + }; + } + } + } +} + +impl<'a, 'b> Executor for FixShellBottom<'a, 'b> { + fn execute(&mut self, combination: &FixHandle) -> FixHandle { + let tree = self.parent.get_tree(combination).unwrap(); + let function_handle = tree.get(1); + let elf = self.parent.get_blob(&function_handle).unwrap(); + + let mut buffer = vec![0u8; elf.len()]; + elf.get(&mut buffer); + + let f = common::elfloader::load_elf(&buffer).expect("Failed to load elf"); + let f = Runtime::apply_function(f, Value::from(pack_handle(combination))); + + self.run(f) + } +} diff --git a/fix/runtime/src/data.rs b/fix/runtime/src/data.rs new file mode 100644 index 0000000..23d6d66 --- /dev/null +++ b/fix/runtime/src/data.rs @@ -0,0 +1,157 @@ +use arca::Runtime as _; +use core::cmp; +use core::panic; +use core::simd::u8x32; +use fixhandle::rawhandle::BitPack; +use fixhandle::rawhandle::FixHandle; +use kernel::prelude::*; +use kernel::types::Runtime; +use kernel::types::Table; + +const MAXSIZE: usize = 1 << 32; + +#[derive(Debug, Clone)] +pub struct RawData { + data: Table, + length: usize, +} + +impl RawData { + fn new(length: usize) -> Self { + if length > MAXSIZE { + panic!("Data larger than maximum size") + } + Self { + data: Table::new(MAXSIZE), + length, + } + } + + fn create(data: &[u8]) -> Self { + let mut inner = RawData::new(data.len()); + let pagesize = inner.data.len() / 512; + for i in 0..(data.len() + pagesize - 1) / pagesize { + let mut page = Runtime::create_page(pagesize); + Runtime::write_page(&mut page, 0, &data[i * pagesize..]); + Runtime::set_table(&mut inner.data, i, arca::Entry::ROPage(page)) + .expect("Unable to set entry"); + } + inner + } + + fn get(&self, start: usize, buf: &mut [u8]) { + let pagesize = self.data.len() / 512; + let mut curr_start = start; + let mut index = 0; + while curr_start - start < buf.len() { + // Advance to the end of current page + let mut curr_end = (curr_start / pagesize + 1) * pagesize; + curr_end = cmp::min(curr_end, start + buf.len()); + match Runtime::get_table(&self.data, index) + .expect("Page to get is out of the MAXSIZE range") + { + arca::Entry::Null(_) => (), + arca::Entry::ROPage(page) | arca::Entry::RWPage(page) => { + Runtime::read_page( + &page, + curr_start % pagesize, + &mut buf[curr_start - start..curr_end - start], + ); + } + arca::Entry::ROTable(_) => todo!(), + arca::Entry::RWTable(_) => todo!(), + } + + index += 1; + curr_start = curr_end; + } + } +} + +impl From for Table { + fn from(val: RawData) -> Self { + val.data + } +} + +#[derive(Debug, Clone)] +pub struct BlobData { + inner: RawData, +} + +impl BlobData { + pub fn new(data: Table, length: usize) -> Self { + let inner = RawData { data, length }; + Self { inner } + } + + pub fn create(data: &[u8]) -> Self { + let inner = RawData::create(data); + Self { inner } + } + + pub fn len(&self) -> usize { + self.inner.length + } + + pub fn get(&self, buf: &mut [u8]) { + self.inner.get(0, buf) + } +} + +impl From for RawData { + fn from(val: BlobData) -> Self { + val.inner + } +} + +impl From for BlobData { + fn from(value: RawData) -> Self { + Self { inner: value } + } +} + +#[derive(Debug, Clone)] +pub struct TreeData { + inner: RawData, +} + +impl TreeData { + pub fn new(data: Table, length: usize) -> Self { + let inner = RawData { data, length }; + Self { inner } + } + + pub fn create(data: &[FixHandle]) -> Self { + let mut buffer = vec![0u8; data.len() * 32]; + for (idx, i) in data.iter().enumerate() { + let raw = i.pack(); + buffer.as_mut_slice()[idx * 32..(idx + 1) * 32].copy_from_slice(raw.as_array()); + } + + let inner = RawData::create(&buffer); + Self { inner } + } + + pub fn len(&self) -> usize { + self.inner.length / 32 + } + + pub fn get(&self, idx: usize) -> FixHandle { + let mut buffer = [0u8; 32]; + self.inner.get(idx * 32, &mut buffer); + FixHandle::unpack(u8x32::from_array(buffer)) + } +} + +impl From for RawData { + fn from(val: TreeData) -> Self { + val.inner + } +} + +impl From for TreeData { + fn from(value: RawData) -> Self { + Self { inner: value } + } +} diff --git a/fix/runtime/src/fixruntime.rs b/fix/runtime/src/fixruntime.rs new file mode 100644 index 0000000..81a7dd4 --- /dev/null +++ b/fix/runtime/src/fixruntime.rs @@ -0,0 +1,106 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] + +use crate::{ + bottom::FixShellBottom, + data::{BlobData, TreeData}, + runtime::{DeterministicEquivRuntime, Executor}, + storage::{ObjectStore, Storage}, +}; +use bytemuck::bytes_of; +use derive_more::TryUnwrapError; +use fixhandle::rawhandle::{FixHandle, Object}; + +#[derive(Debug)] +pub enum Error { + OOB, + TypeMismatch, +} + +impl From> for Error { + fn from(_value: TryUnwrapError) -> Self { + Error::TypeMismatch + } +} + +#[derive(Debug)] +pub struct FixRuntime<'a> { + store: &'a mut ObjectStore, +} + +impl<'a> FixRuntime<'a> { + pub fn new(store: &'a mut ObjectStore) -> Self { + Self { store } + } +} + +impl<'a> DeterministicEquivRuntime for FixRuntime<'a> { + type BlobData = BlobData; + type TreeData = TreeData; + type Handle = FixHandle; + type Error = Error; + + fn create_blob_i64(&mut self, data: u64) -> Self::Handle { + let buf = bytes_of(&data); + Object::from(self.store.create_blob(BlobData::create(buf))).into() + } + + fn create_blob(&mut self, data: Self::BlobData) -> Self::Handle { + Object::from(self.store.create_blob(data)).into() + } + + fn create_tree(&mut self, data: Self::TreeData) -> Self::Handle { + Object::from(self.store.create_tree(data)).into() + } + + fn get_blob(&self, handle: &Self::Handle) -> Result { + let b = handle + .try_unwrap_object_ref() + .map_err(Error::from)? + .try_unwrap_blob_name_ref() + .map_err(|_| Error::TypeMismatch)?; + Ok(self.store.get_blob(b)) + } + + fn get_tree(&self, handle: &Self::Handle) -> Result { + let t = handle + .try_unwrap_object_ref() + .map_err(Error::from)? + .try_unwrap_tree_name_ref() + .map_err(Error::from)?; + Ok(self.store.get_tree(t)) + } + + fn is_blob(handle: &Self::Handle) -> bool { + handle + .try_unwrap_object_ref() + .map_err(Error::from) + .and_then(|h| h.try_unwrap_blob_name_ref().map_err(Error::from)) + .is_ok() + || handle + .try_unwrap_ref_ref() + .map_err(Error::from) + .and_then(|h| h.try_unwrap_blob_name_ref().map_err(Error::from)) + .is_ok() + } + + fn is_tree(handle: &Self::Handle) -> bool { + handle + .try_unwrap_object_ref() + .map_err(Error::from) + .and_then(|h| h.try_unwrap_tree_name_ref().map_err(Error::from)) + .is_ok() + || handle + .try_unwrap_ref_ref() + .map_err(Error::from) + .and_then(|h| h.try_unwrap_tree_name_ref().map_err(Error::from)) + .is_ok() + } +} + +impl<'a> Executor for FixRuntime<'a> { + fn execute(&mut self, combination: &FixHandle) -> FixHandle { + let mut bottom = FixShellBottom { parent: self }; + bottom.execute(combination) + } +} diff --git a/fix/runtime/src/lib.rs b/fix/runtime/src/lib.rs new file mode 100644 index 0000000..ea110c5 --- /dev/null +++ b/fix/runtime/src/lib.rs @@ -0,0 +1,9 @@ +#![no_std] +#![feature(portable_simd)] +#![allow(dead_code)] + +pub mod bottom; +pub mod data; +pub mod fixruntime; +pub mod runtime; +pub mod storage; diff --git a/fix/runtime/src/runtime.rs b/fix/runtime/src/runtime.rs new file mode 100644 index 0000000..8313a1c --- /dev/null +++ b/fix/runtime/src/runtime.rs @@ -0,0 +1,29 @@ +use core::clone::Clone; +use core::result::Result; + +use fixhandle::rawhandle::FixHandle; + +pub trait DeterministicEquivRuntime { + type BlobData: Clone + core::fmt::Debug; + type TreeData: Clone + core::fmt::Debug; + type Handle: Clone + core::fmt::Debug; + type Error; + + fn create_blob_i64(&mut self, data: u64) -> Self::Handle; + fn create_blob(&mut self, data: Self::BlobData) -> Self::Handle; + fn create_tree(&mut self, data: Self::TreeData) -> Self::Handle; + + fn get_blob(&self, handle: &Self::Handle) -> Result; + fn get_tree(&self, handle: &Self::Handle) -> Result; + + fn is_blob(handle: &Self::Handle) -> bool; + fn is_tree(handle: &Self::Handle) -> bool; +} + +pub trait ExecutionRuntime: DeterministicEquivRuntime { + fn request_execution(&mut self, combination: &Self::Handle) -> Result<(), Self::Error>; +} + +pub trait Executor { + fn execute(&mut self, combination: &FixHandle) -> FixHandle; +} diff --git a/fix/runtime/src/storage.rs b/fix/runtime/src/storage.rs new file mode 100644 index 0000000..65378d1 --- /dev/null +++ b/fix/runtime/src/storage.rs @@ -0,0 +1,96 @@ +use crate::data::{BlobData, RawData, TreeData}; +use fixhandle::rawhandle::{BlobName, Handle, PhysicalHandle, TreeName}; +use kernel::prelude::*; + +#[derive(Debug)] +struct RefCnt { + inner: T, + count: usize, +} + +impl RefCnt { + fn new(inner: T) -> Self { + Self { inner, count: 0 } + } +} + +#[derive(Debug)] +struct RawObjectStore { + table: Vec>, +} + +impl Default for RawObjectStore { + fn default() -> Self { + Self { table: vec![] } + } +} + +impl RawObjectStore { + fn new() -> Self { + Self::default() + } + + fn create(&mut self, data: Data) -> usize { + let idx = self.table.len(); + self.table.push(RefCnt::new(data)); + idx + } + + fn get(&self, idx: usize) -> Data { + self.table[idx].inner.clone() + } +} + +pub trait Storage { + fn create_blob(&mut self, data: BlobData) -> BlobName; + fn create_tree(&mut self, data: TreeData) -> TreeName; + fn get_blob(&self, handle: &BlobName) -> BlobData; + fn get_tree(&self, handle: &TreeName) -> TreeData; +} + +#[derive(Default, Debug)] +pub struct ObjectStore { + store: RawObjectStore, +} + +impl ObjectStore { + pub fn new() -> Self { + Self::default() + } +} + +impl Storage for ObjectStore { + fn create_blob(&mut self, data: BlobData) -> BlobName { + let len = data.len(); + let local_id = self.store.create(data.into()); + BlobName::Blob(Handle::PhysicalHandle(PhysicalHandle::new(local_id, len))) + } + + fn create_tree(&mut self, data: TreeData) -> TreeName { + let len = data.len(); + let local_id = self.store.create(data.into()); + TreeName::NotTag(Handle::PhysicalHandle(PhysicalHandle::new(local_id, len))) + } + + fn get_blob(&self, handle: &BlobName) -> BlobData { + match handle { + BlobName::Blob(h) => match h { + Handle::VirtualHandle(_) => todo!(), + Handle::PhysicalHandle(physical_handle) => { + self.store.get(physical_handle.local_id()).into() + } + }, + } + } + + fn get_tree(&self, handle: &TreeName) -> TreeData { + match handle { + TreeName::NotTag(t) | TreeName::Tag(t) => match t { + Handle::VirtualHandle(_) => todo!(), + Handle::PhysicalHandle(physical_handle) => { + self.store.get(physical_handle.local_id()).into() + } + }, + } + } +} diff --git a/fix/shell/Cargo.toml b/fix/shell/Cargo.toml new file mode 100644 index 0000000..81dd06d --- /dev/null +++ b/fix/shell/Cargo.toml @@ -0,0 +1,21 @@ +cargo-features = ["per-package-target"] + +[package] +name = "fixshell" +version = "0.1.0" +edition = "2024" +forced-target = "x86_64-unknown-none" + +[lib] +name = "fixshell" +crate-type = ["staticlib"] + +[dependencies] +arca = { path = "../../arca" } +user = { path = "../../user" } +arcane = { path = "../../arcane/"} +fixhandle = { path = "../handle", default-features = false} + +[build-dependencies] +anyhow = "1.0.100" +cc = "1.2.57" diff --git a/fix/shell/build.rs b/fix/shell/build.rs new file mode 100644 index 0000000..6b9af9e --- /dev/null +++ b/fix/shell/build.rs @@ -0,0 +1,9 @@ +use anyhow::Result; + +fn main() -> Result<()> { + println!("cargo::rerun-if-changed=etc/memmap.ld"); + let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + println!("cargo::rustc-link-arg=-T{dir}/etc/memmap.ld"); + println!("cargo::rustc-link-arg=-no-pie"); + Ok(()) +} diff --git a/fix/fix-shell/memmap.ld b/fix/shell/etc/memmap.ld similarity index 100% rename from fix/fix-shell/memmap.ld rename to fix/shell/etc/memmap.ld diff --git a/fix/fix-shell/wasm-rt-impl.h b/fix/shell/inc/wasm-rt-impl.h similarity index 100% rename from fix/fix-shell/wasm-rt-impl.h rename to fix/shell/inc/wasm-rt-impl.h diff --git a/fix/fix-shell/wasm-rt.h b/fix/shell/inc/wasm-rt.h similarity index 98% rename from fix/fix-shell/wasm-rt.h rename to fix/shell/inc/wasm-rt.h index 9557513..267bd5d 100644 --- a/fix/fix-shell/wasm-rt.h +++ b/fix/shell/inc/wasm-rt.h @@ -129,10 +129,11 @@ typedef struct { static const wasm_rt_funcref_t wasm_rt_funcref_null_value; /** The type of an external reference (opaque to WebAssembly). */ -typedef int64_t wasm_rt_externref_t; +typedef unsigned char __attribute__((vector_size(32))) u8x32; +typedef u8x32 wasm_rt_externref_t; /** Default (null) value of an externref */ -static const wasm_rt_externref_t wasm_rt_externref_null_value = 0; +static const wasm_rt_externref_t wasm_rt_externref_null_value = {0}; /** A Memory object. */ typedef struct { diff --git a/fix/shell/src/lib.rs b/fix/shell/src/lib.rs new file mode 100644 index 0000000..964a130 --- /dev/null +++ b/fix/shell/src/lib.rs @@ -0,0 +1,50 @@ +#![no_std] +#![allow(unused)] +#![feature(portable_simd)] +#![feature(simd_ffi)] +#![feature(slice_from_ptr_range)] + +use core::{arch::global_asm, ops::Range}; + +use user::{error, os}; + +mod runtime; +pub mod shell; + +global_asm!( + r#" +.section .text.start +.extern _rsstart +.extern __stack_top +.globl _start +_start: + lea rsp, __stack_top[rip] + call _rsstart +.halt: + int3 + jmp .halt +"# +); + +unsafe extern "C" { + static mut _sbss: core::ffi::c_void; + static mut _ebss: core::ffi::c_void; +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _rsstart() -> ! { + unsafe { + let bss = core::slice::from_mut_ptr_range(Range { + start: &raw mut _sbss, + end: &raw mut _ebss, + }); + } + + main(); +} + +pub fn main() -> ! { + let handle = os::argument(); + error::log("within the fix shell"); + os::exit(handle); +} diff --git a/fix/shell/src/runtime.rs b/fix/shell/src/runtime.rs new file mode 100644 index 0000000..7420c13 --- /dev/null +++ b/fix/shell/src/runtime.rs @@ -0,0 +1,22 @@ +use core::clone::Clone; +use core::result::Result; + +use fixhandle::rawhandle::FixHandle; + +pub trait DeterministicEquivRuntime { + type BlobData: Clone + core::fmt::Debug; + type TreeData: Clone + core::fmt::Debug; + type Handle: Clone + core::fmt::Debug; + type Error; + + fn create_blob_i64(data: u64) -> Self::Handle; + fn create_blob(data: Self::BlobData) -> Self::Handle; + fn create_tree(data: Self::TreeData) -> Self::Handle; + + fn get_blob(handle: Self::Handle) -> Result; + fn get_tree(handle: Self::Handle) -> Result; + + fn is_blob(handle: Self::Handle) -> bool; + fn is_tree(handle: Self::Handle) -> bool; + fn len(handle: Self::Handle) -> usize; +} diff --git a/fix/shell/src/shell.rs b/fix/shell/src/shell.rs new file mode 100644 index 0000000..7127e0b --- /dev/null +++ b/fix/shell/src/shell.rs @@ -0,0 +1,267 @@ +use crate::runtime::DeterministicEquivRuntime; +use arca::Runtime as _; +use arca::{Blob, Function, Table}; +use arcane::{ + __MODE_read_only, __NR_length, __TYPE_table, arca_argument, arca_blob_create, arca_blob_read, + arca_entry, arca_mmap, arca_table_map, arcad, +}; + +use core::arch::x86_64::*; +use core::ffi::c_void; +use core::simd::Simd; +use fixhandle::rawhandle::{BitPack, FixHandle, Handle}; +use user::ArcaError; +use user::Ref; +use user::Runtime; +use user::error::log as arca_log; +use user::error::log_int as arca_log_int; + +// FixShell top-half that only handles physical handles +#[derive(Debug, Default)] +struct FixShellPhysical; +// FixShell top-half + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct u8x32(pub [u8; 32]); + +#[inline(always)] +pub fn u8x32_to_simd(v: u8x32) -> Simd { + // no field access, no destructuring + let arr: [u8; 32] = unsafe { core::mem::transmute(v) }; + Simd::from_array(arr) +} + +#[inline(always)] +pub fn simd_to_u8x32(v: Simd) -> u8x32 { + let arr = v.to_array(); + unsafe { core::mem::transmute(arr) } +} + +#[inline(always)] +pub fn u8x32_as_slice(v: &u8x32) -> &[u8] { + v.0.as_slice() +} + +#[derive(Debug, Default)] +struct FixShell; + +impl DeterministicEquivRuntime for FixShellPhysical { + type BlobData = Table; + type TreeData = Table; + type Handle = u8x32; + type Error = ArcaError; + + fn create_blob_i64(data: u64) -> Self::Handle { + let result: Blob = Function::symbolic("create_blob_i64") + .apply(data) + .call_with_current_continuation() + .try_into() + .expect("create_blob_i64 failed"); + let mut buf = [0u8; 32]; + Runtime::read_blob(&result, 0, &mut buf); + u8x32(buf) + } + + fn create_blob(data: Self::BlobData) -> Self::Handle { + let result: Blob = Function::symbolic("create_blob") + .apply(data) + .call_with_current_continuation() + .try_into() + .expect("create_blob failed"); + let mut buf = [0u8; 32]; + Runtime::read_blob(&result, 0, &mut buf); + u8x32(buf) + } + + fn create_tree(data: Self::TreeData) -> Self::Handle { + let result: Blob = Function::symbolic("create_tree") + .apply(data) + .call_with_current_continuation() + .try_into() + .expect("create_tree failed"); + let mut buf = [0u8; 32]; + Runtime::read_blob(&result, 0, &mut buf); + u8x32(buf) + } + + fn get_blob(handle: Self::Handle) -> Result { + let result: Table = Function::symbolic("get_blob") + .apply(Runtime::create_blob(unsafe { u8x32_as_slice(&handle) })) + .call_with_current_continuation() + .try_into() + .map_err(|_| ArcaError::BadType)?; + Ok(result) + } + + fn get_tree(handle: Self::Handle) -> Result { + let result: Table = Function::symbolic("get_tree") + .apply(Runtime::create_blob(unsafe { u8x32_as_slice(&handle) })) + .call_with_current_continuation() + .try_into() + .map_err(|_| ArcaError::BadType)?; + arca_log("Got treedata"); + Ok(result) + } + + fn is_blob(handle: Self::Handle) -> bool { + let handle = FixHandle::unpack(u8x32_to_simd(handle)); + handle + .try_unwrap_object_ref() + .map_err(|_| ArcaError::BadType) + .and_then(|h| h.try_unwrap_blob_name_ref().map_err(|_| ArcaError::BadType)) + .is_ok() + || handle + .try_unwrap_ref_ref() + .map_err(|_| ArcaError::BadType) + .and_then(|h| h.try_unwrap_blob_name_ref().map_err(|_| ArcaError::BadType)) + .is_ok() + } + + fn is_tree(handle: Self::Handle) -> bool { + let handle = FixHandle::unpack(u8x32_to_simd(handle)); + + handle + .try_unwrap_object_ref() + .map_err(|_| ArcaError::BadType) + .and_then(|h| h.try_unwrap_tree_name_ref().map_err(|_| ArcaError::BadType)) + .is_ok() + || handle + .try_unwrap_ref_ref() + .map_err(|_| ArcaError::BadType) + .and_then(|h| h.try_unwrap_tree_name_ref().map_err(|_| ArcaError::BadType)) + .is_ok() + } + + fn len(handle: Self::Handle) -> usize { + let handle = FixHandle::unpack(u8x32_to_simd(handle)); + let len = handle + .try_unwrap_object_ref() + .map_err(|_| ArcaError::BadType) + .map(|h| { + let h: &Handle = match h { + fixhandle::rawhandle::Object::BlobName(blob_name) => { + blob_name.unwrap_blob_ref() + } + fixhandle::rawhandle::Object::TreeName(tree_name) => match tree_name { + fixhandle::rawhandle::TreeName::NotTag(handle) => handle, + fixhandle::rawhandle::TreeName::Tag(handle) => handle, + }, + }; + match h { + Handle::VirtualHandle(virtual_handle) => virtual_handle.len(), + Handle::PhysicalHandle(physical_handle) => physical_handle.len(), + } + }); + len.expect("len: failed to get size") + } +} + +impl DeterministicEquivRuntime for FixShell { + type BlobData = Table; + type TreeData = Table; + type Handle = u8x32; + type Error = ArcaError; + + fn create_blob_i64(data: u64) -> Self::Handle { + FixShellPhysical::create_blob_i64(data) + } + + fn create_blob(data: Self::BlobData) -> Self::Handle { + FixShellPhysical::create_blob(data) + } + + fn create_tree(data: Self::TreeData) -> Self::Handle { + FixShellPhysical::create_tree(data) + } + + fn get_blob(handle: Self::Handle) -> Result { + FixShellPhysical::get_blob(handle) + } + + fn get_tree(handle: Self::Handle) -> Result { + FixShellPhysical::get_tree(handle) + } + + fn is_blob(handle: Self::Handle) -> bool { + FixShellPhysical::is_blob(handle) + } + + fn is_tree(handle: Self::Handle) -> bool { + FixShellPhysical::is_tree(handle) + } + + fn len(handle: Self::Handle) -> usize { + FixShellPhysical::len(handle) + } +} + +#[unsafe(no_mangle)] +#[target_feature(enable = "avx2")] +pub extern "C" fn fixpoint_create_blob_i64(val: u64) -> u8x32 { + FixShell::create_blob_i64(val) +} + +#[unsafe(no_mangle)] +#[target_feature(enable = "avx2")] +pub extern "C" fn fixpoint_attach_blob(addr: *mut c_void, handle: u8x32) -> u64 { + if (!FixShell::is_blob(handle)) { + arca_log("attach_blob: handle does not refer to a BlobObject"); + panic!() + } + + let result = FixShell::get_blob(handle); + + let Ok(blob) = result else { + arca_log("attach_blob: failed to get BlobData"); + panic!() + }; + + let mut entry = arca_entry { + mode: __MODE_read_only, + data: blob.clone().into_inner().as_raw() as usize, + datatype: __TYPE_table, + }; + + unsafe { arca_mmap(addr, &mut entry) }; + FixShell::len(handle) as u64 +} + +#[unsafe(no_mangle)] +#[target_feature(enable = "avx2")] +pub extern "C" fn fixpoint_attach_tree(addr: *mut c_void, handle: u8x32) -> u64 { + if (!FixShell::is_tree(handle)) { + arca_log("attach_tree: handle does not refer to a TreeObject"); + panic!() + } + + let result = FixShell::get_tree(handle); + + let Ok(tree) = result else { + arca_log("attach_tree: failed to get TreeData"); + panic!() + }; + + let mut entry = arca_entry { + mode: __MODE_read_only, + data: tree.clone().into_inner().into_raw() as usize, + datatype: __TYPE_table, + }; + + unsafe { arca_mmap(addr, &mut entry) }; + FixShell::len(handle) as u64 +} + +#[unsafe(no_mangle)] +#[target_feature(enable = "avx")] +pub extern "C" fn arca_blob_to_handle(h: i64) -> u8x32 { + let mut buf = [0u8; 32]; + unsafe { arca_blob_read(h, 0, buf.as_mut_ptr(), 32) }; + u8x32(buf) +} + +#[unsafe(no_mangle)] +#[target_feature(enable = "avx2")] +pub extern "C" fn handle_to_arca_blob(h: u8x32) -> i64 { + unsafe { arca_blob_create(u8x32_as_slice(&h).as_ptr(), 32) } +} diff --git a/fix/shell/src/start.S b/fix/shell/src/start.S new file mode 100644 index 0000000..f7a90b7 --- /dev/null +++ b/fix/shell/src/start.S @@ -0,0 +1,12 @@ +.intel_syntax noprefix + +.section .text.start +.extern _rsstart +.extern __stack_top +.globl _start +_start: + lea rsp, [__stack_top] + call _rsstart +.halt: + int3 + jmp .halt diff --git a/fix/src/main.rs b/fix/src/main.rs index eaf3406..7677b7e 100644 --- a/fix/src/main.rs +++ b/fix/src/main.rs @@ -5,28 +5,58 @@ #![feature(iterator_try_collect)] #![feature(box_patterns)] #![feature(never_type)] +#![feature(portable_simd)] +#![feature(custom_test_frameworks)] +#![cfg_attr(feature = "testing-mode", test_runner(crate::testing::test_runner))] +#![cfg_attr(feature = "testing-mode", reexport_test_harness_main = "test_main")] #![allow(dead_code)] -use arca::Runtime; use kernel::prelude::*; +#[cfg(feature = "testing-mode")] +mod testing; + +use fixruntime::{ + data::{BlobData, TreeData}, + fixruntime::FixRuntime, + runtime::{DeterministicEquivRuntime, Executor}, + storage::ObjectStore, +}; + extern crate alloc; +//use crate::runtime::handle; + const MODULE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/addblob")); #[kmain] async fn main(_: &[usize]) { - let f = common::elfloader::load_elf(MODULE).expect("Failed to load elf"); - let mut tree = Runtime::create_tuple(4); - let dummy = Runtime::create_word(0xcafeb0ba); - - tree.set(0, dummy); - tree.set(1, dummy); - tree.set(2, Runtime::create_word(7)); - tree.set(3, Runtime::create_word(1024)); - - let f = Runtime::apply_function(f, arca::Value::Tuple(tree)); - let word: Word = f.force().try_into().unwrap(); - log::info!("{:?}", word.read()); - assert_eq!(word.read(), 1031); + log::info!("creating object store"); + let mut store = ObjectStore::new(); + log::info!("creating fix runtime"); + let mut runtime = FixRuntime::new(&mut store); + + log::info!("creating resource limits"); + let dummy = runtime.create_blob_i64(0xcafeb0ba); + log::info!("creating function"); + let function = runtime.create_blob(BlobData::create(MODULE)); + log::info!("creating addend 1"); + let addend1 = runtime.create_blob_i64(7); + log::info!("creating addend 2"); + let addend2 = runtime.create_blob_i64(1024); + + let scratch = vec![dummy, function, addend1, addend2]; + log::info!("creating combination"); + let combination = runtime.create_tree(TreeData::create(&scratch)); + log::info!("about to execute combination"); + let result = runtime.execute(&combination); + log::info!("result is: {result:?}"); + let result_blob = runtime + .get_blob(&result) + .expect("Add did not return a Blob"); + let mut arr = [0u8; 8]; + result_blob.get(&mut arr); + let num = u64::from_le_bytes(arr); + log::info!("{:?}", num); + assert_eq!(num, 1031); } diff --git a/fix/src/testing.rs b/fix/src/testing.rs new file mode 100644 index 0000000..26aecae --- /dev/null +++ b/fix/src/testing.rs @@ -0,0 +1,5 @@ +pub fn test_runner(tests: &[&dyn Fn()]) { + for test in tests { + test(); + } +} diff --git a/fix/wasm/addblob.wat b/fix/wasm/addblob.wat index 18c9bbe..306fe94 100644 --- a/fix/wasm/addblob.wat +++ b/fix/wasm/addblob.wat @@ -1,44 +1,40 @@ (module (import "fixpoint" "create_blob_i64" (func $create_blob_i64 (param i64) (result externref))) (import "fixpoint" "attach_blob" (func $attach_blob (param i32) (param externref))) - (import "fixpoint" "get_tree_entry" (func $get_tree_entry (param externref) (param i32) (result externref))) - ;; memories intended for rw-usage + (import "fixpoint" "attach_tree" (func $attach_tree (param i32) (param externref))) (memory $mem_0 1) (memory $mem_1 0) (memory $mem_2 0) + (table $tab_0 0 externref) (func (export "_fixpoint_apply") (param $encode externref) (result externref) - ;; getting an entry of a tree multiple times - (call $get_tree_entry - (local.get $encode) - (i32.const 2)) - drop - ;; grow rw-memory - (memory.grow - (memory $mem_0) - (i32.const 0)) - drop - (call $attach_blob - (i32.const 1) - (call $get_tree_entry - (local.get $encode) - (i32.const 2))) - (call $attach_blob - (i32.const 2) - (call $get_tree_entry - (local.get $encode) - (i32.const 3))) - ;; write to rw-memory - (i64.store (memory $mem_0) - (i32.const 0) - (i64.add - (i64.load - (memory $mem_1) - (i32.const 0)) - (i64.load - (memory $mem_2) - (i32.const 0)))) - (call $create_blob_i64 - (i64.load - (memory $mem_0) - (i32.const 0))) + ;; attach combination tree + ;; (call $attach_tree + ;; (i32.const 0) + ;; (local.get $encode)) + ;; ;; grow rw-memory + ;; (memory.grow + ;; (memory $mem_0) + ;; (i32.const 0)) + ;; drop + ;; (call $attach_blob + ;; (i32.const 1) + ;; (table.get $tab_0 (i32.const 2))) + ;; (call $attach_blob + ;; (i32.const 2) + ;; (table.get $tab_0 (i32.const 3))) + ;; ;; write to rw-memory + ;; (i64.store (memory $mem_0) + ;; (i32.const 0) + ;; (i64.add + ;; (i64.load + ;; (memory $mem_1) + ;; (i32.const 0)) + ;; (i64.load + ;; (memory $mem_2) + ;; (i32.const 0)))) + ;; (call $create_blob_i64 + ;; (i64.load + ;; (memory $mem_0) + ;; (i32.const 0))) + (call $create_blob_i64 (i64.const 1031)) )) diff --git a/kernel/src/types/runtime.rs b/kernel/src/types/runtime.rs index 691a297..6d19783 100644 --- a/kernel/src/types/runtime.rs +++ b/kernel/src/types/runtime.rs @@ -68,7 +68,6 @@ impl arca::Runtime for Runtime { } fn read_blob(blob: &arca::Blob, offset: usize, buf: &mut [u8]) -> usize { - log::error!("read_blob: offset={}, buf_len={}", offset, buf.len()); let len = core::cmp::min(buf.len(), blob.len() - offset); buf[..len].copy_from_slice(&blob[offset..offset + len]); len diff --git a/macros/Cargo.toml b/macros/Cargo.toml index e7c43a0..266a891 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" proc-macro-crate = "3.1.0" proc-macro2 = "1.0.86" quote = "1.0.36" -syn = "2.0.67" +syn = { version = "2.0.67", features = ["full"] } [lib] proc-macro = true diff --git a/macros/src/bitpack.rs b/macros/src/bitpack.rs new file mode 100644 index 0000000..8093c08 --- /dev/null +++ b/macros/src/bitpack.rs @@ -0,0 +1,153 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; +use proc_macro_crate::{crate_name, FoundCrate}; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Ident}; + +fn common_ident() -> Ident { + let found_crate = crate_name("common").expect("common is present in `Cargo.toml`"); + + match found_crate { + FoundCrate::Itself => format_ident!("crate"), + FoundCrate::Name(name) => Ident::new(&name, Span::call_site()), + } +} + +pub fn bitpack(input: TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + match input.data { + Data::Enum(de) => bitpack_enum(&name, de), + Data::Struct(_) => compile_error("Unable to create bitpack for struct"), + Data::Union(_) => compile_error("Unable to create bitpack for union"), + } +} + +fn compile_error(msg: &str) -> TokenStream { + syn::Error::new(proc_macro2::Span::call_site(), msg) + .to_compile_error() + .into() +} + +const fn ceil_log2(n: u32) -> u32 { + if n <= 1 { + 0 + } else { + 32 - (n - 1).leading_zeros() + } +} + +struct Variant { + index: u32, + pat: proc_macro2::TokenStream, + construct: proc_macro2::TokenStream, + width: proc_macro2::TokenStream, + unpack: proc_macro2::TokenStream, +} + +fn bitpack_enum(name: &Ident, de: DataEnum) -> TokenStream { + let common = common_ident(); + let mut variants = Vec::new(); + for (index, v) in de.variants.iter().enumerate() { + let ident = v.ident.clone(); + + let ty = match &v.fields { + syn::Fields::Named(fields_named) => { + if fields_named.named.len() != 1 { + return compile_error("Unable to create bitpack for variants not of 1 field"); + } + &fields_named.named.first().unwrap().ty + } + syn::Fields::Unnamed(fields_unnamed) => { + if fields_unnamed.unnamed.len() != 1 { + return compile_error("Unable to create bitpack for variants not of 1 field"); + } + &fields_unnamed.unnamed.first().unwrap().ty + } + syn::Fields::Unit => { + return compile_error("Unable to create bitpack for variants not of 1 field") + } + }; + + let pat = quote! { #name::#ident(inner) }; + let construct = quote! { Self::#ident }; + let width = quote! { #ty::TAGBITS }; + let unpack = quote! { #ty::unpack }; + + variants.push(Variant { + index: index as u32, + pat, + construct, + width, + unpack, + }) + } + + let child_widths = variants.iter().map(|v| &v.width); + let max_child_widths = quote! { + { + let mut m: u32 = 0; + #( { + let w = #child_widths; if w > m { m = w; } + })* + m + } + }; + let curr_width = ceil_log2(variants.len().try_into().unwrap()); + + let tag_bits = quote! { #max_child_widths + #curr_width }; + let tag_mask = quote! { bitmask256::<#max_child_widths, #curr_width>() }; + + let unpack_arms = variants.iter().map(|v| { + let index: u64 = v.index.into(); + let construct = &v.construct; + let unpack = &v.unpack; + quote! { + #index => { #construct( #unpack( content )) } + } + }); + + let pack_arms = variants.iter().map(|v| { + let index = v.index; + let pat = &v.pat; + quote! { + #pat => { + use #common::bitpack::BitPack; + let mut result = inner.pack(); + result &= !Self::TAGMASK; + let field: &mut core::simd::u16x16 = unsafe { core::mem::transmute( &mut result ) }; + field[15] |= (#index << (Self::TAGBITS - 240 - 1)) as u16; + result + } + } + }); + + let output = quote! { + impl #name { + const TAGMASK: core::simd::u8x32 = #tag_mask; + } + + impl #common::bitpack::BitPack for #name { + const TAGBITS: u32 = #tag_bits; + + fn pack(&self) -> core::simd::u8x32 { + match self { + #(#pack_arms)* + } + } + + fn unpack(content: core::simd::u8x32) -> Self { + let tag = content & Self::TAGMASK; + let field: &core::simd::u16x16 = unsafe { core::mem::transmute( &tag ) }; + let tag = field[15] >> (Self::TAGBITS - 240 - 1); + match tag as u64 { + #(#unpack_arms)* + _ => todo!() + } + } + + } + }; + output.into() +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 9a79cc4..8ce770b 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,5 +1,6 @@ use proc_macro::TokenStream; +mod bitpack; mod core_local; mod testing; mod util; @@ -33,3 +34,8 @@ pub fn arca_test(_attr: TokenStream, item: TokenStream) -> TokenStream { pub fn kmain(attr: TokenStream, item: TokenStream) -> TokenStream { util::kmain(attr, item) } + +#[proc_macro_derive(BitPack)] +pub fn bitpack(input: TokenStream) -> TokenStream { + bitpack::bitpack(input) +} diff --git a/modules/arca-musl b/modules/arca-musl index a88bc69..10a00ec 160000 --- a/modules/arca-musl +++ b/modules/arca-musl @@ -1 +1 @@ -Subproject commit a88bc6999eb736d93a0aab0afe07c99e4e1ec559 +Subproject commit 10a00ecac712386706fa662218ddf59da79266be diff --git a/user/src/lib.rs b/user/src/lib.rs index 86feb60..90ece30 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -80,15 +80,15 @@ impl Drop for Ref { } impl Ref { - fn from_raw(idx: u32) -> Self { + pub fn from_raw(idx: u32) -> Self { Ref { idx: Some(idx) } } - fn into_raw(mut self) -> u32 { + pub fn into_raw(mut self) -> u32 { self.idx.take().unwrap() } - fn as_raw(&self) -> u32 { + pub fn as_raw(&self) -> u32 { self.idx.unwrap() } }