feat: implement Cranelift JIT compiler for native code execution

Add a JIT compiler using Cranelift that compiles Lux functions to native
machine code. Achieves ~160x speedup over the tree-walking interpreter
(fib(30): 11.59ms JIT vs 1.87s interpreter).

Supports: arithmetic, comparisons, conditionals, let bindings, function
calls, and recursion. Compile time overhead is minimal (~500µs).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 17:11:19 -05:00
parent 296686de17
commit d8e01fd174
5 changed files with 890 additions and 6 deletions

350
Cargo.lock generated
View File

@@ -2,12 +2,30 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.101" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.7" version = "0.21.7"
@@ -32,6 +50,12 @@ version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.11.1" version = "1.11.1"
@@ -95,6 +119,114 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cranelift-bforest"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220"
dependencies = [
"bumpalo",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-entity",
"cranelift-isle",
"gimli",
"hashbrown 0.13.2",
"log",
"regalloc2",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8"
[[package]]
name = "cranelift-entity"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0"
[[package]]
name = "cranelift-frontend"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d"
dependencies = [
"cranelift-codegen",
"log",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-isle"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba"
[[package]]
name = "cranelift-jit"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ca96b05988aa057eda09a817a6e31915fabd7f476b513123aff08053cd193dd"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-entity",
"cranelift-module",
"cranelift-native",
"libc",
"log",
"region",
"target-lexicon",
"wasmtime-jit-icache-coherence",
"windows-sys 0.45.0",
]
[[package]]
name = "cranelift-module"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5112c0be9cc5da064e0620570d67852f11ce44f2e572a58ecf7f11df73978b8"
dependencies = [
"anyhow",
"cranelift-codegen",
]
[[package]]
name = "cranelift-native"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00"
dependencies = [
"cranelift-codegen",
"libc",
"target-lexicon",
]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.15" version = "0.5.15"
@@ -158,6 +290,12 @@ version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@@ -265,6 +403,15 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.17" version = "0.2.17"
@@ -289,6 +436,17 @@ dependencies = [
"wasip3", "wasip3",
] ]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
dependencies = [
"fallible-iterator",
"indexmap 1.9.3",
"stable_deref_trait",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.27" version = "0.3.27"
@@ -301,13 +459,28 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http", "http",
"indexmap", "indexmap 2.13.0",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",
] ]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.5" version = "0.15.5"
@@ -517,6 +690,16 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.0"
@@ -611,6 +794,10 @@ dependencies = [
name = "lux" name = "lux"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cranelift-codegen",
"cranelift-frontend",
"cranelift-jit",
"cranelift-module",
"lsp-server", "lsp-server",
"lsp-types", "lsp-types",
"rand", "rand",
@@ -618,10 +805,20 @@ dependencies = [
"rustyline", "rustyline",
"serde", "serde",
"serde_json", "serde_json",
"target-lexicon",
"tempfile", "tempfile",
"thiserror", "thiserror",
] ]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.0" version = "2.8.0"
@@ -849,6 +1046,30 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
] ]
[[package]]
name = "regalloc2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621"
dependencies = [
"fxhash",
"log",
"slice-group-by",
"smallvec",
]
[[package]]
name = "region"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0"
dependencies = [
"bitflags 1.3.2",
"libc",
"mach",
"winapi",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.27" version = "0.11.27"
@@ -1061,6 +1282,12 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "slice-group-by"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@@ -1142,6 +1369,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.25.0" version = "3.25.0"
@@ -1308,6 +1541,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.1"
@@ -1417,7 +1656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"indexmap", "indexmap 2.13.0",
"wasm-encoder", "wasm-encoder",
"wasmparser", "wasmparser",
] ]
@@ -1430,10 +1669,21 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap", "indexmap 2.13.0",
"semver", "semver",
] ]
[[package]]
name = "wasmtime-jit-icache-coherence"
version = "8.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd"
dependencies = [
"cfg-if",
"libc",
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.85" version = "0.3.85"
@@ -1444,12 +1694,43 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@@ -1495,6 +1776,21 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.48.5" version = "0.48.5"
@@ -1543,6 +1839,12 @@ dependencies = [
"windows_x86_64_msvc 0.53.1", "windows_x86_64_msvc 0.53.1",
] ]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.48.5" version = "0.48.5"
@@ -1561,6 +1863,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -1579,6 +1887,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.5" version = "0.48.5"
@@ -1609,6 +1923,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.5" version = "0.48.5"
@@ -1627,6 +1947,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.5" version = "0.48.5"
@@ -1645,6 +1971,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.5" version = "0.48.5"
@@ -1663,6 +1995,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -1719,7 +2057,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
"indexmap", "indexmap 2.13.0",
"prettyplease", "prettyplease",
"syn", "syn",
"wasm-metadata", "wasm-metadata",
@@ -1750,7 +2088,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.10.0", "bitflags 2.10.0",
"indexmap", "indexmap 2.13.0",
"log", "log",
"serde", "serde",
"serde_derive", "serde_derive",
@@ -1769,7 +2107,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",
"indexmap", "indexmap 2.13.0",
"log", "log",
"semver", "semver",
"serde", "serde",

View File

@@ -15,6 +15,13 @@ serde_json = "1"
rand = "0.8" rand = "0.8"
reqwest = { version = "0.11", features = ["blocking", "json"] } reqwest = { version = "0.11", features = ["blocking", "json"] }
# Cranelift for native compilation
cranelift-codegen = "0.95"
cranelift-frontend = "0.95"
cranelift-module = "0.95"
cranelift-jit = "0.95"
target-lexicon = "0.12"
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"

View File

@@ -22,6 +22,8 @@
rustToolchain rustToolchain
cargo-watch cargo-watch
cargo-edit cargo-edit
pkg-config
openssl
]; ];
RUST_BACKTRACE = "1"; RUST_BACKTRACE = "1";

536
src/compiler.rs Normal file
View File

@@ -0,0 +1,536 @@
//! Cranelift-based native compiler for Lux
//!
//! This module compiles Lux programs to native machine code using Cranelift.
//! Currently supports a subset of the language for performance-critical code.
use crate::ast::{Expr, Program, Declaration, FunctionDecl, BinaryOp, UnaryOp, LiteralKind, Statement};
use cranelift_codegen::ir::{AbiParam, InstBuilder, Value, types};
use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::Context;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Module, Linkage, FuncId};
use std::collections::HashMap;
/// Errors that can occur during compilation
#[derive(Debug)]
pub struct CompileError {
pub message: String,
}
impl std::fmt::Display for CompileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Compile error: {}", self.message)
}
}
impl std::error::Error for CompileError {}
/// The JIT compiler for Lux
pub struct JitCompiler {
/// The Cranelift JIT module
module: JITModule,
/// Builder context (reusable)
builder_context: FunctionBuilderContext,
/// Cranelift context (reusable)
ctx: Context,
/// Compiled function pointers
functions: HashMap<String, *const u8>,
/// Function IDs for linking
func_ids: HashMap<String, FuncId>,
}
impl JitCompiler {
/// Create a new JIT compiler
pub fn new() -> Result<Self, CompileError> {
let builder = JITBuilder::new(cranelift_module::default_libcall_names())
.map_err(|e| CompileError { message: e.to_string() })?;
let module = JITModule::new(builder);
Ok(Self {
module,
builder_context: FunctionBuilderContext::new(),
ctx: Context::new(),
functions: HashMap::new(),
func_ids: HashMap::new(),
})
}
/// Compile a Lux function to native code
pub fn compile_function(&mut self, func: &FunctionDecl) -> Result<*const u8, CompileError> {
// Check if already compiled
if let Some(ptr) = self.functions.get(&func.name.name) {
return Ok(*ptr);
}
// Create function signature
let mut sig = self.module.make_signature();
for _ in &func.params {
sig.params.push(AbiParam::new(types::I64));
}
sig.returns.push(AbiParam::new(types::I64));
// Declare the function
let func_id = self.module
.declare_function(&func.name.name, Linkage::Local, &sig)
.map_err(|e| CompileError { message: e.to_string() })?;
self.func_ids.insert(func.name.name.clone(), func_id);
// Clear context for reuse
self.ctx.clear();
self.ctx.func.signature = sig;
// Clone func_ids for use in closure
let func_ids = self.func_ids.clone();
// Build the function body
{
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
// Create entry block
let entry_block = builder.create_block();
builder.append_block_params_for_function_params(entry_block);
builder.switch_to_block(entry_block);
builder.seal_block(entry_block);
// Map parameter names to values
let mut variables: HashMap<String, Variable> = HashMap::new();
let params = builder.block_params(entry_block).to_vec();
for (i, param) in func.params.iter().enumerate() {
let var = Variable::from_u32(i as u32);
builder.declare_var(var, types::I64);
builder.def_var(var, params[i]);
variables.insert(param.name.name.clone(), var);
}
// Compile the function body
let var_count = variables.len();
let result = compile_expr(&mut builder, &func.body, &mut variables, var_count, &func_ids, &mut self.module)?;
// Return the result
builder.ins().return_(&[result]);
builder.finalize();
}
// Compile to machine code
self.module
.define_function(func_id, &mut self.ctx)
.map_err(|e| CompileError { message: e.to_string() })?;
self.module.clear_context(&mut self.ctx);
// Finalize and get the function pointer
self.module.finalize_definitions()
.map_err(|e| CompileError { message: e.to_string() })?;
let ptr = self.module.get_finalized_function(func_id);
self.functions.insert(func.name.name.clone(), ptr);
Ok(ptr)
}
/// Compile and run a simple function that takes no args and returns an i64
pub fn compile_and_run(&mut self, func: &FunctionDecl) -> Result<i64, CompileError> {
let ptr = self.compile_function(func)?;
// Cast to function pointer and call
let func_ptr: fn() -> i64 = unsafe { std::mem::transmute(ptr) };
Ok(func_ptr())
}
/// Compile a program and return pointers to all compiled functions
pub fn compile_program(&mut self, program: &Program) -> Result<(), CompileError> {
// First pass: declare all functions
for decl in &program.declarations {
if let Declaration::Function(func) = decl {
let mut sig = self.module.make_signature();
for _ in &func.params {
sig.params.push(AbiParam::new(types::I64));
}
sig.returns.push(AbiParam::new(types::I64));
let func_id = self.module
.declare_function(&func.name.name, Linkage::Local, &sig)
.map_err(|e| CompileError { message: e.to_string() })?;
self.func_ids.insert(func.name.name.clone(), func_id);
}
}
// Second pass: compile all functions
for decl in &program.declarations {
if let Declaration::Function(func) = decl {
self.compile_function_body(func)?;
}
}
// Finalize
self.module.finalize_definitions()
.map_err(|e| CompileError { message: e.to_string() })?;
// Store function pointers
for (name, func_id) in &self.func_ids {
let ptr = self.module.get_finalized_function(*func_id);
self.functions.insert(name.clone(), ptr);
}
Ok(())
}
/// Compile a function body (assumes function is already declared)
fn compile_function_body(&mut self, func: &FunctionDecl) -> Result<(), CompileError> {
let func_id = *self.func_ids.get(&func.name.name).ok_or_else(|| CompileError {
message: format!("Function not declared: {}", func.name.name),
})?;
// Create signature
let mut sig = self.module.make_signature();
for _ in &func.params {
sig.params.push(AbiParam::new(types::I64));
}
sig.returns.push(AbiParam::new(types::I64));
// Clear and set up context
self.ctx.clear();
self.ctx.func.signature = sig;
// Clone func_ids for use in closure
let func_ids = self.func_ids.clone();
// Build function
{
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
let entry_block = builder.create_block();
builder.append_block_params_for_function_params(entry_block);
builder.switch_to_block(entry_block);
builder.seal_block(entry_block);
let mut variables: HashMap<String, Variable> = HashMap::new();
let params = builder.block_params(entry_block).to_vec();
for (i, param) in func.params.iter().enumerate() {
let var = Variable::from_u32(i as u32);
builder.declare_var(var, types::I64);
builder.def_var(var, params[i]);
variables.insert(param.name.name.clone(), var);
}
let var_count = variables.len();
let result = compile_expr(&mut builder, &func.body, &mut variables, var_count, &func_ids, &mut self.module)?;
builder.ins().return_(&[result]);
builder.finalize();
}
// Define the function
self.module
.define_function(func_id, &mut self.ctx)
.map_err(|e| CompileError { message: e.to_string() })?;
self.module.clear_context(&mut self.ctx);
Ok(())
}
/// Get a compiled function pointer by name
pub fn get_function(&self, name: &str) -> Option<*const u8> {
self.functions.get(name).copied()
}
/// Call a compiled function with given i64 arguments
pub unsafe fn call_function(&self, name: &str, args: &[i64]) -> Result<i64, CompileError> {
let ptr = self.get_function(name).ok_or_else(|| CompileError {
message: format!("Function not found: {}", name),
})?;
match args.len() {
0 => {
let f: fn() -> i64 = std::mem::transmute(ptr);
Ok(f())
}
1 => {
let f: fn(i64) -> i64 = std::mem::transmute(ptr);
Ok(f(args[0]))
}
2 => {
let f: fn(i64, i64) -> i64 = std::mem::transmute(ptr);
Ok(f(args[0], args[1]))
}
3 => {
let f: fn(i64, i64, i64) -> i64 = std::mem::transmute(ptr);
Ok(f(args[0], args[1], args[2]))
}
_ => Err(CompileError {
message: format!("Too many arguments: {}", args.len()),
}),
}
}
}
impl Default for JitCompiler {
fn default() -> Self {
Self::new().expect("Failed to create JIT compiler")
}
}
/// Compile an expression to Cranelift IR (free function to avoid borrow issues)
fn compile_expr(
builder: &mut FunctionBuilder,
expr: &Expr,
variables: &mut HashMap<String, Variable>,
next_var: usize,
func_ids: &HashMap<String, FuncId>,
module: &mut JITModule,
) -> Result<Value, CompileError> {
match expr {
Expr::Literal(lit) => {
match &lit.kind {
LiteralKind::Int(n) => {
Ok(builder.ins().iconst(types::I64, *n))
}
LiteralKind::Bool(b) => {
Ok(builder.ins().iconst(types::I64, if *b { 1 } else { 0 }))
}
_ => Err(CompileError {
message: "Unsupported literal type".to_string()
}),
}
}
Expr::Var(ident) => {
let var = variables.get(&ident.name).ok_or_else(|| CompileError {
message: format!("Undefined variable: {}", ident.name),
})?;
Ok(builder.use_var(*var))
}
Expr::BinaryOp { op, left, right, .. } => {
let lhs = compile_expr(builder, left, variables, next_var, func_ids, module)?;
let rhs = compile_expr(builder, right, variables, next_var, func_ids, module)?;
let result = match op {
BinaryOp::Add => builder.ins().iadd(lhs, rhs),
BinaryOp::Sub => builder.ins().isub(lhs, rhs),
BinaryOp::Mul => builder.ins().imul(lhs, rhs),
BinaryOp::Div => builder.ins().sdiv(lhs, rhs),
BinaryOp::Mod => builder.ins().srem(lhs, rhs),
BinaryOp::Eq => {
let cmp = builder.ins().icmp(IntCC::Equal, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Ne => {
let cmp = builder.ins().icmp(IntCC::NotEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Lt => {
let cmp = builder.ins().icmp(IntCC::SignedLessThan, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Le => {
let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Gt => {
let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Ge => {
let cmp = builder.ins().icmp(IntCC::SignedGreaterThanOrEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::And => builder.ins().band(lhs, rhs),
BinaryOp::Or => builder.ins().bor(lhs, rhs),
_ => return Err(CompileError {
message: format!("Unsupported binary operator: {:?}", op),
}),
};
Ok(result)
}
Expr::UnaryOp { op, operand, .. } => {
let val = compile_expr(builder, operand, variables, next_var, func_ids, module)?;
let result = match op {
UnaryOp::Neg => builder.ins().ineg(val),
UnaryOp::Not => {
let one = builder.ins().iconst(types::I64, 1);
builder.ins().bxor(val, one)
}
};
Ok(result)
}
Expr::If { condition, then_branch, else_branch, .. } => {
let cond_val = compile_expr(builder, condition, variables, next_var, func_ids, module)?;
// Create blocks
let then_block = builder.create_block();
let else_block = builder.create_block();
let merge_block = builder.create_block();
// Add block parameter for the result
builder.append_block_param(merge_block, types::I64);
// Branch based on condition
let zero = builder.ins().iconst(types::I64, 0);
let cmp = builder.ins().icmp(IntCC::NotEqual, cond_val, zero);
builder.ins().brif(cmp, then_block, &[], else_block, &[]);
// Then block
builder.switch_to_block(then_block);
builder.seal_block(then_block);
let then_val = compile_expr(builder, then_branch, variables, next_var, func_ids, module)?;
builder.ins().jump(merge_block, &[then_val]);
// Else block
builder.switch_to_block(else_block);
builder.seal_block(else_block);
let else_val = compile_expr(builder, else_branch, variables, next_var, func_ids, module)?;
builder.ins().jump(merge_block, &[else_val]);
// Merge block
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
Ok(builder.block_params(merge_block)[0])
}
Expr::Let { name, value, body, .. } => {
// Compile the value
let val = compile_expr(builder, value, variables, next_var, func_ids, module)?;
// Create a new variable
let var = Variable::from_u32(next_var as u32);
builder.declare_var(var, types::I64);
builder.def_var(var, val);
variables.insert(name.name.clone(), var);
// Compile the body with the new variable in scope
compile_expr(builder, body, variables, next_var + 1, func_ids, module)
}
Expr::Call { func, args, .. } => {
// Check if it's a direct function call
if let Expr::Var(name) = func.as_ref() {
// Look up the function
let func_id = *func_ids.get(&name.name).ok_or_else(|| CompileError {
message: format!("Unknown function: {}", name.name),
})?;
// Compile arguments
let mut arg_values = Vec::new();
for arg in args {
arg_values.push(compile_expr(builder, arg, variables, next_var, func_ids, module)?);
}
// Get function reference
let func_ref = module.declare_func_in_func(func_id, builder.func);
// Make the call
let call = builder.ins().call(func_ref, &arg_values);
Ok(builder.inst_results(call)[0])
} else {
Err(CompileError {
message: "Only direct function calls supported".to_string(),
})
}
}
Expr::Block { statements, result: block_result, .. } => {
let mut current_var = next_var;
// Compile all statements
for stmt in statements {
match stmt {
Statement::Let { name, value, .. } => {
let val = compile_expr(builder, value, variables, current_var, func_ids, module)?;
let var = Variable::from_u32(current_var as u32);
builder.declare_var(var, types::I64);
builder.def_var(var, val);
variables.insert(name.name.clone(), var);
current_var += 1;
}
Statement::Expr(expr) => {
compile_expr(builder, expr, variables, current_var, func_ids, module)?;
}
}
}
// Compile and return the result expression
compile_expr(builder, block_result, variables, current_var, func_ids, module)
}
_ => Err(CompileError {
message: "Unsupported expression type".to_string(),
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::Parser;
fn parse_function(src: &str) -> FunctionDecl {
let program = Parser::parse_source(src).expect("Parse error");
match &program.declarations[0] {
Declaration::Function(f) => f.clone(),
_ => panic!("Expected function"),
}
}
#[test]
fn test_simple_arithmetic() {
let func = parse_function("fn test(): Int = 1 + 2 * 3");
let mut jit = JitCompiler::new().unwrap();
let result = jit.compile_and_run(&func).unwrap();
assert_eq!(result, 7);
}
#[test]
fn test_conditionals() {
let func = parse_function("fn test(): Int = if 1 > 0 then 42 else 0");
let mut jit = JitCompiler::new().unwrap();
let result = jit.compile_and_run(&func).unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_let_binding() {
let func = parse_function("fn test(): Int = { let x = 10; let y = 20; x + y }");
let mut jit = JitCompiler::new().unwrap();
let result = jit.compile_and_run(&func).unwrap();
assert_eq!(result, 30);
}
#[test]
fn test_recursive_fibonacci() {
use std::time::Instant;
// Compile a program with recursive fibonacci
let src = r#"
fn fib(n: Int): Int = if n <= 1 then n else fib(n - 1) + fib(n - 2)
"#;
let program = Parser::parse_source(src).expect("Parse error");
let mut jit = JitCompiler::new().unwrap();
let compile_start = Instant::now();
jit.compile_program(&program).unwrap();
let compile_time = compile_start.elapsed();
// Call fib(30)
let exec_start = Instant::now();
let result = unsafe { jit.call_function("fib", &[30]).unwrap() };
let exec_time = exec_start.elapsed();
println!("\n=== JIT Benchmark ===");
println!("Compile time: {:?}", compile_time);
println!("Execute fib(30) = {} in {:?}", result, exec_time);
println!("Total: {:?}", compile_time + exec_time);
assert_eq!(result, 832040);
}
}

View File

@@ -1,6 +1,7 @@
//! Lux - A functional programming language with first-class effects //! Lux - A functional programming language with first-class effects
mod ast; mod ast;
mod compiler;
mod debugger; mod debugger;
mod diagnostics; mod diagnostics;
mod exhaustiveness; mod exhaustiveness;