Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@ jobs:
- name: Example (C → Wasm → Rust)
run: ./examples/c-to-wasm-to-rust/run.sh

- name: Example FFT (C → Wasm → Rust)
run: ./examples/c-fft/run.sh

- name: Example (Inter-Module Lending)
run: ./examples/inter-module-lending/run.sh
48 changes: 38 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,45 @@ All notable changes to the herkos project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [0.2.0]

### Added
- Pre-open-source code review and cleanup
- Apache-2.0 license
- Community files (CONTRIBUTING.md, CHANGELOG.md)
- Cargo.toml metadata for all crates
- GitHub issue and PR templates
- Bulk memory operations: `memory.fill`, `memory.init`, `data.drop`
- Version info in generated code and module metadata
- Inter-module lending tests and examples with automation scripts
- Memory-intensive benchmarks (sorting, Fibonacci implementations)
- New benchmarks for control flow and arithmetic operations
- Optimization control via `HERKOS_OPTIMIZE` environment variable

### Changed
- Memory operations now use `usize` for better type safety
- Refactored host import handling with uniform `Env<H>` API pattern
- Enhanced SSA IR with improved phi-node lowering and branch resolution
- Improved dead code handling in IR builder with live-check methods for terminators
- Restructured `ControlFrame` enum for better control flow handling
- Simplified data segment parsing using zip for segment indexing

### Fixed
- i32 shift operations now correctly mask shift amounts to 5 bits (& 31) per WebAssembly spec
- Replaced panic-inducing `unwrap()` calls in IR builder with proper error handling
- Changed constructor panics to `Result` types for proper no_std compliance
- Host parameter now properly handled in `call_indirect` dispatch (issue #19)
- Host parameter now transitively propagated through direct calls (issue #19)
- IR now enforces strict SSA form at compile time with `UseVar`/`DefVar` typing
- Removed panic for unoptimizations in transpile function
- Removed unnecessary crate-type configurations from Cargo.toml

### Removed
- Example C usage and header files from repository
- Herkos-bootstrap example implementation

## [0.1.1] - 2026-03-09

### Fixed
- Improved diagram formatting in README.md
- Updated .gitignore to include Cargo.lock
- Removed unused CLI options
- Updated repository and homepage URLs

### Added
- C to WebAssembly example with Rust transpilation

## [0.1.0] - 2026-02-16

Expand Down Expand Up @@ -72,7 +98,9 @@ See [docs/FUTURE.md](docs/FUTURE.md) for planned features.

## Version History

- **0.1.1** (2026-03-09) — C integration example and URL updates
- **0.1.0** (2026-02-16) — Initial release with safe backend, basic transpilation, and import/export support

[Unreleased]: https://github.com/YOUR_ORG/herkos/compare/v0.1.0...HEAD
[Unreleased]: https://github.com/YOUR_ORG/herkos/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/YOUR_ORG/herkos/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/YOUR_ORG/herkos/releases/tag/v0.1.0
8 changes: 4 additions & 4 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ resolver = "2"
members = ["crates/herkos-runtime", "crates/herkos-core", "crates/herkos", "crates/herkos-tests"]

exclude = [
"examples/c-fft",
"examples/c-to-wasm-to-rust",
"examples/inter-module-lending",
"examples/herkos-bootstrap",
Expand Down
2 changes: 1 addition & 1 deletion crates/herkos-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "herkos-core"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
description = "Compile-Time Memory Isolation via WebAssembly and Rust Transpilation — core library"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/herkos-runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "herkos-runtime"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
description = "Runtime library for herkos transpiled output — IsolatedMemory, WasmTrap, capability traits"
license = "Apache-2.0"
Expand Down
4 changes: 2 additions & 2 deletions crates/herkos-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "herkos-tests"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
publish = false
description = "End-to-end compilation tests for herkos transpiler"
Expand All @@ -13,7 +13,7 @@ herkos-runtime = { path = "../herkos-runtime" }
[build-dependencies]
anyhow = { workspace = true }
wat = { workspace = true }
herkos-core = { path = "../herkos-core" }
herkos-core = { version = "0.2.0", path = "../herkos-core" }

[dev-dependencies]
criterion = "0.8.2"
Expand Down
4 changes: 2 additions & 2 deletions crates/herkos/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "herkos"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
description = "Compile-Time Memory Isolation via WebAssembly and Rust Transpilation"
license = "Apache-2.0"
Expand All @@ -12,7 +12,7 @@ categories = ["wasm", "development-tools::build-utils"]
readme = "../../README.md"

[dependencies]
herkos-core = { path = "../herkos-core" }
herkos-core = { version = "0.2.0", path = "../herkos-core" }
anyhow = { workspace = true }
clap = { workspace = true }

Expand Down
26 changes: 26 additions & 0 deletions docs/REQUIREMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,23 @@ memory.grow shall not perform heap allocation. New pages shall be zero-initializ
within pre-allocated storage. Returns previous page count on success, -1 on failure.
```

```{req} Bulk Memory Operations
:id: REQ_MEM_BULK_OPS
:status: open
:tags: memory, bulk-operations, wasm-spec
The transpiler shall support WebAssembly bulk memory operations: memory.fill,
memory.init, and data.drop. All operations shall be bounds-checked. Out-of-bounds
operations shall trap with WasmTrap::OutOfBounds, never panic.
```

```{req} Data Segment Support
:id: REQ_MEM_DATA_SEGMENTS
:status: open
:tags: memory, data-segments
Passive data segments shall be stored as compile-time constants in the generated
output. memory.init shall copy from these constants into the module's linear memory.
```

### 4.2 Module Representation

```{req} Two Module Types
Expand Down Expand Up @@ -199,6 +216,15 @@ shall be formatted (rustfmt), readable, and auditable. No panics, no unwinding
only Result<T, WasmTrap> for error handling.
```

```{req} Version Information in Generated Code
:id: REQ_TRANS_VERSION_INFO
:status: open
:tags: transpilation, output, metadata
Generated code shall include version information: the herkos transpiler version
and the WebAssembly binary format version. This enables traceability and debugging
of transpiled modules.
```

```{req} Deterministic Code Generation
:id: REQ_TRANS_DETERMINISTIC
:status: open
Expand Down
137 changes: 122 additions & 15 deletions docs/SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Where the requirements say *what* the system must do, this specification says *h

For features that are planned but not yet implemented (verified/hybrid backends, temporal isolation, etc.), see [FUTURE.md](FUTURE.md).

**Document Status**: Draft — Version 0.2 — 2026-02-25
**Document Status**: Draft — Version 0.2 — 2026-03-16

---

Expand Down Expand Up @@ -49,6 +49,12 @@ herkos input.wasm --mode safe --output output.rs
| `--output` | Output Rust file path | No |
| `--max-pages` | Maximum memory pages when module declares no maximum | No |

**Environment variables:**

| Variable | Values | Default | Effect |
|----------|--------|---------|--------|
| `HERKOS_OPTIMIZE` | `1` or any other value | Unset (disabled) | When `HERKOS_OPTIMIZE=1`, enables IR optimization passes (currently dead block elimination). Set during transpilation, affects generated code size and performance. |

> **Current limitations**: Only the `safe` backend is implemented. The `--mode` flag accepts `safe`, `hybrid`, and `verified` but all behave identically. `--max-pages` has no effect. See [FUTURE.md](FUTURE.md) for the verified and hybrid backend plans.

### 1.3 Understanding the Output
Expand Down Expand Up @@ -500,27 +506,27 @@ let result = lib.call_export_transform(&mut app.memory, ptr, len)?;
### 3.1 Component Overview

```
┌─────────────────────────────────────────────────────────────────
│ herkos workspace
┌─────────────────────────────────────────────────────────────────┐
│ herkos workspace │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ herkos (CLI) │ │ herkos-runtime │ │ herkos-tests │ │
│ │ ┌───────────┐ │ │ #![no_std] │ │ │ │
│ │ │ Parser │ │ │ │ │ WAT/C/Rust │ │
│ │ │(wasmparser)│ │ │ IsolatedMemory │ │ sources │ │
│ │ │(wasmparser)│ │ │ IsolatedMemory │ │ sources │ │
│ │ ├───────────┤ │ │ Table, FuncRef │ │ → .wasm │ │
│ │ │ IR Builder│ │ │ Module types │ │ → transpile │ │
│ │ │ (SSA-form)│ │ │ WasmTrap │ │ → test │ │
│ │ ├───────────┤ │ │ Wasm ops │ │ │ │
│ │ │ (SSA-form)│ │ │ WasmTrap │ │ → test │ │
│ │ ├───────────┤ │ │ Wasm ops │ │ │ │
│ │ │ Optimizer │ │ │ │ │ benches/ │ │
│ │ ├───────────┤ │ └──────────────────┘ └────────────────┘ │
│ │ │ Backend │ │
│ │ │ (safe) │ │ │ depends on │ depends
│ │ │ Backend │ │ ▲ │
│ │ │ (safe) │ │ │ depends on │ depends
│ │ ├───────────┤ │ │ │ on both │
│ │ │ Codegen │ │ └─────────────────────┘
│ │ └───────────┘ │
│ └─────────────────┘
└─────────────────────────────────────────────────────────────────
│ │ │ Codegen │ │ └─────────────────────┘ │
│ │ └───────────┘ │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```

### 3.2 Runtime (`herkos-runtime`)
Expand Down Expand Up @@ -809,6 +815,61 @@ Type 2: (i32) → i32 → canonical = 2 (new signature)

The transpiler builds a canonical type index mapping at transpile time. Both `FuncRef.type_index` and the type check use canonical indices. At runtime, the check is a simple integer comparison.

### 4.6 Bulk Memory Operations

> Implementation: [crates/herkos-runtime/src/memory.rs](../crates/herkos-runtime/src/memory.rs) lines 149–174

The WebAssembly bulk memory operations allow efficient copying and initialization of memory regions without scalar load/store loops.

#### 4.6.1 `memory.fill`

Fills a region of memory with a byte value. Per Wasm spec, only the low 8 bits of the value are used.

```rust
impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
pub fn fill(&mut self, dst: usize, val: u8, len: usize) -> WasmResult<()>;
}
```

Generated code:
```rust
// Wasm: memory.fill $dst $val $len
memory.fill(dst as usize, val as u8, len as usize)?;
```

Traps `OutOfBounds` if `[dst, dst + len)` exceeds active memory. Length zero is a no-op.

#### 4.6.2 `memory.init`

Copies data from a passive data segment into memory at runtime. Each data segment is stored as a constant `&'static [u8]` in the generated code.

```rust
impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
pub fn init_data_partial(&mut self, dst: usize, data: &[u8], src_offset: usize, len: usize) -> WasmResult<()>;
}
```

Generated code:
```rust
// Wasm: memory.init $data_segment $dst $src_offset $len
memory.init_data_partial(dst as usize, &DATA_SEGMENT_0, src_offset as usize, len as usize)?;
```

Traps `OutOfBounds` if either region (source or destination) exceeds bounds:
- Source: `[src_offset, src_offset + len)` must be within the data segment
- Destination: `[dst, dst + len)` must be within active memory

#### 4.6.3 `data.drop`

Marks a data segment as dropped (per Wasm spec). In the safe backend this is a no-op because data segments are stored as constant references and cannot actually be deallocated.

```rust
// Wasm: data.drop $segment
// (no-op in safe backend — const slices persist)
```

In future verified and hybrid backends, `data.drop` may enable optimizations: proving that dropped segments are never accessed again could allow proving certain addresses as never-in-bounds.

---

## 5. Integration
Expand All @@ -829,7 +890,53 @@ let result = module.process_data(&mut host, ptr, len)?;

Full type safety, zero `unsafe`, zero-cost dispatch via monomorphization.

### 5.2 C-Compatible ABI (Optional)
### 5.2 The Env<H> Context Pattern

> Implementation: [crates/herkos-core/src/codegen/env.rs](../crates/herkos-core/src/codegen/env.rs)

Generated modules use a unified **Env<H>** context struct that bundles the host (generic parameter `H`) and mutable globals, simplifying parameter threading throughout function calls.

```rust
// Generated by transpiler
pub struct Env<'a, H: ModuleHostTrait + ?Sized> {
pub host: &'a mut H,
pub globals: &'a mut Globals,
}

// Every function that needs imports or mutable state receives Env<H>
fn process<H: ModuleHostTrait>(
memory: &mut IsolatedMemory<MAX_PAGES>,
env: &mut Env<H>,
input: i32,
) -> WasmResult<i32> {
// Call imported function via trait
let result = env.host.some_import(input)?;
// Read/write mutable global
env.globals.my_global += 1;
Ok(result)
}
```

**Design rationale:**
- **Unified state**: Avoids threading `host`, `globals`, and other mutable state as separate parameters
- **Type safety**: All imports must be present in the host's trait implementation — checked at compile time
- **Zero overhead**: The Env struct is a thin wrapper; LLVM inlines and optimizes away the indirection
- **Extensibility**: Adding new imports or globals requires only modifying the trait, not all function signatures

**Generated trait:**

```rust
pub trait ModuleHostTrait {
// One method per function import
fn imported_function(&mut self, arg: i32) -> WasmResult<i32>;

// Getter/setter methods for each imported global
fn get_imported_global(&self) -> i32;
fn set_imported_global(&mut self, value: i32);
}
```

### 5.4 C-Compatible ABI (Optional)

For integration with non-Rust systems, an optional `extern "C"` wrapper erases generics:

Expand All @@ -849,7 +956,7 @@ pub extern "C" fn module_call(

The C ABI wrapper uses `unsafe` and raw pointers. Capability enforcement still applies inside — the wrapper calls through trait-bounded functions. This is an escape hatch, not the default.

### 5.3 Native Rust Integration
### 5.5 Native Rust Integration

Native Rust code integrates by implementing import traits directly:

Expand Down
2 changes: 2 additions & 0 deletions examples/c-fft/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/fft_wasm.rs
fft.wasm
Loading
Loading