feat: replace Cranelift JIT with C backend
Remove Cranelift JIT compiler and expose the existing C backend as the compilation target. Generated C code can be compiled with GCC/Clang. Changes: - Remove cranelift-* dependencies from Cargo.toml - Delete src/compiler.rs (565 lines of Cranelift code) - Add compile_to_c() function with -o and --run flags - Fix C backend name mangling (main -> main_lux) to avoid conflicts - Update CLI help text and documentation Usage: lux compile <file.lux> # Output C to stdout lux compile <file.lux> -o out.c # Write to file lux compile <file.lux> --run # Compile and execute C backend supports: functions, basic types, operators, if/then/else, records, enums, Console.print. Future work: closures, lists, patterns. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
350
Cargo.lock
generated
350
Cargo.lock
generated
@@ -2,18 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
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]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
@@ -26,12 +14,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
@@ -56,12 +38,6 @@ version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
@@ -131,114 +107,6 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.15"
|
||||
@@ -302,12 +170,6 @@ version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@@ -415,15 +277,6 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
@@ -448,17 +301,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "h2"
|
||||
version = "0.3.27"
|
||||
@@ -471,28 +313,13 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"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]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
@@ -702,16 +529,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
@@ -806,10 +623,6 @@ dependencies = [
|
||||
name = "lux"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"cranelift-frontend",
|
||||
"cranelift-jit",
|
||||
"cranelift-module",
|
||||
"lsp-server",
|
||||
"lsp-types",
|
||||
"rand",
|
||||
@@ -817,21 +630,11 @@ dependencies = [
|
||||
"rustyline",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tiny_http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
@@ -1059,30 +862,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
@@ -1295,12 +1074,6 @@ version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "slice-group-by"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
@@ -1382,12 +1155,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.25.0"
|
||||
@@ -1566,12 +1333,6 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
@@ -1681,7 +1442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
@@ -1694,21 +1455,10 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"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]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
@@ -1719,43 +1469,12 @@ dependencies = [
|
||||
"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]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -1801,21 +1520,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
@@ -1864,12 +1568,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -1888,12 +1586,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -1912,12 +1604,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -1948,12 +1634,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -1972,12 +1652,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -1996,12 +1670,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -2020,12 +1688,6 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2082,7 +1744,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
@@ -2113,7 +1775,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.10.0",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -2132,7 +1794,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap 2.13.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
|
||||
@@ -16,12 +16,6 @@ rand = "0.8"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
tiny_http = "0.12"
|
||||
|
||||
# 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]
|
||||
tempfile = "3"
|
||||
|
||||
@@ -614,16 +614,15 @@ fn main() =
|
||||
|
||||
**Goal**: Compile to a real target
|
||||
|
||||
- [x] JIT compiler (Cranelift) - works for numeric code (~160x speedup)
|
||||
- [x] C backend scaffolding
|
||||
- [ ] Full JIT (strings, lists, ADTs, effects)
|
||||
- [x] C backend (basic functions, Console.print)
|
||||
- [ ] C backend extensions (closures, lists, pattern variables)
|
||||
- [ ] JavaScript backend
|
||||
- [ ] WASM backend
|
||||
- [ ] Optimization passes
|
||||
|
||||
**Deliverable**: Compiled programs that run natively or in browser
|
||||
|
||||
**Current**: `lux compile <file>` JIT compiles numeric code. C backend in progress.
|
||||
**Current**: `lux compile <file>` compiles to C code. Compile and run with `--run` flag.
|
||||
|
||||
### Phase 3: Schema Evolution ⚠️ PARTIAL
|
||||
|
||||
@@ -719,7 +718,7 @@ fn abs(x: Int): Int
|
||||
|
||||
### Pragmatics (In Progress)
|
||||
|
||||
- Primary target: Tree-walking interpreter (default), JIT for performance
|
||||
- Primary target: Tree-walking interpreter (default), C backend for compilation
|
||||
- FFI: Shell commands via Process effect
|
||||
- [ ] JavaScript backend for browser use
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ fn processAny(x: Int @latest): Int = x // any version
|
||||
|
||||
### Planned (Not Yet Fully Implemented)
|
||||
|
||||
- **Full Compilation**: JIT works for numeric code, strings/lists/ADTs missing
|
||||
- **Full C Backend**: Basic functions work, closures/lists/pattern variables pending
|
||||
- **Auto-migration Generation**: Migration bodies stored, execution pending
|
||||
|
||||
---
|
||||
@@ -234,7 +234,7 @@ Quick iteration with type inference and a REPL.
|
||||
|
||||
| Limitation | Description |
|
||||
|------------|-------------|
|
||||
| **Limited JIT** | Cranelift JIT works for numeric code only |
|
||||
| **Limited C Backend** | Basic functions work, closures/lists pending |
|
||||
| **No Package Manager** | Can't share/publish packages yet |
|
||||
| **New Paradigm** | Effects require learning new concepts |
|
||||
| **Small Ecosystem** | No community packages yet |
|
||||
@@ -297,7 +297,7 @@ Quick iteration with type inference and a REPL.
|
||||
### Not a Good Fit (Yet)
|
||||
|
||||
- Large production applications (early stage)
|
||||
- Performance-critical code (JIT limited to numeric)
|
||||
- Performance-critical code (C backend still basic)
|
||||
- Web frontend development (no JS compilation)
|
||||
- Systems programming (no low-level control)
|
||||
|
||||
@@ -348,13 +348,12 @@ Source Code
|
||||
├─────────────────────────┐
|
||||
▼ ▼
|
||||
┌─────────────┐ ┌──────────────┐
|
||||
│ Interpreter │ │ JIT Compiler │
|
||||
│ (default) │ │ (Cranelift) │
|
||||
│ Interpreter │ │ C Backend │
|
||||
│ (default) │ │ (compile) │
|
||||
└─────────────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
Values + Effects Native Code
|
||||
(~160x speedup)
|
||||
Values + Effects C Code → GCC/Clang
|
||||
```
|
||||
|
||||
---
|
||||
@@ -370,12 +369,12 @@ Values + Effects Native Code
|
||||
- ✅ HTTP Server Effect (listen, accept, respond, stop)
|
||||
- ✅ Test Effect (native testing framework)
|
||||
- ✅ Console.readLine and Console.readInt
|
||||
- ✅ JIT Compiler for numeric code (~160x speedup)
|
||||
- ✅ C Backend (basic functions, Console.print)
|
||||
- ✅ Watch mode / hot reload
|
||||
- ✅ Formatter
|
||||
|
||||
**In Progress:**
|
||||
1. **Behavioral Type Verification** - Total, idempotent, deterministic checking
|
||||
1. **C Backend Extensions** - Closures, lists, pattern variable binding
|
||||
2. **Schema Evolution** - Type system integration, auto-migration
|
||||
3. **Error Message Quality** - Context lines shown, suggestions partial
|
||||
|
||||
@@ -383,4 +382,4 @@ Values + Effects Native Code
|
||||
4. **SQL Effect** - Database access
|
||||
5. **Package Manager** - Share code (manifest parsing exists)
|
||||
6. **JavaScript Backend** - Run in browsers
|
||||
7. **Full JIT** - Strings, lists, ADTs, effects
|
||||
7. **Behavioral Type Verification** - Total, idempotent, deterministic checking
|
||||
|
||||
@@ -67,10 +67,10 @@
|
||||
| Task | Priority | Effort | Status |
|
||||
|------|----------|--------|--------|
|
||||
| Elm-quality error messages | P1 | 2 weeks | ⚠️ Partial (context shown, suggestions missing) |
|
||||
| Full JS compilation | P2 | 4 weeks | ⚠️ JIT for numeric code only |
|
||||
| Full JS compilation | P2 | 4 weeks | ❌ Missing |
|
||||
| Hot reload / watch mode | P2 | — | ✅ Complete |
|
||||
| Debugger improvements | P3 | 2 weeks | ✅ Basic |
|
||||
| JIT CLI integration | P1 | — | ✅ Complete (`lux compile`) |
|
||||
| C backend CLI integration | P1 | — | ✅ Complete (`lux compile`) |
|
||||
|
||||
### Success Criteria for Use Case 1
|
||||
- [ ] Can build a REST API with database access
|
||||
@@ -220,7 +220,8 @@
|
||||
|
||||
| Task | Priority | Effort | Status |
|
||||
|------|----------|--------|--------|
|
||||
| Extend JIT (strings, lists) | P1 | 2 weeks | ❌ Missing |
|
||||
| C backend (basic) | P1 | — | ✅ Complete (functions, Console.print) |
|
||||
| Extend C backend (closures, lists) | P1 | 2 weeks | ❌ Missing |
|
||||
| JS backend | P2 | 4 weeks | ❌ Missing |
|
||||
| WASM backend | P3 | 4 weeks | ❌ Missing |
|
||||
|
||||
@@ -296,7 +297,7 @@
|
||||
- ✅ JSON parsing/serialization (parse, stringify, get, object, array)
|
||||
|
||||
**Tooling:**
|
||||
- ✅ JIT compiler (~160x speedup, CLI: `lux compile`)
|
||||
- ✅ C backend (functions, Console.print, CLI: `lux compile`)
|
||||
- ✅ REPL with history
|
||||
- ✅ Basic LSP server
|
||||
- ✅ Formatter
|
||||
|
||||
@@ -82,8 +82,11 @@ impl CBackend {
|
||||
self.emit_function(f)?;
|
||||
}
|
||||
Declaration::Let(let_decl) => {
|
||||
// Skip run expressions - they're handled in the main wrapper
|
||||
if !matches!(&let_decl.value, Expr::Run { .. }) {
|
||||
self.emit_global_let(&let_decl.name, &let_decl.value)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -245,12 +248,18 @@ impl CBackend {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mangle_name(&self, name: &str) -> String {
|
||||
// Add suffix to avoid conflicts with C keywords and standard library
|
||||
format!("{}_lux", name)
|
||||
}
|
||||
|
||||
fn emit_forward_declarations(&mut self, program: &Program) -> Result<(), CGenError> {
|
||||
for decl in &program.declarations {
|
||||
if let Declaration::Function(f) = decl {
|
||||
let ret_type = self.type_expr_to_c(&f.return_type)?;
|
||||
let params = self.emit_params(&f.params)?;
|
||||
self.writeln(&format!("{} {}({});", ret_type, f.name.name, params));
|
||||
let mangled = self.mangle_name(&f.name.name);
|
||||
self.writeln(&format!("{} {}({});", ret_type, mangled, params));
|
||||
}
|
||||
}
|
||||
self.writeln("");
|
||||
@@ -260,8 +269,9 @@ impl CBackend {
|
||||
fn emit_function(&mut self, func: &FunctionDecl) -> Result<(), CGenError> {
|
||||
let ret_type = self.type_expr_to_c(&func.return_type)?;
|
||||
let params = self.emit_params(&func.params)?;
|
||||
let mangled = self.mangle_name(&func.name.name);
|
||||
|
||||
self.writeln(&format!("{} {}({}) {{", ret_type, func.name.name, params));
|
||||
self.writeln(&format!("{} {}({}) {{", ret_type, mangled, params));
|
||||
self.indent += 1;
|
||||
|
||||
// Emit function body
|
||||
@@ -365,7 +375,14 @@ impl CBackend {
|
||||
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
|
||||
let args_str = arg_strs?.join(", ");
|
||||
|
||||
Ok(format!("{}({})", func_name, args_str))
|
||||
// Mangle user-defined function names
|
||||
let c_func_name = if self.functions.contains(&func_name) {
|
||||
self.mangle_name(&func_name)
|
||||
} else {
|
||||
func_name
|
||||
};
|
||||
|
||||
Ok(format!("{}({})", c_func_name, args_str))
|
||||
}
|
||||
|
||||
Expr::Block { statements, result, .. } => {
|
||||
@@ -517,7 +534,8 @@ impl CBackend {
|
||||
if let Expr::Run { expr, .. } = &let_decl.value {
|
||||
if let Expr::Call { func, .. } = expr.as_ref() {
|
||||
if let Expr::Var(fn_name) = func.as_ref() {
|
||||
self.writeln(&format!("{}();", fn_name.name));
|
||||
let mangled = self.mangle_name(&fn_name.name);
|
||||
self.writeln(&format!("{}();", mangled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -602,7 +620,7 @@ mod tests {
|
||||
fn add(a: Int, b: Int): Int = a + b
|
||||
"#;
|
||||
let c_code = generate(source).unwrap();
|
||||
assert!(c_code.contains("LuxInt add(LuxInt a, LuxInt b)"));
|
||||
assert!(c_code.contains("LuxInt add_lux(LuxInt a, LuxInt b)"));
|
||||
assert!(c_code.contains("return (a + b)"));
|
||||
}
|
||||
|
||||
@@ -613,8 +631,8 @@ mod tests {
|
||||
if n <= 1 then 1 else n * factorial(n - 1)
|
||||
"#;
|
||||
let c_code = generate(source).unwrap();
|
||||
assert!(c_code.contains("LuxInt factorial(LuxInt n)"));
|
||||
assert!(c_code.contains("factorial((n - 1))"));
|
||||
assert!(c_code.contains("LuxInt factorial_lux(LuxInt n)"));
|
||||
assert!(c_code.contains("factorial_lux((n - 1))"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
565
src/compiler.rs
565
src/compiler.rs
@@ -1,565 +0,0 @@
|
||||
//! 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.
|
||||
|
||||
#![allow(dead_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)
|
||||
}
|
||||
|
||||
expr => {
|
||||
use crate::ast::LiteralKind;
|
||||
let expr_type = match expr {
|
||||
Expr::Literal(lit) => match &lit.kind {
|
||||
LiteralKind::String(_) => "String literal",
|
||||
LiteralKind::Float(_) => "Float literal",
|
||||
LiteralKind::Char(_) => "Char literal",
|
||||
LiteralKind::Unit => "Unit literal",
|
||||
_ => "Literal",
|
||||
},
|
||||
Expr::EffectOp { effect, operation, .. } => {
|
||||
return Err(CompileError {
|
||||
message: format!("Effect operation '{}.{}' - effects are not supported in JIT",
|
||||
effect.name, operation.name),
|
||||
});
|
||||
}
|
||||
Expr::Field { .. } => "Field access (records)",
|
||||
Expr::Lambda { .. } => "Lambda/closure",
|
||||
Expr::Match { .. } => "Match expression",
|
||||
Expr::List { .. } => "List literal",
|
||||
Expr::Record { .. } => "Record literal",
|
||||
Expr::Tuple { .. } => "Tuple literal",
|
||||
Expr::Run { .. } => "Run expression (effects)",
|
||||
Expr::Resume { .. } => "Resume expression (effects)",
|
||||
_ => "Unknown expression",
|
||||
};
|
||||
Err(CompileError {
|
||||
message: format!("Unsupported in JIT: {}", expr_type),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
135
src/main.rs
135
src/main.rs
@@ -2,7 +2,6 @@
|
||||
|
||||
mod ast;
|
||||
mod codegen;
|
||||
mod compiler;
|
||||
mod debugger;
|
||||
mod diagnostics;
|
||||
mod exhaustiveness;
|
||||
@@ -141,13 +140,17 @@ fn main() {
|
||||
handle_pkg_command(&args[2..]);
|
||||
}
|
||||
"compile" => {
|
||||
// Compile and run with JIT
|
||||
// Compile to C code
|
||||
if args.len() < 3 {
|
||||
eprintln!("Usage: lux compile <file.lux> [--benchmark]");
|
||||
eprintln!("Usage: lux compile <file.lux> [-o output.c] [--run]");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let benchmark = args.iter().any(|a| a == "--benchmark");
|
||||
compile_file(&args[2], benchmark);
|
||||
let run_after = args.iter().any(|a| a == "--run");
|
||||
let output_path = args.iter()
|
||||
.position(|a| a == "-o")
|
||||
.and_then(|i| args.get(i + 1))
|
||||
.map(|s| s.as_str());
|
||||
compile_to_c(&args[2], output_path, run_after);
|
||||
}
|
||||
path => {
|
||||
// Run a file
|
||||
@@ -166,7 +169,9 @@ fn print_help() {
|
||||
println!("Usage:");
|
||||
println!(" lux Start the REPL");
|
||||
println!(" lux <file.lux> Run a file (interpreter)");
|
||||
println!(" lux compile <file.lux> Compile and run with JIT (--benchmark for timing)");
|
||||
println!(" lux compile <file.lux> Compile to C code (stdout)");
|
||||
println!(" lux compile <f> -o out.c Compile to C file");
|
||||
println!(" lux compile <f> --run Compile and execute");
|
||||
println!(" lux fmt <file.lux> Format a file (--check to verify only)");
|
||||
println!(" lux check <file.lux> Type check without running");
|
||||
println!(" lux test [pattern] Run tests (optional pattern filter)");
|
||||
@@ -257,11 +262,11 @@ fn check_file(path: &str) {
|
||||
println!("{}: OK", path);
|
||||
}
|
||||
|
||||
fn compile_file(path: &str, benchmark: bool) {
|
||||
use compiler::JitCompiler;
|
||||
fn compile_to_c(path: &str, output_path: Option<&str>, run_after: bool) {
|
||||
use codegen::c_backend::CBackend;
|
||||
use modules::ModuleLoader;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
use std::process::Command;
|
||||
|
||||
let file_path = Path::new(path);
|
||||
let source = match std::fs::read_to_string(file_path) {
|
||||
@@ -272,8 +277,7 @@ fn compile_file(path: &str, benchmark: bool) {
|
||||
}
|
||||
};
|
||||
|
||||
// Parse
|
||||
let parse_start = Instant::now();
|
||||
// Parse with module loading
|
||||
let mut loader = ModuleLoader::new();
|
||||
if let Some(parent) = file_path.parent() {
|
||||
loader.add_search_path(parent.to_path_buf());
|
||||
@@ -286,10 +290,8 @@ fn compile_file(path: &str, benchmark: bool) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let parse_time = parse_start.elapsed();
|
||||
|
||||
// Type check
|
||||
let check_start = Instant::now();
|
||||
let mut checker = TypeChecker::new();
|
||||
if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
|
||||
for error in errors {
|
||||
@@ -298,64 +300,87 @@ fn compile_file(path: &str, benchmark: bool) {
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
let check_time = check_start.elapsed();
|
||||
|
||||
// Compile with JIT
|
||||
let compile_start = Instant::now();
|
||||
let mut jit = match JitCompiler::new() {
|
||||
Ok(j) => j,
|
||||
// Generate C code
|
||||
let mut backend = CBackend::new();
|
||||
let c_code = match backend.generate(&program) {
|
||||
Ok(code) => code,
|
||||
Err(e) => {
|
||||
eprintln!("JIT initialization error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = jit.compile_program(&program) {
|
||||
eprintln!("Compilation error: {}", e);
|
||||
eprintln!("C codegen error: {}", e);
|
||||
eprintln!();
|
||||
eprintln!("Note: The JIT compiler currently only supports:");
|
||||
eprintln!(" - Integer arithmetic and comparisons");
|
||||
eprintln!(" - Conditionals (if/then/else)");
|
||||
eprintln!("Note: The C backend currently supports:");
|
||||
eprintln!(" - Integer, Float, Bool, String, Char, Unit literals");
|
||||
eprintln!(" - Arithmetic and comparison operators");
|
||||
eprintln!(" - If/then/else conditionals");
|
||||
eprintln!(" - Let bindings and blocks");
|
||||
eprintln!(" - Function calls (including recursion)");
|
||||
eprintln!(" - Function definitions and calls");
|
||||
eprintln!(" - Records and enums (basic)");
|
||||
eprintln!(" - Console.print effect");
|
||||
eprintln!();
|
||||
eprintln!("Not yet supported: strings, floats, lists, records,");
|
||||
eprintln!("pattern matching, effects, ADTs.");
|
||||
eprintln!("Not yet supported: closures, lists, pattern variable binding,");
|
||||
eprintln!("other effects, higher-order functions.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let compile_time = compile_start.elapsed();
|
||||
|
||||
// Find main function or last expression
|
||||
let exec_start = Instant::now();
|
||||
let result = if jit.get_function("main").is_some() {
|
||||
unsafe { jit.call_function("main", &[]) }
|
||||
} else {
|
||||
// Try to find any function to call
|
||||
eprintln!("No 'main' function found.");
|
||||
eprintln!("Define a function like: fn main(): Int = ...");
|
||||
std::process::exit(1);
|
||||
};
|
||||
let exec_time = exec_start.elapsed();
|
||||
|
||||
match result {
|
||||
Ok(value) => {
|
||||
println!("{}", value);
|
||||
// Determine output
|
||||
if run_after {
|
||||
// Write to temp file, compile, and run
|
||||
let temp_c = std::env::temp_dir().join("lux_output.c");
|
||||
let temp_bin = std::env::temp_dir().join("lux_output");
|
||||
|
||||
if benchmark {
|
||||
println!();
|
||||
println!("=== JIT Benchmark ===");
|
||||
println!("Parse time: {:?}", parse_time);
|
||||
println!("Check time: {:?}", check_time);
|
||||
println!("Compile time: {:?}", compile_time);
|
||||
println!("Execute time: {:?}", exec_time);
|
||||
println!("Total time: {:?}", parse_time + check_time + compile_time + exec_time);
|
||||
if let Err(e) = std::fs::write(&temp_c, &c_code) {
|
||||
eprintln!("Error writing temp file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Try to find a C compiler
|
||||
let cc = std::env::var("CC").unwrap_or_else(|_| "cc".to_string());
|
||||
|
||||
let compile_result = Command::new(&cc)
|
||||
.args(["-O2", "-o"])
|
||||
.arg(&temp_bin)
|
||||
.arg(&temp_c)
|
||||
.output();
|
||||
|
||||
match compile_result {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
eprintln!("C compilation failed:");
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Execution error: {}", e);
|
||||
eprintln!("Failed to run C compiler '{}': {}", cc, e);
|
||||
eprintln!("Make sure gcc or clang is installed, or set CC environment variable.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the compiled binary
|
||||
let run_result = Command::new(&temp_bin).status();
|
||||
match run_result {
|
||||
Ok(status) => {
|
||||
std::process::exit(status.code().unwrap_or(1));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to run compiled binary: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else if let Some(out_path) = output_path {
|
||||
// Write to specified file
|
||||
if let Err(e) = std::fs::write(out_path, &c_code) {
|
||||
eprintln!("Error writing file '{}': {}", out_path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
eprintln!("Compiled to {}", out_path);
|
||||
eprintln!("Compile with: cc -O2 -o output {}", out_path);
|
||||
} else {
|
||||
// Print to stdout
|
||||
println!("{}", c_code);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_tests(args: &[String]) {
|
||||
|
||||
Reference in New Issue
Block a user