Casting, GEPs, partial LLVM backend
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
target/
|
||||||
|
test.leaf
|
||||||
|
out.asm
|
||||||
|
a.out
|
||||||
Generated
+163
-43
@@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arcstr"
|
name = "arcstr"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -10,9 +16,9 @@ checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.10.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "boxcar"
|
name = "boxcar"
|
||||||
@@ -22,9 +28,9 @@ checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@@ -32,6 +38,16 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -55,18 +71,18 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "2.1.0"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618"
|
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more-impl",
|
"derive_more-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more-impl"
|
name = "derive_more-impl"
|
||||||
version = "2.1.0"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b"
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -76,6 +92,18 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fxhash"
|
name = "fxhash"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -96,6 +124,45 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inkwell"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1def4112dfb2ce2993db7027f7acdb43c1f4ee1c70a082a2eef306ed5d0df365"
|
||||||
|
dependencies = [
|
||||||
|
"inkwell_internals",
|
||||||
|
"libc",
|
||||||
|
"llvm-sys",
|
||||||
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inkwell_internals"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63736175c9a30ea123f7018de9f26163e0b39cd6978990ae486b510c4f3bad69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leaf_allocators"
|
name = "leaf_allocators"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -117,6 +184,18 @@ dependencies = [
|
|||||||
"scc",
|
"scc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leaf_backend_llvm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arcstr",
|
||||||
|
"derive_more",
|
||||||
|
"fxhash",
|
||||||
|
"inkwell",
|
||||||
|
"leaf_assembly",
|
||||||
|
"scc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leaf_compiler"
|
name = "leaf_compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -124,24 +203,13 @@ dependencies = [
|
|||||||
"arcstr",
|
"arcstr",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
|
"itertools",
|
||||||
"leaf_allocators",
|
"leaf_allocators",
|
||||||
"leaf_assembly",
|
"leaf_assembly",
|
||||||
|
"leaf_backend_llvm",
|
||||||
"leaf_parser",
|
"leaf_parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "leaf_interpreter"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"fxhash",
|
|
||||||
"leaf_allocators",
|
|
||||||
"leaf_assembly",
|
|
||||||
"leaf_compiler",
|
|
||||||
"leaf_parser",
|
|
||||||
"scc",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leaf_parser"
|
name = "leaf_parser"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -151,6 +219,32 @@ dependencies = [
|
|||||||
"peg",
|
"peg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.182"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "llvm-sys"
|
||||||
|
version = "211.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "108b3ad2b2eaf2a561fc74196273b20e3436e4a688b8b44e250d83974dc1b2e2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cc",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"regex-lite",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peg"
|
name = "peg"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -180,22 +274,28 @@ checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.103"
|
version = "1.0.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.42"
|
version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-lite"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -207,15 +307,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "saa"
|
name = "saa"
|
||||||
version = "5.3.3"
|
version = "5.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dfac11c0cd0606aaf7eb9ef66f82c119438a96dc487715abb8b57fdf08ad4fe"
|
checksum = "16c7f49c9d5caa3bf4b3106900484b447b9253fe99670ceb81cb6cb5027855e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scc"
|
name = "scc"
|
||||||
version = "3.3.7"
|
version = "3.6.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3904515dbe9c10a126a54faa1d44fd40b893224247a1198d8c5dab6ef028bbdc"
|
checksum = "6012e652611b2fdcb557a7b4be8cee00d8be19397c70011906a68aa4dac2fe37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"saa",
|
"saa",
|
||||||
"sdd",
|
"sdd",
|
||||||
@@ -223,9 +323,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdd"
|
name = "sdd"
|
||||||
version = "4.3.1"
|
version = "4.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd556f4cb1d30590889c126f4951eb87f8a70d736ed5a2f62cc89d290721a1c2"
|
checksum = "4becc2f27bd39aafb78a8a1b4e1d6877ed0a6b5bf096722ed538dc028367a9b5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
@@ -234,16 +334,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "shlex"
|
||||||
version = "1.15.1"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.110"
|
version = "2.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -251,10 +351,30 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "thiserror"
|
||||||
version = "1.0.22"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
@@ -270,18 +390,18 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.27"
|
version = "0.8.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.27"
|
version = "0.8.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["allocators","assembly", "compiler", "interpreter", "parser"]
|
members = ["allocators", "assembly", "compiler", "parser", "backends/llvm"]
|
||||||
|
|||||||
+82
-49
@@ -5,69 +5,102 @@ use std::{alloc::Layout, sync::Mutex};
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ArenaAllocator {
|
pub struct ArenaAllocator {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
allocations: Vec<AllocationEntry>,
|
allocations: Vec<AllocationEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Allocator for ArenaAllocator {
|
impl Allocator for ArenaAllocator {
|
||||||
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = self.bump.alloc_layout(layout).as_ptr();
|
let ptr = self.bump.alloc_layout(layout).as_ptr();
|
||||||
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
||||||
self.allocations.push(AllocationEntry {
|
self.allocations.push(AllocationEntry {
|
||||||
ptr,
|
ptr,
|
||||||
layout,
|
layout,
|
||||||
drop_fn,
|
drop_fn,
|
||||||
});
|
});
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn alloc_uninit(&self, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
|
let ptr = self.bump.alloc_layout(layout).as_ptr();
|
||||||
|
self.allocations.push(AllocationEntry {
|
||||||
|
ptr,
|
||||||
|
layout,
|
||||||
|
drop_fn,
|
||||||
|
});
|
||||||
|
ptr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ArenaAllocator {
|
impl Drop for ArenaAllocator {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
for AllocationEntry { ptr, drop_fn, .. } in std::mem::take(&mut self.allocations) {
|
for AllocationEntry {
|
||||||
if let Some(drop) = drop_fn {
|
ptr,
|
||||||
drop(ptr);
|
drop_fn,
|
||||||
}
|
layout,
|
||||||
}
|
} in std::mem::take(&mut self.allocations)
|
||||||
}
|
{
|
||||||
}
|
if let Some(drop) = drop_fn {
|
||||||
|
drop(ptr, layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SyncArenaAllocator {
|
pub struct SyncArenaAllocator {
|
||||||
bump: Mutex<Bump>,
|
bump: Mutex<Bump>,
|
||||||
allocations: Vec<AllocationEntry>,
|
allocations: Vec<AllocationEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Allocator for SyncArenaAllocator {
|
impl Allocator for SyncArenaAllocator {
|
||||||
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = {
|
let ptr = {
|
||||||
let bump = self.bump.lock().unwrap();
|
let bump = self.bump.lock().unwrap();
|
||||||
bump.alloc_layout(layout).as_ptr()
|
bump.alloc_layout(layout).as_ptr()
|
||||||
};
|
};
|
||||||
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
||||||
self.allocations.push(AllocationEntry {
|
self.allocations.push(AllocationEntry {
|
||||||
ptr,
|
ptr,
|
||||||
layout,
|
layout,
|
||||||
drop_fn,
|
drop_fn,
|
||||||
});
|
});
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn alloc_uninit(&self, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
|
let ptr = {
|
||||||
|
let bump = self.bump.lock().unwrap();
|
||||||
|
bump.alloc_layout(layout).as_ptr()
|
||||||
|
};
|
||||||
|
self.allocations.push(AllocationEntry {
|
||||||
|
ptr,
|
||||||
|
layout,
|
||||||
|
drop_fn,
|
||||||
|
});
|
||||||
|
ptr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for SyncArenaAllocator {
|
impl Drop for SyncArenaAllocator {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
for AllocationEntry { ptr, drop_fn, .. } in std::mem::take(&mut self.allocations) {
|
for AllocationEntry {
|
||||||
if let Some(drop) = drop_fn {
|
ptr,
|
||||||
drop(ptr);
|
drop_fn,
|
||||||
}
|
layout,
|
||||||
}
|
} in std::mem::take(&mut self.allocations)
|
||||||
}
|
{
|
||||||
}
|
if let Some(drop) = drop_fn {
|
||||||
|
drop(ptr, layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-28
@@ -4,38 +4,50 @@ use std::alloc::Layout;
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GlobalAllocator {
|
pub struct GlobalAllocator {
|
||||||
allocations: Vec<AllocationEntry>,
|
allocations: Vec<AllocationEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Allocator for GlobalAllocator {
|
impl Allocator for GlobalAllocator {
|
||||||
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = std::alloc::alloc(layout);
|
let ptr = std::alloc::alloc(layout);
|
||||||
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
std::ptr::copy_nonoverlapping(data, ptr, layout.size());
|
||||||
self.allocations.push(AllocationEntry {
|
self.allocations.push(AllocationEntry {
|
||||||
ptr,
|
ptr,
|
||||||
layout,
|
layout,
|
||||||
drop_fn,
|
drop_fn,
|
||||||
});
|
});
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn alloc_uninit(&self, layout: Layout, drop_fn: DropFn) -> *mut u8 {
|
||||||
|
unsafe {
|
||||||
|
let ptr = std::alloc::alloc(layout);
|
||||||
|
self.allocations.push(AllocationEntry {
|
||||||
|
ptr,
|
||||||
|
layout,
|
||||||
|
drop_fn,
|
||||||
|
});
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GlobalAllocator {
|
impl Drop for GlobalAllocator {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
for AllocationEntry {
|
for AllocationEntry {
|
||||||
ptr,
|
ptr,
|
||||||
layout,
|
layout,
|
||||||
drop_fn,
|
drop_fn,
|
||||||
} in std::mem::take(&mut self.allocations)
|
} in std::mem::take(&mut self.allocations)
|
||||||
{
|
{
|
||||||
if let Some(drop) = drop_fn {
|
if let Some(drop) = drop_fn {
|
||||||
drop(ptr);
|
drop(ptr, layout);
|
||||||
}
|
}
|
||||||
std::alloc::dealloc(ptr, layout);
|
std::alloc::dealloc(ptr, layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-2
@@ -5,7 +5,7 @@ pub use arena::*;
|
|||||||
pub use global::*;
|
pub use global::*;
|
||||||
use std::{alloc::Layout, mem::MaybeUninit};
|
use std::{alloc::Layout, mem::MaybeUninit};
|
||||||
|
|
||||||
pub type DropFn = Option<unsafe fn(*mut u8)>;
|
pub type DropFn = Option<unsafe fn(*mut u8, Layout)>;
|
||||||
|
|
||||||
pub struct AllocationEntry {
|
pub struct AllocationEntry {
|
||||||
ptr: *mut u8,
|
ptr: *mut u8,
|
||||||
@@ -17,6 +17,7 @@ unsafe impl Send for AllocationEntry {}
|
|||||||
unsafe impl Sync for AllocationEntry {}
|
unsafe impl Sync for AllocationEntry {}
|
||||||
|
|
||||||
pub trait Allocator {
|
pub trait Allocator {
|
||||||
|
unsafe fn alloc_uninit(&self, layout: Layout, drop: DropFn) -> *mut u8;
|
||||||
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop: DropFn) -> *mut u8;
|
unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop: DropFn) -> *mut u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ impl<'l> dyn Allocator + 'l {
|
|||||||
let layout = Layout::new::<T>();
|
let layout = Layout::new::<T>();
|
||||||
let drop: DropFn = match std::mem::needs_drop::<T>() {
|
let drop: DropFn = match std::mem::needs_drop::<T>() {
|
||||||
false => None,
|
false => None,
|
||||||
true => Some(|ptr: *mut u8| std::ptr::drop_in_place(ptr as *mut T)),
|
true => Some(|ptr, _| std::ptr::drop_in_place(ptr as *mut T)),
|
||||||
};
|
};
|
||||||
&mut *(self.alloc_unsafe(data, layout, drop) as *mut T)
|
&mut *(self.alloc_unsafe(data, layout, drop) as *mut T)
|
||||||
}
|
}
|
||||||
@@ -47,6 +48,29 @@ impl<'l> dyn Allocator + 'l {
|
|||||||
std::str::from_utf8_unchecked(slice)
|
std::str::from_utf8_unchecked(slice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alloc_slice<T>(&'l self, iter: impl Iterator<Item = T> + ExactSizeIterator) -> &'l [T] {
|
||||||
|
unsafe {
|
||||||
|
let len = iter.len();
|
||||||
|
let layout = Layout::array::<T>(len).unwrap();
|
||||||
|
let ptr = self.alloc_uninit(
|
||||||
|
layout,
|
||||||
|
match std::mem::needs_drop::<T>() {
|
||||||
|
false => None,
|
||||||
|
true => Some(|ptr, layout| {
|
||||||
|
let len = layout.size() / size_of::<T>();
|
||||||
|
let slice = std::slice::from_raw_parts_mut(ptr as *mut T, len);
|
||||||
|
std::ptr::drop_in_place(slice);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
) as *mut MaybeUninit<T>;
|
||||||
|
let slice = std::slice::from_raw_parts_mut(ptr, len);
|
||||||
|
for (dst, val) in slice.iter_mut().zip(iter) {
|
||||||
|
dst.write(val);
|
||||||
|
}
|
||||||
|
slice.assume_init_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'l> dyn SyncAllocator + 'l {
|
impl<'l> dyn SyncAllocator + 'l {
|
||||||
@@ -57,4 +81,8 @@ impl<'l> dyn SyncAllocator + 'l {
|
|||||||
pub fn alloc_str(&'l self, value: &str) -> &'l str {
|
pub fn alloc_str(&'l self, value: &str) -> &'l str {
|
||||||
<dyn Allocator>::alloc_str(self, value)
|
<dyn Allocator>::alloc_str(self, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alloc_slice<T>(&'l self, iter: impl Iterator<Item = T> + ExactSizeIterator) -> &'l [T] {
|
||||||
|
<dyn Allocator>::alloc_slice(self, iter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
functions::Function,
|
functions::Function,
|
||||||
types::derivations::{FuncT, TypeDerivations},
|
types::derivations::{FuncT, TypeDerivations},
|
||||||
values::Const,
|
|
||||||
};
|
};
|
||||||
use derive_more::Debug;
|
use derive_more::{Debug, Display};
|
||||||
use fxhash::FxBuildHasher;
|
use fxhash::FxBuildHasher;
|
||||||
use leaf_allocators::SyncAllocator;
|
use leaf_allocators::SyncAllocator;
|
||||||
use scc::HashMap;
|
use scc::HashMap;
|
||||||
use std::{borrow::Cow, hash::Hash, sync::OnceLock};
|
use std::{borrow::Cow, fmt::Display, hash::Hash, sync::OnceLock};
|
||||||
|
|
||||||
#[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(
|
||||||
|
derive_more::Debug, Display, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
#[debug("{major}.{minor}.{patch}.{build}")]
|
#[debug("{major}.{minor}.{patch}.{build}")]
|
||||||
|
#[display("{major}.{minor}.{patch}.{build}")]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
pub major: u16,
|
pub major: u16,
|
||||||
pub minor: u16,
|
pub minor: u16,
|
||||||
@@ -18,7 +20,8 @@ pub struct Version {
|
|||||||
pub build: u16,
|
pub build: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Display, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[display("{name} v{version}")]
|
||||||
pub struct AssemblyIdentifier {
|
pub struct AssemblyIdentifier {
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
@@ -57,6 +60,10 @@ impl<'l> Context<'l> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alloc(&self) -> &'l dyn SyncAllocator {
|
||||||
|
self.alloc
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_assembly(&'l self, ident: &AssemblyIdentifier) -> Option<&'l Assembly<'l>> {
|
pub fn get_assembly(&'l self, ident: &AssemblyIdentifier) -> Option<&'l Assembly<'l>> {
|
||||||
self.assemblies.get_sync(ident).map(|v| *v)
|
self.assemblies.get_sync(ident).map(|v| *v)
|
||||||
}
|
}
|
||||||
@@ -114,6 +121,10 @@ impl<'l> Assembly<'l> {
|
|||||||
self.ctx
|
self.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ident(&self) -> &AssemblyIdentifier {
|
||||||
|
&self.ident
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_function(&'l self, ty: &'l FuncT<'l>) -> &'l Function<'l> {
|
pub fn create_function(&'l self, ty: &'l FuncT<'l>) -> &'l Function<'l> {
|
||||||
let func = self.ctx.alloc.alloc(Function {
|
let func = self.ctx.alloc.alloc(Function {
|
||||||
ty,
|
ty,
|
||||||
@@ -125,6 +136,10 @@ impl<'l> Assembly<'l> {
|
|||||||
func
|
func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn functions(&'l self) -> boxcar::vec::Iter<'l, &'l Function<'l>> {
|
||||||
|
self.functions.iter()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_function(
|
pub fn find_function(
|
||||||
&'l self,
|
&'l self,
|
||||||
filter: impl Fn(&'l Function<'l>) -> bool,
|
filter: impl Fn(&'l Function<'l>) -> bool,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assembly::Ctx,
|
assembly::Ctx,
|
||||||
functions::{Function, FunctionBody},
|
functions::{Function, FunctionBody},
|
||||||
types::{Type, derivations::*},
|
types::{IntT, Type, derivations::*},
|
||||||
values::{Value, ValueFlags},
|
values::{Value, ValueFlags},
|
||||||
};
|
};
|
||||||
use derive_more::{Debug, Display};
|
use derive_more::{Debug, Display};
|
||||||
@@ -25,15 +25,15 @@ impl Eq for Instruction<'_> {}
|
|||||||
|
|
||||||
impl PartialEq for Instruction<'_> {
|
impl PartialEq for Instruction<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eq(&self, _: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
false
|
std::ptr::eq(self, other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Instruction<'_> {
|
impl Hash for Instruction<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
(self as *const Self).hash(state);
|
std::ptr::hash(self, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +60,10 @@ impl<'l> Instruction<'l> {
|
|||||||
pub fn value_flags(&self) -> ValueFlags {
|
pub fn value_flags(&self) -> ValueFlags {
|
||||||
match self.variant {
|
match self.variant {
|
||||||
InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
|
InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
|
||||||
|
InstructionVariant::GetElementPtr(v, _) if v.flags().contains(ValueFlags::LValue) => {
|
||||||
|
ValueFlags::LValue
|
||||||
|
}
|
||||||
|
InstructionVariant::Reinterpret(_, _, f) => f,
|
||||||
_ => ValueFlags::empty(),
|
_ => ValueFlags::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,12 +77,35 @@ impl<'l> Instruction<'l> {
|
|||||||
Type::Ptr(PtrT { base, .. }) => *base,
|
Type::Ptr(PtrT { base, .. }) => *base,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
InstructionVariant::GetElementPtr(v, _) => match v.ty() {
|
||||||
|
Type::Ptr(PtrT {
|
||||||
|
base: Type::Array(ArrayT { base, .. }),
|
||||||
|
mutable,
|
||||||
|
}) => base.make_ptr(*mutable).into(),
|
||||||
|
Type::Ref(RefT {
|
||||||
|
base: Type::Array(ArrayT { base, .. }),
|
||||||
|
mutable,
|
||||||
|
}) => base.make_ref(*mutable).into(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
InstructionVariant::IAdd(a, _) => a.ty(),
|
InstructionVariant::IAdd(a, _) => a.ty(),
|
||||||
|
InstructionVariant::ISub(a, _) => a.ty(),
|
||||||
InstructionVariant::IMul(a, _) => a.ty(),
|
InstructionVariant::IMul(a, _) => a.ty(),
|
||||||
|
InstructionVariant::IDiv(a, _) => a.ty(),
|
||||||
|
InstructionVariant::IMod(a, _) => a.ty(),
|
||||||
|
InstructionVariant::SExt(_, t) => Type::Int(t),
|
||||||
|
InstructionVariant::ZExt(_, t) => Type::Int(t),
|
||||||
|
InstructionVariant::Trunc(_, t) => Type::Int(t),
|
||||||
InstructionVariant::FAdd(a, _) => a.ty(),
|
InstructionVariant::FAdd(a, _) => a.ty(),
|
||||||
|
InstructionVariant::FSub(a, _) => a.ty(),
|
||||||
InstructionVariant::FMul(a, _) => a.ty(),
|
InstructionVariant::FMul(a, _) => a.ty(),
|
||||||
|
InstructionVariant::FDiv(a, _) => a.ty(),
|
||||||
|
InstructionVariant::FMod(a, _) => a.ty(),
|
||||||
InstructionVariant::ICmp(_, _, _) => Type::Bool,
|
InstructionVariant::ICmp(_, _, _) => Type::Bool,
|
||||||
InstructionVariant::Call(f, _) => f.ty.ret_t,
|
InstructionVariant::Call(f, _) => f.ty.ret_t,
|
||||||
|
InstructionVariant::Jump(_) => Type::Void,
|
||||||
|
InstructionVariant::Branch { .. } => Type::Void,
|
||||||
|
InstructionVariant::Reinterpret(_, t, _) => t,
|
||||||
_ => todo!("{self:?}"),
|
_ => todo!("{self:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +114,7 @@ impl<'l> Instruction<'l> {
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Cmp {
|
pub enum Cmp {
|
||||||
Eq,
|
Eq,
|
||||||
|
Ne,
|
||||||
Lt,
|
Lt,
|
||||||
Gt,
|
Gt,
|
||||||
Le,
|
Le,
|
||||||
@@ -100,11 +128,21 @@ pub enum InstructionVariant<'l> {
|
|||||||
|
|
||||||
Load(Value<'l>),
|
Load(Value<'l>),
|
||||||
Store(Value<'l>, Value<'l>),
|
Store(Value<'l>, Value<'l>),
|
||||||
|
GetElementPtr(Value<'l>, Value<'l>),
|
||||||
|
|
||||||
IAdd(Value<'l>, Value<'l>),
|
IAdd(Value<'l>, Value<'l>),
|
||||||
|
ISub(Value<'l>, Value<'l>),
|
||||||
IMul(Value<'l>, Value<'l>),
|
IMul(Value<'l>, Value<'l>),
|
||||||
|
IDiv(Value<'l>, Value<'l>),
|
||||||
|
IMod(Value<'l>, Value<'l>),
|
||||||
|
SExt(Value<'l>, IntT),
|
||||||
|
ZExt(Value<'l>, IntT),
|
||||||
|
Trunc(Value<'l>, IntT),
|
||||||
FAdd(Value<'l>, Value<'l>),
|
FAdd(Value<'l>, Value<'l>),
|
||||||
|
FSub(Value<'l>, Value<'l>),
|
||||||
FMul(Value<'l>, Value<'l>),
|
FMul(Value<'l>, Value<'l>),
|
||||||
|
FDiv(Value<'l>, Value<'l>),
|
||||||
|
FMod(Value<'l>, Value<'l>),
|
||||||
|
|
||||||
ICmp(Value<'l>, Value<'l>, Cmp),
|
ICmp(Value<'l>, Value<'l>, Cmp),
|
||||||
FCmp(Value<'l>, Value<'l>, Cmp),
|
FCmp(Value<'l>, Value<'l>, Cmp),
|
||||||
@@ -117,6 +155,8 @@ pub enum InstructionVariant<'l> {
|
|||||||
false_case: &'l Block<'l>,
|
false_case: &'l Block<'l>,
|
||||||
},
|
},
|
||||||
Return(Option<Value<'l>>),
|
Return(Option<Value<'l>>),
|
||||||
|
|
||||||
|
Reinterpret(Value<'l>, Type<'l>, ValueFlags),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionVariant<'_> {
|
impl InstructionVariant<'_> {
|
||||||
@@ -137,10 +177,20 @@ impl std::fmt::Debug for InstructionVariant<'_> {
|
|||||||
Self::GCAlloc(ty) => write!(f, "gcalloc {ty}"),
|
Self::GCAlloc(ty) => write!(f, "gcalloc {ty}"),
|
||||||
Self::Load(v) => write!(f, "load {v}"),
|
Self::Load(v) => write!(f, "load {v}"),
|
||||||
Self::Store(t, v) => write!(f, "store {t}, {v}"),
|
Self::Store(t, v) => write!(f, "store {t}, {v}"),
|
||||||
|
Self::GetElementPtr(t, v) => write!(f, "gep {t}, {v}"),
|
||||||
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"),
|
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"),
|
||||||
|
Self::ISub(a, b) => write!(f, "isub {a}, {b}"),
|
||||||
Self::IMul(a, b) => write!(f, "imul {a}, {b}"),
|
Self::IMul(a, b) => write!(f, "imul {a}, {b}"),
|
||||||
|
Self::IDiv(a, b) => write!(f, "idiv {a}, {b}"),
|
||||||
|
Self::IMod(a, b) => write!(f, "imod {a}, {b}"),
|
||||||
|
Self::SExt(a, b) => write!(f, "sext {a}, {b}"),
|
||||||
|
Self::ZExt(a, b) => write!(f, "zext {a}, {b}"),
|
||||||
|
Self::Trunc(a, b) => write!(f, "trunc {a}, {b}"),
|
||||||
Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"),
|
Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"),
|
||||||
|
Self::FSub(a, b) => write!(f, "fsub {a}, {b}"),
|
||||||
Self::FMul(a, b) => write!(f, "fmul {a}, {b}"),
|
Self::FMul(a, b) => write!(f, "fmul {a}, {b}"),
|
||||||
|
Self::FDiv(a, b) => write!(f, "fdiv {a}, {b}"),
|
||||||
|
Self::FMod(a, b) => write!(f, "fmod {a}, {b}"),
|
||||||
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
|
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
|
||||||
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
|
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
|
||||||
Self::Call(func, args) => {
|
Self::Call(func, args) => {
|
||||||
@@ -164,6 +214,7 @@ impl std::fmt::Debug for InstructionVariant<'_> {
|
|||||||
Self::Jump(b) => write!(f, "jump #{}", b.id),
|
Self::Jump(b) => write!(f, "jump #{}", b.id),
|
||||||
Self::Return(None) => write!(f, "return"),
|
Self::Return(None) => write!(f, "return"),
|
||||||
Self::Return(Some(v)) => write!(f, "return {v}"),
|
Self::Return(Some(v)) => write!(f, "return {v}"),
|
||||||
|
Self::Reinterpret(v, t, fl) => write!(f, "reinterpret {v} as {t} {fl:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +311,23 @@ impl<'l> BlockBuilder<'l> {
|
|||||||
let inst = self.push_instruction(InstructionVariant::FAdd(a, b))?;
|
let inst = self.push_instruction(InstructionVariant::FAdd(a, b))?;
|
||||||
Ok(inst.into())
|
Ok(inst.into())
|
||||||
}
|
}
|
||||||
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
|
_ => Err(format!("Cannot add values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let [a_ty, b_ty] = [a.ty(), b.ty()];
|
||||||
|
|
||||||
|
match (a_ty, b_ty) {
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::ISub(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::FSub(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
_ => Err(format!("Cannot subtract values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +343,52 @@ impl<'l> BlockBuilder<'l> {
|
|||||||
let inst = self.push_instruction(InstructionVariant::FMul(a, b))?;
|
let inst = self.push_instruction(InstructionVariant::FMul(a, b))?;
|
||||||
Ok(inst.into())
|
Ok(inst.into())
|
||||||
}
|
}
|
||||||
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
|
_ => Err(format!("Cannot multiply values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let [a_ty, b_ty] = [a.ty(), b.ty()];
|
||||||
|
|
||||||
|
match (a_ty, b_ty) {
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::IDiv(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::FDiv(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
_ => Err(format!("Cannot divide values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modulo(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let [a_ty, b_ty] = [a.ty(), b.ty()];
|
||||||
|
|
||||||
|
match (a_ty, b_ty) {
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::IMod(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::FMod(a, b))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
_ => Err(format!("Cannot divide values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trunc(&mut self, val: Value<'l>, target: IntT) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let ty = val.ty();
|
||||||
|
match ty {
|
||||||
|
Type::Int(a_ty) if a_ty.precision > target.precision => {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::Trunc(val, target))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
_ => Err(
|
||||||
|
format!("Cannot truncate value of type `{ty}` to one of type `{target}`.").into(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,10 +409,34 @@ impl<'l> BlockBuilder<'l> {
|
|||||||
let inst = self.push_instruction(InstructionVariant::FCmp(a, b, cmp))?;
|
let inst = self.push_instruction(InstructionVariant::FCmp(a, b, cmp))?;
|
||||||
Ok(inst.into())
|
Ok(inst.into())
|
||||||
}
|
}
|
||||||
_ => Err(format!("Cannot compare values of type `{a_ty}` and `b_ty`.").into()),
|
_ => Err(format!("Cannot compare values of type `{a_ty}` and `{b_ty}`.").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gep(&mut self, value: Value<'l>, index: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let v_ty = value.ty();
|
||||||
|
let i_ty = index.ty();
|
||||||
|
|
||||||
|
let _ = match v_ty {
|
||||||
|
Type::Ptr(PtrT {
|
||||||
|
base: Type::Array(ArrayT { base, .. }),
|
||||||
|
..
|
||||||
|
}) => *base,
|
||||||
|
Type::Ref(RefT {
|
||||||
|
base: Type::Array(ArrayT { base, .. }),
|
||||||
|
..
|
||||||
|
}) => *base,
|
||||||
|
_ => return Err(format!("Cannot index a value of type `{}`.", v_ty).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if i_ty != Type::USIZE {
|
||||||
|
return Err(format!("Expeted index type `usize`, found {}.", i_ty).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let inst = self.push_instruction(InstructionVariant::GetElementPtr(value, index))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
if !std::ptr::eq(block.func, self.block.func) {
|
if !std::ptr::eq(block.func, self.block.func) {
|
||||||
return Err("Block does not belong to this function.".into());
|
return Err("Block does not belong to this function.".into());
|
||||||
@@ -376,6 +512,21 @@ impl<'l> BlockBuilder<'l> {
|
|||||||
Ok(inst.into())
|
Ok(inst.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// WARNING: Incorrect usage of this function is very likely to cause catastrophic problems. Be sure to know what you're doing.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The target reinterpretation must maintain the IR's invariants.
|
||||||
|
pub unsafe fn reinterpret(
|
||||||
|
&mut self,
|
||||||
|
value: Value<'l>,
|
||||||
|
ty: Type<'l>,
|
||||||
|
flags: ValueFlags,
|
||||||
|
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty, flags))?;
|
||||||
|
Ok(inst.into())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn has_termination(&self) -> bool {
|
pub fn has_termination(&self) -> bool {
|
||||||
match self.instructions.as_slice() {
|
match self.instructions.as_slice() {
|
||||||
@@ -494,10 +645,26 @@ impl<'l> FunctionBodyBuilder<'l> {
|
|||||||
self.current_builder().add(a, b)
|
self.current_builder().add(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sub(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
self.current_builder().sub(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
self.current_builder().mul(a, b)
|
self.current_builder().mul(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn div(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
self.current_builder().div(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modulo(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
self.current_builder().modulo(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trunc(&mut self, val: Value<'l>, target: IntT) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
self.current_builder().trunc(val, target)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cmp(
|
pub fn cmp(
|
||||||
&mut self,
|
&mut self,
|
||||||
a: Value<'l>,
|
a: Value<'l>,
|
||||||
@@ -507,6 +674,10 @@ impl<'l> FunctionBodyBuilder<'l> {
|
|||||||
self.current_builder().cmp(a, b, cmp)
|
self.current_builder().cmp(a, b, cmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gep(&mut self, value: Value<'l>, index: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
self.current_builder().gep(value, index)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
self.current_builder().jump(block)
|
self.current_builder().jump(block)
|
||||||
}
|
}
|
||||||
@@ -532,6 +703,20 @@ impl<'l> FunctionBodyBuilder<'l> {
|
|||||||
self.current_builder().ret(value)
|
self.current_builder().ret(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// WARNING: Incorrect usage of this function is very likely to cause catastrophic problems. Be sure to know what you're doing.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The target reinterpretation must maintain the IR's invariants.
|
||||||
|
pub unsafe fn reinterpret(
|
||||||
|
&mut self,
|
||||||
|
value: Value<'l>,
|
||||||
|
ty: Type<'l>,
|
||||||
|
flags: ValueFlags,
|
||||||
|
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||||
|
unsafe { self.current_builder().reinterpret(value, ty, flags) }
|
||||||
|
}
|
||||||
|
|
||||||
fn current_builder(&mut self) -> &mut BlockBuilder<'l> {
|
fn current_builder(&mut self) -> &mut BlockBuilder<'l> {
|
||||||
&mut self.blocks[self.current_block]
|
&mut self.blocks[self.current_block]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ impl<'l> Function<'l> {
|
|||||||
self.declaring_assembly.ctx()
|
self.declaring_assembly.ctx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declaring_assembly(&self) -> &'l Assembly<'l> {
|
||||||
|
self.declaring_assembly
|
||||||
|
}
|
||||||
|
|
||||||
pub fn body(&self) -> Option<&FunctionBody<'l>> {
|
pub fn body(&self) -> Option<&FunctionBody<'l>> {
|
||||||
self.body.get()
|
self.body.get()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub struct TypeDerivations<'l> {
|
|||||||
alloc: &'l dyn SyncAllocator,
|
alloc: &'l dyn SyncAllocator,
|
||||||
ptr_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
ptr_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
||||||
ref_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
ref_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
||||||
|
arr_t: HashMap<(Type<'l>, Option<u32>), Type<'l>>,
|
||||||
fun_t: HashMap<(Type<'l>, Arc<[Type<'l>]>), Type<'l>>,
|
fun_t: HashMap<(Type<'l>, Arc<[Type<'l>]>), Type<'l>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ impl<'l> TypeDerivations<'l> {
|
|||||||
alloc,
|
alloc,
|
||||||
ptr_t: HashMap::new(),
|
ptr_t: HashMap::new(),
|
||||||
ref_t: HashMap::new(),
|
ref_t: HashMap::new(),
|
||||||
|
arr_t: HashMap::new(),
|
||||||
fun_t: HashMap::new(),
|
fun_t: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +45,17 @@ impl<'l> TypeDerivations<'l> {
|
|||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_array(&self, base: Type<'l>, length: Option<u32>) -> &'l ArrayT<'l> {
|
||||||
|
let Type::Array(ty) = *self
|
||||||
|
.arr_t
|
||||||
|
.entry_sync((base, length))
|
||||||
|
.or_insert_with(|| (&*self.alloc.alloc(ArrayT { base, length })).into())
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_fn(&'l self, ret_t: Type<'l>, par_t: Arc<[Type<'l>]>) -> &'l FuncT<'l> {
|
pub fn make_fn(&'l self, ret_t: Type<'l>, par_t: Arc<[Type<'l>]>) -> &'l FuncT<'l> {
|
||||||
let Type::Func(ty) = *self
|
let Type::Func(ty) = *self
|
||||||
.fun_t
|
.fun_t
|
||||||
@@ -95,6 +108,23 @@ pub struct RefT<'l> {
|
|||||||
pub mutable: bool,
|
pub mutable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ArrayT<'l> {
|
||||||
|
#[debug("{base}")]
|
||||||
|
pub base: Type<'l>,
|
||||||
|
pub length: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArrayT<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.length {
|
||||||
|
None => write!(f, "[{}]", self.base),
|
||||||
|
Some(len) => write!(f, "[{}; {len}]", self.base),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct FuncT<'l> {
|
pub struct FuncT<'l> {
|
||||||
@@ -124,6 +154,10 @@ impl<'l> Type<'l> {
|
|||||||
self.ctx().derivations.make_ref(*self, mutable)
|
self.ctx().derivations.make_ref(*self, mutable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_array(&self, length: Option<u32>) -> &'l ArrayT<'l> {
|
||||||
|
self.ctx().derivations.make_array(*self, length)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_fn(&self, par_t: impl IntoIterator<Item = Type<'l>>) -> &'l FuncT<'l> {
|
pub fn make_fn(&self, par_t: impl IntoIterator<Item = Type<'l>>) -> &'l FuncT<'l> {
|
||||||
self.ctx()
|
self.ctx()
|
||||||
.derivations
|
.derivations
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ pub enum Type<'l> {
|
|||||||
#[display("{_0}")]
|
#[display("{_0}")]
|
||||||
Ref(&'l RefT<'l>),
|
Ref(&'l RefT<'l>),
|
||||||
|
|
||||||
|
#[debug("{_0:?}")]
|
||||||
|
#[display("{_0}")]
|
||||||
|
Array(&'l ArrayT<'l>),
|
||||||
|
|
||||||
#[debug("{_0:?}")]
|
#[debug("{_0:?}")]
|
||||||
#[display("{_0}")]
|
#[display("{_0}")]
|
||||||
Func(&'l FuncT<'l>),
|
Func(&'l FuncT<'l>),
|
||||||
@@ -82,8 +86,9 @@ impl<'l> Type<'l> {
|
|||||||
Type::ConstStr => None,
|
Type::ConstStr => None,
|
||||||
Type::Int(_) => None,
|
Type::Int(_) => None,
|
||||||
Type::Float(_) => None,
|
Type::Float(_) => None,
|
||||||
Type::Ptr(_) => None,
|
Type::Ptr(PtrT { base, .. }) => base.non_default_ctx(),
|
||||||
Type::Ref(_) => None,
|
Type::Ref(RefT { base, .. }) => base.non_default_ctx(),
|
||||||
|
Type::Array(ArrayT { base, .. }) => base.non_default_ctx(),
|
||||||
Type::Func(f) => match f.ret_t.non_default_ctx() {
|
Type::Func(f) => match f.ret_t.non_default_ctx() {
|
||||||
Some(ctx) => Some(ctx),
|
Some(ctx) => Some(ctx),
|
||||||
None => f.par_t.iter().find_map(|t| t.non_default_ctx()),
|
None => f.par_t.iter().find_map(|t| t.non_default_ctx()),
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
use crate::{functions::Function, types::Type, values::ValueFlags};
|
use crate::{
|
||||||
|
functions::Function,
|
||||||
|
types::{Type, derivations::ArrayT},
|
||||||
|
values::ValueFlags,
|
||||||
|
};
|
||||||
use derive_more::{Debug, *};
|
use derive_more::{Debug, *};
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
@@ -55,6 +59,18 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ListDisplay<'l>(&'l [AnyConst<'l>]);
|
||||||
|
|
||||||
|
impl std::fmt::Display for ListDisplay<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut list = f.debug_list();
|
||||||
|
for ele in self.0 {
|
||||||
|
list.entry(&format_args!("{ele}"));
|
||||||
|
}
|
||||||
|
list.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)]
|
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)]
|
||||||
#[from(forward)]
|
#[from(forward)]
|
||||||
pub enum AnyConst<'l> {
|
pub enum AnyConst<'l> {
|
||||||
@@ -79,6 +95,9 @@ pub enum AnyConst<'l> {
|
|||||||
#[debug("{:?}", _0)]
|
#[debug("{:?}", _0)]
|
||||||
#[display("{}", _0)]
|
#[display("{}", _0)]
|
||||||
Function(&'l Function<'l>),
|
Function(&'l Function<'l>),
|
||||||
|
#[debug("{:?}", _0)]
|
||||||
|
#[display("{}", ListDisplay(_0))]
|
||||||
|
Array(&'l [AnyConst<'l>]),
|
||||||
Type(Type<'l>),
|
Type(Type<'l>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +123,11 @@ impl<'l> AnyConst<'l> {
|
|||||||
Self::Float(Float::F16(_)) => Type::F16,
|
Self::Float(Float::F16(_)) => Type::F16,
|
||||||
Self::Float(Float::F32(_)) => Type::F32,
|
Self::Float(Float::F32(_)) => Type::F32,
|
||||||
Self::Float(Float::F64(_)) => Type::F64,
|
Self::Float(Float::F64(_)) => Type::F64,
|
||||||
|
Self::Array([]) => Type::Array(&ArrayT {
|
||||||
|
base: Type::Void,
|
||||||
|
length: Some(0),
|
||||||
|
}),
|
||||||
|
Self::Array(a @ [v, ..]) => Type::Array(v.ty().make_array(Some(a.len() as u32))),
|
||||||
_ => todo!("{self:?}"),
|
_ => todo!("{self:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ impl<'l> Value<'l> {
|
|||||||
|
|
||||||
pub fn flags(&self) -> ValueFlags {
|
pub fn flags(&self) -> ValueFlags {
|
||||||
match self {
|
match self {
|
||||||
Value::Instruction(instruction) => instruction.value_flags(),
|
Value::Instruction(v) => v.value_flags(),
|
||||||
Value::Parameter(_, _) => ValueFlags::empty(),
|
Value::Parameter(_, _) => ValueFlags::empty(),
|
||||||
Value::Constant(c) => c.flags(),
|
Value::Constant(c) => c.flags(),
|
||||||
_ => todo!("{self:?}"),
|
_ => todo!("{self:?}"),
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "leaf_backend_llvm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
arcstr = "1.2.0"
|
||||||
|
fxhash = "0.2.1"
|
||||||
|
leaf_assembly = { path = "../../assembly" }
|
||||||
|
inkwell = { version = "0.8.0", default-features = false, features = ["llvm21-1", "target-aarch64", "target-x86"] }
|
||||||
|
derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] }
|
||||||
|
scc = "3.6.6"
|
||||||
@@ -0,0 +1,395 @@
|
|||||||
|
use fxhash::{FxBuildHasher, FxHashMap};
|
||||||
|
pub use inkwell;
|
||||||
|
use inkwell::{
|
||||||
|
AddressSpace, IntPredicate,
|
||||||
|
module::Module,
|
||||||
|
targets::TargetMachine,
|
||||||
|
types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicTypeEnum, IntType},
|
||||||
|
values::{AnyValue, BasicValue, BasicValueEnum},
|
||||||
|
};
|
||||||
|
use leaf_assembly::{
|
||||||
|
assembly::Assembly,
|
||||||
|
functions::{
|
||||||
|
Function,
|
||||||
|
ir::{Cmp, Instruction, InstructionVariant},
|
||||||
|
},
|
||||||
|
types::{
|
||||||
|
Type,
|
||||||
|
derivations::{ArrayT, FuncT, PtrT, RefT},
|
||||||
|
},
|
||||||
|
values::{AnyConst, Int, Value},
|
||||||
|
};
|
||||||
|
use scc::HashMap;
|
||||||
|
|
||||||
|
pub type LlvmContext = inkwell::context::Context;
|
||||||
|
pub type LlvmModule<'l> = inkwell::module::Module<'l>;
|
||||||
|
pub type LlvmFunction<'l> = inkwell::values::FunctionValue<'l>;
|
||||||
|
pub type LlvmType<'l> = inkwell::types::AnyTypeEnum<'l>;
|
||||||
|
|
||||||
|
pub struct CompilationContext<'l> {
|
||||||
|
ctx: &'l LlvmContext,
|
||||||
|
native_int_ty: IntType<'l>,
|
||||||
|
types: HashMap<Type<'l>, AnyTypeEnum<'l>, FxBuildHasher>,
|
||||||
|
modules: HashMap<&'l Assembly<'l>, Module<'l>, FxBuildHasher>,
|
||||||
|
functions: HashMap<(&'l Assembly<'l>, &'l Function<'l>), LlvmFunction<'l>, FxBuildHasher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'l> CompilationContext<'l> {
|
||||||
|
pub fn new(ctx: &'l LlvmContext, target: &TargetMachine) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
types: HashMap::default(),
|
||||||
|
modules: HashMap::default(),
|
||||||
|
functions: HashMap::default(),
|
||||||
|
native_int_ty: match target.get_target_data().get_pointer_byte_size(None) {
|
||||||
|
8 => ctx.i8_type().into(),
|
||||||
|
16 => ctx.i16_type().into(),
|
||||||
|
32 => ctx.i32_type().into(),
|
||||||
|
64 => ctx.i64_type().into(),
|
||||||
|
128 => ctx.i128_type().into(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&self, assembly: &'l Assembly<'l>) -> LlvmModule<'l> {
|
||||||
|
let mut exists = true;
|
||||||
|
let module = self
|
||||||
|
.modules
|
||||||
|
.entry_sync(assembly)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
exists = false;
|
||||||
|
self.ctx.create_module(&assembly.ident().to_string())
|
||||||
|
})
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, func) in assembly.functions() {
|
||||||
|
let ty = self.get_type(Type::Func(func.ty)).into_function_type();
|
||||||
|
let name = match func.name.get() {
|
||||||
|
Some(n) => *n,
|
||||||
|
None => &format!("<fn_{i}>"),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.functions
|
||||||
|
.entry_sync((assembly, func))
|
||||||
|
.or_insert_with(|| module.add_function(name, ty, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, func) in assembly.functions() {
|
||||||
|
let llvm_func = self.functions.get_sync(&(assembly, *func)).unwrap().clone();
|
||||||
|
|
||||||
|
let Some(body) = func.body() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut values = FxHashMap::<Value, Option<BasicValueEnum>>::default();
|
||||||
|
for (i, ty) in func.ty.par_t.iter().enumerate() {
|
||||||
|
let ty = self.get_type(*ty);
|
||||||
|
if BasicMetadataTypeEnum::try_from(ty).is_ok() {
|
||||||
|
values.insert(
|
||||||
|
Value::Parameter(i, func),
|
||||||
|
Some(llvm_func.get_nth_param(values.len() as u32).unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut blocks = vec![];
|
||||||
|
let builder = self.ctx.create_builder();
|
||||||
|
for block in &body.blocks {
|
||||||
|
blocks.push(
|
||||||
|
self.ctx
|
||||||
|
.append_basic_block(llvm_func, &format!("block_{}", block.id)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for block in &body.blocks {
|
||||||
|
builder.position_at_end(blocks[block.id as usize]);
|
||||||
|
for inst @ Instruction { variant, .. } in block.instructions() {
|
||||||
|
let llvm_value = match variant {
|
||||||
|
InstructionVariant::StackAlloc(ty) => {
|
||||||
|
let ty = self.get_type(*ty);
|
||||||
|
BasicTypeEnum::try_from(ty)
|
||||||
|
.ok()
|
||||||
|
.map(|ty| builder.build_alloca(ty, "").unwrap().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::Load(value) => 'val: {
|
||||||
|
let Type::Ptr(PtrT { base, .. }) = value.ty() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let Ok(ty): Result<BasicTypeEnum, _> = self.get_type(*base).try_into()
|
||||||
|
else {
|
||||||
|
break 'val None;
|
||||||
|
};
|
||||||
|
let Some(value) = self.get_value(&values, value) else {
|
||||||
|
break 'val None;
|
||||||
|
};
|
||||||
|
let ptr = value.into_pointer_value();
|
||||||
|
Some(builder.build_load(ty, ptr, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::Store(target, value) => 'val: {
|
||||||
|
let Some(t_val) = self.get_value(&values, target) else {
|
||||||
|
break 'val None;
|
||||||
|
};
|
||||||
|
let Some(v_val) = self.get_value(&values, value) else {
|
||||||
|
break 'val None;
|
||||||
|
};
|
||||||
|
let t_val = t_val.into_pointer_value();
|
||||||
|
builder.build_store(t_val, v_val).unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
InstructionVariant::GetElementPtr(ptr, idx) => 'val: {
|
||||||
|
let pointee_ty = self.get_type(match inst.value_ty() {
|
||||||
|
Type::Ptr(PtrT { base, .. }) => *base,
|
||||||
|
Type::Ref(RefT { base, .. }) => *base,
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
let Ok(pointee_ty): Result<BasicTypeEnum, _> = pointee_ty.try_into()
|
||||||
|
else {
|
||||||
|
break 'val None;
|
||||||
|
};
|
||||||
|
let ptr = self.get_value(&values, ptr).unwrap().into_pointer_value();
|
||||||
|
let idx = self.get_value(&values, idx).unwrap().into_int_value();
|
||||||
|
unsafe {
|
||||||
|
let ptr = builder.build_gep(pointee_ty, ptr, &[idx], "").unwrap();
|
||||||
|
Some(ptr.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::IAdd(lhs, rhs) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_add(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::ISub(lhs, rhs) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_sub(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::IMul(lhs, rhs) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_mul(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::IDiv(lhs, rhs) if is_signed(lhs.ty()) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_signed_div(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::IDiv(lhs, rhs) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_unsigned_div(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::IMod(lhs, rhs) if is_signed(lhs.ty()) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_signed_rem(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::IMod(lhs, rhs) => {
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_unsigned_rem(lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::Trunc(val, target) => {
|
||||||
|
let val = self.get_value(&values, val).unwrap().into_int_value();
|
||||||
|
let target = self.get_type(Type::Int(*target)).into_int_type();
|
||||||
|
Some(builder.build_int_truncate(val, target, "").unwrap().into())
|
||||||
|
}
|
||||||
|
InstructionVariant::ICmp(lhs, rhs, cmp) => {
|
||||||
|
let u = !is_signed(lhs.ty());
|
||||||
|
let cmp = match (cmp, u) {
|
||||||
|
(Cmp::Eq, _) => IntPredicate::EQ,
|
||||||
|
(Cmp::Ne, _) => IntPredicate::NE,
|
||||||
|
(Cmp::Lt, true) => IntPredicate::ULT,
|
||||||
|
(Cmp::Le, true) => IntPredicate::ULE,
|
||||||
|
(Cmp::Gt, true) => IntPredicate::UGT,
|
||||||
|
(Cmp::Ge, true) => IntPredicate::UGE,
|
||||||
|
(Cmp::Lt, false) => IntPredicate::SLT,
|
||||||
|
(Cmp::Le, false) => IntPredicate::SLE,
|
||||||
|
(Cmp::Gt, false) => IntPredicate::SGT,
|
||||||
|
(Cmp::Ge, false) => IntPredicate::SGE,
|
||||||
|
};
|
||||||
|
let lhs = self.get_value(&values, lhs).unwrap().into_int_value();
|
||||||
|
let rhs = self.get_value(&values, rhs).unwrap().into_int_value();
|
||||||
|
Some(builder.build_int_compare(cmp, lhs, rhs, "").unwrap().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::Call(func, args) => {
|
||||||
|
// TODO This will fail with external assemblies. Fix this.
|
||||||
|
let func = self.functions.get_sync(&(assembly, *func)).unwrap().clone();
|
||||||
|
let args: Vec<_> = args
|
||||||
|
.iter()
|
||||||
|
.filter_map(|a| self.get_value(&values, a).map(|a| a.into()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let value = builder.build_direct_call(func, &args, "").unwrap();
|
||||||
|
value.as_any_value_enum().try_into().ok()
|
||||||
|
}
|
||||||
|
InstructionVariant::Jump(block) => {
|
||||||
|
builder
|
||||||
|
.build_unconditional_branch(blocks[block.id as usize])
|
||||||
|
.unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
InstructionVariant::Branch {
|
||||||
|
cond,
|
||||||
|
true_case,
|
||||||
|
false_case,
|
||||||
|
} => {
|
||||||
|
let cond = self.get_value(&values, cond).unwrap().into_int_value();
|
||||||
|
builder
|
||||||
|
.build_conditional_branch(
|
||||||
|
cond,
|
||||||
|
blocks[true_case.id as usize],
|
||||||
|
blocks[false_case.id as usize],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::Return(None) => {
|
||||||
|
builder.build_return(None).unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::Return(Some(v)) => {
|
||||||
|
let v = self.get_value(&values, v);
|
||||||
|
builder
|
||||||
|
.build_return(v.as_ref().map(|v| v as &dyn BasicValue))
|
||||||
|
.unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionVariant::Reinterpret(v, t, _) => match (v.ty(), t) {
|
||||||
|
(Type::Ptr(_), Type::Ptr(_)) => {
|
||||||
|
Some(self.get_value(&values, v).unwrap())
|
||||||
|
}
|
||||||
|
_ => todo!("{inst:?}"),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
module.print_to_stderr();
|
||||||
|
todo!("{inst:?}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
values.insert((*inst).into(), llvm_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(&self, ty: Type<'l>) -> AnyTypeEnum<'l> {
|
||||||
|
if let Some(ty) = self.types.get_sync(&ty) {
|
||||||
|
return *ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
let llvm_ty = match ty {
|
||||||
|
Type::Void => self.ctx.void_type().into(),
|
||||||
|
Type::I8 | Type::U8 => self.ctx.i8_type().into(),
|
||||||
|
Type::I16 | Type::U16 => self.ctx.i16_type().into(),
|
||||||
|
Type::I32 | Type::U32 => self.ctx.i32_type().into(),
|
||||||
|
Type::I64 | Type::U64 => self.ctx.i64_type().into(),
|
||||||
|
Type::I128 | Type::U128 => self.ctx.i128_type().into(),
|
||||||
|
Type::USIZE | Type::ISIZE => self.native_int_ty.into(),
|
||||||
|
|
||||||
|
Type::Ptr(_) => self.ctx.ptr_type(AddressSpace::default()).into(),
|
||||||
|
|
||||||
|
Type::Func(FuncT { ret_t, par_t, .. }) => {
|
||||||
|
let ret_t = self.get_type(*ret_t);
|
||||||
|
let par_t = par_t
|
||||||
|
.iter()
|
||||||
|
.map(|ty| self.get_type(*ty))
|
||||||
|
.filter_map(|ty| BasicMetadataTypeEnum::try_from(ty).ok())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
match ret_t {
|
||||||
|
AnyTypeEnum::VoidType(ty) => ty.fn_type(&par_t, false).into(),
|
||||||
|
AnyTypeEnum::IntType(ty) => ty.fn_type(&par_t, false).into(),
|
||||||
|
_ => todo!("{ret_t:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::Array(ArrayT {
|
||||||
|
base,
|
||||||
|
length: Some(length),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let base = self.get_type(*base);
|
||||||
|
match base {
|
||||||
|
AnyTypeEnum::IntType(t) => t.array_type(*length).into(),
|
||||||
|
AnyTypeEnum::FloatType(t) => t.array_type(*length).into(),
|
||||||
|
AnyTypeEnum::StructType(t) => t.array_type(*length).into(),
|
||||||
|
_ => todo!("{ty:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!("{ty:#?}"),
|
||||||
|
};
|
||||||
|
self.types.entry_sync(ty).or_insert(llvm_ty);
|
||||||
|
llvm_ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_value(
|
||||||
|
&self,
|
||||||
|
map: &FxHashMap<Value<'l>, Option<BasicValueEnum<'l>>>,
|
||||||
|
val: &Value<'l>,
|
||||||
|
) -> Option<BasicValueEnum<'l>> {
|
||||||
|
if let Some(value) = map.get(val) {
|
||||||
|
return value.clone();
|
||||||
|
}
|
||||||
|
match val {
|
||||||
|
Value::Constant(val) => match val {
|
||||||
|
AnyConst::Int(Int::U8(v)) => {
|
||||||
|
Some(self.ctx.i8_type().const_int(*v as u64, false).into())
|
||||||
|
}
|
||||||
|
AnyConst::Int(Int::U16(v)) => {
|
||||||
|
Some(self.ctx.i16_type().const_int(*v as u64, false).into())
|
||||||
|
}
|
||||||
|
AnyConst::Int(Int::U32(v)) => {
|
||||||
|
Some(self.ctx.i32_type().const_int(*v as u64, false).into())
|
||||||
|
}
|
||||||
|
AnyConst::Int(Int::U64(v)) => {
|
||||||
|
Some(self.ctx.i64_type().const_int(*v as u64, false).into())
|
||||||
|
}
|
||||||
|
AnyConst::Int(Int::USize(v)) => {
|
||||||
|
Some(self.native_int_ty.const_int(*v as u64, false).into())
|
||||||
|
}
|
||||||
|
AnyConst::Array([]) => todo!("{val:?}"),
|
||||||
|
AnyConst::Array(array) => {
|
||||||
|
let ty = self.get_type(array[0].ty());
|
||||||
|
match ty {
|
||||||
|
AnyTypeEnum::IntType(t) => {
|
||||||
|
let mut values = vec![];
|
||||||
|
for v in *array {
|
||||||
|
let Some(BasicValueEnum::IntValue(v)) =
|
||||||
|
self.get_value(map, &Value::Constant(*v))
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
values.push(v);
|
||||||
|
}
|
||||||
|
Some(t.const_array(&values).into())
|
||||||
|
}
|
||||||
|
_ => todo!("{ty:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!("{val:?}"),
|
||||||
|
},
|
||||||
|
_ => unreachable!("{val:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_signed(ty: Type) -> bool {
|
||||||
|
match ty {
|
||||||
|
Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 | Type::USIZE => false,
|
||||||
|
Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 | Type::ISIZE => true,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ edition = "2024"
|
|||||||
leaf_parser = { path = "../parser" }
|
leaf_parser = { path = "../parser" }
|
||||||
leaf_assembly = { path = "../assembly" }
|
leaf_assembly = { path = "../assembly" }
|
||||||
leaf_allocators = { path = "../allocators" }
|
leaf_allocators = { path = "../allocators" }
|
||||||
|
leaf_backend_llvm = { path = "../backends/llvm" }
|
||||||
arcstr = "1.2.0"
|
arcstr = "1.2.0"
|
||||||
derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] }
|
derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] }
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
|
itertools = "0.14.0"
|
||||||
|
|||||||
+41
-4
@@ -1,6 +1,13 @@
|
|||||||
use arcstr::ArcStr;
|
use arcstr::ArcStr;
|
||||||
use leaf_allocators::SyncArenaAllocator;
|
use leaf_allocators::SyncArenaAllocator;
|
||||||
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
|
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
|
||||||
|
use leaf_backend_llvm::{
|
||||||
|
LlvmContext,
|
||||||
|
inkwell::{
|
||||||
|
OptimizationLevel,
|
||||||
|
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
|
||||||
|
},
|
||||||
|
};
|
||||||
use leaf_compiler::CompilationContext;
|
use leaf_compiler::CompilationContext;
|
||||||
use leaf_parser::SourceCode;
|
use leaf_parser::SourceCode;
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
@@ -15,12 +22,42 @@ fn main() {
|
|||||||
if let Err(err) = context.extend(
|
if let Err(err) = context.extend(
|
||||||
ident.clone(),
|
ident.clone(),
|
||||||
&[Arc::new(SourceCode {
|
&[Arc::new(SourceCode {
|
||||||
text: ArcStr::from(std::fs::read_to_string("../test.leaf").unwrap()),
|
text: ArcStr::from(std::fs::read_to_string("test.leaf").unwrap()),
|
||||||
file: PathBuf::from("../test.leaf"),
|
file: PathBuf::from("test.leaf"),
|
||||||
})],
|
})],
|
||||||
) {
|
) {
|
||||||
println!("{:#?}", err);
|
println!("{:#?}", err);
|
||||||
} else {
|
return;
|
||||||
println!("{:#?}", context.get_assembly(&ident));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let assembly = context.get_assembly(&ident).unwrap();
|
||||||
|
println!("{:#?}\n", assembly);
|
||||||
|
|
||||||
|
Target::initialize_all(&InitializationConfig::default());
|
||||||
|
let target_triple = TargetMachine::get_default_triple();
|
||||||
|
let target = Target::from_name("aarch64").unwrap();
|
||||||
|
|
||||||
|
let target_machine = target
|
||||||
|
.create_target_machine(
|
||||||
|
&target_triple,
|
||||||
|
"generic",
|
||||||
|
"", // CPU features
|
||||||
|
OptimizationLevel::Default,
|
||||||
|
RelocMode::Default,
|
||||||
|
CodeModel::Default,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ctx = LlvmContext::create();
|
||||||
|
let ctx = leaf_backend_llvm::CompilationContext::new(&ctx, &target_machine);
|
||||||
|
let module = ctx.compile(assembly);
|
||||||
|
module.print_to_stderr();
|
||||||
|
module.verify().unwrap();
|
||||||
|
|
||||||
|
let asm = target_machine
|
||||||
|
.write_to_memory_buffer(&module, FileType::Assembly)
|
||||||
|
.unwrap();
|
||||||
|
let asm = std::str::from_utf8(asm.as_slice()).unwrap();
|
||||||
|
eprintln!("{asm}");
|
||||||
|
std::fs::write("out.asm", asm).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
+196
-22
@@ -5,12 +5,12 @@ use leaf_assembly::{
|
|||||||
Function,
|
Function,
|
||||||
ir::{Cmp, FunctionBodyBuilder},
|
ir::{Cmp, FunctionBodyBuilder},
|
||||||
},
|
},
|
||||||
types::{Type, derivations::PtrT},
|
types::{IntT, Type, derivations::PtrT},
|
||||||
values::{AnyConst, Int, Value, ValueFlags},
|
values::{AnyConst, Int, Value, ValueFlags},
|
||||||
};
|
};
|
||||||
use leaf_parser::{
|
use leaf_parser::{
|
||||||
SourceCode,
|
SourceCode,
|
||||||
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, NamePattern, While},
|
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, IndexingExpr, NamePattern, While},
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
@@ -177,12 +177,12 @@ impl<'l> Scope<'l> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Binary(expr) => {
|
Expr::Binary(bin_expr) => {
|
||||||
let BinaryExpr {
|
let BinaryExpr {
|
||||||
lhs: lhs_expr,
|
lhs: lhs_expr,
|
||||||
rhs: rhs_expr,
|
rhs: rhs_expr,
|
||||||
op,
|
op,
|
||||||
} = &**expr;
|
} = &**bin_expr;
|
||||||
|
|
||||||
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
|
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
|
||||||
if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) {
|
if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) {
|
||||||
@@ -196,28 +196,84 @@ impl<'l> Scope<'l> {
|
|||||||
|
|
||||||
let builder = ctx.builder.as_mut().unwrap();
|
let builder = ctx.builder.as_mut().unwrap();
|
||||||
match (lhs.ty(), rhs.ty(), op) {
|
match (lhs.ty(), rhs.ty(), op) {
|
||||||
(Type::U32, Type::U32, BinaryOp::Add(_)) => Ok(builder.add(lhs, rhs).unwrap()),
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Add(_)) if a_ty == b_ty => {
|
||||||
(Type::U32, Type::U32, BinaryOp::Mul(_)) => Ok(builder.mul(lhs, rhs).unwrap()),
|
Ok(builder.add(lhs, rhs).unwrap())
|
||||||
(Type::U32, Type::U32, BinaryOp::Lt(_)) => {
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Sub(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.sub(lhs, rhs).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mul(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.mul(lhs, rhs).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Div(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.div(lhs, rhs).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mod(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.modulo(lhs, rhs).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Eq(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.cmp(lhs, rhs, Cmp::Eq).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ne(_)) if a_ty == b_ty => {
|
||||||
|
Ok(builder.cmp(lhs, rhs, Cmp::Ne).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Lt(_)) if a_ty == b_ty => {
|
||||||
Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap())
|
Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap())
|
||||||
}
|
}
|
||||||
(Type::Ptr(PtrT { base, mutable, .. }), ty, BinaryOp::Assign(_)) => {
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Gt(_)) if a_ty == b_ty => {
|
||||||
match *base == ty {
|
Ok(builder.cmp(lhs, rhs, Cmp::Gt).unwrap())
|
||||||
true => Ok(builder.store(lhs, rhs).unwrap()),
|
}
|
||||||
false => Err(CompilationError {
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Le(_)) if a_ty == b_ty => {
|
||||||
kind: Kind::InvalidType,
|
Ok(builder.cmp(lhs, rhs, Cmp::Le).unwrap())
|
||||||
message: format!(
|
}
|
||||||
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
|
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ge(_)) if a_ty == b_ty => {
|
||||||
),
|
Ok(builder.cmp(lhs, rhs, Cmp::Ge).unwrap())
|
||||||
|
}
|
||||||
|
(Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty {
|
||||||
|
true => Ok(builder.store(lhs, rhs).unwrap()),
|
||||||
|
false => Err(CompilationError {
|
||||||
|
kind: Kind::InvalidType,
|
||||||
|
message: format!(
|
||||||
|
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
|
||||||
|
),
|
||||||
|
location: Location::Range {
|
||||||
|
file: self.source.clone(),
|
||||||
|
range: rhs_expr.range(),
|
||||||
|
},
|
||||||
|
cause: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(src_ty, Type::Type, BinaryOp::Cast(_)) => {
|
||||||
|
let Value::Constant(AnyConst::Type(dst_ty)) = rhs else {
|
||||||
|
return Err(CompilationError {
|
||||||
|
kind: Kind::NotAType,
|
||||||
|
message: format!("Cannot perform cast."),
|
||||||
location: Location::Range {
|
location: Location::Range {
|
||||||
file: self.source.clone(),
|
file: self.source.clone(),
|
||||||
range: rhs_expr.range(),
|
range: expr.range(),
|
||||||
},
|
},
|
||||||
cause: None,
|
cause: Some(Box::new(CompilationError {
|
||||||
}),
|
kind: Kind::NotAType,
|
||||||
|
message: format!("Cast target is not a type."),
|
||||||
|
location: Location::Range {
|
||||||
|
file: self.source.clone(),
|
||||||
|
range: rhs_expr.range(),
|
||||||
|
},
|
||||||
|
cause: None,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
match (src_ty, dst_ty) {
|
||||||
|
(Type::Int(src_ty), Type::Int(dst_ty)) => {
|
||||||
|
if dst_ty.precision < src_ty.precision {
|
||||||
|
return Ok(builder.trunc(lhs, dst_ty).unwrap().into());
|
||||||
|
}
|
||||||
|
todo!("{src_ty} as {dst_ty}");
|
||||||
|
}
|
||||||
|
_ => todo!("{src_ty} as {dst_ty}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(a, b, _) => unimplemented!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"),
|
(a, b, _) => todo!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,15 +338,123 @@ impl<'l> Scope<'l> {
|
|||||||
Ok(builder.call(func, args).unwrap())
|
Ok(builder.call(func, args).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Type(expr) => match &**expr {
|
Expr::Type(ty_expr) => match &**ty_expr {
|
||||||
ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? {
|
ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? {
|
||||||
Value::Constant(AnyConst::Type(ty)) => {
|
Value::Constant(AnyConst::Type(ty)) => {
|
||||||
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into())
|
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into())
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
Value::Instruction(inst) if inst.value_flags().contains(ValueFlags::LValue) => {
|
||||||
|
let Type::Ptr(PtrT {
|
||||||
|
base,
|
||||||
|
mutable: is_mut,
|
||||||
|
..
|
||||||
|
}) = inst.value_ty()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
if *mutable && !*is_mut {
|
||||||
|
return Err(CompilationError {
|
||||||
|
kind: Kind::NotAFunction,
|
||||||
|
message: "Cannot obtain a mutable pointer to an immutable value."
|
||||||
|
.into(),
|
||||||
|
location: Location::Range {
|
||||||
|
file: self.source.clone(),
|
||||||
|
range: expr.range(),
|
||||||
|
},
|
||||||
|
cause: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut flags = inst.value_flags();
|
||||||
|
let builder = ctx.builder.as_mut().unwrap();
|
||||||
|
flags.remove(
|
||||||
|
ValueFlags::Mutable | ValueFlags::Volatile | ValueFlags::LValue,
|
||||||
|
);
|
||||||
|
let ptr = Type::Ptr(base.make_ptr(*mutable));
|
||||||
|
unsafe {
|
||||||
|
Ok(builder
|
||||||
|
.reinterpret(Value::Instruction(inst), ptr, flags)
|
||||||
|
.unwrap()
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => todo!("{v:?}"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Expr::List(expr) => {
|
||||||
|
let mut expr = expr.iter();
|
||||||
|
let mut values = Vec::with_capacity(expr.len());
|
||||||
|
match expr.next() {
|
||||||
|
None => return Ok(Value::Constant(AnyConst::Array(&[]))),
|
||||||
|
Some(expr) => {
|
||||||
|
let value = self.compile_expression(expr, ctx)?;
|
||||||
|
// TODO Check if it matches the ctx type hint
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let element_ty = values[0].ty();
|
||||||
|
for expr in expr {
|
||||||
|
let value = self.compile_expression(expr, ctx)?;
|
||||||
|
if value.ty() != element_ty {
|
||||||
|
return Err(CompilationError {
|
||||||
|
kind: Kind::InvalidType,
|
||||||
|
message: format!(
|
||||||
|
"Expected type `{}`, found `{}`",
|
||||||
|
element_ty,
|
||||||
|
value.ty()
|
||||||
|
),
|
||||||
|
location: Location::Range {
|
||||||
|
file: self.source.clone(),
|
||||||
|
range: expr.range(),
|
||||||
|
},
|
||||||
|
cause: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
if values.iter().all(|v| matches!(v, Value::Constant(_))) {
|
||||||
|
let alloc = self.assembly.ctx().alloc();
|
||||||
|
return Ok(Value::Constant(AnyConst::Array(alloc.alloc_slice(
|
||||||
|
values.into_iter().map(|v| match v {
|
||||||
|
Value::Constant(c) => c,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Index(expr) => {
|
||||||
|
let IndexingExpr { value, index } = &**expr;
|
||||||
|
let value = self.compile_expression(value, ctx)?;
|
||||||
|
let mut index = self.compile_expression(index, ctx)?;
|
||||||
|
let builder = ctx.builder.as_mut().unwrap();
|
||||||
|
|
||||||
|
if index.flags().contains(ValueFlags::LValue) {
|
||||||
|
index = builder.load(index).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Add support for custom indexing operations
|
||||||
|
if index.ty() != Type::USIZE {
|
||||||
|
return Err(CompilationError {
|
||||||
|
kind: Kind::InvalidType,
|
||||||
|
message: "Value is not of type `usize`".into(),
|
||||||
|
location: Location::Range {
|
||||||
|
file: self.source.clone(),
|
||||||
|
range: expr.index.range(),
|
||||||
|
},
|
||||||
|
cause: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.flags().contains(ValueFlags::LValue) {
|
||||||
|
let gep = builder.gep(value, index).unwrap();
|
||||||
|
return Ok(gep);
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!("{:#?}", value.ty());
|
||||||
|
}
|
||||||
|
|
||||||
_ => todo!("{expr:#?}"),
|
_ => todo!("{expr:#?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +506,17 @@ impl<'l> Scope<'l> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !builder.current_block().has_termination() {
|
if !builder.current_block().has_termination() {
|
||||||
builder.ret(last_expr).unwrap();
|
match func.ty.ret_t {
|
||||||
|
Type::Void => builder.ret(None).unwrap(),
|
||||||
|
_ => {
|
||||||
|
if let Some(expr) = last_expr.as_mut() {
|
||||||
|
if expr.flags().contains(ValueFlags::LValue) {
|
||||||
|
*expr = builder.load(*expr).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.ret(last_expr).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build().unwrap();
|
builder.build().unwrap();
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "leaf_interpreter"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
fxhash = "0.2.1"
|
|
||||||
leaf_allocators = { path = "../allocators" }
|
|
||||||
leaf_assembly = { path = "../assembly" }
|
|
||||||
leaf_compiler = { path = "../compiler" }
|
|
||||||
leaf_parser = { path = "../parser" }
|
|
||||||
scc = "3.3.7"
|
|
||||||
smallvec = "1.15.1"
|
|
||||||
@@ -1,328 +0,0 @@
|
|||||||
use crate::layout_cache::{GetLayout, LayoutCache};
|
|
||||||
use fxhash::{FxBuildHasher, FxHashMap};
|
|
||||||
use leaf_assembly::{
|
|
||||||
functions::{
|
|
||||||
Function,
|
|
||||||
ir::{Cmp, Instruction, InstructionVariant},
|
|
||||||
},
|
|
||||||
types::{IntT, Type},
|
|
||||||
values::{AnyConst, Int, Value},
|
|
||||||
};
|
|
||||||
use scc::HashMap;
|
|
||||||
use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum OpCode<'l> {
|
|
||||||
Store_CL_U8(u8, usize),
|
|
||||||
Store_CL_U16(u16, usize),
|
|
||||||
Store_CL_U32(u32, usize),
|
|
||||||
Store_CL_U64(u64, usize),
|
|
||||||
|
|
||||||
Add_LL_U32(usize, usize, usize),
|
|
||||||
Add_LC_U32(usize, u32, usize),
|
|
||||||
|
|
||||||
CmpEq_LC_U32(usize, u32, usize),
|
|
||||||
CmpLt_LC_U32(usize, u32, usize),
|
|
||||||
CmpGt_LC_U32(usize, u32, usize),
|
|
||||||
CmpLe_LC_U32(usize, u32, usize),
|
|
||||||
CmpGe_LC_U32(usize, u32, usize),
|
|
||||||
|
|
||||||
Call(&'l Function<'l>, Vec<Range<usize>>, Range<usize>),
|
|
||||||
Jump(usize),
|
|
||||||
Branch {
|
|
||||||
cond: usize,
|
|
||||||
true_case: usize,
|
|
||||||
false_case: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
CopyRange(Range<usize>, Range<usize>),
|
|
||||||
ReturnRange(Range<usize>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct InstructionCacheEntry<'l> {
|
|
||||||
pub layout: Layout,
|
|
||||||
pub opcodes: Vec<OpCode<'l>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for InstructionCacheEntry<'_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
struct OpCodes<'a, 'b>(&'a [OpCode<'b>]);
|
|
||||||
impl Debug for OpCodes<'_, '_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let mut dbg = f.debug_list();
|
|
||||||
for op in self.0 {
|
|
||||||
dbg.entry(&format_args!("{op:?}"));
|
|
||||||
}
|
|
||||||
dbg.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.debug_struct("InstructionCacheEntry")
|
|
||||||
.field("layout", &self.layout)
|
|
||||||
.field("opcodes", &OpCodes(&self.opcodes))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'l> InstructionCacheEntry<'l> {
|
|
||||||
pub fn new(func: &'l Function<'l>, layouts: &LayoutCache<'l>) -> Self {
|
|
||||||
let mut opcodes = Vec::new();
|
|
||||||
let mut layout = Layout::new::<()>();
|
|
||||||
let mut memory_ranges = FxHashMap::default();
|
|
||||||
|
|
||||||
macro_rules! alloc_range {
|
|
||||||
($ty:expr, $id:expr) => {{
|
|
||||||
&*memory_ranges.entry($id).or_insert_with(|| {
|
|
||||||
let l = layouts.get_layout($ty);
|
|
||||||
let (new_layout, offset) = layout.extend(l.layout).unwrap();
|
|
||||||
layout = new_layout;
|
|
||||||
offset..offset + l.layout.size()
|
|
||||||
})
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(body) = func.body() else {
|
|
||||||
unreachable!("InstructionCacheEntry::new called with a function without a body.");
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut backpatch = vec![];
|
|
||||||
let mut block_starts = Vec::with_capacity(body.blocks.len());
|
|
||||||
|
|
||||||
for inst in body.blocks.iter().flat_map(|b| b.instructions()) {
|
|
||||||
match &inst.variant {
|
|
||||||
InstructionVariant::StackAlloc(ty) => {
|
|
||||||
let _ = alloc_range!(*ty, inst.id());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for block in &body.blocks {
|
|
||||||
block_starts.push(opcodes.len());
|
|
||||||
for inst in block.instructions() {
|
|
||||||
match &inst.variant {
|
|
||||||
InstructionVariant::StackAlloc(_) => {}
|
|
||||||
|
|
||||||
InstructionVariant::Store(
|
|
||||||
Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U8(v))),
|
|
||||||
) => {
|
|
||||||
assert_eq!(Type::U8, *ty);
|
|
||||||
opcodes.push(OpCode::Store_CL_U8(*v, memory_ranges[&t.id()].start));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Store(
|
|
||||||
Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U16(v))),
|
|
||||||
) => {
|
|
||||||
assert_eq!(Type::U16, *ty);
|
|
||||||
opcodes.push(OpCode::Store_CL_U16(*v, memory_ranges[&t.id()].start));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Store(
|
|
||||||
Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U32(v))),
|
|
||||||
) => {
|
|
||||||
assert_eq!(Type::U32, *ty);
|
|
||||||
opcodes.push(OpCode::Store_CL_U32(*v, memory_ranges[&t.id()].start));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Store(
|
|
||||||
Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U64(v))),
|
|
||||||
) => {
|
|
||||||
assert_eq!(Type::U64, *ty);
|
|
||||||
opcodes.push(OpCode::Store_CL_U64(*v, memory_ranges[&t.id()].start));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Store(
|
|
||||||
Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Value::Instruction(v),
|
|
||||||
) => {
|
|
||||||
let src = alloc_range!(*ty, v.id()).clone();
|
|
||||||
let dst = alloc_range!(*ty, t.id()).clone();
|
|
||||||
opcodes.push(OpCode::CopyRange(src, dst));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Load(Value::Instruction(
|
|
||||||
t @ Instruction {
|
|
||||||
variant: InstructionVariant::StackAlloc(ty),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
)) => {
|
|
||||||
let src = alloc_range!(*ty, t.id()).clone();
|
|
||||||
let dst = alloc_range!(*ty, inst.id()).clone();
|
|
||||||
opcodes.push(OpCode::CopyRange(src, dst));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::IAdd(Value::Instruction(a), Value::Instruction(b)) => {
|
|
||||||
match a.value_ty() {
|
|
||||||
ty @ Type::Int(IntT {
|
|
||||||
signed: false,
|
|
||||||
precision: 32,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let a = memory_ranges[&a.id()].start;
|
|
||||||
let b = memory_ranges[&b.id()].start;
|
|
||||||
let c = alloc_range!(ty, inst.id()).start;
|
|
||||||
opcodes.push(OpCode::Add_LL_U32(a, b, c));
|
|
||||||
}
|
|
||||||
_ => todo!("Unimplemented type `{}`", a.value_ty()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::IAdd(
|
|
||||||
Value::Instruction(a),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U32(b))),
|
|
||||||
) => {
|
|
||||||
assert!(matches!(
|
|
||||||
a.value_ty(),
|
|
||||||
Type::Int(IntT {
|
|
||||||
signed: false,
|
|
||||||
precision: 32,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
));
|
|
||||||
let a = memory_ranges[&a.id()].start;
|
|
||||||
let c = alloc_range!(Type::U32, inst.id()).start;
|
|
||||||
opcodes.push(OpCode::Add_LC_U32(a, *b, c));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::ICmp(
|
|
||||||
Value::Instruction(a),
|
|
||||||
Value::Constant(AnyConst::Int(Int::U32(b))),
|
|
||||||
cmp,
|
|
||||||
) => {
|
|
||||||
assert!(matches!(
|
|
||||||
a.value_ty(),
|
|
||||||
Type::Int(IntT {
|
|
||||||
signed: false,
|
|
||||||
precision: 32,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
));
|
|
||||||
let a = memory_ranges[&a.id()].start;
|
|
||||||
let target = alloc_range!(Type::Bool, inst.id()).start;
|
|
||||||
match *cmp {
|
|
||||||
Cmp::Eq => opcodes.push(OpCode::CmpEq_LC_U32(a, *b, target)),
|
|
||||||
Cmp::Lt => opcodes.push(OpCode::CmpLt_LC_U32(a, *b, target)),
|
|
||||||
Cmp::Gt => opcodes.push(OpCode::CmpGt_LC_U32(a, *b, target)),
|
|
||||||
Cmp::Le => opcodes.push(OpCode::CmpLe_LC_U32(a, *b, target)),
|
|
||||||
Cmp::Ge => opcodes.push(OpCode::CmpGe_LC_U32(a, *b, target)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Call(func, args) => {
|
|
||||||
let mut ranges = Vec::with_capacity(args.len());
|
|
||||||
for arg in args {
|
|
||||||
match arg {
|
|
||||||
Value::Instruction(i) => {
|
|
||||||
ranges.push(memory_ranges[&i.id()].clone());
|
|
||||||
}
|
|
||||||
_ => todo!("Unimplemented variant `{arg:?}`."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opcodes.push(OpCode::Call(
|
|
||||||
func,
|
|
||||||
ranges,
|
|
||||||
alloc_range!(func.ty.ret_t, inst.id()).clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Jump(target) => {
|
|
||||||
backpatch.push(opcodes.len());
|
|
||||||
opcodes.push(OpCode::Jump(target.id as usize));
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Branch {
|
|
||||||
cond: Value::Instruction(i),
|
|
||||||
true_case,
|
|
||||||
false_case,
|
|
||||||
} => {
|
|
||||||
assert!(matches!(i.value_ty(), Type::Bool));
|
|
||||||
backpatch.push(opcodes.len());
|
|
||||||
opcodes.push(OpCode::Branch {
|
|
||||||
cond: memory_ranges[&i.id()].start,
|
|
||||||
true_case: true_case.id as usize,
|
|
||||||
false_case: false_case.id as usize,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionVariant::Return(Some(Value::Instruction(t))) => {
|
|
||||||
assert_eq!(func.ty.ret_t, t.value_ty());
|
|
||||||
opcodes.push(OpCode::ReturnRange(memory_ranges[&t.id()].clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => todo!("Unimplemented instruction `{inst:?}`"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx in backpatch {
|
|
||||||
match &mut opcodes[idx] {
|
|
||||||
OpCode::Jump(target) => {
|
|
||||||
*target = block_starts[*target];
|
|
||||||
}
|
|
||||||
OpCode::Branch {
|
|
||||||
true_case,
|
|
||||||
false_case,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
*true_case = block_starts[*true_case];
|
|
||||||
*false_case = block_starts[*false_case];
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { layout, opcodes }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InstructionCache<'l> {
|
|
||||||
layouts: Arc<LayoutCache<'l>>,
|
|
||||||
entries: HashMap<*const Function<'l>, Arc<InstructionCacheEntry<'l>>, FxBuildHasher>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'l> InstructionCache<'l> {
|
|
||||||
pub fn new(layouts: Arc<LayoutCache<'l>>) -> Self {
|
|
||||||
Self {
|
|
||||||
layouts,
|
|
||||||
entries: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, func: &'l Function<'l>) -> Arc<InstructionCacheEntry<'l>> {
|
|
||||||
self.entries
|
|
||||||
.entry_sync(func as *const Function<'l>)
|
|
||||||
.or_insert_with(|| Arc::new(InstructionCacheEntry::new(func, &self.layouts)))
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
instruction_cache::{InstructionCache, InstructionCacheEntry, OpCode},
|
|
||||||
layout_cache::LayoutCache,
|
|
||||||
};
|
|
||||||
use fxhash::FxHashMap;
|
|
||||||
use leaf_assembly::{
|
|
||||||
assembly::Ctx,
|
|
||||||
functions::Function,
|
|
||||||
types::{IntT, Type},
|
|
||||||
values::{AnyConst, Int, Value},
|
|
||||||
};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::{ops::Range, sync::Arc};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
InvalidParameterCount,
|
|
||||||
InvalidParameterType,
|
|
||||||
FunctionHasNoBody,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum AnyValue {
|
|
||||||
Void,
|
|
||||||
Int(Int),
|
|
||||||
Ptr(*const u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum NativeFunction {
|
|
||||||
Plain(fn(&mut Interpreter, &[Range<usize>]) -> Result<AnyValue, Error>),
|
|
||||||
Closure(Arc<dyn Fn(&mut Interpreter, &[Range<usize>]) -> Result<AnyValue, Error>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Interpreter<'l> {
|
|
||||||
ctx: Ctx<'l>,
|
|
||||||
stack: Vec<u8>,
|
|
||||||
layouts: Arc<LayoutCache<'l>>,
|
|
||||||
instructions: Arc<InstructionCache<'l>>,
|
|
||||||
native_funcs: FxHashMap<*const Function<'l>, NativeFunction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'l> Interpreter<'l> {
|
|
||||||
pub fn new(ctx: Ctx<'l>) -> Self {
|
|
||||||
let layouts = Arc::new(LayoutCache::default());
|
|
||||||
Self {
|
|
||||||
ctx,
|
|
||||||
stack: vec![0; 1024],
|
|
||||||
layouts: layouts.clone(),
|
|
||||||
instructions: Arc::new(InstructionCache::new(layouts)),
|
|
||||||
native_funcs: FxHashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_function(&mut self, func: &'l Function<'l>, native_function: NativeFunction) {
|
|
||||||
self.native_funcs.insert(func, native_function);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self, func: &'l Function<'l>, args: Vec<Value<'l>>) -> Result<AnyValue, Error> {
|
|
||||||
if func.body().is_none() {
|
|
||||||
return Err(Error::FunctionHasNoBody);
|
|
||||||
};
|
|
||||||
|
|
||||||
if func.ty.par_t.len() != args.len() {
|
|
||||||
return Err(Error::InvalidParameterCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if func
|
|
||||||
.ty
|
|
||||||
.par_t
|
|
||||||
.iter()
|
|
||||||
.zip(&args)
|
|
||||||
.any(|(ty, arg)| *ty != arg.ty())
|
|
||||||
{
|
|
||||||
return Err(Error::InvalidParameterType);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut sp = 0;
|
|
||||||
let mut args_ranges: SmallVec<[Range<usize>; 4]> = args
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| self.push_ctx_val(&mut sp, &v))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let func_entry = self.instructions.get(func);
|
|
||||||
let ret_range = self.call(sp, &mut args_ranges, &func_entry);
|
|
||||||
match func.ty.ret_t {
|
|
||||||
Type::Int(IntT {
|
|
||||||
signed: false,
|
|
||||||
precision: 32,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let mem = &self.stack[ret_range];
|
|
||||||
let val = u32::from_ne_bytes(mem.try_into().unwrap());
|
|
||||||
Ok(AnyValue::Int(Int::U32(val)))
|
|
||||||
}
|
|
||||||
_ => todo!("Unsupported type `{}`", func.ty.ret_t),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(
|
|
||||||
&mut self,
|
|
||||||
sp: usize,
|
|
||||||
_args: &[Range<usize>],
|
|
||||||
function: &InstructionCacheEntry<'l>,
|
|
||||||
) -> Range<usize> {
|
|
||||||
unsafe {
|
|
||||||
let l = function.layout;
|
|
||||||
let sp = sp + (self.stack.as_ptr().add(sp)).align_offset(l.align());
|
|
||||||
assert!(sp + l.size() < self.stack.len());
|
|
||||||
|
|
||||||
// Since the earlier assertion guarantees the stack won't overflow, we can skip the range checks.
|
|
||||||
let mut i = 0;
|
|
||||||
while let Some(opcode) = function.opcodes.get(i) {
|
|
||||||
match opcode {
|
|
||||||
OpCode::Store_CL_U32(v, offset) => {
|
|
||||||
let start = sp + *offset;
|
|
||||||
self.stack
|
|
||||||
.get_unchecked_mut(start..start + 4)
|
|
||||||
.copy_from_slice(&v.to_ne_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::CopyRange(src, dst) => {
|
|
||||||
let len = src.len();
|
|
||||||
let src = self.stack.as_ptr().add(sp + src.start);
|
|
||||||
let dst = self.stack.as_mut_ptr().add(sp + dst.start);
|
|
||||||
std::ptr::copy_nonoverlapping(src, dst, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::Add_LL_U32(a, b, dst) => {
|
|
||||||
let [a, b, dst] = [
|
|
||||||
sp + a..sp + a + 4,
|
|
||||||
sp + b..sp + b + 4,
|
|
||||||
sp + dst..sp + dst + 4,
|
|
||||||
];
|
|
||||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
|
||||||
let b = u32::from_ne_bytes(self.stack.get_unchecked(b).try_into().unwrap());
|
|
||||||
self.stack
|
|
||||||
.get_unchecked_mut(dst)
|
|
||||||
.copy_from_slice(&a.wrapping_add(b).to_ne_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::Add_LC_U32(a, b, dst) => {
|
|
||||||
let [a, dst] = [sp + a..sp + a + 4, sp + dst..sp + dst + 4];
|
|
||||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
|
||||||
self.stack
|
|
||||||
.get_unchecked_mut(dst)
|
|
||||||
.copy_from_slice(&a.wrapping_add(*b).to_ne_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::CmpLt_LC_U32(a, b, dst) => {
|
|
||||||
let a = sp + a..sp + a + 4;
|
|
||||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
|
||||||
*self.stack.get_unchecked_mut(*dst) = (a < *b) as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::Jump(target) => {
|
|
||||||
i = *target;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::Branch {
|
|
||||||
cond,
|
|
||||||
true_case,
|
|
||||||
false_case,
|
|
||||||
} => {
|
|
||||||
i = match self.stack().get_unchecked(*cond) {
|
|
||||||
0 => *false_case,
|
|
||||||
_ => *true_case,
|
|
||||||
};
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::Call(func, args, out) => {
|
|
||||||
let args: SmallVec<[Range<usize>; 4]> = args
|
|
||||||
.iter()
|
|
||||||
.map(|range| sp + range.start..sp + range.end)
|
|
||||||
.collect();
|
|
||||||
let key = *func as *const Function<'l>;
|
|
||||||
match self.native_funcs.get(&key).cloned() {
|
|
||||||
Some(NativeFunction::Plain(native_func)) => {
|
|
||||||
let res = native_func(self, &args).unwrap();
|
|
||||||
self.write_any_val(out, &res);
|
|
||||||
}
|
|
||||||
Some(NativeFunction::Closure(native_func)) => {
|
|
||||||
let res = native_func(self, &args).unwrap();
|
|
||||||
self.write_any_val(out, &res);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let func = self.instructions.get(func);
|
|
||||||
let res = self.call(sp, &args, &func);
|
|
||||||
self.stack.copy_within(res, out.start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCode::ReturnRange(range) => {
|
|
||||||
return sp + range.start..sp + range.end;
|
|
||||||
}
|
|
||||||
_ => todo!("Unimplemented opcode `{opcode:?}`"),
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!("Execution has produced no results");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stack(&self) -> &[u8] {
|
|
||||||
&self.stack
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_ctx_val(&mut self, sp: &mut usize, value: &Value) -> Range<usize> {
|
|
||||||
let ptr = &self.stack[*sp] as *const u8;
|
|
||||||
match value {
|
|
||||||
Value::Constant(AnyConst::Int(Int::U32(v))) => {
|
|
||||||
*sp += ptr.align_offset(align_of::<u32>());
|
|
||||||
let range = *sp..*sp + size_of::<u32>();
|
|
||||||
self.stack[range.clone()].copy_from_slice(&v.to_ne_bytes());
|
|
||||||
range
|
|
||||||
}
|
|
||||||
_ => todo!("Unsupported type `{}`", value.ty()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_any_val(&mut self, range: &Range<usize>, value: &AnyValue) {
|
|
||||||
match value {
|
|
||||||
AnyValue::Void => assert_eq!(range.len(), 0),
|
|
||||||
AnyValue::Int(Int::U32(v)) => {
|
|
||||||
self.stack[range.clone()].copy_from_slice(&v.to_ne_bytes());
|
|
||||||
}
|
|
||||||
_ => todo!("Unsupported value `{:?}`", value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
use fxhash::FxBuildHasher;
|
|
||||||
use leaf_assembly::types::{IntT, Type, derivations::PtrT};
|
|
||||||
use scc::HashMap;
|
|
||||||
use std::{
|
|
||||||
alloc::Layout,
|
|
||||||
any::Any,
|
|
||||||
marker::PhantomData,
|
|
||||||
ops::Range,
|
|
||||||
sync::{Arc, OnceLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct LayoutElement {
|
|
||||||
pub offset: usize,
|
|
||||||
pub layout: Arc<TypeLayout>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutElement {
|
|
||||||
pub fn range(&self) -> Range<usize> {
|
|
||||||
self.offset..self.offset + self.layout.layout.size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TypeLayout {
|
|
||||||
pub layout: Layout,
|
|
||||||
pub elements: Vec<(usize, Arc<TypeLayout>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct LayoutCache<'l> {
|
|
||||||
phantom: PhantomData<&'l ()>,
|
|
||||||
layouts: HashMap<*const u8, Arc<dyn Any>, FxBuildHasher>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GetLayout<T> {
|
|
||||||
type LayoutT;
|
|
||||||
fn get_layout(&self, value: T) -> Arc<Self::LayoutT>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'l> GetLayout<Type<'_>> for LayoutCache<'l> {
|
|
||||||
type LayoutT = TypeLayout;
|
|
||||||
|
|
||||||
fn get_layout(&self, ty: Type<'_>) -> Arc<Self::LayoutT> {
|
|
||||||
match ty {
|
|
||||||
Type::Void => {
|
|
||||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
|
||||||
LAYOUT
|
|
||||||
.get_or_init(|| {
|
|
||||||
Arc::new(TypeLayout {
|
|
||||||
layout: Layout::new::<()>(),
|
|
||||||
elements: Vec::new(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Bool => {
|
|
||||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
|
||||||
LAYOUT
|
|
||||||
.get_or_init(|| {
|
|
||||||
Arc::new(TypeLayout {
|
|
||||||
layout: Layout::new::<bool>(),
|
|
||||||
elements: Vec::new(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Int(IntT {
|
|
||||||
signed: false,
|
|
||||||
precision: 32,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
|
||||||
LAYOUT
|
|
||||||
.get_or_init(|| {
|
|
||||||
Arc::new(TypeLayout {
|
|
||||||
layout: Layout::new::<u32>(),
|
|
||||||
elements: Vec::new(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Ptr(PtrT { .. }) => {
|
|
||||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
|
||||||
LAYOUT
|
|
||||||
.get_or_init(|| {
|
|
||||||
Arc::new(TypeLayout {
|
|
||||||
layout: Layout::new::<*const u8>(),
|
|
||||||
elements: Vec::new(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => todo!("Unsupported type {ty}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod interpreter;
|
|
||||||
pub mod instruction_cache;
|
|
||||||
pub mod layout_cache;
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
use leaf_allocators::SyncArenaAllocator;
|
|
||||||
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
|
|
||||||
use leaf_compiler::CompilationContext;
|
|
||||||
use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction};
|
|
||||||
use leaf_parser::SourceCode;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let allocator = SyncArenaAllocator::default();
|
|
||||||
let context = CompilationContext::new(&allocator);
|
|
||||||
let ident = AssemblyIdentifier {
|
|
||||||
version: Version::default(),
|
|
||||||
name: "interpreter.il".into(),
|
|
||||||
};
|
|
||||||
context
|
|
||||||
.extend(
|
|
||||||
ident.clone(),
|
|
||||||
&[Arc::new(SourceCode {
|
|
||||||
text: std::fs::read_to_string("../test.leaf").unwrap().into(),
|
|
||||||
file: PathBuf::from("../test.leaf"),
|
|
||||||
})],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut interpreter = Interpreter::new(&context);
|
|
||||||
let assembly = context.get_assembly(&ident).unwrap();
|
|
||||||
|
|
||||||
let puts = assembly
|
|
||||||
.find_function(|f| f.name.get() == Some(&"puts"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let print_u32 = assembly
|
|
||||||
.find_function(|f| f.name.get() == Some(&"print_u32"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let main = assembly
|
|
||||||
.find_function(|f| f.name.get() == Some(&"main"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
interpreter.register_function(
|
|
||||||
puts,
|
|
||||||
NativeFunction::Plain(|int, args| match args {
|
|
||||||
[range] => unsafe {
|
|
||||||
let mem = &int.stack()[range.clone()];
|
|
||||||
let ptr = usize::from_ne_bytes(mem.try_into().unwrap());
|
|
||||||
let cstr = CStr::from_ptr(ptr as *const i8);
|
|
||||||
print!("{cstr:?}");
|
|
||||||
Ok(AnyValue::Void)
|
|
||||||
},
|
|
||||||
_ => Err(Error::InvalidParameterCount),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
interpreter.register_function(
|
|
||||||
print_u32,
|
|
||||||
NativeFunction::Plain(|int, args| match args {
|
|
||||||
[range] => {
|
|
||||||
let mem = &int.stack()[range.clone()];
|
|
||||||
let val = u32::from_ne_bytes(mem.try_into().unwrap());
|
|
||||||
println!("{val}");
|
|
||||||
Ok(AnyValue::Void)
|
|
||||||
}
|
|
||||||
_ => Err(Error::InvalidParameterCount),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
let now = Instant::now();
|
|
||||||
let result = interpreter.run(main, vec![]);
|
|
||||||
let elapsed = now.elapsed();
|
|
||||||
println!("Ret Value: {result:?}");
|
|
||||||
println!("Cold Time: {:?}", elapsed)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const LOOPS: u32 = 1;
|
|
||||||
let mut duration = Duration::default();
|
|
||||||
for _ in 0..LOOPS {
|
|
||||||
let now = Instant::now();
|
|
||||||
let _ = interpreter.run(main, vec![]);
|
|
||||||
duration += now.elapsed();
|
|
||||||
}
|
|
||||||
println!("Warm Time: {:?}", duration / LOOPS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-1
@@ -55,7 +55,8 @@ pub enum Expr {
|
|||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn range(&self) -> Range<usize> {
|
pub fn range(&self) -> Range<usize> {
|
||||||
match self {
|
match self {
|
||||||
_ => todo!(),
|
Self::Ident(e) => e.range(),
|
||||||
|
_ => todo!("{self:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,12 +83,15 @@ pub enum BinaryOp {
|
|||||||
#[debug("{_0}")] Div(Substr),
|
#[debug("{_0}")] Div(Substr),
|
||||||
#[debug("{_0}")] Mod(Substr),
|
#[debug("{_0}")] Mod(Substr),
|
||||||
#[debug("{_0}")] Dot(Substr),
|
#[debug("{_0}")] Dot(Substr),
|
||||||
|
#[debug("{_0}")] Eq(Substr),
|
||||||
|
#[debug("{_0}")] Ne(Substr),
|
||||||
#[debug("{_0}")] Lt(Substr),
|
#[debug("{_0}")] Lt(Substr),
|
||||||
#[debug("{_0}")] Gt(Substr),
|
#[debug("{_0}")] Gt(Substr),
|
||||||
#[debug("{_0}")] Le(Substr),
|
#[debug("{_0}")] Le(Substr),
|
||||||
#[debug("{_0}")] Ge(Substr),
|
#[debug("{_0}")] Ge(Substr),
|
||||||
#[debug("{_0}")] Range(Substr),
|
#[debug("{_0}")] Range(Substr),
|
||||||
#[debug("{_0}")] Assign(Substr),
|
#[debug("{_0}")] Assign(Substr),
|
||||||
|
#[debug("{_0}")] Cast(Substr),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ peg::parser! {
|
|||||||
// ### EXPRESSIONS ####
|
// ### EXPRESSIONS ####
|
||||||
|
|
||||||
rule expr() -> Expr = precedence! {
|
rule expr() -> Expr = precedence! {
|
||||||
lhs:(@) __ op:$("=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs })) }
|
lhs:(@) __ op:$("as") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Cast(op), rhs })) }
|
||||||
|
--
|
||||||
|
lhs:@ __ op:$("=") __ rhs:expr() { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs })) }
|
||||||
lhs:(@) __ op:$(".") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Dot(op), rhs })) }
|
lhs:(@) __ op:$(".") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Dot(op), rhs })) }
|
||||||
lhs:(@) "(" __ args:(expr() ** ("," __)) __ ")" { Expr::Call { func: Box::new(lhs), args } }
|
lhs:(@) "(" __ args:(expr() ** ("," __)) __ ")" { Expr::Call { func: Box::new(lhs), args } }
|
||||||
value:(@) "[" __ index:expr() __ "]" { Expr::Index(Box::new(IndexingExpr { value, index })) }
|
value:(@) "[" __ index:expr() __ "]" { Expr::Index(Box::new(IndexingExpr { value, index })) }
|
||||||
@@ -84,6 +86,8 @@ peg::parser! {
|
|||||||
--
|
--
|
||||||
lhs:(@) __ op:$("..") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) }
|
lhs:(@) __ op:$("..") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) }
|
||||||
--
|
--
|
||||||
|
lhs:(@) __ op:$("==") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Eq(op), rhs })) }
|
||||||
|
lhs:(@) __ op:$("!=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Ne(op), rhs })) }
|
||||||
lhs:(@) __ op:$("<") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs })) }
|
lhs:(@) __ op:$("<") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs })) }
|
||||||
lhs:(@) __ op:$(">") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs })) }
|
lhs:(@) __ op:$(">") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs })) }
|
||||||
lhs:(@) __ op:$("<=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs })) }
|
lhs:(@) __ op:$("<=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs })) }
|
||||||
@@ -96,7 +100,7 @@ peg::parser! {
|
|||||||
var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) }
|
var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) }
|
||||||
const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) }
|
const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) }
|
||||||
"(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) }
|
"(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) }
|
||||||
"[" __ list:(expr() **<0,> ("," __)) __ "]" { Expr::List(list) }
|
"[" __ list:(expr() ** ("," __)) __ "]" { Expr::List(list) }
|
||||||
"(" __ v:expr() __ ")" { v }
|
"(" __ v:expr() __ ")" { v }
|
||||||
"*" __ m:"mut"? __ v:expr() { Expr::Type(Box::new(Type::Ptr { base:v, mutable: m.is_some() })) }
|
"*" __ m:"mut"? __ v:expr() { Expr::Type(Box::new(Type::Ptr { base:v, mutable: m.is_some() })) }
|
||||||
v:string() { Expr::String(v) }
|
v:string() { Expr::String(v) }
|
||||||
|
|||||||
Reference in New Issue
Block a user