Skip to content

Commit

Permalink
WIP null gc
Browse files Browse the repository at this point in the history
  • Loading branch information
fitzgen committed Oct 14, 2024
1 parent ef271bf commit 92386c6
Show file tree
Hide file tree
Showing 23 changed files with 537 additions and 100 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,12 @@ jobs:
-p wasmtime --no-default-features --features gc
-p wasmtime --no-default-features --features runtime,gc
-p wasmtime --no-default-features --features cranelift,gc
-p wasmtime --no-default-features --features gc-drc
-p wasmtime --no-default-features --features runtime,gc-drc
-p wasmtime --no-default-features --features cranelift,gc-drc
-p wasmtime --no-default-features --features gc-null
-p wasmtime --no-default-features --features runtime,gc-null
-p wasmtime --no-default-features --features cranelift,gc-null
-p wasmtime --no-default-features --features runtime
-p wasmtime --no-default-features --features threads
-p wasmtime --no-default-features --features runtime,threads
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ default = [
"component-model",
"threads",
"gc",
"gc-drc",
"gc-null",
"winch",

# Enable some nice features of clap by default, but they come at a binary size
Expand Down Expand Up @@ -443,7 +445,9 @@ coredump = ["wasmtime-cli-flags/coredump"]
addr2line = ["wasmtime/addr2line"]
debug-builtins = ["wasmtime/debug-builtins"]
threads = ["wasmtime-cli-flags/threads"]
gc = ["wasmtime-cli-flags/gc"]
gc = ["wasmtime-cli-flags/gc", "wasmtime/gc"]
gc-drc = ["gc", "wasmtime/gc-drc"]
gc-null = ["gc", "wasmtime/gc-null"]

# CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help`
# for more information on each subcommand.
Expand Down
2 changes: 2 additions & 0 deletions crates/cli-flags/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ logging = ["dep:file-per-thread-logger", "dep:tracing-subscriber"]
cranelift = ["wasmtime/cranelift"]
coredump = ["wasmtime/coredump"]
gc = ["wasmtime/gc"]
gc-drc = ["gc", "wasmtime/gc-drc"]
gc-null = ["gc", "wasmtime/gc-null"]
threads = ["wasmtime/threads"]
memory-protection-keys = ["wasmtime/memory-protection-keys"]
29 changes: 28 additions & 1 deletion crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains the common Wasmtime command line interface (CLI) flags.

use anyhow::Result;
use anyhow::{bail, Result};
use clap::Parser;
use std::time::Duration;
use wasmtime::Config;
Expand Down Expand Up @@ -182,6 +182,16 @@ wasmtime_option_group! {
/// Currently only `cranelift` and `winch` are supported, but not all
/// builds of Wasmtime have both built in.
pub compiler: Option<wasmtime::Strategy>,
/// Which garbage collector to use: `drc` or `null`.
///
/// `drc` is the deferred reference-counting collector.
///
/// `null` is the null garbage collector, which does not collect any
/// garbage.
///
/// Note that not all builds of Wasmtime will have support for garbage
/// collection included.
pub collector: Option<wasmtime::Collector>,
/// Enable Cranelift's internal debug verifier (expensive)
pub cranelift_debug_verifier: Option<bool>,
/// Whether or not to enable caching of compiled modules.
Expand Down Expand Up @@ -532,6 +542,23 @@ impl CommonOptions {
strategy => config.strategy(strategy),
_ => err,
}
match_feature! {
["gc" : self.codegen.collector]
collector => match collector {
#[cfg(not(feature = "gc-drc"))]
wasmtime::Collector::DeferredReferenceCounting => bail!(
"support for the `drc` collector is unavailable because the \
`gc-drc` feature was disabled at compile time",
),
#[cfg(not(feature = "gc-null"))]
wasmtime::Collector::Null => bail!(
"support for the `null` collector is unavailable because the \
`gc-null` feature was disabled at compile time",
),
_ => config.collector(collector),
},
_ => err,
}
match_feature! {
["cranelift" : target]
target => config.target(target)?,
Expand Down
11 changes: 11 additions & 0 deletions crates/cli-flags/src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,17 @@ impl WasmtimeOptionValue for wasmtime::Strategy {
}
}

impl WasmtimeOptionValue for wasmtime::Collector {
const VAL_HELP: &'static str = "=drc|null";
fn parse(val: Option<&str>) -> Result<Self> {
match String::parse(val)?.as_str() {
"drc" => Ok(wasmtime::Collector::DeferredReferenceCounting),
"null" => Ok(wasmtime::Collector::Null),
other => bail!("unknown collector `{other}` only `drc` and `null` accepted",),
}
}
}

impl WasmtimeOptionValue for WasiNnGraph {
const VAL_HELP: &'static str = "=<format>::<dir>";
fn parse(val: Option<&str>) -> Result<Self> {
Expand Down
2 changes: 2 additions & 0 deletions crates/cranelift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ component-model = ["wasmtime-environ/component-model"]
incremental-cache = ["cranelift-codegen/incremental-cache"]
wmemcheck = ["wasmtime-environ/wmemcheck"]
gc = ["wasmtime-environ/gc"]
gc-drc = ["gc", "wasmtime-environ/gc-drc"]
gc-null = ["gc", "wasmtime-environ/gc-null"]
threads = ["wasmtime-environ/threads"]
2 changes: 1 addition & 1 deletion crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub struct FuncEnvironment<'module_environment> {
/// Offsets to struct fields accessed by JIT code.
pub(crate) offsets: VMOffsets<u8>,

tunables: &'module_environment Tunables,
pub(crate) tunables: &'module_environment Tunables,

/// A function-local variable which stores the cached value of the amount of
/// fuel remaining to execute. If used this is modified frequently so it's
Expand Down
35 changes: 30 additions & 5 deletions crates/cranelift/src/gc/enabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,41 @@ use cranelift_codegen::{
use cranelift_entity::packed_option::ReservedValue;
use cranelift_frontend::FunctionBuilder;
use wasmtime_environ::{
GcArrayLayout, GcLayout, GcStructLayout, ModuleInternedTypeIndex, PtrSize, TypeIndex, VMGcKind,
WasmCompositeType, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult, WasmStorageType,
WasmValType, I31_DISCRIMINANT, NON_NULL_NON_I31_MASK,
wasm_unsupported, Collector, GcArrayLayout, GcLayout, GcStructLayout, ModuleInternedTypeIndex,
PtrSize, TypeIndex, VMGcKind, WasmCompositeType, WasmHeapTopType, WasmHeapType, WasmRefType,
WasmResult, WasmStorageType, WasmValType, I31_DISCRIMINANT, NON_NULL_NON_I31_MASK,
};

#[cfg(feature = "gc-drc")]
mod drc;
#[cfg(feature = "gc-null")]
mod null;

/// Get the default GC compiler.
pub fn gc_compiler(_func_env: &FuncEnvironment<'_>) -> WasmResult<Box<dyn GcCompiler>> {
Ok(Box::new(drc::DrcCompiler::default()))
pub fn gc_compiler(func_env: &FuncEnvironment<'_>) -> WasmResult<Box<dyn GcCompiler>> {
match func_env.tunables.collector {
#[cfg(feature = "gc-drc")]
Some(Collector::DeferredReferenceCounting) => Ok(Box::new(drc::DrcCompiler::default())),
#[cfg(not(feature = "gc-drc"))]
Some(Collector::DeferredReferenceCounting) => Err(wasm_unsupported!(
"the null collector is unavailable because the `gc-drc` feature \
was disabled at compile time",
)),

#[cfg(feature = "gc-null")]
Some(Collector::Null) => Ok(Box::new(null::NullCompiler::default())),
#[cfg(not(feature = "gc-null"))]
Some(Collector::Null) => Err(wasm_unsupported!(
"the null collector is unavailable because the `gc-null` feature \
was disabled at compile time",
)),

None => Err(wasm_unsupported!(
"support for Wasm GC disabled because no collector implementation \
was selected at compile time; enable one of the `gc-drc` or \
`gc-null` features",
)),
}
}

fn unbarriered_load_gc_ref(
Expand Down
71 changes: 71 additions & 0 deletions crates/cranelift/src/gc/enabled/null.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! Compiler for the null collector.

#![allow(unused)] // TODO FITZGEN

use super::*;
use crate::gc::{gc_compiler, ArrayInit};
use crate::translate::TargetEnvironment;
use crate::{func_environ::FuncEnvironment, gc::GcCompiler, TRAP_INTERNAL_ASSERT};
use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_frontend::FunctionBuilder;
use smallvec::SmallVec;
use wasmtime_environ::{
null::NullTypeLayouts, GcArrayLayout, GcTypeLayouts, ModuleInternedTypeIndex, PtrSize,
TypeIndex, VMGcKind, WasmCompositeType, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult,
WasmStorageType, WasmValType,
};

#[derive(Default)]
pub struct NullCompiler {
layouts: NullTypeLayouts,
}

impl GcCompiler for NullCompiler {
fn layouts(&self) -> &dyn GcTypeLayouts {
&self.layouts
}

fn alloc_array(
&mut self,
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
array_type_index: TypeIndex,
init: super::ArrayInit<'_>,
) -> WasmResult<ir::Value> {
todo!("FITZGEN: alloc_array")
}

fn alloc_struct(
&mut self,
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
struct_type_index: TypeIndex,
field_vals: &[ir::Value],
) -> WasmResult<ir::Value> {
todo!("FITZGEN: alloc_struct")
}

fn translate_read_gc_reference(
&mut self,
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder,
ty: WasmRefType,
src: ir::Value,
flags: ir::MemFlags,
) -> WasmResult<ir::Value> {
todo!("FITZGEN: translate_read_gc_reference")
}

fn translate_write_gc_reference(
&mut self,
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder,
ty: WasmRefType,
dst: ir::Value,
new_val: ir::Value,
flags: ir::MemFlags,
) -> WasmResult<()> {
todo!("FITZGEN: translate_write_gc_reference")
}
}
2 changes: 2 additions & 0 deletions crates/environ/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ component-model = [
]
demangle = ['std', 'dep:rustc-demangle', 'dep:cpp_demangle']
gc = []
gc-drc = ["gc"]
gc-null = ["gc"]
compile = [
'gimli/write',
'object/write_core',
Expand Down
6 changes: 3 additions & 3 deletions crates/environ/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ macro_rules! foreach_builtin_function {
// once it will no longer be used again. (Note: `val` is not of type
// `reference` because it needn't appear in any stack maps, as it
// must not be live after this call.)
#[cfg(feature = "gc")]
#[cfg(feature = "gc-drc")]
drop_gc_ref(vmctx: vmctx, val: i32);

// Do a GC, treating the optional `root` as a GC root and returning
// the updated `root` (so that, in the case of moving collectors,
// callers have a valid version of `root` again).
#[cfg(feature = "gc")]
#[cfg(feature = "gc-drc")]
gc(vmctx: vmctx, root: reference) -> reference;

// Allocate a new, uninitialized GC object and return a reference to
// it.
#[cfg(feature = "gc")]
#[cfg(feature = "gc-drc")]
gc_alloc_raw(
vmctx: vmctx,
kind: i32,
Expand Down
97 changes: 96 additions & 1 deletion crates/environ/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
//! on our various `gc` cargo features is the actual garbage collection
//! functions and their associated impact on binary size anyways.

#[cfg(feature = "gc")]
#[cfg(feature = "gc-drc")]
pub mod drc;

#[cfg(feature = "gc-null")]
pub mod null;

use crate::prelude::*;
use crate::{WasmArrayType, WasmCompositeType, WasmStorageType, WasmStructType, WasmValType};
use core::alloc::Layout;
Expand Down Expand Up @@ -49,6 +52,98 @@ pub fn byte_size_of_wasm_ty_in_gc_heap(ty: &WasmStorageType) -> u32 {
}
}

/// Align `offset` up to `bytes`, updating `max_align` if `align` is the
/// new maximum alignment, and returning the aligned offset.
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
fn align_up(offset: &mut u32, max_align: &mut u32, align: u32) -> u32 {
debug_assert!(max_align.is_power_of_two());
debug_assert!(align.is_power_of_two());
*offset = offset.checked_add(align - 1).unwrap() & !(align - 1);
*max_align = core::cmp::max(*max_align, align);
*offset
}

/// Define a new field of size and alignment `bytes`, updating the object's
/// total `size` and `align` as necessary. The offset of the new field is
/// returned.
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
fn field(size: &mut u32, align: &mut u32, bytes: u32) -> u32 {
let offset = align_up(size, align, bytes);
*size += bytes;
offset
}

/// Common code to define a GC array's layout, given the size and alignment of
/// the collector's GC header and its expected offset of the array length field.
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
fn common_array_layout(
ty: &WasmArrayType,
header_size: u32,
header_align: u32,
expected_array_length_offset: u32,
) -> GcArrayLayout {
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);

let mut size = header_size;
let mut align = header_align;

let length_field_offset = field(&mut size, &mut align, 4);
assert_eq!(length_field_offset, expected_array_length_offset);

let elem_size = byte_size_of_wasm_ty_in_gc_heap(&ty.0.element_type);
let elems_offset = align_up(&mut size, &mut align, elem_size);
assert_eq!(elems_offset, size);

GcArrayLayout {
base_size: size,
align,
elem_size,
}
}

/// Common code to define a GC struct's layout, given the size and alignment of
/// the collector's GC header and its expected offset of the array length field.
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
fn common_struct_layout(
ty: &WasmStructType,
header_size: u32,
header_align: u32,
) -> GcStructLayout {
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);

// Process each field, aligning it to its natural alignment.
//
// We don't try and do any fancy field reordering to minimize padding
// (yet?) because (a) the toolchain probably already did that and (b)
// we're just doing the simple thing first. We can come back and improve
// things here if we find that (a) isn't actually holding true in
// practice.
let mut size = header_size;
let mut align = header_align;

let fields = ty
.fields
.iter()
.map(|f| {
let field_size = byte_size_of_wasm_ty_in_gc_heap(&f.element_type);
field(&mut size, &mut align, field_size)
})
.collect();

// Ensure that the final size is a multiple of the alignment, for
// simplicity.
let align_size_to = align;
align_up(&mut size, &mut align, align_size_to);

GcStructLayout {
size,
align,
fields,
}
}

/// A trait for getting the layout of a Wasm GC struct or array inside a
/// particular collector.
pub trait GcTypeLayouts {
Expand Down
Loading

0 comments on commit 92386c6

Please sign in to comment.