From 4f29898da768e28992aebb646bf0502a67a782ff Mon Sep 17 00:00:00 2001
From: rarbore2 <rarbore2@illinois.edu>
Date: Sat, 25 May 2024 18:39:59 -0500
Subject: [PATCH] Minimal runtime for simple examples

---
 Cargo.lock                                    | 1053 -----------------
 Cargo.toml                                    |    6 +-
 hercules_cg/Cargo.toml                        |    1 -
 hercules_cg/src/common.rs                     |   23 +-
 hercules_cg/src/cpu.rs                        |    2 -
 hercules_cg/src/lib.rs                        |    2 -
 hercules_cg/src/top.rs                        |   16 +-
 hercules_ir/src/ir.rs                         |  222 ++--
 hercules_ir/src/lib.rs                        |    2 +
 hercules_ir/src/manifest.rs                   |  195 +++
 hercules_rt/Cargo.toml                        |    3 +-
 hercules_rt/src/lib.rs                        |   46 +-
 hercules_rt/src/manifest.rs                   |   97 --
 hercules_rt_proc/Cargo.toml                   |   14 +
 hercules_rt_proc/src/lib.rs                   |  315 +++++
 hercules_samples/matmul/src/main.rs           |   38 +-
 hercules_samples/task_parallel/Cargo.toml     |   11 +
 hercules_samples/task_parallel/src/main.rs    |   11 +
 .../{ => task_parallel}/task_parallel.hir     |    0
 19 files changed, 711 insertions(+), 1346 deletions(-)
 delete mode 100644 Cargo.lock
 create mode 100644 hercules_ir/src/manifest.rs
 delete mode 100644 hercules_rt/src/manifest.rs
 create mode 100644 hercules_rt_proc/Cargo.toml
 create mode 100644 hercules_rt_proc/src/lib.rs
 create mode 100644 hercules_samples/task_parallel/Cargo.toml
 create mode 100644 hercules_samples/task_parallel/src/main.rs
 rename hercules_samples/{ => task_parallel}/task_parallel.hir (100%)

diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index 54f370fc..00000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,1053 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "anstream"
-version = "0.6.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
-dependencies = [
- "windows-sys",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
-dependencies = [
- "anstyle",
- "windows-sys",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.83"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
-
-[[package]]
-name = "atomic-polyfill"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
-dependencies = [
- "critical-section",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
-
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
-[[package]]
-name = "bincode"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bitflags"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitvec"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
-name = "cactus"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbc26382d871df4b7442e3df10a9402bf3cf5e55cbd66f12be38861425f0564"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "cfgrammar"
-version = "0.13.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "163348850b1cd34fa99ef1592b5d598ea7e6752f18aff2125b67537e887edb36"
-dependencies = [
- "indexmap",
- "lazy_static",
- "num-traits",
- "regex",
- "serde",
- "vob",
-]
-
-[[package]]
-name = "clap"
-version = "4.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
-
-[[package]]
-name = "cobs"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
-
-[[package]]
-name = "critical-section"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
-
-[[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
-]
-
-[[package]]
-name = "embedded-io"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
-
-[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
-name = "filetime"
-version = "0.2.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "windows-sys",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "funty"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
-
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "hash32"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "heapless"
-version = "0.7.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
-dependencies = [
- "atomic-polyfill",
- "hash32",
- "rustc_version",
- "serde",
- "spin",
- "stable_deref_trait",
-]
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "hercules_cg"
-version = "0.1.0"
-dependencies = [
- "bitvec",
- "hercules_ir",
- "hercules_rt",
-]
-
-[[package]]
-name = "hercules_cpu_beta"
-version = "0.1.0"
-dependencies = [
- "clap",
- "hercules_cg",
- "hercules_ir",
- "hercules_opt",
- "rand",
-]
-
-[[package]]
-name = "hercules_dot"
-version = "0.1.0"
-dependencies = [
- "clap",
- "hercules_ir",
- "hercules_opt",
- "rand",
-]
-
-[[package]]
-name = "hercules_driver"
-version = "0.1.0"
-dependencies = [
- "clap",
- "hercules_ir",
- "hercules_opt",
- "ron",
-]
-
-[[package]]
-name = "hercules_ir"
-version = "0.1.0"
-dependencies = [
- "bitvec",
- "nom",
- "ordered-float",
- "rand",
- "serde",
-]
-
-[[package]]
-name = "hercules_matmul"
-version = "0.1.0"
-dependencies = [
- "clap",
- "hercules_rt",
- "rand",
-]
-
-[[package]]
-name = "hercules_opt"
-version = "0.1.0"
-dependencies = [
- "bitvec",
- "hercules_cg",
- "hercules_ir",
- "ordered-float",
- "postcard",
- "serde",
- "take_mut",
-]
-
-[[package]]
-name = "hercules_rt"
-version = "0.1.0"
-dependencies = [
- "hercules_ir",
- "libc",
- "postcard",
- "serde",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
-
-[[package]]
-name = "itoa"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
-
-[[package]]
-name = "juno_frontend"
-version = "0.1.0"
-dependencies = [
- "cfgrammar",
- "clap",
- "hercules_ir",
- "hercules_opt",
- "lrlex",
- "lrpar",
- "num-rational",
- "num-traits",
- "ordered-float",
- "phf",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "libc"
-version = "0.2.154"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
-
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "lrlex"
-version = "0.13.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77ff18e1bd3ed77d7bc2800a0f8b0e922a3c7ba525505be8bab9cf45dfc4984b"
-dependencies = [
- "cfgrammar",
- "getopts",
- "lazy_static",
- "lrpar",
- "num-traits",
- "quote",
- "regex",
- "regex-syntax",
- "serde",
- "vergen",
-]
-
-[[package]]
-name = "lrpar"
-version = "0.13.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efea5a41b9988b5ae41ea9b2375a52cfa0e483f0210357209caa8d361a24a368"
-dependencies = [
- "bincode",
- "cactus",
- "cfgrammar",
- "filetime",
- "indexmap",
- "lazy_static",
- "lrtable",
- "num-traits",
- "packedvec",
- "regex",
- "serde",
- "static_assertions",
- "vergen",
- "vob",
-]
-
-[[package]]
-name = "lrtable"
-version = "0.13.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff5668c3bfd279ed24d5b0d24568c48dc993f9beabd51f74d1865a78c1d206ab"
-dependencies = [
- "cfgrammar",
- "fnv",
- "num-traits",
- "serde",
- "sparsevec",
- "vob",
-]
-
-[[package]]
-name = "memchr"
-version = "2.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
-
-[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-
-[[package]]
-name = "nom"
-version = "7.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
-dependencies = [
- "memchr",
- "minimal-lexical",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
-dependencies = [
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
-[[package]]
-name = "num-integer"
-version = "0.1.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
-dependencies = [
- "autocfg",
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "num_threads"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "ordered-float"
-version = "4.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "packedvec"
-version = "1.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde3c690ec20e4a2b4fb46f0289a451181eb50011a1e2acc8d85e2fde9062a45"
-dependencies = [
- "num-traits",
- "serde",
-]
-
-[[package]]
-name = "phf"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
-dependencies = [
- "phf_macros",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
-dependencies = [
- "phf_shared",
- "rand",
-]
-
-[[package]]
-name = "phf_macros"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
-dependencies = [
- "phf_generator",
- "phf_shared",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "postcard"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
-dependencies = [
- "cobs",
- "embedded-io",
- "heapless",
- "serde",
-]
-
-[[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.82"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radium"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "regex"
-version = "1.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
-
-[[package]]
-name = "ron"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
-dependencies = [
- "base64",
- "bitflags 2.5.0",
- "serde",
- "serde_derive",
-]
-
-[[package]]
-name = "rustc_version"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
-dependencies = [
- "semver",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
-
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "semver"
-version = "1.0.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
-
-[[package]]
-name = "serde"
-version = "1.0.200"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.200"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "siphasher"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
-
-[[package]]
-name = "sparsevec"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35df5d2e580b29f3f7ec5b4ed49b0ab3acf7f3624122b3e823cafb9630f293b8"
-dependencies = [
- "num-traits",
- "packedvec",
- "serde",
- "vob",
-]
-
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-dependencies = [
- "lock_api",
-]
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "syn"
-version = "2.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "take_mut"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "time"
-version = "0.3.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
-dependencies = [
- "deranged",
- "itoa",
- "libc",
- "num-conv",
- "num_threads",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
-
-[[package]]
-name = "utf8parse"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
-
-[[package]]
-name = "vergen"
-version = "8.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525"
-dependencies = [
- "anyhow",
- "rustversion",
- "time",
-]
-
-[[package]]
-name = "vob"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c058f4c41e71a043c67744cb76dcc1ae63ece328c1732a72489ccccc2dec23e6"
-dependencies = [
- "num-traits",
- "rustc_version",
- "serde",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
-
-[[package]]
-name = "wyz"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
-dependencies = [
- "tap",
-]
diff --git a/Cargo.toml b/Cargo.toml
index bded2dcf..ba6ffb28 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,12 +5,14 @@ members = [
 	"hercules_ir",
 	"hercules_opt",
 	"hercules_rt",
+	"hercules_rt_proc",
 
 	"hercules_tools/hercules_driver",
 	"hercules_tools/hercules_dot",
 	"hercules_tools/hercules_cpu_beta",
 
-  "juno_frontend",
+	"juno_frontend",
 
-	"hercules_samples/matmul"
+	"hercules_samples/matmul",
+	"hercules_samples/task_parallel"
 ]
diff --git a/hercules_cg/Cargo.toml b/hercules_cg/Cargo.toml
index b5479669..94641530 100644
--- a/hercules_cg/Cargo.toml
+++ b/hercules_cg/Cargo.toml
@@ -6,4 +6,3 @@ authors = ["Russel Arbore <rarbore2@illinois.edu>"]
 [dependencies]
 bitvec = "*"
 hercules_ir = { path = "../hercules_ir" }
-hercules_rt = { path = "../hercules_rt" }
diff --git a/hercules_cg/src/common.rs b/hercules_cg/src/common.rs
index 9bc91b57..f3d5a89d 100644
--- a/hercules_cg/src/common.rs
+++ b/hercules_cg/src/common.rs
@@ -1,10 +1,8 @@
 extern crate hercules_ir;
-extern crate hercules_rt;
 
 use std::collections::HashMap;
 
 use self::hercules_ir::*;
-use self::hercules_rt::manifest::*;
 
 /*
  * Pretty much all of the codegen functions need to take in some large subset of
@@ -188,10 +186,10 @@ impl<'a> FunctionContext<'a> {
     pub(crate) fn partition_control_successors(
         &self,
         partition_id: PartitionID,
-    ) -> Vec<PartitionID> {
+    ) -> Vec<(PartitionID, NodeID)> {
         let partition = &self.partitions_inverted_map[partition_id.idx()];
 
-        let mut partitions: Vec<PartitionID> = partition
+        partition
             .iter()
             // Only consider nodes in other partitions that are successors of
             // control nodes. These are necessarily other control nodes.
@@ -205,14 +203,11 @@ impl<'a> FunctionContext<'a> {
                     .into_iter()
                     .map(|id| self.plan.partitions[id.idx()])
                     .filter(|id| *id != partition_id)
+                    .map(move |part_id| (part_id, *id))
             })
             // We want a flat list of all such partitions.
             .flatten()
-            .collect();
-
-        // We only want one copy of the ID per partition.
-        partitions.dedup();
-        partitions
+            .collect()
     }
 
     /*
@@ -376,6 +371,12 @@ impl<'a> PartitionContext<'a> {
             manifest.outputs.push(PartitionOutput::ControlIndicator);
         }
 
+        // Store the successor partitions.
+        manifest.successor_partitions = control_successors
+            .into_iter()
+            .map(|(part_id, control_id)| (control_id.idx() as u32, part_id.idx() as u32))
+            .collect();
+
         PartitionContext {
             function,
             partition_id,
@@ -438,7 +439,7 @@ pub(crate) fn generate_type_strings(module: &Module) -> Vec<String> {
     // since a type may reference a type ID ahead of it in the vector. Instead,
     // iterate types in a bottom up order with respect to the type intern DAGs.
     let mut llvm_types = vec!["".to_string(); module.types.len()];
-    for id in module.types_bottom_up() {
+    for id in types_bottom_up(&module.types) {
         llvm_types[id.idx()] = generate_type_string(&module.types[id.idx()], &llvm_types);
     }
 
@@ -497,7 +498,7 @@ pub(crate) fn generate_constant_strings(module: &Module) -> Vec<String> {
     // Render constants into LLVM IR. This is done in a very similar manner as
     // types.
     let mut llvm_constants = vec!["".to_string(); module.constants.len()];
-    for id in module.constants_bottom_up() {
+    for id in constants_bottom_up(&module.constants) {
         llvm_constants[id.idx()] = generate_constant_string(
             id,
             &module.constants[id.idx()],
diff --git a/hercules_cg/src/cpu.rs b/hercules_cg/src/cpu.rs
index 4f4726a7..134e56a8 100644
--- a/hercules_cg/src/cpu.rs
+++ b/hercules_cg/src/cpu.rs
@@ -1,6 +1,5 @@
 extern crate bitvec;
 extern crate hercules_ir;
-extern crate hercules_rt;
 
 use std::collections::HashMap;
 use std::collections::VecDeque;
@@ -12,7 +11,6 @@ use std::fmt::Write;
 use self::bitvec::prelude::*;
 
 use self::hercules_ir::*;
-use self::hercules_rt::manifest::*;
 
 use crate::*;
 
diff --git a/hercules_cg/src/lib.rs b/hercules_cg/src/lib.rs
index 280910dd..c33fd857 100644
--- a/hercules_cg/src/lib.rs
+++ b/hercules_cg/src/lib.rs
@@ -2,10 +2,8 @@
 
 pub mod common;
 pub mod cpu;
-pub mod cpu_beta;
 pub mod top;
 
 pub use crate::common::*;
 pub use crate::cpu::*;
-pub use crate::cpu_beta::*;
 pub use crate::top::*;
diff --git a/hercules_cg/src/top.rs b/hercules_cg/src/top.rs
index b992344d..626c4257 100644
--- a/hercules_cg/src/top.rs
+++ b/hercules_cg/src/top.rs
@@ -1,11 +1,9 @@
 extern crate hercules_ir;
-extern crate hercules_rt;
 
 use std::collections::HashMap;
 use std::fmt::Write;
 
 use self::hercules_ir::*;
-use self::hercules_rt::manifest::*;
 
 use crate::*;
 
@@ -109,6 +107,17 @@ impl<'a> FunctionContext<'a> {
             })
             .collect();
 
+        // Collect all the node IDs that are values returned by this function.
+        let returned_values = self
+            .function
+            .nodes
+            .iter()
+            .filter_map(|node| node.try_return().map(|(_, data)| data.idx() as u32))
+            .collect::<Vec<u32>>();
+
+        // Get the partition ID of the start node.
+        let top_partition = self.plan.partitions[0].idx() as u32;
+
         // Generate code for each individual partition. This generates a single
         // LLVM function per partition. These functions will be called in async
         // tasks by the Hercules runtime.
@@ -125,11 +134,14 @@ impl<'a> FunctionContext<'a> {
         Ok(FunctionManifest {
             name: self.function.name.clone(),
             param_types: self.function.param_types.clone(),
+            return_type: self.function.return_type,
             typing: self.typing.clone(),
             num_dynamic_constant_parameters: self.function.num_dynamic_constants,
             partitions: manifests,
             // TODO: populate dynamic constant rules.
             dynamic_constant_rules: vec![],
+            top_partition,
+            returned_values,
         })
     }
 }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 075d10c1..3a315aed 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -372,133 +372,131 @@ impl Module {
     }
 
     /*
-     * Create an iterator that traverses all the types in the module bottom up.
-     * This uses a coroutine to make iteratively traversing the type DAGs
-     * easier.
+     * Unfortunately, determining if a constant is an array requires both
+     * knowledge of constants and types, due to zero initializer constants.
      */
-    pub fn types_bottom_up(&self) -> impl Iterator<Item = TypeID> + '_ {
-        let types = &self.types;
-        let mut visited = bitvec![u8, Lsb0; 0; self.types.len()];
-        let mut stack = (0..self.types.len())
-            .map(TypeID::new)
-            .collect::<Vec<TypeID>>();
-        let coroutine = #[coroutine] move || {
-            // Since this is a coroutine, handle recursion manually.
-            while let Some(id) = stack.pop() {
-                if visited[id.idx()] {
-                    continue;
-                }
-                match &types[id.idx()] {
-                    Type::Product(children) | Type::Summation(children) => {
-                        // We have to yield the children of this node before
-                        // this node itself. We keep track of which nodes have
-                        // yielded using visited.
-                        let can_yield = children.iter().all(|x| visited[x.idx()]);
-                        if can_yield {
-                            visited.set(id.idx(), true);
-                            yield id;
-                        } else {
-                            // Push ourselves, then children, so that children
-                            // get popped first.
-                            stack.push(id);
-                            for id in children.iter() {
-                                stack.push(*id);
-                            }
-                        }
-                    }
-                    Type::Array(child, _) => {
-                        // Same idea as product / summation, but there's only
-                        // one child.
-                        let can_yield = visited[child.idx()];
-                        if can_yield {
-                            visited.set(id.idx(), true);
-                            yield id;
-                        } else {
-                            stack.push(id);
-                            stack.push(*child);
+    pub fn is_array_constant(&self, cons_id: ConstantID) -> bool {
+        if let Constant::Zero(ty_id) = self.constants[cons_id.idx()] {
+            self.types[ty_id.idx()].is_array()
+        } else {
+            self.constants[cons_id.idx()].is_strictly_array()
+        }
+    }
+}
+
+/*
+ * Create an iterator that traverses all the types in the module bottom up.
+ * This uses a coroutine to make iteratively traversing the type DAGs
+ * easier.
+ */
+pub fn types_bottom_up(types: &Vec<Type>) -> impl Iterator<Item = TypeID> + '_ {
+    let mut visited = bitvec![u8, Lsb0; 0; types.len()];
+    let mut stack = (0..types.len()).map(TypeID::new).collect::<Vec<TypeID>>();
+    let coroutine = #[coroutine]
+    move || {
+        // Since this is a coroutine, handle recursion manually.
+        while let Some(id) = stack.pop() {
+            if visited[id.idx()] {
+                continue;
+            }
+            match &types[id.idx()] {
+                Type::Product(children) | Type::Summation(children) => {
+                    // We have to yield the children of this node before
+                    // this node itself. We keep track of which nodes have
+                    // yielded using visited.
+                    let can_yield = children.iter().all(|x| visited[x.idx()]);
+                    if can_yield {
+                        visited.set(id.idx(), true);
+                        yield id;
+                    } else {
+                        // Push ourselves, then children, so that children
+                        // get popped first.
+                        stack.push(id);
+                        for id in children.iter() {
+                            stack.push(*id);
                         }
                     }
-                    _ => {
+                }
+                Type::Array(child, _) => {
+                    // Same idea as product / summation, but there's only
+                    // one child.
+                    let can_yield = visited[child.idx()];
+                    if can_yield {
                         visited.set(id.idx(), true);
                         yield id;
+                    } else {
+                        stack.push(id);
+                        stack.push(*child);
                     }
                 }
+                _ => {
+                    visited.set(id.idx(), true);
+                    yield id;
+                }
             }
-        };
-        CoroutineIterator {
-            coroutine: Box::new(coroutine),
         }
+    };
+    CoroutineIterator {
+        coroutine: Box::new(coroutine),
     }
+}
 
-    /*
-     * Create an iterator that traverses all the constants in the module bottom up.
-     * This uses a coroutine to make iteratively traversing the constant DAGs
-     * easier.
-     */
-    pub fn constants_bottom_up(&self) -> impl Iterator<Item = ConstantID> + '_ {
-        let constants = &self.constants;
-        let mut visited = bitvec![u8, Lsb0; 0; self.constants.len()];
-        let mut stack = (0..self.constants.len())
-            .map(ConstantID::new)
-            .collect::<Vec<ConstantID>>();
-        let coroutine = #[coroutine] move || {
-            // Since this is a coroutine, handle recursion manually.
-            while let Some(id) = stack.pop() {
-                if visited[id.idx()] {
-                    continue;
-                }
-                match &constants[id.idx()] {
-                    Constant::Product(_, children) | Constant::Array(_, children) => {
-                        // We have to yield the children of this node before
-                        // this node itself. We keep track of which nodes have
-                        // yielded using visited.
-                        let can_yield = children.iter().all(|x| visited[x.idx()]);
-                        if can_yield {
-                            visited.set(id.idx(), true);
-                            yield id;
-                        } else {
-                            // Push ourselves, then children, so that children
-                            // get popped first.
-                            stack.push(id);
-                            for id in children.iter() {
-                                stack.push(*id);
-                            }
-                        }
-                    }
-                    Constant::Summation(_, _, child) => {
-                        // Same idea as product / summation, but there's only
-                        // one child.
-                        let can_yield = visited[child.idx()];
-                        if can_yield {
-                            visited.set(id.idx(), true);
-                            yield id;
-                        } else {
-                            stack.push(id);
-                            stack.push(*child);
+/*
+ * Create an iterator that traverses all the constants in the module bottom up.
+ * This uses a coroutine to make iteratively traversing the constant DAGs
+ * easier.
+ */
+pub fn constants_bottom_up(constants: &Vec<Constant>) -> impl Iterator<Item = ConstantID> + '_ {
+    let mut visited = bitvec![u8, Lsb0; 0; constants.len()];
+    let mut stack = (0..constants.len())
+        .map(ConstantID::new)
+        .collect::<Vec<ConstantID>>();
+    let coroutine = #[coroutine]
+    move || {
+        // Since this is a coroutine, handle recursion manually.
+        while let Some(id) = stack.pop() {
+            if visited[id.idx()] {
+                continue;
+            }
+            match &constants[id.idx()] {
+                Constant::Product(_, children) | Constant::Array(_, children) => {
+                    // We have to yield the children of this node before
+                    // this node itself. We keep track of which nodes have
+                    // yielded using visited.
+                    let can_yield = children.iter().all(|x| visited[x.idx()]);
+                    if can_yield {
+                        visited.set(id.idx(), true);
+                        yield id;
+                    } else {
+                        // Push ourselves, then children, so that children
+                        // get popped first.
+                        stack.push(id);
+                        for id in children.iter() {
+                            stack.push(*id);
                         }
                     }
-                    _ => {
+                }
+                Constant::Summation(_, _, child) => {
+                    // Same idea as product / summation, but there's only
+                    // one child.
+                    let can_yield = visited[child.idx()];
+                    if can_yield {
                         visited.set(id.idx(), true);
                         yield id;
+                    } else {
+                        stack.push(id);
+                        stack.push(*child);
                     }
                 }
+                _ => {
+                    visited.set(id.idx(), true);
+                    yield id;
+                }
             }
-        };
-        CoroutineIterator {
-            coroutine: Box::new(coroutine),
-        }
-    }
-
-    /*
-     * Unfortunately, determining if a constant is an array requires both
-     * knowledge of constants and types, due to zero initializer constants.
-     */
-    pub fn is_array_constant(&self, cons_id: ConstantID) -> bool {
-        if let Constant::Zero(ty_id) = self.constants[cons_id.idx()] {
-            self.types[ty_id.idx()].is_array()
-        } else {
-            self.constants[cons_id.idx()].is_strictly_array()
         }
+    };
+    CoroutineIterator {
+        coroutine: Box::new(coroutine),
     }
 }
 
@@ -899,6 +897,14 @@ impl Node {
         }
     }
 
+    pub fn try_return(&self) -> Option<(NodeID, NodeID)> {
+        if let Node::Return { control, data } = self {
+            Some((*control, *data))
+        } else {
+            None
+        }
+    }
+
     pub fn try_constant(&self) -> Option<ConstantID> {
         if let Node::Constant { id } = self {
             Some(*id)
diff --git a/hercules_ir/src/lib.rs b/hercules_ir/src/lib.rs
index 44252678..c2fe35b7 100644
--- a/hercules_ir/src/lib.rs
+++ b/hercules_ir/src/lib.rs
@@ -9,6 +9,7 @@ pub mod dot;
 pub mod gcm;
 pub mod ir;
 pub mod loops;
+pub mod manifest;
 pub mod parse;
 pub mod schedule;
 pub mod subgraph;
@@ -24,6 +25,7 @@ pub use crate::dot::*;
 pub use crate::gcm::*;
 pub use crate::ir::*;
 pub use crate::loops::*;
+pub use crate::manifest::*;
 pub use crate::parse::*;
 pub use crate::schedule::*;
 pub use crate::subgraph::*;
diff --git a/hercules_ir/src/manifest.rs b/hercules_ir/src/manifest.rs
new file mode 100644
index 00000000..74dc2ce4
--- /dev/null
+++ b/hercules_ir/src/manifest.rs
@@ -0,0 +1,195 @@
+extern crate serde;
+
+use self::serde::Deserialize;
+use self::serde::Serialize;
+
+use crate::*;
+
+/*
+ * Every .hbin file contains a manifest which describes the Hercules functions
+ * contained in the module. This information is used by the runtime to execute
+ * the functions properly, the chief concern being how to stitch together the
+ * execution of each partition.
+ */
+#[derive(Debug, Serialize, Deserialize, Hash)]
+pub struct ModuleManifest {
+    // A module contains a manifest per individual function.
+    pub functions: Vec<FunctionManifest>,
+    // All of the types used in the module.
+    pub types: Vec<Type>,
+    // The only constants that aren't baked into the generated code are array
+    // constants. These are explicitly stored in and loaded from the manifest.
+    // Arrays are composed of the underlying array bytes. We don't need to store
+    // the dimensions of arrays at this point, since the runtime doesn't
+    // manipulate or otherwise need the dimensions of constant arrays.
+    pub array_constants: Vec<Vec<u8>>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Hash)]
+pub struct FunctionManifest {
+    pub name: String,
+    // Types of the function parameters.
+    pub param_types: Vec<TypeID>,
+    // Type of the return value.
+    pub return_type: TypeID,
+    // Types of all of the nodes in this function. Used for figuring out the
+    // type of partition data inputs and outputs.
+    pub typing: Vec<TypeID>,
+    // Number of dynamic constant parameters that need to provided.
+    pub num_dynamic_constant_parameters: u32,
+    // Manifests for constituent partitions.
+    pub partitions: Vec<PartitionManifest>,
+    // When using dynamic constants, certain constraints are generated. For
+    // example, if one uses division in dynamic constant math, the resulting
+    // dynamic constant must be an integer, so the numerator dynamic constant
+    // must be divisible by the denominator dynamic constant. These are stored
+    // per function, since different functions have different contraints on
+    // their dynamic constant parameters.
+    pub dynamic_constant_rules: Vec<DynamicConstantRule>,
+    // The partition containing the start node might not be partition 0.
+    pub top_partition: u32,
+    // Keep track of which node values are returned from the overall function.
+    // Integer is the node ID.
+    pub returned_values: Vec<u32>,
+}
+
+/*
+ * Rules for validity of provided dynamic constants. Integers refer to dynamic
+ * constant parameters of a function.
+ */
+#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
+pub enum DynamicConstantRule {
+    // Generated from subtraction.
+    LessThan(u32, u32),
+    // Generated from division.
+    Divides(u32, u32),
+}
+
+#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
+pub enum PartitionInput {
+    // Data input from another partition within this function. Integer is the
+    // node ID used from the other partition.
+    DataInput(u32),
+    // An argument from the function parameters. Integer is the parameter index.
+    FunctionArgument(u32),
+    // An array constant used in this function. Integer is the array constant
+    // number.
+    ArrayConstant(u32),
+    // A dynamic constant parameter of this function. Integer is the dynamic
+    // constant parameter number.
+    DynamicConstant(u32),
+}
+
+#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
+pub enum PartitionOutput {
+    // Data output used by another partition within this function, or to be
+    // returned from this function. Integer is the node ID used in the other
+    // partition or by a return node.
+    DataOutput(u32),
+    // Value indicating control flow that the runtime should take.
+    ControlIndicator,
+}
+
+#[derive(Debug, Serialize, Deserialize, Default, Hash)]
+pub struct PartitionManifest {
+    // Top node for this partition, as an integer.
+    pub top_node: u32,
+    pub inputs: Vec<PartitionInput>,
+    pub outputs: Vec<PartitionOutput>,
+
+    // Store the partitions that come after this partition. The first element of
+    // the pair is the control returning node in the current partition that
+    // corresponds to a particular successor, and the second element of the pair
+    // is the partition index of the corresponding successor partition.
+    pub successor_partitions: Vec<(u32, u32)>,
+}
+
+/*
+ * Get the data inputs of partitions in a function.
+ */
+pub fn function_partition_inputs(manifest: &FunctionManifest) -> Vec<u32> {
+    let mut all_data_inputs = manifest
+        .partitions
+        .iter()
+        .map(|manifest| {
+            manifest.inputs.iter().filter_map(|input| {
+                if let PartitionInput::DataInput(idx) = input {
+                    Some(*idx)
+                } else {
+                    None
+                }
+            })
+        })
+        .flatten()
+        .collect::<Vec<u32>>();
+    all_data_inputs.sort();
+    all_data_inputs.dedup();
+    all_data_inputs
+}
+
+/*
+ * Get the data outputs of partitions in a function.
+ */
+pub fn function_partition_outputs(manifest: &FunctionManifest) -> Vec<u32> {
+    let mut all_data_outputs = manifest
+        .partitions
+        .iter()
+        .map(|manifest| {
+            manifest.outputs.iter().filter_map(|output| {
+                if let PartitionOutput::DataOutput(idx) = output {
+                    Some(*idx)
+                } else {
+                    None
+                }
+            })
+        })
+        .flatten()
+        .collect::<Vec<u32>>();
+    all_data_outputs.sort();
+    all_data_outputs.dedup();
+    all_data_outputs
+}
+
+/*
+ * Verify a manifest is valid.
+ */
+pub fn verify_manifest(manifest: &ModuleManifest) -> Result<(), String> {
+    manifest
+        .functions
+        .iter()
+        .map(|manifest| {
+            // First, check that all partitiion data inputs get used, and that
+            // all partition data outputs are produced.
+            let all_data_inputs = function_partition_inputs(manifest);
+            let all_data_outputs = function_partition_outputs(manifest);
+
+            // Data outputs include values returned from the overall function.
+            // These are obviously not used by another partition, but that's
+            // fine. Remove them here explicitly.
+            let all_data_outputs = all_data_outputs.into_iter().filter(|idx| !manifest.returned_values.contains(idx)).collect::<Vec<u32>>();
+            if all_data_inputs != all_data_outputs {
+                return Err(format!("PANIC: Partitions in manifest contain inconsistent data inputs and data outputs of partitions.\nHere are the data input IDs:\n{:?}\nHere are the data output IDs:\n{:?}\n", all_data_inputs, all_data_outputs));
+            }
+
+            // Next, check that partitions contain multiple successors if and
+            // only if a control indicator is present in their outputs.
+            // TODO: this will fail to verify a partition with a return node and
+            // a single successor partition, which technically is allowed. Deal
+            // with this later.
+            manifest.partitions.iter().map(|manifest| {
+                let multiple_successors = manifest.successor_partitions.len() > 1;
+                let control_indicator = manifest.outputs.contains(&PartitionOutput::ControlIndicator);
+                if !multiple_successors && control_indicator {
+                    Err("PANIC: Partition in manifest doesn't have multiple successor partitions, but has a control indicator output.".to_string())
+                } else if multiple_successors && !control_indicator {
+                    Err("PANIC: Partition in manifest has multiple successor partitions, but not a control indicator output.".to_string())
+                } else {
+                    Ok(())
+                }
+            }).collect::<Result<Vec<()>, String>>()?;
+
+            Ok(())
+        })
+        .collect::<Result<Vec<()>, String>>()
+        .map(|_| ())
+}
diff --git a/hercules_rt/Cargo.toml b/hercules_rt/Cargo.toml
index 3df92d6a..76c940b7 100644
--- a/hercules_rt/Cargo.toml
+++ b/hercules_rt/Cargo.toml
@@ -2,9 +2,10 @@
 name = "hercules_rt"
 version = "0.1.0"
 authors = ["Russel Arbore <rarbore2@illinois.edu>"]
+edition = "2021"
 
 [dependencies]
 libc = "*"
 postcard = { version = "*", features = ["alloc"] }
 serde = { version = "*", features = ["derive"] }
-hercules_ir = { path = "../hercules_ir" }
+hercules_rt_proc = { path = "../hercules_rt_proc" }
diff --git a/hercules_rt/src/lib.rs b/hercules_rt/src/lib.rs
index 2d19454d..c8a9c729 100644
--- a/hercules_rt/src/lib.rs
+++ b/hercules_rt/src/lib.rs
@@ -1,21 +1,23 @@
-extern crate postcard;
-
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::Path;
+extern crate hercules_rt_proc;
 
 pub(crate) mod elf;
-pub mod manifest;
 pub(crate) use crate::elf::*;
-pub(crate) use crate::manifest::*;
+pub use hercules_rt_proc::use_hbin;
 
 #[derive(Debug)]
 pub struct Module {
-    manifest: ModuleManifest,
     elf: Elf,
 }
 
 impl Module {
+    /*
+     * Parse ELF object from ELF bytes buffer.
+     */
+    pub fn new(buffer: &[u8]) -> Self {
+        let elf = unsafe { parse_elf(buffer) };
+        Module { elf }
+    }
+
     /*
      * Get the function pointer corresponding to a function name. Panic if not
      * found.
@@ -31,31 +33,3 @@ impl Module {
         )
     }
 }
-
-pub fn load_binary(path: &Path) -> Module {
-    let mut f = File::open(path).unwrap();
-    let mut buffer = vec![];
-    f.read_to_end(&mut buffer).unwrap();
-    let manifest_and_elf_bytes: (ModuleManifest, Vec<u8>) = postcard::from_bytes(&buffer).unwrap();
-    let elf = unsafe { parse_elf(&manifest_and_elf_bytes.1) };
-    Module {
-        manifest: manifest_and_elf_bytes.0,
-        elf,
-    }
-}
-
-/*
- * An ugly, unchecked macro for looking up Hercules functions in a module. Curse
- * Rust for not supporting variadic generics and type pattern matching :shrug:.
- * TODO: Generate per-lookup struct type for checking that the provided types
- * are correct.
- */
-#[macro_export]
-macro_rules! lookup_function {
-    ($module:expr, $function:expr, $($param_ty:ty),*, => $ret_ty:ty) => {
-        {
-            let fn_ptr: fn($($param_ty),*) -> $ret_ty = unsafe { std::mem::transmute($module.get_function_ptr($function)) };
-            fn_ptr
-        }
-    };
-}
diff --git a/hercules_rt/src/manifest.rs b/hercules_rt/src/manifest.rs
deleted file mode 100644
index c184e8b1..00000000
--- a/hercules_rt/src/manifest.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-extern crate hercules_ir;
-extern crate serde;
-
-use self::serde::Deserialize;
-use self::serde::Serialize;
-
-use self::hercules_ir::ir::*;
-
-/*
- * Every .hbin file contains a manifest which describes the Hercules functions
- * contained in the module. This information is used by the runtime to execute
- * the functions properly, the chief concern being how to stitch together the
- * execution of each partition.
- */
-#[derive(Debug, Serialize, Deserialize)]
-pub struct ModuleManifest {
-    // A module contains a manifest per individual function.
-    pub functions: Vec<FunctionManifest>,
-    // All of the types used in the module.
-    pub types: Vec<Type>,
-    // The only constants that aren't baked into the generated code are array
-    // constants. These are explicitly stored in and loaded from the manifest.
-    // Arrays are composed of the underlying array bytes. We don't need to store
-    // the dimensions of arrays at this point, since the runtime doesn't
-    // manipulate or otherwise need the dimensions of constant arrays.
-    pub array_constants: Vec<Vec<u8>>,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct FunctionManifest {
-    pub name: String,
-    // Types of the function parameters.
-    pub param_types: Vec<TypeID>,
-    // Types of all of the nodes in this function. Used for figuring out the
-    // type of partition data inputs and outputs.
-    pub typing: Vec<TypeID>,
-    // Number of dynamic constant parameters that need to provided.
-    pub num_dynamic_constant_parameters: u32,
-    // Manifests for constituent partitions.
-    pub partitions: Vec<PartitionManifest>,
-    // When using dynamic constants, certain constraints are generated. For
-    // example, using a dynamic constant in a fork means that it must be non-
-    // zero, since fork-join nests are guaranteed to execute at least one
-    // iteration. Also, if one uses division in dynamic constant math, the
-    // resulting dynamic constant must be an integer, so the numerator dynamic
-    // constant must be divisible by the denominator dynamic constant. These are
-    // stored per function, since different functions have different contraints
-    // on their dynamic constant parameters.
-    pub dynamic_constant_rules: Vec<DynamicConstantRule>,
-}
-
-/*
- * Rules for validity of provided dynamic constants. Integers refer to dynamic
- * constant parameters of a function.
- */
-#[derive(Debug, Serialize, Deserialize)]
-pub enum DynamicConstantRule {
-    // Generated from forks.
-    NonZero(u32),
-    // Generated from subtraction.
-    LessThan(u32, u32),
-    // Generated from division.
-    Divides(u32, u32),
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum PartitionInput {
-    // Data input from another partition within this function. Integer is the
-    // node ID used from the other partition.
-    DataInput(u32),
-    // An argument from the function parameters. Integer is the parameter index.
-    FunctionArgument(u32),
-    // An array constant used in this function. Integer is the array constant
-    // number.
-    ArrayConstant(u32),
-    // A dynamic constant parameter of this function. Integer is the dynamic
-    // constant parameter number.
-    DynamicConstant(u32),
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum PartitionOutput {
-    // Data output used by another partition within this function, or to be
-    // returned from this function. Integer is the node ID used in the other
-    // partition or by a return node.
-    DataOutput(u32),
-    // Value indicating control flow that the runtime should take.
-    ControlIndicator,
-}
-
-#[derive(Debug, Serialize, Deserialize, Default)]
-pub struct PartitionManifest {
-    // Top node for this partition, as an integer.
-    pub top_node: u32,
-    pub inputs: Vec<PartitionInput>,
-    pub outputs: Vec<PartitionOutput>,
-}
diff --git a/hercules_rt_proc/Cargo.toml b/hercules_rt_proc/Cargo.toml
new file mode 100644
index 00000000..eafa8a44
--- /dev/null
+++ b/hercules_rt_proc/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "hercules_rt_proc"
+version = "0.1.0"
+authors = ["Russel Arbore <rarbore2@illinois.edu>"]
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+postcard = { version = "*", features = ["alloc"] }
+serde = { version = "*", features = ["derive"] }
+hercules_ir = { path = "../hercules_ir" }
+anyhow = "*"
\ No newline at end of file
diff --git a/hercules_rt_proc/src/lib.rs b/hercules_rt_proc/src/lib.rs
new file mode 100644
index 00000000..b335a013
--- /dev/null
+++ b/hercules_rt_proc/src/lib.rs
@@ -0,0 +1,315 @@
+#![feature(iter_intersperse)]
+
+extern crate anyhow;
+extern crate hercules_ir;
+extern crate postcard;
+extern crate proc_macro;
+
+use std::ffi::OsStr;
+use std::fmt::Write;
+use std::fs::File;
+use std::hash::{DefaultHasher, Hash, Hasher};
+use std::io::prelude::*;
+use std::path::Path;
+
+use proc_macro::*;
+
+use self::hercules_ir::*;
+
+/*
+ * Parse manifest from header of .hbin file.
+ */
+fn manifest_and_module_bytes(buffer: &[u8]) -> (ModuleManifest, Vec<u8>) {
+    postcard::from_bytes(buffer).unwrap()
+}
+
+/*
+ * Convert Hercules types to the Rust types generated in the interface.
+ */
+fn generate_type_string(ty: &Type, rust_types: &Vec<String>) -> String {
+    match ty {
+        Type::Control(_) => "NOT_A_REAL_TYPE".to_string(),
+        Type::Boolean => "bool".to_string(),
+        Type::Integer8 => "i8".to_string(),
+        Type::Integer16 => "i16".to_string(),
+        Type::Integer32 => "i32".to_string(),
+        Type::Integer64 => "i64".to_string(),
+        Type::UnsignedInteger8 => "u8".to_string(),
+        Type::UnsignedInteger16 => "u16".to_string(),
+        Type::UnsignedInteger32 => "u32".to_string(),
+        Type::UnsignedInteger64 => "u64".to_string(),
+        Type::Float32 => "f32".to_string(),
+        Type::Float64 => "f64".to_string(),
+        Type::Product(fields) => {
+            fields
+                .iter()
+                .map(|field_id| &rust_types[field_id.idx()] as &str)
+                .fold("(".to_string(), |acc, field| acc + field + ",")
+                + ")"
+        }
+        Type::Summation(_) => todo!(),
+        Type::Array(elem, _) => format!("::std::boxed::Box<[{}]>", &rust_types[elem.idx()]),
+    }
+}
+
+/*
+ * Generate async Rust code orchestrating partition execution.
+ */
+fn codegen(manifest: &ModuleManifest, elf: &[u8]) -> Result<String, anyhow::Error> {
+    // Verify the manifest.
+    verify_manifest(manifest).map_err(anyhow::Error::msg)?;
+
+    // Convert Hercules IR types to Rust types.
+    let mut rust_types = vec!["".to_string(); manifest.types.len()];
+    for id in types_bottom_up(&manifest.types) {
+        rust_types[id.idx()] = generate_type_string(&manifest.types[id.idx()], &rust_types);
+    }
+
+    // Write to a String containing all of the Rust code.
+    let mut rust_code = "".to_string();
+
+    // Emit the ELF bytes as a static byte string constant.
+    write!(
+        rust_code,
+        "const __HERCULES_ELF_OBJ: &[u8] = {};",
+        Literal::byte_string(elf)
+    )?;
+
+    // Emit the async functions.
+    for function in &manifest.functions {
+        // Emit the function signature.
+        write!(rust_code, "async fn {}(", function.name)?;
+        let param_tys = function.param_types.iter().map(|id| &rust_types[id.idx()]);
+        for (idx, param) in param_tys.enumerate() {
+            write!(rust_code, "param_{}: {},", idx, param)?;
+        }
+        for dyn_cons_idx in 0..function.num_dynamic_constant_parameters {
+            write!(rust_code, "dyn_cons_{}: u64,", dyn_cons_idx)?;
+        }
+        write!(
+            rust_code,
+            ") -> {} {{",
+            rust_types[function.return_type.idx()]
+        )?;
+
+        // Check that the ELF got embedded properly.
+        write!(
+            rust_code,
+            "use std::hash::{{DefaultHasher, Hash, Hasher}};debug_assert_eq!({{let mut s = DefaultHasher::new(); __HERCULES_ELF_OBJ.hash(&mut s); s.finish()}}, {});",
+            {
+                let mut s = DefaultHasher::new();
+                elf.hash(&mut s);
+                s.finish()
+            }
+        )?;
+
+        // Load the ELF object, and cast the appropriate pointers.
+        write!(
+            rust_code,
+            r#"let module = ::hercules_rt::Module::new(__HERCULES_ELF_OBJ);"#
+        )?;
+        for (idx, partition) in function.partitions.iter().enumerate() {
+            // Determine the signature for the partition function.
+            let u64_ty_id = TypeID::new(
+                manifest
+                    .types
+                    .iter()
+                    .position(|ty| *ty == Type::UnsignedInteger64)
+                    .unwrap(),
+            );
+            let input_types = partition.inputs.iter().map(|input| match input {
+                PartitionInput::DataInput(node_idx) => function.typing[*node_idx as usize],
+                PartitionInput::FunctionArgument(param_idx) => {
+                    function.param_types[*param_idx as usize]
+                }
+                PartitionInput::ArrayConstant(_) => todo!(),
+                PartitionInput::DynamicConstant(_) => u64_ty_id,
+            });
+            let output_type = Type::Product(
+                partition
+                    .outputs
+                    .iter()
+                    .map(|input| match input {
+                        PartitionOutput::DataOutput(node_idx) => {
+                            function.typing[*node_idx as usize]
+                        }
+                        PartitionOutput::ControlIndicator => u64_ty_id,
+                    })
+                    .collect(),
+            );
+
+            // Get the pointer for the partition function, and cast it to the
+            // correct function pointer type.
+            write!(
+                rust_code,
+                r#"let fn_ptr_part_{}: extern "C" fn({}) -> {} = unsafe {{ ::core::mem::transmute(module.get_function_ptr("{}_part_{}")) }};"#,
+                idx,
+                input_types
+                    .map(|id| &rust_types[id.idx()] as &str)
+                    .intersperse(", ")
+                    .fold("".to_string(), |acc, field| acc + field),
+                // Generate the output product type on the fly. This type is not
+                // necessarily present in the typing of the original IR, before
+                // code generation.
+                generate_type_string(&output_type, &rust_types),
+                function.name,
+                idx
+            )?;
+        }
+
+        // Declare all of the input / output intermediate variables. We declare
+        // them as Options since we want to catch use before definition bugs. We
+        // get the list of variables from partition outputs specifically, since
+        // this also includes returned values.
+        let all_partition_outputs = function_partition_outputs(function);
+        for node in all_partition_outputs {
+            write!(
+                rust_code,
+                "let mut node_{}: Option<{}> = None;",
+                node,
+                &rust_types[function.typing[node as usize].idx()]
+            )?;
+        }
+
+        // The core executor is a Rust loop. We literally run a "control token"
+        // as described in the original sea of nodes paper through the
+        // partitions to drive execution.
+        write!(
+            rust_code,
+            "let mut control_token: u32 = {};loop {{",
+            function.top_partition
+        )?;
+
+        // Match on the control token position to determine which partition to
+        // execute.
+        write!(rust_code, "match control_token {{")?;
+        for (idx, partition) in function.partitions.iter().enumerate() {
+            // The control token stores the current partition index.
+            write!(rust_code, "{} => {{", idx)?;
+
+            // Calculate the inputs.
+            for (input_idx, input) in partition.inputs.iter().enumerate() {
+                write!(
+                    rust_code,
+                    "let input_{} = {};",
+                    input_idx,
+                    match input {
+                        PartitionInput::DataInput(idx) => format!(
+                            "node_{}.expect(\"PANIC: Encountered use before def in runtime.\")",
+                            idx
+                        ),
+                        PartitionInput::FunctionArgument(idx) => format!("param_{}", idx),
+                        PartitionInput::ArrayConstant(_) => todo!(),
+                        PartitionInput::DynamicConstant(idx) => format!("dyn_cons_{}", idx),
+                    }
+                )?;
+            }
+
+            // Make the call.
+            write!(rust_code, "let output = fn_ptr_part_{}(", idx)?;
+            for input_idx in 0..partition.inputs.len() {
+                write!(rust_code, "input_{},", input_idx)?;
+            }
+            write!(rust_code, ");")?;
+
+            // Assign the outputs.
+            for (output_idx, output) in partition.outputs.iter().enumerate() {
+                write!(
+                    rust_code,
+                    "{} = Some(output.{});",
+                    match output {
+                        PartitionOutput::DataOutput(idx) => format!("node_{}", idx),
+                        PartitionOutput::ControlIndicator => "todo!()".to_string(),
+                    },
+                    output_idx
+                )?;
+            }
+
+            // If there are no control successors, return from the function. If
+            // there is one control successor, unconditionally set the control
+            // token to that successor's index. If there are multiple control
+            // successors, set the control token to the control indicator output
+            // of the partition function.
+            // TODO: handle functions with multiple returned values.
+            // TODO: handle partitions with a return node and a single successor
+            // partition.
+            if partition.successor_partitions.len() == 0 {
+                assert_eq!(function.returned_values.len(), 1);
+                write!(
+                    rust_code,
+                    "return node_{}.expect(\"PANIC: Encountered use before def in runtime.\");",
+                    function.returned_values[0]
+                )?;
+            } else if partition.successor_partitions.len() == 1 {
+                write!(
+                    rust_code,
+                    "control_token = {};",
+                    partition.successor_partitions[0].1
+                )?;
+            } else {
+                todo!();
+            }
+
+            // Close the branch.
+            write!(rust_code, "}}")?;
+        }
+
+        // Close the match, and handle invalid control token values.
+        write!(
+            rust_code,
+            "_ => panic!(\"PANIC: Invalid control token value.\"),}}"
+        )?;
+
+        // Close the loop.
+        write!(rust_code, "}}")?;
+
+        // Close the function.
+        write!(rust_code, "}}")?;
+    }
+
+    eprintln!("{}", rust_code);
+
+    Ok(rust_code)
+}
+
+/*
+ * Load a Hercules compiled module from a .hbin file.
+ */
+#[proc_macro]
+pub fn use_hbin(path: TokenStream) -> TokenStream {
+    use TokenTree::Literal;
+
+    // Get the path as a Rust path object, and make sure it's a .hbin file.
+    let mut tokens_iter = path.into_iter();
+    let token = tokens_iter
+        .next()
+        .expect("Please provide a path to a .hbin file to the use_hbin! macro.");
+    assert!(tokens_iter.next().is_none(), "Too many tokens provided to the use_hbin! macro. Please provide only a path to a .hbin file.");
+    let literal = if let Literal(literal) = token {
+        literal
+    } else {
+        panic!("Please provide a string literal containing the path to a .hbin file to the use_hbin! macro.");
+    };
+    let literal_string = literal.to_string();
+    let path = Path::new(&literal_string[1..(literal_string.len() - 1)]);
+    assert_eq!(
+        path.extension(),
+        Some(OsStr::new("hbin")),
+        "Please provide only .hbin files to the use_hbin! macro."
+    );
+    assert_eq!(
+        path.try_exists().ok(),
+        Some(true),
+        "Please provide a valid path to a .hbin file to the use_hbin! macro."
+    );
+
+    // Load manifest from path.
+    let mut f = File::open(path).unwrap();
+    let mut buffer = vec![];
+    f.read_to_end(&mut buffer).unwrap();
+    let (manifest, elf) = manifest_and_module_bytes(&buffer);
+    eprintln!("{:?}", manifest);
+
+    // Generate Rust code.
+    codegen(&manifest, &elf).unwrap().parse().unwrap()
+}
diff --git a/hercules_samples/matmul/src/main.rs b/hercules_samples/matmul/src/main.rs
index 36177bea..f281b97e 100644
--- a/hercules_samples/matmul/src/main.rs
+++ b/hercules_samples/matmul/src/main.rs
@@ -1,36 +1,12 @@
 extern crate clap;
+extern crate hercules_rt;
 
-use std::path::Path;
+hercules_rt::use_hbin!("matmul.hbin");
 
 fn main() {
-    let module = hercules_rt::load_binary(Path::new("matmul.hbin"));
-
-    println!("{:?}", module);
-
-    let matmul = hercules_rt::lookup_function!(
-        module,
-        "matmul_part_1",
-        *const f32,
-        *const f32,
-        *mut f32,
-        u64,
-        u64,
-        u64,
-        => *const f32
-    );
-
-    let a = [[1.0f32, 2.0f32], [3.0f32, 4.0f32]];
-    let b = [[5.0f32, 6.0f32], [7.0f32, 8.0f32]];
-    let mut c = [[0.0f32, 0.0f32], [0.0f32, 0.0f32]];
-    unsafe {
-        matmul(
-            std::mem::transmute(a.as_ptr()),
-            std::mem::transmute(b.as_ptr()),
-            std::mem::transmute(c.as_mut_ptr()),
-            2,
-            2,
-            2,
-        )
-    };
-    println!("{} {}\n{} {}", c[0][0], c[0][1], c[1][0], c[1][1]);
+    //let a = [[1.0f32, 2.0f32], [3.0f32, 4.0f32]];
+    //let b = [[5.0f32, 6.0f32], [7.0f32, 8.0f32]];
+    //let mut c = [[0.0f32, 0.0f32], [0.0f32, 0.0f32]];
+    //matmul(&a, &b, &mut c, 2, 2, 2);
+    //println!("{} {}\n{} {}", c[0][0], c[0][1], c[1][0], c[1][1]);
 }
diff --git a/hercules_samples/task_parallel/Cargo.toml b/hercules_samples/task_parallel/Cargo.toml
new file mode 100644
index 00000000..03c402c6
--- /dev/null
+++ b/hercules_samples/task_parallel/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "hercules_task_parallel"
+version = "0.1.0"
+authors = ["Russel Arbore <rarbore2@illinois.edu>"]
+edition = "2021"
+
+[dependencies]
+clap = { version = "*", features = ["derive"] }
+hercules_rt = { path = "../../hercules_rt" }
+rand = "*"
+async-std = "*"
diff --git a/hercules_samples/task_parallel/src/main.rs b/hercules_samples/task_parallel/src/main.rs
new file mode 100644
index 00000000..01480279
--- /dev/null
+++ b/hercules_samples/task_parallel/src/main.rs
@@ -0,0 +1,11 @@
+extern crate async_std;
+extern crate clap;
+extern crate hercules_rt;
+
+hercules_rt::use_hbin!("task_parallel.hbin");
+
+fn main() {
+    async_std::task::block_on(async {
+        println!("{}", task_parallel(16).await);
+    });
+}
diff --git a/hercules_samples/task_parallel.hir b/hercules_samples/task_parallel/task_parallel.hir
similarity index 100%
rename from hercules_samples/task_parallel.hir
rename to hercules_samples/task_parallel/task_parallel.hir
-- 
GitLab