Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# https://github.com/rui314/mold?tab=readme-ov-file#how-to-use
# Use a linker wrapper that invokes mold then strips junk sections with objcopy.
# mold cannot discard .eh_frame/.dynstr/.comment via linker scripts, so we do
# it as a post-link step.
# See:
# <https://github.com/rui314/mold?tab=readme-ov-file#how-to-use>
[target.'cfg(target_os = "linux")']
rustflags = [ "-C", "link-arg=-fuse-ld=mold" ]
linker = "scripts/ld-wrapper"
rustflags = [
# No C runtime, we provide _start ourselves
"-C",
"link-arg=-nostartfiles",
# Fully static, no dynamic linker, no .interp/.dynsym/.dynamic overhead
"-C",
"link-arg=-static",
# Static PIE is incompatible with -static :(
"-C",
"relocation-model=static",
# Suppress .eh_frame emission from our own codegen (does not cover compiler_builtins;
# those remnants are removed by the linker wrapper via objcopy post-link)
"-C",
"force-unwind-tables=no",
# Linker flags
"-C",
"link-arg=-Wl,--gc-sections", # remove unreferenced input sections
"-C",
"link-arg=-Wl,--strip-all", # strip all symbol table entries
"-C",
"link-arg=-Wl,--build-id=none", # omit the .note.gnu.build-id section
"-C",
"link-arg=-Wl,-z,norelro", # disable RELRO (removes relro_padding)
]
5 changes: 5 additions & 0 deletions .github/workflows/hotpath-profile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ jobs:
fetch-depth: 0

- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
rustflags: ""

- name: Make Mold the default linker
uses: rui314/setup-mold@v1

- name: Create metrics directory
run: mkdir -p /tmp/metrics
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: ${{ matrix.target }}
rustflags: ""

- name: "Make Mold the default linker"
uses: rui314/setup-mold@v1
Expand Down
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ rust-version = "1.92.0"
version = "0.4.13"

[workspace.dependencies]
microfetch-asm = { path = "./crates/asm" }
microfetch-lib = { path = "./crates/lib" }
microfetch-alloc = { path = "./crates/alloc" }
microfetch-asm = { path = "./crates/asm" }
microfetch-lib = { path = "./crates/lib" }

criterion = { default-features = false, features = [ "cargo_bench_support" ], version = "0.8.2" }
criterion-cycles-per-byte = "0.8.0"

[profile.dev]
opt-level = 1
panic = "abort"

[profile.release]
codegen-units = 1
Expand Down
12 changes: 12 additions & 0 deletions crates/alloc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "microfetch-alloc"
description = "Simple, std-free bump allocator for Microfetch"
version.workspace = true
edition.workspace = true
authors.workspace = true
rust-version.workspace = true
license.workspace = true
publish = false

[lints]
workspace = true
106 changes: 106 additions & 0 deletions crates/alloc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Simple bump allocator for `no_std` environments. Uses a statically allocated
//! 32KB buffer and provides O(1) allocation with no deallocation support
//! (memory is never freed).
#![no_std]
use core::{
alloc::{GlobalAlloc, Layout},
cell::UnsafeCell,
ptr::null_mut,
};

/// Default heap size is 32KB, should be plenty for Microfetch. Technically it
/// can be invoked with more (or less) depending on our needs but I am quite
/// sure 32KB is more than enough.
pub const DEFAULT_HEAP_SIZE: usize = 32 * 1024;

/// A simple bump allocator that never frees memory.
///
/// This allocator maintains a static buffer and a bump pointer. Allocations are
/// fast (just bump the pointer), but memory is never reclaimed. While you might
/// be inclined to point out that this is ugly, it's suitable for a short-lived
/// program with bounded memory usage.
pub struct BumpAllocator<const N: usize = DEFAULT_HEAP_SIZE> {
heap: UnsafeCell<[u8; N]>,
next: UnsafeCell<usize>,
}

// SAFETY: BumpAllocator is thread-safe because it uses UnsafeCell
// and the allocator is only used in single-threaded contexts (i.e., no_std).
unsafe impl<const N: usize> Sync for BumpAllocator<N> {}

impl<const N: usize> BumpAllocator<N> {
/// Creates a new bump allocator with the specified heap size.
#[must_use]
pub const fn new() -> Self {
Self {
heap: UnsafeCell::new([0; N]),
next: UnsafeCell::new(0),
}
}

/// Returns the number of bytes currently allocated.
#[must_use]
pub fn used(&self) -> usize {
// SAFETY: We're just reading the value, and this is only called
// in single-threaded contexts.
unsafe { *self.next.get() }
}

/// Returns the total heap size.
#[must_use]
pub const fn capacity(&self) -> usize {
N
}

/// Returns the number of bytes remaining.
#[must_use]
pub fn remaining(&self) -> usize {
N - self.used()
}
}

impl<const N: usize> Default for BumpAllocator<N> {
fn default() -> Self {
Self::new()
}
}

unsafe impl<const N: usize> GlobalAlloc for BumpAllocator<N> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe {
let next = self.next.get();
let heap = self.heap.get();

// Align the current position
let align = layout.align();
let start = (*next + align - 1) & !(align - 1);
let end = start + layout.size();

if end > N {
// Out of memory
null_mut()
} else {
*next = end;
(*heap).as_mut_ptr().add(start)
}
}
}

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
// Bump allocator doesn't support deallocation
// Memory is reclaimed when the program exits
}
}

/// Static bump allocator instance with 32KB heap.
///
/// # Example
///
/// Use this with `#[global_allocator]` in your binary:
///
///
/// ```rust,ignore
/// #[global_allocator]
/// static ALLOCATOR: BumpAllocator = BumpAllocator::new();
/// ```
pub type BumpAlloc = BumpAllocator<DEFAULT_HEAP_SIZE>;
Loading
Loading