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
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ fn main() -> anyhow::Result<()> {
))
"#;

let runtime = Runtime::from_sources(stdlib::sources().chain([source]), GpuDialect::Hip)?;
let [result] = runtime.exec("two-plus-two", [])?;
let mut runtime = Runtime::new(GpuDialect::Hip)?;
let artifact = runtime.load_sources(stdlib::sources().chain([source]))?;
let [result] = artifact.exec("two-plus-two", [])?;
let Value::U64(result) = result else {
anyhow::bail!("two-plus-two returned non-u64 value: {result:?}");
};
Expand All @@ -53,11 +54,11 @@ fn main() -> anyhow::Result<()> {
}
```

The same example is available as
[catena-lang/examples/readme.rs](catena-lang/examples/readme.rs):
A more complete example is available as
[catena-lang/examples/runtime.rs](catena-lang/examples/runtime.rs):

```sh
cargo run -p catena-lang --example readme
cargo run -p catena-lang --example runtime
```

NOTE: by default this will run using the
Expand All @@ -66,5 +67,5 @@ With [Nix](https://nix.dev/), you can run the example with the required
dependencies as follows:

```sh
nix develop --command cargo run -p catena-lang --example readme
nix develop --command cargo run -p catena-lang --example runtime
```
14 changes: 1 addition & 13 deletions catena-gpu/src/codegen/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ fn input(a: &GpuAssign, index: usize) -> Result<String, GpuRenderError> {
pub(super) fn value_expr(value: &GpuValue) -> String {
match value {
GpuValue::Var(var) => var.name.clone(),
GpuValue::FnSymbol(target) => sanitize_ident(&format!("program.{target}")),
GpuValue::FnSymbol(symbol) => symbol.to_string(),
}
}

Expand Down Expand Up @@ -441,18 +441,6 @@ fn render_ifc_call<'a>(
));
}

pub(super) fn sanitize_ident(name: &str) -> String {
name.chars()
.map(|character| {
if character.is_ascii_alphanumeric() {
character
} else {
'_'
}
})
.collect()
}

fn arity(a: &GpuAssign, expected: usize) -> GpuRenderError {
GpuRenderError::InvalidArity {
op: a.op.clone(),
Expand Down
3 changes: 1 addition & 2 deletions catena-gpu/tests/cases/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ fn check_readable_sum(destination_size: usize, grid_x: u32, block_x: u32) -> any
let source_values = [17_u64, 25, 99];
let source = runtime.mem_u64(&source_values)?;

let [destination] = runtime.exec(
&artifact,
let [destination] = artifact.exec(
"fill-with-readable-sum",
[
destination.into(),
Expand Down
3 changes: 1 addition & 2 deletions catena-gpu/tests/cases/matmul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ fn check_naive_u64_matmul(
let a = runtime.mem_u64(&a_values)?;
let b = runtime.mem_u64(&b_values)?;

let [c] = runtime.exec(
&artifact,
let [c] = artifact.exec(
"naive-u64-matmul",
[
c.into(),
Expand Down
3 changes: 1 addition & 2 deletions catena-lang/examples/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ fn main() -> anyhow::Result<()> {
let owned = runtime.mem_u64(&[3, 5])?;
let borrowed = runtime.mem_u64(&[8, 13])?;

let [returned, sum] = runtime.exec(
&artifact,
let [returned, sum] = artifact.exec(
"add-first-and-return-owned",
[owned.into(), borrowed.as_ref().into()],
)?;
Expand Down
8 changes: 4 additions & 4 deletions catena-lang/examples/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ fn main() -> anyhow::Result<()> {
runtime.load(stdlib::paths_from(&root).chain([root.join("examples/example.hex")]))?;
let plus_one = runtime.load_sources(stdlib::sources().chain([ARRAY_HEAD_PLUS_ONE]))?;

let [result] = runtime.exec(&artifact, "two-times-two", [])?;
let [result] = artifact.exec("two-times-two", [])?;
let Value::U64(result) = result else {
anyhow::bail!("two-times-two returned non-u64 value: {result:?}");
};
println!("two-times-two: {result}");
anyhow::ensure!(result == 4, "two-times-two returned {result}, expected 4");

let [] = runtime.exec(&artifact, "require-true", [true.into()])?;
let [] = artifact.exec("require-true", [true.into()])?;

// Input values for `array-head-u64`
let values = [0x123456789abcdef0_u64, 7, 11];
Expand All @@ -48,7 +48,7 @@ fn main() -> anyhow::Result<()> {

// Execute array-head-u64 with values above
let input = runtime.mem_u64(&values)?;
let [head] = runtime.exec(&artifact, "array-head-u64", [input.as_ref().into()])?;
let [head] = artifact.exec("array-head-u64", [input.as_ref().into()])?;
let Value::U64(head) = head else {
anyhow::bail!("array-head-u64 returned non-u64 value: {head:?}");
};
Expand All @@ -62,7 +62,7 @@ fn main() -> anyhow::Result<()> {

// Run the second .so's version of the same program with the device
// allocation created above.
let [head_plus_one] = runtime.exec(&plus_one, "array-head-u64", [input.as_ref().into()])?;
let [head_plus_one] = plus_one.exec("array-head-u64", [input.as_ref().into()])?;
let Value::U64(head_plus_one) = head_plus_one else {
anyhow::bail!("second array-head-u64 returned non-u64 value: {head_plus_one:?}");
};
Expand Down
18 changes: 9 additions & 9 deletions catena-lang/examples/safe_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,51 +29,51 @@ fn main() -> anyhow::Result<()> {
]))?;
let add_one = runtime.load_sources(stdlib::sources().chain([ADD_ONE_SOURCE]))?;

let [added] = runtime.exec(&add_one, "add-one", [41_u64.into()])?;
let [added] = add_one.exec("add-one", [41_u64.into()])?;
anyhow::ensure!(matches!(added, Value::U64(42)));

let [result] = runtime.exec(&artifact, "two-times-two", [])?;
let [result] = artifact.exec("two-times-two", [])?;
let Value::U64(result) = result else {
anyhow::bail!("two-times-two returned non-u64 value: {result:?}");
};
println!("two-times-two: {result}");
anyhow::ensure!(result == 4, "two-times-two returned {result}, expected 4");

let [] = runtime.exec(&artifact, "require-true", [true.into()])?;
let [] = artifact.exec("require-true", [true.into()])?;

let input = MemOwn::from_u64_slice(&[17, 19, 23], dialect)?;
let [head] = runtime.exec(&artifact, "array-head-u64", [input.as_ref().into()])?;
let [head] = artifact.exec("array-head-u64", [input.as_ref().into()])?;
let Value::U64(head) = head else {
anyhow::bail!("array-head-u64 returned non-u64 value: {head:?}");
};
anyhow::ensure!(head == 17, "array-head-u64 returned {head}, expected 17");
let [head_again] = runtime.exec(&artifact, "array-head-u64", [input.as_ref().into()])?;
let [head_again] = artifact.exec("array-head-u64", [input.as_ref().into()])?;
anyhow::ensure!(matches!(head_again, Value::U64(17)));
// The child only imported a borrowed mapping, so the parent allocation is
// still valid after successive calls.
anyhow::ensure!(input.try_to_u64_vec()? == [17, 19, 23]);
println!("array-head-u64 through IPC: {head}");

let [returned] = runtime.exec(&artifact, "mem-own-identity", [input.into()])?;
let [returned] = artifact.exec("mem-own-identity", [input.into()])?;
let Value::MemOwn(returned) = returned else {
anyhow::bail!("mem-own-identity returned non-owned memory: {returned:?}");
};
anyhow::ensure!(returned.try_to_u64_vec()? == [17, 19, 23]);

let [materialized] = runtime.exec(&artifact, "materialize-indexes", [4_u64.into()])?;
let [materialized] = artifact.exec("materialize-indexes", [4_u64.into()])?;
let Value::MemOwn(materialized) = materialized else {
anyhow::bail!("materialize-indexes returned non-owned memory: {materialized:?}");
};
anyhow::ensure!(materialized.try_to_u64_vec()? == [1, 1, 1, 1]);

let empty = MemOwn::from_u64_slice(&[], dialect)?;
let [empty] = runtime.exec(&artifact, "mem-own-identity", [empty.into()])?;
let [empty] = artifact.exec("mem-own-identity", [empty.into()])?;
let Value::MemOwn(empty) = empty else {
anyhow::bail!("empty mem-own-identity returned non-owned memory: {empty:?}");
};
anyhow::ensure!(empty.try_to_u64_vec()?.is_empty());

match runtime.exec::<1, 0>(&artifact, "require-true", [false.into()]) {
match artifact.exec::<1, 0>("require-true", [false.into()]) {
Err(SafeExecError::ChildTerminated { status, stderr }) => {
anyhow::ensure!(!status.success(), "asserting child exited successfully");
anyhow::ensure!(
Expand Down
36 changes: 0 additions & 36 deletions catena-lang/src/runtime/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::{
ffi::OsString,
path::{Path, PathBuf},
process::{Command, ExitStatus},
sync::atomic::{AtomicU64, Ordering},
};

use thiserror::Error;
Expand All @@ -29,41 +28,6 @@ pub enum ArtifactError {
},
}

/// Identifies one compiled Catena artifact belonging to a runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Artifact {
runtime_id: RuntimeId,
index: usize,
}

impl Artifact {
pub(crate) fn new(runtime_id: RuntimeId, index: usize) -> Self {
Self { runtime_id, index }
}

pub(crate) fn belongs_to(&self, runtime_id: RuntimeId) -> bool {
self.runtime_id == runtime_id
}

pub(crate) fn index(&self) -> usize {
self.index
}
}

static NEXT_RUNTIME_ID: AtomicU64 = AtomicU64::new(1);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct RuntimeId(u64);

impl RuntimeId {
pub(crate) fn new() -> Self {
let id = NEXT_RUNTIME_ID
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
.expect("runtime ID space exhausted");
Self(id)
}
}

/// A shared object file created by compiling generated Catena GPU C++.
#[derive(Debug)]
pub(super) struct SharedObject {
Expand Down
20 changes: 9 additions & 11 deletions catena-lang/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut runtime = Runtime::new(GpuDialect::Hip)?;
//! let artifact = runtime.load_sources(stdlib::sources().chain([PROGRAM]))?;
//! let [result] = runtime.exec(&artifact, "add-one", [41_u64.into()])?;
//! let [result] = artifact.exec("add-one", [41_u64.into()])?;
//! let Value::U64(sum) = result else {
//! panic!("`add-one` returned an unexpected value: {result:?}");
//! };
Expand All @@ -26,20 +26,20 @@
//!
//! ## Quick reference
//!
//! - [`Runtime::new`] creates an empty runtime.
//! - [`Runtime::new`] creates a process-local GPU context.
//! - [`Runtime::load`] and [`Runtime::load_sources`] compile programs into an [`Artifact`].
//! - [`Runtime::exec`] calls a program from a selected artifact with [`Value`] inputs.
//! - [`Artifact::exec`] calls a program from a compiled artifact with [`Value`] inputs.
//! - [`Runtime::mem_u16`], [`Runtime::mem_u64`], and [`Runtime::mem_f32`] copy host slices into owned device memory.
//! ### [`Value`] and Memory
//!
//! Values are input to a catena program by supplying [`Value`]s to [`Runtime::exec`].
//! Values are input to a catena program by supplying [`Value`]s to [`Artifact::exec`].
//! In addition to scalars like [`Value::U64`], you can supply two kinds of memory:
//! [`MemRef`] and [`MemOwn`].
//!
//! Both are *length tagged device byte pointers* with differing ownership semantics:
//! Both are *length-tagged device byte pointers* with differing ownership semantics:
//!
//! - [`MemRef`]: A reference; ownership retained by Rust
//! - [`MemOwn`]: A *owned* buffer: ownership is *transferred to the catena program*
//! - [`MemOwn`]: An *owned* buffer: ownership is *transferred to the catena program*
//!
//! Raw device pointers can be wrapped with unsafe [`MemOwn::from_raw_parts`] or
//! [`MemRef::from_raw_parts`], depending on ownership. For example, use [`MemRef::from_raw_parts`]
Expand All @@ -51,7 +51,7 @@ pub mod value;
/// Helpers for creating and freeing Catena memory values on program boundaries
pub mod mem;

/// manage and run compiled catena programs
/// Compile and run catena programs
pub mod runtime;

/// Compile generated GPU C++ to a shared object.
Expand All @@ -66,13 +66,11 @@ mod signature;
//#[cfg(test)]
//mod tests;

pub(crate) use artifact::RuntimeId;
pub use artifact::{Artifact, ArtifactError};
pub use artifact::ArtifactError;
pub use mem::MemError;
pub use mem::MemOwn;
pub use mem::MemRef;
pub use runtime::Runtime;
pub use runtime::{ExecError, InitError};
pub use runtime::{Artifact, ExecError, InitError, Runtime};
#[cfg(feature = "experimental-catena-gpu")]
pub use signature::GeneratedFunction;
pub use value::Value;
Expand Down
Loading
Loading