Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

135 changed files with 3047 additions and 4621 deletions

454
Cargo.lock generated
View File

@ -12,7 +12,7 @@ dependencies = [
"getrandom 0.2.15",
"once_cell",
"version_check",
"zerocopy 0.7.35",
"zerocopy",
]
[[package]]
@ -106,9 +106,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.9.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "block-buffer"
@ -127,9 +127,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.18"
version = "1.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
dependencies = [
"shlex",
]
@ -142,9 +142,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.35"
version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
dependencies = [
"clap_builder",
"clap_derive",
@ -152,9 +152,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.35"
version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
dependencies = [
"anstream",
"anstyle",
@ -164,14 +164,14 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.32"
version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
dependencies = [
"heck",
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
@ -188,9 +188,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "console"
version = "0.15.11"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
dependencies = [
"encode_unicode",
"libc",
@ -222,9 +222,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
dependencies = [
"crossbeam-utils",
]
@ -285,15 +285,15 @@ dependencies = [
[[package]]
name = "dissimilar"
version = "1.0.10"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
[[package]]
name = "either"
version = "1.15.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "ena"
@ -312,15 +312,15 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
version = "1.0.2"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.11"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
@ -340,24 +340,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.1.5"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "function_name"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1ab577a896d09940b5fe12ec5ae71f9d8211fff62c919c03a3750a9901e98a7"
dependencies = [
"function_name-proc-macro",
]
[[package]]
name = "function_name-proc-macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673464e1e314dd67a0fd9544abc99e8eb28d0c7e3b69b033bcff9b2d00b87333"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]]
name = "fxhash"
@ -400,14 +385,14 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.3.2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
@ -416,6 +401,12 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.15.2"
@ -425,6 +416,12 @@ dependencies = [
"foldhash",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
@ -442,24 +439,35 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.9.0"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.15.2",
]
[[package]]
name = "indoc"
version = "2.0.6"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "inkwell"
version = "0.5.0"
source = "git+https://github.com/Derppening/inkwell?tag=0.5.0_llvm15-typed-ptr#9c70145c6aaa29be79715fc4998df9da67575ecf"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40fb405537710d51f6bdbc8471365ddd4cd6d3a3c3ad6e0c8291691031ba94b2"
dependencies = [
"either",
"inkwell_internals",
@ -472,23 +480,25 @@ dependencies = [
[[package]]
name = "inkwell_internals"
version = "0.10.0"
source = "git+https://github.com/Derppening/inkwell?tag=0.5.0_llvm15-typed-ptr#9c70145c6aaa29be79715fc4998df9da67575ecf"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd28cfd4cfba665d47d31c08a6ba637eed16770abca2eccbbc3ca831fef1e44"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "insta"
version = "1.42.2"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
checksum = "da408b722765c64aad796c666b756aa1dda2a6c1b44f98797f2d8ea8f197746f"
dependencies = [
"console",
"linked-hash-map",
"once_cell",
"pin-project",
"serde",
"serde_json",
"serde_yaml",
"similar",
]
@ -509,9 +519,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.15"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "keccak"
@ -562,9 +572,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
@ -584,15 +594,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "llvm-sys"
version = "160.2.1"
version = "140.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e73861901245d32e1c3d8b35b639cf100859b4cd0c9da56fe0273040acbb3ea4"
checksum = "e3dc78e9857c0231ec11e3bdccf63870493fdc7d0570b0ea7d50bf5df0cb1a0c"
dependencies = [
"cc",
"lazy_static",
@ -613,9 +623,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.27"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
@ -636,7 +646,6 @@ dependencies = [
name = "nac3artiq"
version = "0.1.0"
dependencies = [
"indexmap",
"itertools",
"nac3core",
"nac3ld",
@ -659,8 +668,7 @@ name = "nac3core"
version = "0.1.0"
dependencies = [
"crossbeam",
"function_name",
"indexmap",
"indexmap 2.7.1",
"indoc",
"inkwell",
"insta",
@ -682,7 +690,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
"trybuild",
]
@ -726,9 +734,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "once_cell"
version = "1.21.3"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "parking_lot"
@ -760,7 +768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
"indexmap",
"indexmap 2.7.1",
]
[[package]]
@ -770,7 +778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
"phf_shared 0.11.3",
]
[[package]]
@ -780,7 +788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
dependencies = [
"phf_generator",
"phf_shared",
"phf_shared 0.11.3",
]
[[package]]
@ -789,7 +797,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"phf_shared 0.11.3",
"rand",
]
@ -800,10 +808,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"phf_shared 0.11.3",
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher 0.3.11",
]
[[package]]
@ -812,7 +829,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
"siphasher 1.0.1",
]
[[package]]
@ -821,39 +838,19 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "portable-atomic"
version = "1.11.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy 0.8.24",
"zerocopy",
]
[[package]]
@ -888,24 +885,24 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pyo3"
version = "0.24.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17da310086b068fbdcefbba30aeb3721d5bb9af8db4987d6735b2183ca567229"
checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
dependencies = [
"cfg-if",
"indoc",
"libc",
"memoffset",
"once_cell",
"parking_lot",
"portable-atomic",
"pyo3-build-config",
"pyo3-ffi",
@ -915,9 +912,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.24.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27165889bd793000a098bb966adc4300c312497ea25cf7a690a9f0ac5aa5fc1"
checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
dependencies = [
"once_cell",
"target-lexicon",
@ -925,9 +922,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
version = "0.24.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05280526e1dbf6b420062f3ef228b78c0c54ba94e157f5cb724a609d0f2faabc"
checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
dependencies = [
"libc",
"pyo3-build-config",
@ -935,44 +932,38 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.24.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c3ce5686aa4d3f63359a5100c62a127c9f15e8398e5fdeb5deef1fed5cd5f44"
checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "pyo3-macros-backend"
version = "0.24.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4cf6faa0cbfb0ed08e89beb8103ae9724eb4750e3a78084ba4017cbe94f3855"
checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"pyo3-build-config",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.8.5"
@ -1005,9 +996,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.11"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags",
]
@ -1050,9 +1041,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.5"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
@ -1063,15 +1054,15 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.20"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "ryu"
version = "1.0.20"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
[[package]]
name = "same-file"
@ -1090,35 +1081,35 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.26"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "serde_json"
version = "1.0.140"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
dependencies = [
"itoa",
"memchr",
@ -1135,6 +1126,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap 1.9.3",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "sha3"
version = "0.10.8"
@ -1157,6 +1160,12 @@ version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "siphasher"
version = "1.0.1"
@ -1165,29 +1174,30 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "smallvec"
version = "1.15.0"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "string-interner"
version = "0.19.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0"
checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b"
dependencies = [
"hashbrown",
"hashbrown 0.15.2",
"serde",
]
[[package]]
name = "string_cache"
version = "0.8.9"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot",
"phf_shared",
"phf_shared 0.10.0",
"precomputed-hash",
]
@ -1199,21 +1209,21 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.1"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
[[package]]
name = "strum_macros"
version = "0.27.1"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck",
"heck 0.5.0",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
@ -1223,14 +1233,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.100"
version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
@ -1239,24 +1250,25 @@ dependencies = [
[[package]]
name = "target-lexicon"
version = "0.13.2"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "target-triple"
version = "0.1.4"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078"
[[package]]
name = "tempfile"
version = "3.19.1"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
dependencies = [
"cfg-if",
"fastrand",
"getrandom 0.3.2",
"getrandom 0.3.1",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@ -1283,35 +1295,15 @@ dependencies = [
[[package]]
name = "test-case"
version = "3.3.1"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
checksum = "e9e5f048404b43e8ae66dce036163515b6057024cf58c6377be501f250bd3c6a"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
"test-case-core",
"syn 1.0.109",
"version_check",
]
[[package]]
@ -1331,14 +1323,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]
[[package]]
name = "toml"
version = "0.8.20"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [
"serde",
"serde_spanned",
@ -1357,11 +1349,11 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.24"
version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap",
"indexmap 2.7.1",
"serde",
"serde_spanned",
"toml_datetime",
@ -1370,9 +1362,9 @@ dependencies = [
[[package]]
name = "trybuild"
version = "1.0.104"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ae08be68c056db96f0e6c6dd820727cca756ced9e1f4cc7fdd20e2a55e23898"
checksum = "b812699e0c4f813b872b373a4471717d9eb550da14b311058a4d9cf4173cbca6"
dependencies = [
"dissimilar",
"glob",
@ -1386,9 +1378,9 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.18.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unic-char-property"
@ -1444,9 +1436,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.18"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "unicode-width"
@ -1484,9 +1476,9 @@ dependencies = [
[[package]]
name = "unindent"
version = "0.2.4"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
[[package]]
name = "utf8parse"
@ -1518,9 +1510,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
@ -1618,38 +1610,39 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.6"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive 0.8.24",
"byteorder",
"zerocopy-derive",
]
[[package]]
@ -1660,16 +1653,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
"syn 2.0.96",
]

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1744098102,
"narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=",
"lastModified": 1738680400,
"narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7",
"rev": "799ba5bffed04ced7067a91798353d360788b30d",
"type": "github"
},
"original": {

View File

@ -13,8 +13,8 @@
llvm-tools-irrt = pkgs.runCommandNoCC "llvm-tools-irrt" {}
''
mkdir -p $out/bin
ln -s ${pkgs.llvmPackages_16.clang-unwrapped}/bin/clang $out/bin/clang-irrt
ln -s ${pkgs.llvmPackages_16.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
ln -s ${pkgs.llvmPackages_14.clang-unwrapped}/bin/clang $out/bin/clang-irrt
ln -s ${pkgs.llvmPackages_14.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
'';
demo-linalg-stub = pkgs.rustPlatform.buildRustPackage {
name = "demo-linalg-stub";
@ -39,12 +39,9 @@
src = self;
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"inkwell-0.5.0" = "sha256-Qsqy/fhD/qK0rKBeXetE99vW9G9XAePxlXv0An3Yeuo=";
};
};
passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_16.clang) llvm-tools-irrt pkgs.llvmPackages_16.llvm.out pkgs.llvmPackages_16.bintools llvm-nac3 ];
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out pkgs.llvmPackages_14.bintools llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
checkPhase =
@ -80,7 +77,7 @@
# LLVM PGO support
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_16.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
};
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
@ -88,13 +85,13 @@
name = "nac3artiq-instrumented";
src = self;
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-instrumented ];
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_14.bintools llvm-nac3-instrumented ];
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ];
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
doCheck = false;
configurePhase =
''
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_16.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_14.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
'';
installPhase =
''
@ -116,14 +113,14 @@
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "artiq";
rev = "554b0749ca5985bf4d006c4f29a05e83de0a226d";
sha256 = "sha256-3eSNHTSlmdzLMcEMIspxqjmjrcQe4aIGqIfRgquUg18=";
rev = "28c9de3e251daa89a8c9fd79d5ab64a3ec03bac6";
sha256 = "sha256-vAvpbHc5B+1wtG8zqN7j9dQE1ON+i22v+uqA+tw6Gak=";
})
];
buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ]))
pkgs.llvmPackages_16.llvm.out
pkgs.llvmPackages_16.bintools
pkgs.llvmPackages_14.llvm.out
pkgs.llvmPackages_14.bintools
];
phases = [ "buildPhase" "installPhase" ];
buildPhase =
@ -143,7 +140,7 @@
'';
};
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_16.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
};
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
@ -151,7 +148,7 @@
name = "nac3artiq-pgo";
src = self;
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-pgo ];
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_14.bintools llvm-nac3-pgo ];
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
cargoBuildFlags = [ "--package" "nac3artiq" ];
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
@ -172,12 +169,12 @@
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
(pkgs.wrapClangMulti llvmPackages_16.clang) llvmPackages_16.llvm.out llvmPackages_16.bintools # for running nac3standalone demos
(pkgs.wrapClangMulti llvmPackages_14.clang) llvmPackages_14.llvm.out llvmPackages_14.bintools # for running nac3standalone demos
packages.x86_64-linux.llvm-tools-irrt
cargo
rustc
# runtime dependencies
lld_16 # for running kernels on the host
lld_14 # for running kernels on the host
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
# development tools
cargo-insta

View File

@ -2,18 +2,17 @@
name = "nac3artiq"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2024"
edition = "2021"
[lib]
name = "nac3artiq"
crate-type = ["cdylib"]
[dependencies]
indexmap = "2.8"
itertools = "0.14"
pyo3 = { version = "0.24", features = ["extension-module"] }
pyo3 = { version = "0.21", features = ["extension-module", "gil-refs"] }
parking_lot = "0.12"
tempfile = "3.19"
tempfile = "3.16"
nac3core = { path = "../nac3core" }
nac3ld = { path = "../nac3ld" }

View File

@ -1,84 +0,0 @@
from numpy import int32
from min_artiq import *
@nac3
class PassContextManager:
@kernel
def __init__(self):
pass
@kernel
def __enter__(self):
pass
@kernel
def __exit__(self):
pass
@nac3
class CustomDataContextManager:
a: Kernel[int32]
b: Kernel[int32]
@kernel
def __init__(self, a: int32, b: int32):
self.a = a
self.b = b
@kernel
def __enter__(self):
self.a += 1
print_int32(self.a)
@kernel
def __exit__(self):
self.b += 1
print_int32(self.b)
@nac3
class WithAsContextManager:
a: Kernel[int32]
@kernel
def __init__(self, val: int32):
self.a = val
@kernel
def __enter__(self) -> int32:
print_int32(self.a)
return self.a
@kernel
def __exit__(self):
print_int32(self.a)
@nac3
class CtxMgrTest:
core: KernelInvariant[Core]
def __init__(self):
self.core = Core()
@kernel
def run(self):
x = 0
a = PassContextManager()
with a:
x += 1
h = CustomDataContextManager(1, 2)
with h:
x += h.a + h.b
with WithAsContextManager(15) as num:
x += num
with WithAsContextManager(10) as c, WithAsContextManager(5) as d:
e: int32 = c + d
x += e
print_int32(x)
return
if __name__ == "__main__":
CtxMgrTest().run()

View File

@ -1,27 +0,0 @@
from min_artiq import kernel, KernelInvariant, nac3
import min_artiq as artiq
@nac3
class Demo:
core: KernelInvariant[artiq.Core]
led0: KernelInvariant[artiq.TTLOut]
led1: KernelInvariant[artiq.TTLOut]
def __init__(self):
self.core = artiq.Core()
self.led0 = artiq.TTLOut(self.core, 18)
self.led1 = artiq.TTLOut(self.core, 19)
@kernel
def run(self):
self.core.reset()
while True:
with artiq.parallel:
self.led0.pulse(100.*artiq.ms)
self.led1.pulse(100.*artiq.ms)
self.core.delay(100.*artiq.ms)
if __name__ == "__main__":
Demo().run()

View File

@ -1,9 +1,9 @@
from inspect import getfullargspec
from functools import wraps
from types import SimpleNamespace
from numpy import int32, int64
from typing import Generic, TypeVar
from math import floor, ceil
from numpy import int32, int64, uint32, uint64, float64, bool_, str_, ndarray
from types import GenericAlias, ModuleType, SimpleNamespace
from typing import _GenericAlias, Generic, TypeVar
import nac3artiq
@ -16,7 +16,7 @@ __all__ = [
"rpc", "ms", "us", "ns",
"print_int32", "print_int64",
"Core", "TTLOut",
"parallel", "legacy_parallel", "sequential"
"parallel", "sequential"
]
@ -40,10 +40,10 @@ class Option(Generic[T]):
def is_none(self):
return self._nac3_option is None
def is_some(self):
return not self.is_none()
def unwrap(self):
if self.is_none():
raise UnwrapNoneError()
@ -54,7 +54,7 @@ class Option(Generic[T]):
return "none"
else:
return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str:
if self.is_none():
return "none"
@ -85,46 +85,13 @@ def ceil64(x):
import device_db
core_arguments = device_db.device_db["core"]["arguments"]
builtins = {
"int": int,
"float": float,
"bool": bool,
"str": str,
"list": list,
"tuple": tuple,
"Exception": Exception,
"types": {
"GenericAlias": GenericAlias,
"ModuleType": ModuleType,
},
"typing": {
"_GenericAlias": _GenericAlias,
"TypeVar": TypeVar,
},
"numpy": {
"int32": int32,
"int64": int64,
"uint32": uint32,
"uint64": uint64,
"float64": float64,
"bool_": bool_,
"str_": str_,
"ndarray": ndarray,
},
"artiq": {
"Kernel": Kernel,
"KernelInvariant": KernelInvariant,
"_ConstGenericMarker": _ConstGenericMarker,
"none": none,
"virtual": virtual,
"Option": Option,
},
artiq_builtins = {
"none": none,
"virtual": virtual,
"_ConstGenericMarker": _ConstGenericMarker,
"Option": Option,
}
compiler = nac3artiq.NAC3(core_arguments["target"], builtins)
compiler = nac3artiq.NAC3(core_arguments["target"], artiq_builtins)
allow_registration = True
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
registered_functions = set()
@ -185,9 +152,9 @@ def nac3(cls):
return cls
ms: KernelInvariant[float] = 1e-3
us: KernelInvariant[float] = 1e-6
ns: KernelInvariant[float] = 1e-9
ms = 1e-3
us = 1e-6
ns = 1e-9
@extern
def rtio_init():
@ -278,7 +245,7 @@ class Core:
embedding = EmbeddingMap()
if allow_registration:
compiler.analyze(registered_functions, registered_classes, special_ids, set())
compiler.analyze(registered_functions, registered_classes, set())
allow_registration = False
if hasattr(method, "__self__"):
@ -368,12 +335,5 @@ class UnwrapNoneError(Exception):
"""raised when unwrapping a none value"""
artiq_builtin = True
parallel: KernelInvariant[KernelContextManager] = KernelContextManager()
legacy_parallel: KernelInvariant[KernelContextManager] = KernelContextManager()
sequential: KernelInvariant[KernelContextManager] = KernelContextManager()
special_ids = {
"parallel": id(parallel),
"legacy_parallel": id(legacy_parallel),
"sequential": id(sequential),
}
parallel = KernelContextManager()
sequential = KernelContextManager()

View File

@ -1,5 +1,5 @@
use std::{
collections::{HashMap, hash_map::DefaultHasher},
collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
iter::once,
mem,
@ -8,47 +8,42 @@ use std::{
use itertools::Itertools;
use pyo3::{
PyObject, PyResult, Python,
prelude::*,
types::{PyDict, PyList},
PyObject, PyResult, Python,
};
use super::{symbol_resolver::InnerResolver, timeline::TimeFns};
use nac3core::{
codegen::{
CodeGenContext, CodeGenerator,
expr::{create_fn_and_call, destructure_range, gen_call, infer_and_call_function},
expr::{destructure_range, gen_call},
llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave},
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
type_aligned_alloca,
types::{RangeType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, RangeType},
values::{
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
UntypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
},
inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
context::Context,
module::Linkage,
targets::TargetMachine,
types::{BasicType, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace, IntPredicate, OptimizationLevel,
},
nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef},
symbol_resolver::ValueEnum,
toplevel::{
DefinitionId, GenCall,
helper::{PrimDef, extract_ndims},
helper::{extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys,
DefinitionId, GenCall,
},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, FuncArg, Type, TypeEnum, VarMap, iter_type_vars},
},
typecheck::typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
};
use super::{SpecialPythonId, symbol_resolver::InnerResolver, timeline::TimeFns};
/// The parallelism mode within a block.
#[derive(Copy, Clone, Eq, PartialEq)]
enum ParallelMode {
@ -88,9 +83,6 @@ pub struct ArtiqCodeGenerator<'a> {
/// The current parallel context refers to the nearest `with parallel` or `with legacy_parallel`
/// statement, which is used to determine when and how the timeline should be updated.
parallel_mode: ParallelMode,
/// Specially treated python IDs to identify `with parallel` and `with sequential` blocks.
special_ids: SpecialPythonId,
}
impl<'a> ArtiqCodeGenerator<'a> {
@ -98,7 +90,6 @@ impl<'a> ArtiqCodeGenerator<'a> {
name: String,
size_t: IntType<'_>,
timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> {
assert!(matches!(size_t.get_bit_width(), 32 | 64));
ArtiqCodeGenerator {
@ -109,7 +100,6 @@ impl<'a> ArtiqCodeGenerator<'a> {
end: None,
timeline,
parallel_mode: ParallelMode::None,
special_ids,
}
}
@ -119,10 +109,9 @@ impl<'a> ArtiqCodeGenerator<'a> {
ctx: &Context,
target_machine: &TargetMachine,
timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> {
let llvm_usize = ctx.ptr_sized_int_type(&target_machine.get_target_data(), None);
Self::new(name, llvm_usize, timeline, special_ids)
Self::new(name, llvm_usize, timeline)
}
/// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
@ -191,7 +180,11 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
}
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
if self.size_t == 32 { ctx.i32_type() } else { ctx.i64_type() }
if self.size_t == 32 {
ctx.i32_type()
} else {
ctx.i64_type()
}
}
fn gen_block<'ctx, 'a, 'c, I: Iterator<Item = &'c Stmt<Option<Type>>>>(
@ -264,140 +257,122 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
// - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
let resolver = ctx.resolver.clone();
if let Some(static_value) =
if let Some((_ptr, static_value, _counter)) = ctx.var_assignment.get(id) {
static_value.clone()
} else if let Some(ValueEnum::Static(val)) =
resolver.get_symbol_value(*id, ctx, self)
{
Some(val)
} else {
None
}
{
let python_id = static_value.get_unique_identifier();
if python_id == self.special_ids.parallel
|| python_id == self.special_ids.legacy_parallel
{
let old_start = self.start.take();
let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
if id == &"parallel".into() || id == &"legacy_parallel".into() {
let old_start = self.start.take();
let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let start = self
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
.unwrap();
ctx.builder.build_store(start, now).unwrap();
Ok(Some(start_expr)) as Result<_, String>
},
|v| Ok(Some(v)),
)?;
let end = format!("with-{}-end", self.name_counter).into();
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: end, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let end = self.gen_store_target(ctx, &end_expr, Some("end.addr"))?.unwrap();
ctx.builder.build_store(end, now).unwrap();
self.end = Some(end_expr);
self.name_counter += 1;
self.parallel_mode = if python_id == self.special_ids.parallel {
ParallelMode::Deep
} else if python_id == self.special_ids.legacy_parallel {
ParallelMode::Legacy
} else {
unreachable!()
};
self.gen_block(ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
end_expr.custom.unwrap(),
)?;
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let start = self
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
.unwrap();
ctx.builder.build_store(start, now).unwrap();
Ok(Some(start_expr)) as Result<_, String>
},
|v| Ok(Some(v)),
)?;
let end = format!("with-{}-end", self.name_counter).into();
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: end, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let end = self.gen_store_target(ctx, &end_expr, Some("end.addr"))?.unwrap();
ctx.builder.build_store(end, now).unwrap();
self.end = Some(end_expr);
self.name_counter += 1;
self.parallel_mode = match id.to_string().as_str() {
"parallel" => ParallelMode::Deep,
"legacy_parallel" => ParallelMode::Legacy,
_ => unreachable!(),
};
// inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.gen_block(ctx, body.iter())?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
let current = ctx.builder.get_insert_block().unwrap();
if reset_position {
ctx.builder.position_at_end(current);
}
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
return Ok(());
} else if python_id == self.special_ids.sequential {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
ctx,
self,
end_expr.custom.unwrap(),
)?;
// Reset the timeline when we are exiting the sequential block
// Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// Reset the timeline when we are exiting the sequential block
// Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
}
}
}
@ -414,7 +389,12 @@ fn gen_rpc_tag(
) -> Result<(), String> {
use nac3core::typecheck::typedef::TypeEnum::*;
let PrimitiveStore { int32, int64, float, bool, str, none, .. } = ctx.primitives;
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let bool = ctx.primitives.bool;
let str = ctx.primitives.str;
let none = ctx.primitives.none;
if ctx.unifier.unioned(ty, int32) {
buffer.push(b'i');
@ -450,10 +430,7 @@ fn gen_rpc_tag(
&*ctx.unifier.get_ty_immutable(ndarray_ndims)
{
if values.len() != 1 {
return Err(format!(
"NDArray types with multiple literal bounds for ndims is not supported: {}",
ctx.unifier.stringify(ty)
));
return Err(format!("NDArray types with multiple literal bounds for ndims is not supported: {}", ctx.unifier.stringify(ty)));
}
let value = values[0].clone();
@ -493,6 +470,7 @@ fn format_rpc_arg<'ctx>(
// NAC3: NDArray = { usize, usize*, T* }
// libproto_artiq: NDArray = [data[..], dim_sz[..]]
let llvm_i1 = ctx.ctx.bool_type();
let llvm_usize = ctx.get_size_type();
let (elem_ty, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, arg_ty);
@ -510,11 +488,11 @@ fn format_rpc_arg<'ctx>(
let sizeof_usize = llvm_usize.size_of();
let sizeof_usize =
ctx.builder.build_int_truncate_or_bit_cast(sizeof_usize, llvm_usize, "").unwrap();
ctx.builder.build_int_z_extend_or_bit_cast(sizeof_usize, llvm_usize, "").unwrap();
let sizeof_pdata = dtype.ptr_type(AddressSpace::default()).size_of();
let sizeof_pdata =
ctx.builder.build_int_truncate_or_bit_cast(sizeof_pdata, llvm_usize, "").unwrap();
ctx.builder.build_int_z_extend_or_bit_cast(sizeof_pdata, llvm_usize, "").unwrap();
let sizeof_buf_shape = ctx.builder.build_int_mul(sizeof_usize, ndims, "").unwrap();
let sizeof_buf = ctx.builder.build_int_add(sizeof_buf_shape, sizeof_pdata, "").unwrap();
@ -529,13 +507,13 @@ fn format_rpc_arg<'ctx>(
// Write to `buf->data`
let carray_data = carray.load_data(ctx);
let carray_data = ctx.builder.build_pointer_cast(carray_data, llvm_pi8, "").unwrap();
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata);
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata, llvm_i1.const_zero());
// Write to `buf->shape`
let carray_shape = ndarray.shape().base_ptr(ctx, generator);
let carray_shape_i8 =
ctx.builder.build_pointer_cast(carray_shape, llvm_pi8, "").unwrap();
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape);
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape, llvm_i1.const_zero());
buf.base_ptr(ctx, generator)
}
@ -835,7 +813,7 @@ fn rpc_codegen_callback_fn<'ctx>(
let ptr_type = int8.ptr_type(AddressSpace::default());
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1.0 as u64, false);
let service_id = int32.const_int(fun.1 .0 as u64, false);
// -- setup rpc tags
let mut tag = Vec::new();
if obj.is_some() {
@ -858,7 +836,7 @@ fn rpc_codegen_callback_fn<'ctx>(
let tag_arr_ptr = ctx.module.add_global(
int8.array_type(tag.len() as u32),
None,
format!("tagptr{}", fun.1.0).as_str(),
format!("tagptr{}", fun.1 .0).as_str(),
);
tag_arr_ptr.set_initializer(&int8.const_array(
&tag.iter().map(|v| int8.const_int(u64::from(*v), false)).collect::<Vec<_>>(),
@ -936,14 +914,47 @@ fn rpc_codegen_callback_fn<'ctx>(
}
// call
infer_and_call_function(
ctx,
if is_async { "rpc_send_async" } else { "rpc_send" },
None,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
Some("rpc.send"),
None,
);
if is_async {
let rpc_send_async = ctx.module.get_function("rpc_send_async").unwrap_or_else(|| {
ctx.module.add_function(
"rpc_send_async",
ctx.ctx.void_type().fn_type(
&[
int32.into(),
tag_ptr_type.ptr_type(AddressSpace::default()).into(),
ptr_type.ptr_type(AddressSpace::default()).into(),
],
false,
),
None,
)
});
ctx.builder
.build_call(
rpc_send_async,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
)
.unwrap();
} else {
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {
ctx.module.add_function(
"rpc_send",
ctx.ctx.void_type().fn_type(
&[
int32.into(),
tag_ptr_type.ptr_type(AddressSpace::default()).into(),
ptr_type.ptr_type(AddressSpace::default()).into(),
],
false,
),
None,
)
});
ctx.builder
.build_call(rpc_send, &[service_id.into(), tag_ptr.into(), args_ptr.into()], "rpc.send")
.unwrap();
}
// reclaim stack space used by arguments
call_stackrestore(ctx, stackptr);
@ -971,7 +982,7 @@ pub fn attributes_writeback<'ctx>(
return_obj: Option<(Type, ValueEnum<'ctx>)>,
) -> Result<(), String> {
Python::with_gil(|py| -> PyResult<Result<(), String>> {
let host_attributes = host_attributes.downcast_bound::<PyList>(py)?;
let host_attributes: &PyList = host_attributes.downcast(py)?;
let top_levels = ctx.top_level.definitions.read();
let globals = inner_resolver.global_value_ids.read();
let int32 = ctx.ctx.i32_type();
@ -984,7 +995,7 @@ pub fn attributes_writeback<'ctx>(
}
for val in (*globals).values() {
let val = val.bind(py);
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(
py,
val,
@ -1015,7 +1026,7 @@ pub fn attributes_writeback<'ctx>(
*field_ty,
ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
),
));
@ -1056,7 +1067,7 @@ pub fn attributes_writeback<'ctx>(
*field_ty,
ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
),
));
@ -1157,22 +1168,29 @@ fn polymorphic_print<'ctx>(
debug_assert!(!fmt.is_empty());
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
let llvm_i32 = ctx.ctx.i32_type();
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let fn_name = if as_rtio { "rtio_log" } else { "core_log" };
let print_fn = ctx.module.get_function(fn_name).unwrap_or_else(|| {
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let fn_t = if as_rtio {
let llvm_void = ctx.ctx.void_type();
llvm_void.fn_type(&[llvm_pi8.into()], true)
} else {
let llvm_i32 = ctx.ctx.i32_type();
llvm_i32.fn_type(&[llvm_pi8.into()], true)
};
ctx.module.add_function(fn_name, fn_t, None)
});
let fmt = ctx.gen_string(generator, fmt);
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
create_fn_and_call(
ctx,
if as_rtio { "rtio_log" } else { "core_log" },
if as_rtio { None } else { Some(llvm_i32.into()) },
&[llvm_pi8.into()],
&once(fmt.into()).chain(args).collect_vec(),
true,
None,
None,
);
ctx.builder
.build_call(
print_fn,
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
"",
)
.unwrap();
};
let llvm_i32 = ctx.ctx.i32_type();

View File

@ -1,273 +0,0 @@
use itertools::Itertools;
use nac3core::{toplevel::TopLevelDef, typecheck::typedef::Unifier};
use super::{InnerResolver, symbol_resolver::PyValueHandle};
impl InnerResolver {
pub fn debug_str(&self, tld: Option<&[TopLevelDef]>, unifier: &Option<&mut Unifier>) -> String {
fn fmt_elems(elems: &str) -> String {
if elems.is_empty() { String::new() } else { format!("\n{elems}\n\t") }
}
fn stringify_pyvalue_handle(handle: &PyValueHandle) -> String {
format!("(id: {}, value: {})", handle.0, handle.1)
}
fn stringify_tld(tld: &TopLevelDef) -> String {
match tld {
TopLevelDef::Module { name, .. } => {
format!("TopLevelDef::Module {{ name: {name} }}")
}
TopLevelDef::Class { name, .. } => {
format!("TopLevelDef::Class {{ name: {name} }}")
}
TopLevelDef::Function { name, .. } => {
format!("TopLevelDef::Function {{ name: {name} }}")
}
TopLevelDef::Variable { name, .. } => {
format!("TopLevelDef::Variable {{ name: {name} }}")
}
}
}
let mut str = String::new();
str.push_str("nac3artiq::InnerResolver {");
{
let id_to_type = self.id_to_type.read();
str.push_str(
format!(
"\n\tid_to_type: {{{}}},",
fmt_elems(
id_to_type
.iter()
.sorted_by_cached_key(|(k, _)| k.to_string())
.map(|(k, v)| {
let ty_str = unifier.as_ref().map_or_else(
|| format!("{v:?}"),
|unifier| unifier.stringify(*v),
);
format!("\t\t{k} -> {ty_str}")
})
.join(",\n")
.as_str()
),
)
.as_str(),
);
}
{
let id_to_def = self.id_to_def.read();
str.push_str(
format!(
"\n\tid_to_def: {{{}}},",
fmt_elems(
id_to_def
.iter()
.sorted_by_cached_key(|(k, _)| k.to_string())
.map(|(k, v)| {
let tld_str = tld.map_or_else(
|| format!("{v:?}"),
|tlds| stringify_tld(&tlds[v.0]),
);
format!("\t\t{k} -> {tld_str}")
})
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let id_to_pyval = self.id_to_pyval.read();
str.push_str(
format!(
"\n\tid_to_pyval: {{{}}},",
fmt_elems(
id_to_pyval
.iter()
.sorted_by_cached_key(|(k, _)| k.to_string())
.map(|(k, v)| { format!("\t\t{k} -> {}", stringify_pyvalue_handle(v)) })
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let id_to_primitive = self.id_to_primitive.read();
str.push_str(
format!(
"\n\tid_to_primitive: {{{}}},",
fmt_elems(
id_to_primitive
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| { format!("\t\t{k} -> {v:?}") })
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let field_to_val = self.field_to_val.read();
str.push_str(
format!(
"\n\tfield_to_val: {{{}}},",
fmt_elems(
field_to_val
.iter()
.sorted_by_key(|((id, _), _)| *id)
.map(|((id, name), pyval)| {
format!(
"\t\t({id}, {name}) -> {}",
pyval.as_ref().map_or_else(
|| String::from("None"),
|pyval| format!(
"Some({})",
stringify_pyvalue_handle(pyval)
)
)
)
})
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let global_value_ids = self.global_value_ids.read();
str.push_str(
format!(
"\n\tglobal_value_ids: {{{}}},",
fmt_elems(
global_value_ids
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| format!("\t\t{k} -> {v}"))
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let pyid_to_def = self.pyid_to_def.read();
str.push_str(
format!(
"\n\tpyid_to_def: {{{}}},",
fmt_elems(
pyid_to_def
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| {
let tld_str = tld.map_or_else(
|| format!("{v:?}"),
|tlds| stringify_tld(&tlds[v.0]),
);
format!("\t\t{k} -> {tld_str}")
})
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let pyid_to_type = self.pyid_to_type.read();
str.push_str(
format!(
"\n\tpyid_to_type: {{{}}},",
fmt_elems(
pyid_to_type
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| {
let ty_str = unifier.as_ref().map_or_else(
|| format!("{v:?}"),
|unifier| unifier.stringify(*v),
);
format!("\t\t{k} -> {ty_str}")
})
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let string_store = self.string_store.read();
str.push_str(
format!(
"\n\tstring_store: {{{}}},",
fmt_elems(
string_store
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| format!("\t\t{k} -> {v}"))
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
{
let exception_ids = self.exception_ids.read();
str.push_str(
format!(
"\n\texception_ids: {{{}}},",
fmt_elems(
exception_ids
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(k, v)| format!("\t\t{k} -> {v}"))
.join(",\n")
.as_str()
)
)
.as_str(),
);
}
let name_to_pyid = &self.name_to_pyid;
str.push_str(
format!(
"\n\tname_to_pyid: {{{}}},",
fmt_elems(
name_to_pyid
.iter()
.sorted_by_cached_key(|(k, _)| k.to_string())
.map(|(k, v)| format!("\t\t{k} -> {v}"))
.join(",\n")
.as_str()
)
)
.as_str(),
);
let module = &self.module;
str.push_str(format!("\n\tmodule: {module}").as_str());
str.push_str("\n}");
str
}
}

View File

@ -1,6 +1,7 @@
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
#![warn(clippy::pedantic)]
#![allow(
unsafe_op_in_unsafe_fn,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::enum_glob_use,
@ -10,21 +11,18 @@
)]
use std::{
cell::LazyCell,
collections::{HashMap, HashSet},
fs,
io::Write,
path::Path,
process::Command,
rc::Rc,
sync::Arc,
};
use indexmap::IndexMap;
use itertools::Itertools;
use parking_lot::{Mutex, RwLock};
use pyo3::{
IntoPyObjectExt, create_exception, exceptions,
create_exception, exceptions,
prelude::*,
types::{PyBytes, PyDict, PyNone, PySet},
};
@ -32,17 +30,17 @@ use tempfile::{self, TempDir};
use nac3core::{
codegen::{
CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall,
WorkerRegistry, concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt,
concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall, WorkerRegistry,
},
inkwell::{
OptimizationLevel,
context::Context,
memory_buffer::MemoryBuffer,
module::{FlagBehavior, Linkage, Module},
passes::PassBuilderOptions,
support::is_multithreaded,
targets::*,
OptimizationLevel,
},
nac3parser::{
ast::{self, Constant, ExprKind, Located, Stmt, StmtKind, StrRef},
@ -50,31 +48,27 @@ use nac3core::{
},
symbol_resolver::SymbolResolver,
toplevel::{
DefinitionId, GenCall, TopLevelDef,
builtins::get_exn_constructor,
composer::{BuiltinFuncCreator, BuiltinFuncSpec, ComposerConfig, TopLevelComposer},
DefinitionId, GenCall, TopLevelDef,
},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap, into_var_map},
typedef::{into_var_map, FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
},
};
use nac3ld::Linker;
use codegen::{
ArtiqCodeGenerator, attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback,
attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback, ArtiqCodeGenerator,
};
use symbol_resolver::{DeferredEvaluationStore, InnerResolver, PythonHelper, Resolver};
use timeline::TimeFns;
mod codegen;
mod debug;
mod symbol_resolver;
mod timeline;
const ENV_NAC3_EMIT_LLVM_BC: &str = "NAC3_EMIT_LLVM_BC";
const ENV_NAC3_EMIT_LLVM_LL: &str = "NAC3_EMIT_LLVM_LL";
#[derive(PartialEq, Clone, Copy)]
enum Isa {
Host,
@ -89,7 +83,7 @@ impl Isa {
match self {
Isa::Host => TargetMachine::get_default_triple(),
Isa::RiscV32G | Isa::RiscV32IMA => TargetTriple::create("riscv32-unknown-linux"),
Isa::CortexA9 => TargetTriple::create("armv7-unknown-linux-eabihf"),
Isa::CortexA9 => TargetTriple::create("armv7-unknown-linux-gnueabihf"),
}
}
@ -166,21 +160,9 @@ pub struct PrimitivePythonId {
virtual_id: u64,
option: u64,
module: u64,
kernel: u64,
kernel_invariant: u64,
}
#[derive(Clone, Default)]
pub struct SpecialPythonId {
parallel: u64,
legacy_parallel: u64,
sequential: u64,
}
/// An [`IndexMap`] storing the `id()` of values, mapped to a handle of the value itself.
type PyValueMap = IndexMap<u64, Arc<PyObject>>;
type TopLevelComponent = (Stmt, String, Arc<PyObject>);
type TopLevelComponent = (Stmt, String, PyObject);
// TopLevelComposer is unsendable as it holds the unification table, which is
// unsendable due to Rc. Arc would cause a performance hit.
@ -197,7 +179,6 @@ struct Nac3 {
string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore,
special_ids: SpecialPythonId,
/// LLVM-related options for code generation.
llvm_options: CodeGenLLVMOptions,
}
@ -207,17 +188,17 @@ create_exception!(nac3artiq, CompileError, exceptions::PyException);
impl Nac3 {
fn register_module(
&mut self,
module: &Arc<PyObject>,
module: &PyObject,
registered_class_ids: &HashSet<u64>,
) -> PyResult<()> {
let (module_name, source_file, source) =
Python::with_gil(|py| -> PyResult<(String, String, String)> {
let module = module.bind(py);
let module: &PyAny = module.extract(py)?;
let source_file = module.getattr("__file__");
let (source_file, source) = if let Ok(source_file) = source_file {
let source_file = source_file.extract::<&str>()?;
let source_file = source_file.extract()?;
(
source_file.to_string(),
source_file,
fs::read_to_string(source_file).map_err(|e| {
exceptions::PyIOError::new_err(format!(
"failed to read input file: {e}"
@ -227,26 +208,18 @@ impl Nac3 {
} else {
// kernels submitted by content have no file
// but still can provide source by StringLoader
let get_src_fn = module.getattr("__loader__")?.getattr("get_source")?;
(String::from("<expcontent>"), get_src_fn.call1((PyNone::get(py),))?.extract()?)
let get_src_fn = module
.getattr("__loader__")?
.extract::<PyObject>()?
.getattr(py, "get_source")?;
("<expcontent>", get_src_fn.call1(py, (PyNone::get(py),))?.extract(py)?)
};
Ok((module.getattr("__name__")?.extract()?, source_file, source))
Ok((module.getattr("__name__")?.extract()?, source_file.to_string(), source))
})?;
let parser_result = parse_program(&source, source_file.into())
.map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {e}")))?;
let id_fn = LazyCell::new(|| {
Python::with_gil(|py| {
PyModule::import(py, "builtins").unwrap().getattr("id").unwrap().unbind()
})
});
let get_type_hints_fn = LazyCell::new(|| {
Python::with_gil(|py| {
PyModule::import(py, "typing").unwrap().getattr("get_type_hints").unwrap().unbind()
})
});
for mut stmt in parser_result {
let include = match stmt.node {
StmtKind::ClassDef { ref decorator_list, ref mut body, ref mut bases, .. } => {
@ -263,15 +236,15 @@ impl Nac3 {
// Drop unregistered (i.e. host-only) base classes.
bases.retain(|base| {
Python::with_gil(|py| -> PyResult<bool> {
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
match &base.node {
ExprKind::Name { id, .. } => {
if *id == "Exception".into() {
Ok(true)
} else {
let base_obj =
module.bind(py).getattr(id.to_string().as_str())?;
let base_id =
id_fn.bind(py).call1((base_obj,))?.extract()?;
module.getattr(py, id.to_string().as_str())?;
let base_id = id_fn.call1((base_obj,))?.extract()?;
Ok(registered_class_ids.contains(&base_id))
}
}
@ -304,28 +277,10 @@ impl Nac3 {
}
})
}
// Allow global variable declaration with `Kernel` or `KernelInvariant` type annotation
StmtKind::AnnAssign { ref target, .. } => match &target.node {
ExprKind::Name { id, .. } => Python::with_gil(|py| {
let py_type_hints =
get_type_hints_fn.bind(py).call1((module.bind(py),)).unwrap();
let py_type_hints = py_type_hints.downcast::<PyDict>().unwrap();
let var_type_hint =
py_type_hints.get_item(id.to_string().as_str()).unwrap().unwrap();
let var_type = var_type_hint.getattr_opt("__origin__").unwrap();
if let Some(var_type) = var_type {
let var_type_id = id_fn.bind(py).call1((var_type,)).unwrap();
let var_type_id = var_type_id.extract::<u64>().unwrap();
[self.primitive_ids.kernel, self.primitive_ids.kernel_invariant]
.contains(&var_type_id)
} else {
false
}
}),
_ => false,
},
// Allow global variable declaration with `Kernel` type annotation
StmtKind::AnnAssign { ref annotation, .. } => {
matches!(&annotation.node, ExprKind::Subscript { value, .. } if matches!(&value.node, ExprKind::Name {id, ..} if id == &"Kernel".into()))
}
_ => false,
};
@ -358,7 +313,7 @@ impl Nac3 {
None => {
return Some(format!(
"object launching kernel does not have method `{method_name}`"
));
))
}
}
} else {
@ -379,7 +334,7 @@ impl Nac3 {
None if default_value.is_none() => {
return Some(format!(
"argument `{name}` not provided when launching kernel function"
));
))
}
_ => break,
};
@ -393,7 +348,7 @@ impl Nac3 {
Err(e) => {
return Some(format!(
"type error ({e}) at parameter #{i} when calling kernel function"
));
))
}
};
if let Err(e) = unifier.unify(in_ty, *ty) {
@ -467,13 +422,13 @@ impl Nac3 {
]
}
fn compile_method<'py, T>(
fn compile_method<T>(
&self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
args: Vec<&PyAny>,
embedding_map: &PyAny,
py: Python,
link_fn: &dyn Fn(&Module) -> PyResult<T>,
) -> PyResult<T> {
let size_t = self.isa.get_size_type(&Context::create());
@ -489,20 +444,19 @@ impl Nac3 {
let id_fn = builtins.getattr("id")?;
let issubclass = builtins.getattr("issubclass")?;
let exn_class = builtins.getattr("Exception")?;
let store_obj = embedding_map.getattr("store_object").unwrap();
let store_str = embedding_map.getattr("store_str").unwrap();
let store_fun = embedding_map.getattr("store_function").unwrap().into_py_any(py)?;
let host_attributes =
embedding_map.getattr("attributes_writeback").unwrap().into_py_any(py)?;
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py);
let host_attributes = embedding_map.getattr("attributes_writeback").unwrap().to_object(py);
let global_value_ids: Arc<RwLock<HashMap<_, _>>> = Arc::new(RwLock::new(HashMap::new()));
let helper = PythonHelper {
id_fn: Arc::new(builtins.getattr("id").unwrap().into_py_any(py)?),
len_fn: Arc::new(builtins.getattr("len").unwrap().into_py_any(py)?),
type_fn: Arc::new(builtins.getattr("type").unwrap().into_py_any(py)?),
origin_ty_fn: Arc::new(typings.getattr("get_origin").unwrap().into_py_any(py)?),
args_ty_fn: Arc::new(typings.getattr("get_args").unwrap().into_py_any(py)?),
store_obj: Arc::new(store_obj.clone().into_py_any(py)?),
store_str: Arc::new(store_str.into_py_any(py)?),
id_fn: builtins.getattr("id").unwrap().to_object(py),
len_fn: builtins.getattr("len").unwrap().to_object(py),
type_fn: builtins.getattr("type").unwrap().to_object(py),
origin_ty_fn: typings.getattr("get_origin").unwrap().to_object(py),
args_ty_fn: typings.getattr("get_args").unwrap().to_object(py),
store_obj: store_obj.clone(),
store_str,
};
let pyid_to_type = Arc::new(RwLock::new(HashMap::<u64, Type>::new()));
@ -525,14 +479,14 @@ impl Nac3 {
let mut rpc_ids = vec![];
for (stmt, path, module) in &self.top_levels {
let py_module = module.bind(py);
let py_module: &PyAny = module.extract(py)?;
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
let module_name: String = py_module.getattr("__name__")?.extract()?;
let helper = helper.clone();
let class_obj;
if let StmtKind::ClassDef { name, .. } = &stmt.node {
let class = py_module.getattr(name.to_string().as_str()).unwrap();
if issubclass.call1((&class, &exn_class)).unwrap().extract().unwrap()
if issubclass.call1((class, exn_class)).unwrap().extract().unwrap()
&& class.getattr("artiq_builtin").is_err()
{
class_obj = Some(class);
@ -545,8 +499,8 @@ impl Nac3 {
let (name_to_pyid, resolver, _, _) =
module_to_resolver_cache.get(&module_id).cloned().unwrap_or_else(|| {
let mut name_to_pyid: HashMap<StrRef, u64> = HashMap::new();
let members = py_module.getattr("__dict__").unwrap();
let members = members.downcast::<PyDict>().unwrap();
let members: &PyDict =
py_module.getattr("__dict__").unwrap().downcast().unwrap();
for (key, val) in members {
let key: &str = key.extract().unwrap();
let val = id_fn.call1((val,)).unwrap().extract().unwrap();
@ -592,79 +546,52 @@ impl Nac3 {
if let Some(class_obj) = class_obj {
self.exception_ids
.write()
.insert(def_id.0, store_obj.call1((class_obj,))?.extract()?);
.insert(def_id.0, store_obj.call1(py, (class_obj,))?.extract(py)?);
}
match &stmt.node {
StmtKind::FunctionDef { decorator_list, .. } => {
for decorator in decorator_list {
if let Some(decorator_str) = decorator_id_string(decorator) {
if decorator_str == "rpc" {
store_fun
.call1(
py,
(
def_id.0.into_py_any(py)?,
module
.bind(py)
.getattr(name.to_string().as_str())
.unwrap(),
),
)
.unwrap();
if decorator_list
.iter()
.any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string()))
{
store_fun
.call1(
py,
(
def_id.0.into_py(py),
module.getattr(py, name.to_string().as_str()).unwrap(),
),
)
.unwrap();
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
rpc_ids.push((None, def_id, is_async));
}
}
StmtKind::ClassDef { name, body, .. } => {
let class_name = name.to_string();
let class_obj = module.getattr(py, class_name.as_str()).unwrap();
for stmt in body {
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
if decorator_list.iter().any(|decorator| {
decorator_id_string(decorator) == Some("rpc".to_string())
}) {
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
rpc_ids.push((None, def_id, is_async));
} else if decorator_str != "kernel"
&& decorator_str != "portable"
&& decorator_str != "extern"
{
return Err(CompileError::new_err(format!(
"compilation failed\n----------\nDecorator {} is not supported (at {})",
decorator_id_string(decorator).unwrap(),
stmt.location
)));
}
}
}
}
StmtKind::ClassDef { name, body, .. } => {
let class_name = name.to_string();
let class_obj = module.bind(py).getattr(class_name.as_str()).unwrap();
for stmt in body {
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
for decorator in decorator_list {
if let Some(decorator_str) = decorator_id_string(decorator) {
if decorator_str == "rpc" {
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator).iter().any(|constant| {
*constant == Constant::Str("async".into())
})
});
if name == &"__init__".into() {
return Err(CompileError::new_err(format!(
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
class_name, stmt.location
)));
}
rpc_ids.push((
Some((class_obj.clone(), *name)),
def_id,
is_async,
));
} else if decorator_str != "kernel"
&& decorator_str != "portable"
{
return Err(CompileError::new_err(format!(
"compilation failed\n----------\nDecorator {} is not supported (at {})",
decorator_id_string(decorator).unwrap(),
stmt.location
)));
}
if name == &"__init__".into() {
return Err(CompileError::new_err(format!(
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
class_name, stmt.location
)));
}
rpc_ids.push((Some((class_obj.clone(), *name)), def_id, is_async));
}
}
}
@ -708,7 +635,7 @@ impl Nac3 {
let mut arg_names = vec![];
for (i, arg) in args.into_iter().enumerate() {
let name = format!("tmp{i}");
module.add(&*name, &arg)?;
module.add(&name, arg)?;
name_to_pyid.insert(name.clone().into(), id_fun.call1((arg,))?.extract()?);
arg_names.push(name);
}
@ -730,7 +657,7 @@ impl Nac3 {
id_to_primitive: RwLock::default(),
field_to_val: RwLock::default(),
name_to_pyid,
module: Arc::new(module.into_py_any(py)?),
module: module.to_object(py),
helper: helper.clone(),
string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(),
@ -804,8 +731,10 @@ impl Nac3 {
.call1(
py,
(
id.0.into_py_any(py)?,
class_def.getattr(name.to_string().as_str()).unwrap(),
id.0.into_py(py),
class_def
.getattr(py, name.to_string().as_str())
.unwrap(),
),
)
.unwrap();
@ -815,7 +744,7 @@ impl Nac3 {
TopLevelDef::Variable { .. } => {
return Err(CompileError::new_err(String::from(
"Unsupported @rpc annotation on global variable",
)));
)))
}
TopLevelDef::Module { .. } => {
unreachable!("Type module cannot be decorated with @rpc")
@ -868,7 +797,6 @@ impl Nac3 {
&context,
&self.get_llvm_target_machine(),
self.time_fns,
self.special_ids.clone(),
))
})
.collect();
@ -885,7 +813,6 @@ impl Nac3 {
&context,
&self.get_llvm_target_machine(),
self.time_fns,
self.special_ids.clone(),
);
let module = context.create_module("main");
let target_machine = self.llvm_options.create_target_machine().unwrap();
@ -939,18 +866,6 @@ impl Nac3 {
embedding_map.setattr("expects_return", has_return).unwrap();
let emit_llvm_bc = std::env::var(ENV_NAC3_EMIT_LLVM_BC).is_ok();
let emit_llvm_ll = std::env::var(ENV_NAC3_EMIT_LLVM_LL).is_ok();
let emit_llvm = |module: &Module<'_>, filename: &str| {
if emit_llvm_bc {
module.write_bitcode_to_path(Path::new(format!("{filename}.bc").as_str()));
}
if emit_llvm_ll {
module.print_to_file(Path::new(format!("{filename}.ll").as_str())).unwrap();
}
};
// Link all modules into `main`.
let buffers = membuffers.lock();
let main = context
@ -959,8 +874,6 @@ impl Nac3 {
"main",
))
.unwrap();
emit_llvm(&main, "main");
for buffer in buffers.iter().rev().skip(1) {
let other = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main"))
@ -968,10 +881,7 @@ impl Nac3 {
main.link_in_module(other).map_err(|err| CompileError::new_err(err.to_string()))?;
}
emit_llvm(&main, "main.merged");
main.link_in_module(irrt).map_err(|err| CompileError::new_err(err.to_string()))?;
emit_llvm(&main, "main.fat");
let mut function_iter = main.get_first_function();
while let Some(func) = function_iter {
@ -991,8 +901,6 @@ impl Nac3 {
global_option = global.get_next_global();
}
emit_llvm(&main, "main.pre-opt");
let target_machine = self
.llvm_options
.target
@ -1007,15 +915,12 @@ impl Nac3 {
panic!("Failed to run optimization for module `main`: {}", err.to_string());
}
emit_llvm(&main, "main.post-opt");
Python::with_gil(|py| {
let string_store = self.string_store.read();
let mut string_store_vec = string_store.iter().collect::<Vec<_>>();
string_store_vec.sort_by(|(_s1, key1), (_s2, key2)| key1.cmp(key2));
for (s, key) in string_store_vec {
let embed_key: i32 =
helper.store_str.bind(py).call1((s,)).unwrap().extract().unwrap();
let embed_key: i32 = helper.store_str.call1(py, (s,)).unwrap().extract(py).unwrap();
assert_eq!(
embed_key, *key,
"string {s} is out of sync between embedding map (key={embed_key}) and \
@ -1125,7 +1030,7 @@ fn add_exceptions(
#[pymethods]
impl Nac3 {
#[new]
fn new<'py>(isa: &str, artiq_builtins: &Bound<'py, PyDict>, py: Python<'py>) -> PyResult<Self> {
fn new(isa: &str, artiq_builtins: &PyDict, py: Python) -> PyResult<Self> {
let isa = match isa {
"host" => Isa::Host,
"rv32g" => Isa::RiscV32G,
@ -1192,59 +1097,42 @@ impl Nac3 {
let builtins_mod = PyModule::import(py, "builtins").unwrap();
let id_fn = builtins_mod.getattr("id").unwrap();
let numpy_mod = PyModule::import(py, "numpy").unwrap();
let typing_mod = PyModule::import(py, "typing").unwrap();
let types_mod = PyModule::import(py, "types").unwrap();
let get_id = |x: &Bound<PyAny>| id_fn.call1((x,)).and_then(|id| id.extract()).unwrap();
let get_artiq_builtin = |mod_name: Option<&str>, name: &str| -> Bound<PyAny> {
if let Some(mod_name) = mod_name {
artiq_builtins
.get_item(mod_name)
.unwrap()
.unwrap_or_else(|| {
panic!("no module key '{mod_name}' present in artiq_builtins")
})
.downcast::<PyDict>()
.unwrap()
.get_item(name)
.unwrap()
.unwrap_or_else(|| {
panic!("no key '{name}' present in artiq_builtins.{mod_name}")
})
} else {
artiq_builtins
.get_item(name)
.unwrap()
.unwrap_or_else(|| panic!("no key '{name}' present in artiq_builtins"))
}
let get_id = |x: &PyAny| id_fn.call1((x,)).and_then(PyAny::extract).unwrap();
let get_attr_id = |obj: &PyModule, attr| {
id_fn.call1((obj.getattr(attr).unwrap(),)).unwrap().extract().unwrap()
};
let primitive_ids = PrimitivePythonId {
virtual_id: get_id(&get_artiq_builtin(Some("artiq"), "virtual")),
virtual_id: get_id(artiq_builtins.get_item("virtual").ok().flatten().unwrap()),
generic_alias: (
get_id(&get_artiq_builtin(Some("typing"), "_GenericAlias")),
get_id(&get_artiq_builtin(Some("types"), "GenericAlias")),
get_attr_id(typing_mod, "_GenericAlias"),
get_attr_id(types_mod, "GenericAlias"),
),
none: get_id(&get_artiq_builtin(Some("artiq"), "none")),
typevar: get_id(&get_artiq_builtin(Some("typing"), "TypeVar")),
const_generic_marker: get_id(&get_artiq_builtin(Some("artiq"), "_ConstGenericMarker")),
int: get_id(&get_artiq_builtin(None, "int")),
int32: get_id(&get_artiq_builtin(Some("numpy"), "int32")),
int64: get_id(&get_artiq_builtin(Some("numpy"), "int64")),
uint32: get_id(&get_artiq_builtin(Some("numpy"), "uint32")),
uint64: get_id(&get_artiq_builtin(Some("numpy"), "uint64")),
bool: get_id(&get_artiq_builtin(None, "bool")),
np_bool_: get_id(&get_artiq_builtin(Some("numpy"), "bool_")),
string: get_id(&get_artiq_builtin(None, "str")),
np_str_: get_id(&get_artiq_builtin(Some("numpy"), "str_")),
float: get_id(&get_artiq_builtin(None, "float")),
float64: get_id(&get_artiq_builtin(Some("numpy"), "float64")),
list: get_id(&get_artiq_builtin(None, "list")),
ndarray: get_id(&get_artiq_builtin(Some("numpy"), "ndarray")),
tuple: get_id(&get_artiq_builtin(None, "tuple")),
exception: get_id(&get_artiq_builtin(None, "Exception")),
option: get_id(&get_artiq_builtin(Some("artiq"), "Option")),
module: get_id(&get_artiq_builtin(Some("types"), "ModuleType")),
kernel: get_id(&get_artiq_builtin(Some("artiq"), "Kernel")),
kernel_invariant: get_id(&get_artiq_builtin(Some("artiq"), "KernelInvariant")),
none: get_id(artiq_builtins.get_item("none").ok().flatten().unwrap()),
typevar: get_attr_id(typing_mod, "TypeVar"),
const_generic_marker: get_id(
artiq_builtins.get_item("_ConstGenericMarker").ok().flatten().unwrap(),
),
int: get_attr_id(builtins_mod, "int"),
int32: get_attr_id(numpy_mod, "int32"),
int64: get_attr_id(numpy_mod, "int64"),
uint32: get_attr_id(numpy_mod, "uint32"),
uint64: get_attr_id(numpy_mod, "uint64"),
bool: get_attr_id(builtins_mod, "bool"),
np_bool_: get_attr_id(numpy_mod, "bool_"),
string: get_attr_id(builtins_mod, "str"),
np_str_: get_attr_id(numpy_mod, "str_"),
float: get_attr_id(builtins_mod, "float"),
float64: get_attr_id(numpy_mod, "float64"),
list: get_attr_id(builtins_mod, "list"),
ndarray: get_attr_id(numpy_mod, "ndarray"),
tuple: get_attr_id(builtins_mod, "tuple"),
exception: get_attr_id(builtins_mod, "Exception"),
option: get_id(artiq_builtins.get_item("Option").ok().flatten().unwrap()),
module: get_attr_id(types_mod, "ModuleType"),
};
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
@ -1304,7 +1192,6 @@ impl Nac3 {
string_store: Arc::new(string_store.into()),
exception_ids: Arc::default(),
deferred_eval_store: DeferredEvaluationStore::new(),
special_ids: SpecialPythonId::default(),
llvm_options: CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default,
target: isa.get_llvm_target_options(),
@ -1312,45 +1199,36 @@ impl Nac3 {
})
}
fn analyze<'py>(
fn analyze(
&mut self,
functions: &Bound<'py, PySet>,
classes: &Bound<'py, PySet>,
special_ids: &Bound<'py, PyDict>,
content_modules: &Bound<'py, PySet>,
functions: &PySet,
classes: &PySet,
content_modules: &PySet,
) -> PyResult<()> {
let (modules, class_ids) =
Python::with_gil(|py| -> PyResult<(PyValueMap, HashSet<u64>)> {
let mut modules: IndexMap<u64, Arc<PyObject>> = IndexMap::new();
Python::with_gil(|py| -> PyResult<(HashMap<u64, PyObject>, HashSet<u64>)> {
let mut modules: HashMap<u64, PyObject> = HashMap::new();
let mut class_ids: HashSet<u64> = HashSet::new();
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
let getmodule_fn = PyModule::import(py, "inspect")?.getattr("getmodule")?;
for function in functions {
let module = getmodule_fn.call1((&function,))?;
if !module.is_none() {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = getmodule_fn.call1((function,))?.extract()?;
if !module.is_none(py) {
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
}
for class in classes {
let module = getmodule_fn.call1((&class,))?;
if !module.is_none() {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = getmodule_fn.call1((class,))?.extract()?;
if !module.is_none(py) {
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
class_ids.insert(id_fn.call1((&class,))?.extract()?);
class_ids.insert(id_fn.call1((class,))?.extract()?);
}
for module in content_modules {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = module.extract()?;
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
Ok((modules, class_ids))
})?;
@ -1358,40 +1236,22 @@ impl Nac3 {
for module in modules.into_values() {
self.register_module(&module, &class_ids)?;
}
self.special_ids = SpecialPythonId {
parallel: special_ids.get_item("parallel").ok().flatten().unwrap().extract().unwrap(),
legacy_parallel: special_ids
.get_item("legacy_parallel")
.ok()
.flatten()
.unwrap()
.extract()
.unwrap(),
sequential: special_ids
.get_item("sequential")
.ok()
.flatten()
.unwrap()
.extract()
.unwrap(),
};
Ok(())
}
fn compile_method_to_file<'py>(
fn compile_method_to_file(
&mut self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
args: Vec<&PyAny>,
filename: &str,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
embedding_map: &PyAny,
py: Python,
) -> PyResult<()> {
let target_machine = self.get_llvm_target_machine();
let link_fn = |module: &Module| {
if self.isa == Isa::Host {
if self.isa == Isa::Host {
let link_fn = |module: &Module| {
let working_directory = self.working_directory.path().to_owned();
target_machine
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
@ -1401,7 +1261,11 @@ impl Nac3 {
working_directory.join("module.o").to_string_lossy().to_string(),
)?;
Ok(())
} else {
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
} else {
let link_fn = |module: &Module| {
let object_mem = target_machine
.write_to_memory_buffer(module, FileType::Object)
.expect("couldn't write module to object file buffer");
@ -1415,23 +1279,24 @@ impl Nac3 {
} else {
Err(CompileError::new_err("linker failed to process object file"))
}
}
};
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
}
}
fn compile_method_to_mem<'py>(
fn compile_method_to_mem(
&mut self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
args: Vec<&PyAny>,
embedding_map: &PyAny,
py: Python,
) -> PyResult<PyObject> {
let target_machine = self.get_llvm_target_machine();
let link_fn = |module: &Module| {
if self.isa == Isa::Host {
if self.isa == Isa::Host {
let link_fn = |module: &Module| {
let working_directory = self.working_directory.path().to_owned();
target_machine
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
@ -1445,7 +1310,11 @@ impl Nac3 {
)?;
Ok(PyBytes::new(py, &fs::read(filename).unwrap()).into())
} else {
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
} else {
let link_fn = |module: &Module| {
let object_mem = target_machine
.write_to_memory_buffer(module, FileType::Object)
.expect("couldn't write module to object file buffer");
@ -1454,20 +1323,20 @@ impl Nac3 {
} else {
Err(CompileError::new_err("linker failed to process object file"))
}
}
};
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
}
}
}
#[cfg(feature = "init-llvm-profile")]
unsafe extern "C" {
extern "C" {
fn __llvm_profile_initialize();
}
#[pymodule]
fn nac3artiq<'py>(py: Python<'py>, m: &Bound<'py, PyModule>) -> PyResult<()> {
fn nac3artiq(py: Python, m: &PyModule) -> PyResult<()> {
#[cfg(feature = "init-llvm-profile")]
unsafe {
__llvm_profile_initialize();

View File

@ -1,48 +1,44 @@
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
sync::{
Arc,
atomic::{AtomicBool, Ordering::Relaxed},
Arc,
},
};
use itertools::Itertools;
use parking_lot::RwLock;
use pyo3::{
IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python,
prelude::*,
types::{PyDict, PyTuple},
PyAny, PyErr, PyObject, PyResult, Python,
};
use super::PrimitivePythonId;
use nac3core::{
codegen::{
CodeGenContext, CodeGenerator,
types::{ProxyType, ndarray::NDArrayType, structure::StructProxyType},
types::{ndarray::NDArrayType, structure::StructProxyType, ProxyType},
values::ndarray::make_contiguous_strides,
CodeGenContext, CodeGenerator,
},
inkwell::{
AddressSpace,
module::Linkage,
types::{BasicType, BasicTypeEnum},
values::{BasicValue, BasicValueEnum},
AddressSpace,
},
nac3parser::ast::{self, StrRef},
symbol_resolver::{StaticValue, SymbolResolver, SymbolValue, ValueEnum},
toplevel::{
DefinitionId, TopLevelDef,
helper::PrimDef,
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
DefinitionId, TopLevelDef,
},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, TypeVar, Unifier, VarMap, into_var_map, iter_type_vars},
typedef::{into_var_map, iter_type_vars, Type, TypeEnum, TypeVar, Unifier, VarMap},
},
};
use super::PrimitivePythonId;
#[derive(Debug)]
pub enum PrimitiveValue {
I32(i32),
I64(i64),
@ -75,17 +71,16 @@ impl DeferredEvaluationStore {
/// A class field as stored in the [`InnerResolver`], represented by the ID and name of the
/// associated [`PythonValue`].
pub(crate) type ResolverField = (u64, StrRef);
/// A value as stored in Python, represented by the `id()` and [`PyObject`] of the value.
pub(crate) type PyValueHandle = (u64, Arc<PyObject>);
type ResolverField = (u64, StrRef);
/// A class field as stored in Python, represented by the `id()` and [`PyObject`] of the field.
type PyFieldHandle = (u64, PyObject);
pub struct InnerResolver {
pub id_to_type: RwLock<HashMap<StrRef, Type>>,
pub id_to_def: RwLock<HashMap<StrRef, DefinitionId>>,
pub id_to_pyval: RwLock<HashMap<StrRef, PyValueHandle>>,
pub id_to_pyval: RwLock<HashMap<StrRef, (u64, PyObject)>>,
pub id_to_primitive: RwLock<HashMap<u64, PrimitiveValue>>,
pub field_to_val: RwLock<HashMap<ResolverField, Option<PyValueHandle>>>,
pub field_to_val: RwLock<HashMap<ResolverField, Option<PyFieldHandle>>>,
pub global_value_ids: Arc<RwLock<HashMap<u64, PyObject>>>,
pub pyid_to_def: Arc<RwLock<HashMap<u64, DefinitionId>>>,
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
@ -96,33 +91,26 @@ pub struct InnerResolver {
pub deferred_eval_store: DeferredEvaluationStore,
// module specific
pub name_to_pyid: HashMap<StrRef, u64>,
pub module: Arc<PyObject>,
pub module: PyObject,
}
impl Debug for InnerResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.debug_str(None, &None))
}
}
#[derive(Debug)]
pub struct Resolver(pub Arc<InnerResolver>);
#[derive(Clone)]
pub struct PythonHelper {
pub type_fn: Arc<PyObject>,
pub len_fn: Arc<PyObject>,
pub id_fn: Arc<PyObject>,
pub origin_ty_fn: Arc<PyObject>,
pub args_ty_fn: Arc<PyObject>,
pub store_obj: Arc<PyObject>,
pub store_str: Arc<PyObject>,
pub type_fn: PyObject,
pub len_fn: PyObject,
pub id_fn: PyObject,
pub origin_ty_fn: PyObject,
pub args_ty_fn: PyObject,
pub store_obj: PyObject,
pub store_str: PyObject,
}
struct PythonValue {
id: u64,
value: Arc<PyObject>,
store_obj: Arc<PyObject>,
value: PyObject,
store_obj: PyObject,
resolver: Arc<InnerResolver>,
}
@ -139,7 +127,7 @@ impl StaticValue for PythonValue {
ctx.module.get_global(format!("{}_const", self.id).as_str()).map_or_else(
|| {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
let id: u32 = self.store_obj.bind(py).call1((&*self.value,))?.extract()?;
let id: u32 = self.store_obj.call1(py, (self.value.clone(),))?.extract(py)?;
let struct_type = ctx.ctx.struct_type(&[ctx.ctx.i32_type().into()], false);
let global = ctx.module.add_global(
struct_type,
@ -188,7 +176,7 @@ impl StaticValue for PythonValue {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
self.resolver
.get_obj_value(py, (*self.value).bind(py), ctx, generator, expected_ty)
.get_obj_value(py, self.value.as_ref(py), ctx, generator, expected_ty)
.map(Option::unwrap)
})
.map_err(|e| e.to_string())
@ -204,94 +192,63 @@ impl StaticValue for PythonValue {
field_to_val.get(&(self.id, name)).cloned()
}
.unwrap_or_else(|| {
Python::with_gil(|py| -> PyResult<Option<PyValueHandle>> {
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
let helper = &self.resolver.helper;
let id = helper.id_fn.bind(py).call1((&*self.value,))?.extract::<u64>()?;
let ty = helper.type_fn.bind(py).call1((&*self.value,))?;
let ty_id: u64 = helper.id_fn.bind(py).call1((ty,))?.extract()?;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
// for optimizing unwrap KernelInvariant
if ty_id == self.resolver.primitive_ids.option && name == "_nac3_option".into() {
let obj = self.value.bind(py).getattr(name.to_string().as_str())?;
let id = self.resolver.helper.id_fn.bind(py).call1((&obj,))?.extract()?;
let obj = Arc::new(obj.into_py_any(py)?);
let obj = self.value.getattr(py, name.to_string().as_str())?;
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
return if self.id == self.resolver.primitive_ids.none {
Ok(None)
} else {
Ok(Some((id, obj)))
};
}
let result = if let Some(def_id) =
self.resolver.pyid_to_def.read().get(&ty_id).copied()
{
let mut mutable = true;
let defs = ctx.top_level.definitions.read();
if let TopLevelDef::Class { fields, .. } = &*defs[def_id.0].read() {
for (field_name, _, is_mutable) in fields {
if field_name == &name {
mutable = *is_mutable;
break;
}
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
let mut mutable = true;
let defs = ctx.top_level.definitions.read();
if let TopLevelDef::Class { fields, .. } = &*defs[def_id.0].read() {
for (field_name, _, is_mutable) in fields {
if field_name == &name {
mutable = *is_mutable;
break;
}
}
if mutable {
None
} else {
let obj = self.value.bind(py).getattr(name.to_string().as_str())?;
let id = self.resolver.helper.id_fn.bind(py).call1((&obj,))?.extract()?;
let obj = Arc::new(obj.into_py_any(py)?);
Some((id, obj))
}
} else if let Some(def_id) = self.resolver.pyid_to_def.read().get(&id).copied() {
// Check if self.value is a module
let in_mod_ctx = ctx
.top_level
.definitions
.read()
.get(def_id.0)
.is_some_and(|def| matches!(&*def.read(), TopLevelDef::Module { .. }));
if in_mod_ctx {
let obj = self.value.bind(py).getattr(name.to_string().as_str())?;
let id = self.resolver.helper.id_fn.bind(py).call1((&obj,))?.extract()?;
let obj = Arc::new(obj.into_py_any(py)?);
Some((id, obj))
} else {
None
}
} else {
}
let result = if mutable {
None
} else {
let obj = self.value.getattr(py, name.to_string().as_str())?;
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
Some((id, obj))
};
self.resolver.field_to_val.write().insert((self.id, name), result.clone());
Ok(result)
})
.unwrap()
})
.map(|(id, obj)| {
Python::with_gil(|_| {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: obj,
store_obj: self.store_obj.clone(),
resolver: self.resolver.clone(),
}))
})
ValueEnum::Static(Arc::new(PythonValue {
id,
value: obj,
store_obj: self.store_obj.clone(),
resolver: self.resolver.clone(),
}))
})
}
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>> {
Python::with_gil(|py| -> PyResult<Option<PyValueHandle>> {
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
let helper = &self.resolver.helper;
let ty = helper.type_fn.bind(py).call1((&*self.value,))?;
let ty_id: u64 = helper.id_fn.bind(py).call1((ty,))?.extract()?;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
assert_eq!(ty_id, self.resolver.primitive_ids.tuple);
let tup = self.value.bind(py).downcast::<PyTuple>()?;
let elem = Arc::new(tup.get_item(index as usize)?.into_py_any(py)?);
let id = self.resolver.helper.id_fn.bind(py).call1((&*elem,))?.extract()?;
Ok(Some((id, elem)))
let tup: &PyTuple = self.value.extract(py)?;
let elem = tup.get_item(index as usize)?;
let id = self.resolver.helper.id_fn.call1(py, (elem,))?.extract(py)?;
Ok(Some((id, elem.into())))
})
.unwrap()
.map(|(id, obj)| {
@ -306,23 +263,23 @@ impl StaticValue for PythonValue {
}
impl InnerResolver {
fn get_list_elem_type<'py>(
fn get_list_elem_type(
&self,
py: Python<'py>,
list: &Bound<'py, PyAny>,
py: Python,
list: &PyAny,
len: usize,
unifier: &mut Unifier,
defs: &[Arc<RwLock<TopLevelDef>>],
primitives: &PrimitiveStore,
) -> PyResult<Result<Type, String>> {
let mut ty = match self.get_obj_type(py, &list.get_item(0)?, unifier, defs, primitives)? {
let mut ty = match self.get_obj_type(py, list.get_item(0)?, unifier, defs, primitives)? {
Ok(t) => t,
Err(e) => return Ok(Err(format!("type error ({e}) at element #0 of the list"))),
};
for i in 1..len {
let b = match list
.get_item(i)
.map(|elem| self.get_obj_type(py, &elem, unifier, defs, primitives))??
.map(|elem| self.get_obj_type(py, elem, unifier, defs, primitives))??
{
Ok(t) => t,
Err(e) => return Ok(Err(format!("type error ({e}) at element #{i} of the list"))),
@ -333,7 +290,7 @@ impl InnerResolver {
return Ok(Err(format!(
"inhomogeneous type ({}) at element #{i} of the list",
e.to_display(unifier)
)));
)))
}
};
}
@ -346,21 +303,17 @@ impl InnerResolver {
/// `TypeVars` and `GenericAlias`(`A[int, bool]`) should use `ty_ty_id` to check.
///
/// The `bool` value returned indicates whether they are instantiated or not
fn get_pyty_obj_type<'py>(
fn get_pyty_obj_type(
&self,
py: Python<'py>,
pyty: &Bound<'py, PyAny>,
py: Python,
pyty: &PyAny,
unifier: &mut Unifier,
defs: &[Arc<RwLock<TopLevelDef>>],
primitives: &PrimitiveStore,
) -> PyResult<Result<(Type, bool), String>> {
let ty_id: u64 = self.helper.id_fn.bind(py).call1((pyty,))?.extract()?;
let ty_ty_id: u64 = self
.helper
.id_fn
.bind(py)
.call1((self.helper.type_fn.bind(py).call1((pyty,))?,))?
.extract()?;
let ty_id: u64 = self.helper.id_fn.call1(py, (pyty,))?.extract(py)?;
let ty_ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (pyty,))?,))?.extract(py)?;
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
Ok(Ok((primitives.int32, true)))
@ -441,8 +394,7 @@ impl InnerResolver {
(unifier.add_ty(ty), false)
}))
} else if ty_ty_id == self.primitive_ids.typevar {
let name = pyty.getattr("__name__").unwrap();
let name = name.extract::<&str>().unwrap();
let name: &str = pyty.getattr("__name__").unwrap().extract().unwrap();
let (constraint_types, is_const_generic) = {
let constraints = pyty.getattr("__constraints__").unwrap();
let mut result: Vec<Type> = vec![];
@ -450,9 +402,8 @@ impl InnerResolver {
let mut is_const_generic = false;
for i in 0usize.. {
if let Ok(constr) = &constraints.get_item(i) {
let constr_id: u64 =
self.helper.id_fn.bind(py).call1((constr,))?.extract()?;
if let Ok(constr) = constraints.get_item(i) {
let constr_id: u64 = self.helper.id_fn.call1(py, (constr,))?.extract(py)?;
if constr_id == self.primitive_ids.const_generic_marker {
is_const_generic = true;
continue;
@ -511,23 +462,24 @@ impl InnerResolver {
} else if ty_ty_id == self.primitive_ids.generic_alias.0
|| ty_ty_id == self.primitive_ids.generic_alias.1
{
let origin = self.helper.origin_ty_fn.bind(py).call1((pyty,))?;
let args = self.helper.args_ty_fn.bind(py).call1((pyty,))?;
let args = args.downcast::<PyTuple>()?;
let origin_ty = match self.get_pyty_obj_type(py, &origin, unifier, defs, primitives)? {
Ok((ty, false)) => ty,
Ok((_, true)) => {
return Ok(Err("instantiated type does not take type parameters".into()));
}
Err(err) => return Ok(Err(err)),
};
let origin = self.helper.origin_ty_fn.call1(py, (pyty,))?;
let args = self.helper.args_ty_fn.call1(py, (pyty,))?;
let args: &PyTuple = args.downcast(py)?;
let origin_ty =
match self.get_pyty_obj_type(py, origin.as_ref(py), unifier, defs, primitives)? {
Ok((ty, false)) => ty,
Ok((_, true)) => {
return Ok(Err("instantiated type does not take type parameters".into()))
}
Err(err) => return Ok(Err(err)),
};
match &*unifier.get_ty(origin_ty) {
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
if args.len() == 1 {
let ty = match self.get_pyty_obj_type(
py,
&args.get_item(0)?,
args.get_item(0)?,
unifier,
defs,
primitives,
@ -572,10 +524,10 @@ impl InnerResolver {
// npt.NDArray[T] == np.ndarray[Any, np.dtype[T]]
let ndarray_dtype_pyty =
self.helper.args_ty_fn.bind(py).call1((args.get_item(1)?,))?;
let dtype = ndarray_dtype_pyty.downcast::<PyTuple>()?.get_item(0)?;
self.helper.args_ty_fn.call1(py, (args.get_item(1)?,))?;
let dtype = ndarray_dtype_pyty.downcast::<PyTuple>(py)?.get_item(0)?;
let ty = match self.get_pyty_obj_type(py, &dtype, unifier, defs, primitives)? {
let ty = match self.get_pyty_obj_type(py, dtype, unifier, defs, primitives)? {
Ok(ty) => ty,
Err(err) => return Ok(Err(err)),
};
@ -591,7 +543,7 @@ impl InnerResolver {
TypeEnum::TTuple { .. } => {
let args = match args
.iter()
.map(|x| self.get_pyty_obj_type(py, &x, unifier, defs, primitives))
.map(|x| self.get_pyty_obj_type(py, x, unifier, defs, primitives))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.collect::<Result<Vec<_>, _>>() {
@ -624,7 +576,7 @@ impl InnerResolver {
}
let args = match args
.iter()
.map(|x| self.get_pyty_obj_type(py, &x, unifier, defs, primitives))
.map(|x| self.get_pyty_obj_type(py, x, unifier, defs, primitives))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.collect::<Result<Vec<_>, _>>() {
@ -651,7 +603,7 @@ impl InnerResolver {
if args.len() == 1 {
let ty = match self.get_pyty_obj_type(
py,
&args.get_item(0)?,
args.get_item(0)?,
unifier,
defs,
primitives,
@ -682,22 +634,23 @@ impl InnerResolver {
false,
)))
} else {
let str_fn = PyModule::import(py, "builtins").unwrap().getattr("repr").unwrap();
let str_fn =
pyo3::types::PyModule::import(py, "builtins").unwrap().getattr("repr").unwrap();
let str_repr: String = str_fn.call1((pyty,)).unwrap().extract().unwrap();
Ok(Err(format!("{str_repr} is not registered with NAC3 (@nac3 decorator missing?)")))
}
}
pub fn get_obj_type<'py>(
pub fn get_obj_type(
&self,
py: Python<'py>,
obj: &Bound<'py, PyAny>,
py: Python,
obj: &PyAny,
unifier: &mut Unifier,
defs: &[Arc<RwLock<TopLevelDef>>],
primitives: &PrimitiveStore,
) -> PyResult<Result<Type, String>> {
let ty = self.helper.type_fn.bind(py).call1((obj,)).unwrap();
let py_obj_id: u64 = self.helper.id_fn.bind(py).call1((obj,))?.extract()?;
let ty = self.helper.type_fn.call1(py, (obj,)).unwrap();
let py_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if let Some(ty) = self.pyid_to_type.read().get(&py_obj_id) {
return Ok(Ok(*ty));
}
@ -722,7 +675,8 @@ impl InnerResolver {
});
// check if obj is module
if self.helper.id_fn.bind(py).call1((&ty,))?.extract::<u64>()? == self.primitive_ids.module
if self.helper.id_fn.call1(py, (ty.clone(),))?.extract::<u64>(py)?
== self.primitive_ids.module
&& self.pyid_to_def.read().contains_key(&py_obj_id)
{
let def_id = self.pyid_to_def.read()[&py_obj_id];
@ -737,7 +691,7 @@ impl InnerResolver {
for (name, _) in attributes {
let attribute_obj = obj.getattr(name.to_string().as_str())?;
let attribute_ty =
self.get_obj_type(py, &attribute_obj, unifier, defs, primitives)?;
self.get_obj_type(py, attribute_obj, unifier, defs, primitives)?;
if let Ok(attribute_ty) = attribute_ty {
module_attributes.insert(*name, (attribute_ty, false));
} else {
@ -747,7 +701,7 @@ impl InnerResolver {
for name in methods.keys() {
let method_obj = obj.getattr(name.to_string().as_str())?;
let method_ty = self.get_obj_type(py, &method_obj, unifier, defs, primitives)?;
let method_ty = self.get_obj_type(py, method_obj, unifier, defs, primitives)?;
if let Ok(method_ty) = method_ty {
module_attributes.insert(*name, (method_ty, true));
} else {
@ -775,11 +729,11 @@ impl InnerResolver {
self.primitive_ids.generic_alias.0,
self.primitive_ids.generic_alias.1,
]
.contains(&self.helper.id_fn.bind(py).call1((&ty,))?.extract::<u64>()?)
.contains(&self.helper.id_fn.call1(py, (ty.clone(),))?.extract::<u64>(py)?)
{
obj
} else {
&ty
ty.as_ref(py)
}
},
unifier,
@ -815,7 +769,7 @@ impl InnerResolver {
// do the instantiation for these four types
(TypeEnum::TObj { obj_id, params, .. }, false) if *obj_id == PrimDef::List.id() => {
let ty = iter_type_vars(params).nth(0).unwrap().ty;
let len: usize = self.helper.len_fn.bind(py).call1((obj,))?.extract()?;
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
if len == 0 {
assert!(matches!(
&*unifier.get_ty(ty),
@ -867,7 +821,7 @@ impl InnerResolver {
Ok(Ok(extracted_ty))
} else {
let dtype = obj.getattr("dtype")?.getattr("type")?;
let dtype_ty = self.get_pyty_obj_type(py, &dtype, unifier, defs, primitives)?;
let dtype_ty = self.get_pyty_obj_type(py, dtype, unifier, defs, primitives)?;
match dtype_ty {
Ok((t, _)) => match unifier.unify(ty, t) {
Ok(()) => {
@ -886,10 +840,10 @@ impl InnerResolver {
}
}
(TypeEnum::TTuple { .. }, false) => {
let elements = obj.downcast::<PyTuple>()?;
let elements: &PyTuple = obj.downcast()?;
let types: Result<Result<Vec<_>, _>, _> = elements
.iter()
.map(|elem| self.get_obj_type(py, &elem, unifier, defs, primitives))
.map(|elem| self.get_obj_type(py, elem, unifier, defs, primitives))
.collect();
let types = types?;
Ok(types.map(|types| {
@ -901,11 +855,11 @@ impl InnerResolver {
(TypeEnum::TObj { obj_id, params, .. }, false)
if *obj_id == primitives.option.obj_id(unifier).unwrap() =>
{
let Ok(field_data) = &obj.getattr("_nac3_option") else {
let Ok(field_data) = obj.getattr("_nac3_option") else {
unreachable!("cannot be None")
};
// if is `none`
let zelf_id: u64 = self.helper.id_fn.bind(py).call1((obj,))?.extract()?;
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if zelf_id == self.primitive_ids.none {
let ty_enum = unifier.get_ty_immutable(primitives.option);
let TypeEnum::TObj { params, .. } = ty_enum.as_ref() else {
@ -930,7 +884,7 @@ impl InnerResolver {
Err(e) => {
return Ok(Err(format!(
"error when getting type of the option object ({e})"
)));
)))
}
};
let new_var_map: VarMap = params.iter().map(|(id, _)| (*id, ty)).collect();
@ -953,10 +907,10 @@ impl InnerResolver {
// loop through non-function fields of the class to get the instantiated value
for field in fields {
let name: String = (*field.0).into();
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1.0) {
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1 .0) {
continue;
}
let field_data = &match obj.getattr(name.as_str()) {
let field_data = match obj.getattr(name.as_str()) {
Ok(d) => d,
Err(e) => return Ok(Err(format!("{e}"))),
};
@ -966,10 +920,10 @@ impl InnerResolver {
Err(e) => {
return Ok(Err(format!(
"error when getting type of field `{name}` ({e})"
)));
)))
}
};
let field_ty = unifier.subst(field.1.0, &var_map).unwrap_or(field.1.0);
let field_ty = unifier.subst(field.1 .0, &var_map).unwrap_or(field.1 .0);
if let Err(e) = unifier.unify(ty, field_ty) {
// field type mismatch
return Ok(Err(format!(
@ -1000,22 +954,22 @@ impl InnerResolver {
// check integer bounds
if unifier.unioned(extracted_ty, primitives.int32) {
obj.extract::<i32>().map_or_else(
|_| Ok(Err(format!("{obj:?} is not in the range of int32"))),
|_| Ok(Err(format!("{obj} is not in the range of int32"))),
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.int64) {
obj.extract::<i64>().map_or_else(
|_| Ok(Err(format!("{obj:?} is not in the range of int64"))),
|_| Ok(Err(format!("{obj} is not in the range of int64"))),
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.uint32) {
obj.extract::<u32>().map_or_else(
|_| Ok(Err(format!("{obj:?} is not in the range of uint32"))),
|_| Ok(Err(format!("{obj} is not in the range of uint32"))),
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.uint64) {
obj.extract::<u64>().map_or_else(
|_| Ok(Err(format!("{obj:?} is not in the range of uint64"))),
|_| Ok(Err(format!("{obj} is not in the range of uint64"))),
|_| Ok(Ok(extracted_ty)),
)
} else if unifier.unioned(extracted_ty, primitives.bool) {
@ -1024,11 +978,11 @@ impl InnerResolver {
{
Ok(Ok(extracted_ty))
} else {
Ok(Err(format!("{obj:?} is not in the range of bool")))
Ok(Err(format!("{obj} is not in the range of bool")))
}
} else if unifier.unioned(extracted_ty, primitives.float) {
obj.extract::<f64>().map_or_else(
|_| Ok(Err(format!("{obj:?} is not in the range of float64"))),
|_| Ok(Err(format!("{obj} is not in the range of float64"))),
|_| Ok(Ok(extracted_ty)),
)
} else {
@ -1038,21 +992,17 @@ impl InnerResolver {
}
}
pub fn get_obj_value<'ctx, 'py>(
pub fn get_obj_value<'ctx>(
&self,
py: Python<'py>,
obj: &Bound<'py, PyAny>,
py: Python,
obj: &PyAny,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
let ty_id: u64 = self
.helper
.id_fn
.bind(py)
.call1((self.helper.type_fn.bind(py).call1((obj,))?,))?
.extract()?;
let id: u64 = self.helper.id_fn.bind(py).call1((obj,))?.extract()?;
let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
let val: i32 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::I32(val));
@ -1092,7 +1042,7 @@ impl InnerResolver {
return Ok(Some(global.as_pointer_value().into()));
}
let len: usize = self.helper.len_fn.bind(py).call1((obj,))?.extract()?;
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
let elem_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
iter_type_vars(params).nth(0).unwrap().ty
@ -1119,13 +1069,13 @@ impl InnerResolver {
});
return Ok(Some(global.as_pointer_value().into()));
}
self.global_value_ids.write().insert(id, obj.as_unbound().into_py_any(py)?);
self.global_value_ids.write().insert(id, obj.into());
}
let arr: Result<Option<Vec<_>>, _> = (0..len)
.map(|i| {
obj.get_item(i).and_then(|elem| {
self.get_obj_value(py, &elem, ctx, generator, elem_ty).map_err(|e| {
self.get_obj_value(py, elem, ctx, generator, elem_ty).map_err(|e| {
super::CompileError::new_err(format!("Error getting element {i}: {e}"))
})
})
@ -1203,14 +1153,13 @@ impl InnerResolver {
});
return Ok(Some(global.as_pointer_value().into()));
}
self.global_value_ids.write().insert(id, obj.as_unbound().into_py_any(py)?);
self.global_value_ids.write().insert(id, obj.into());
}
let ndims = llvm_ndarray.ndims();
// Obtain the shape of the ndarray
let shape_tuple = obj.getattr("shape")?;
let shape_tuple = shape_tuple.downcast::<PyTuple>()?;
let shape_tuple: &PyTuple = obj.getattr("shape")?.downcast()?;
assert_eq!(shape_tuple.len(), ndims as usize);
// The Rust type inferencer cannot figure this out
@ -1219,7 +1168,7 @@ impl InnerResolver {
.enumerate()
.map(|(i, elem)| {
let value = self
.get_obj_value(py, &elem, ctx, generator, ctx.primitives.usize())
.get_obj_value(py, elem, ctx, generator, ctx.primitives.usize())
.map_err(|e| {
super::CompileError::new_err(format!("Error getting element {i}: {e}"))
})?
@ -1256,7 +1205,7 @@ impl InnerResolver {
.map(|i| {
obj.getattr("flat")?.get_item(i).and_then(|elem| {
let value = self
.get_obj_value(py, &elem, ctx, generator, ndarray_dtype)
.get_obj_value(py, elem, ctx, generator, ndarray_dtype)
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting element {i}: {e}"
@ -1389,14 +1338,14 @@ impl InnerResolver {
};
let tup_tys = ty.iter();
let elements = obj.downcast::<PyTuple>()?;
let elements: &PyTuple = obj.downcast()?;
assert_eq!(elements.len(), tup_tys.len());
let val: Result<Option<Vec<_>>, _> = elements
.iter()
.enumerate()
.zip(tup_tys)
.map(|((i, elem), ty)| {
self.get_obj_value(py, &elem, ctx, generator, *ty).map_err(|e| {
self.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e| {
super::CompileError::new_err(format!("Error getting element {i}: {e}"))
})
})
@ -1425,7 +1374,7 @@ impl InnerResolver {
match self
.get_obj_value(
py,
&obj.getattr("_nac3_option").unwrap(),
obj.getattr("_nac3_option").unwrap(),
ctx,
generator,
option_val_ty,
@ -1449,9 +1398,7 @@ impl InnerResolver {
});
return Ok(Some(global.as_pointer_value().into()));
}
self.global_value_ids
.write()
.insert(id, obj.as_unbound().into_py_any(py)?);
self.global_value_ids.write().insert(id, obj.into());
}
let global = ctx.module.add_global(
v.get_type(),
@ -1488,7 +1435,7 @@ impl InnerResolver {
});
return Ok(Some(global.as_pointer_value().into()));
}
self.global_value_ids.write().insert(id, obj.as_unbound().into_py_any(py)?);
self.global_value_ids.write().insert(id, obj.into());
}
let fields = {
@ -1498,7 +1445,7 @@ impl InnerResolver {
attributes
.iter()
.filter_map(|f| {
let definition = top_level_defs.get(f.1.0).unwrap().read();
let definition = top_level_defs.get(f.1 .0).unwrap().read();
if let TopLevelDef::Variable { ty, .. } = &*definition {
Some((f.0, *ty))
} else {
@ -1513,7 +1460,7 @@ impl InnerResolver {
.map(|(name, ty)| {
self.get_obj_value(
py,
&obj.getattr(name.to_string().as_str())?,
obj.getattr(name.to_string().as_str())?,
ctx,
generator,
*ty,
@ -1558,7 +1505,7 @@ impl InnerResolver {
});
return Ok(Some(global.as_pointer_value().into()));
}
self.global_value_ids.write().insert(id, obj.as_unbound().into_py_any(py)?);
self.global_value_ids.write().insert(id, obj.into());
}
// should be classes
let definition =
@ -1570,7 +1517,7 @@ impl InnerResolver {
.map(|(name, ty, _)| {
self.get_obj_value(
py,
&obj.getattr(name.to_string().as_str())?,
obj.getattr(name.to_string().as_str())?,
ctx,
generator,
*ty,
@ -1594,18 +1541,14 @@ impl InnerResolver {
}
}
fn get_default_param_obj_value<'py>(
fn get_default_param_obj_value(
&self,
py: Python<'py>,
obj: &Bound<'py, PyAny>,
py: Python,
obj: &PyAny,
) -> PyResult<Result<SymbolValue, String>> {
let id: u64 = self.helper.id_fn.bind(py).call1((obj,))?.extract()?;
let ty_id: u64 = self
.helper
.id_fn
.bind(py)
.call1((self.helper.type_fn.bind(py).call1((obj,))?,))?
.extract()?;
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
Ok(if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
let val: i32 = obj.extract()?;
Ok(SymbolValue::I32(val))
@ -1631,15 +1574,15 @@ impl InnerResolver {
let val: f64 = obj.extract()?;
Ok(SymbolValue::Double(val))
} else if ty_id == self.primitive_ids.tuple {
let elements = obj.downcast::<PyTuple>()?;
let elements: &PyTuple = obj.downcast()?;
let elements: Result<Result<Vec<_>, String>, _> =
elements.iter().map(|elem| self.get_default_param_obj_value(py, &elem)).collect();
elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect();
elements?.map(SymbolValue::Tuple)
} else if ty_id == self.primitive_ids.option {
if id == self.primitive_ids.none {
Ok(SymbolValue::OptionNone)
} else {
self.get_default_param_obj_value(py, &obj.getattr("_nac3_option").unwrap())?
self.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
.map(|v| SymbolValue::OptionSome(Box::new(v)))
}
} else {
@ -1655,14 +1598,13 @@ impl SymbolResolver for Resolver {
};
Python::with_gil(|py| -> PyResult<Option<SymbolValue>> {
let obj = self.0.module.bind(py);
let members = obj.getattr("__dict__").unwrap();
let members = members.downcast::<PyDict>().unwrap();
let obj: &PyAny = self.0.module.extract(py)?;
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
let mut sym_value = None;
for (key, val) in members {
let key: &str = key.extract()?;
if key == id.to_string() {
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, &val) {
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
sym_value = Some(v);
}
break;
@ -1696,14 +1638,13 @@ impl SymbolResolver for Resolver {
Ok(t)
} else {
Python::with_gil(|py| -> PyResult<Result<Type, String>> {
let obj = self.0.module.bind(py);
let obj: &PyAny = self.0.module.extract(py)?;
let mut sym_ty = Err(format!("cannot find symbol `{str}`"));
let members = obj.getattr("__dict__").unwrap();
let members = members.downcast::<PyDict>().unwrap();
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
for (key, val) in members {
let key: &str = key.extract()?;
if key == str.to_string() {
sym_ty = self.0.get_obj_type(py, &val, unifier, defs, primitives)?;
sym_ty = self.0.get_obj_type(py, val, unifier, defs, primitives)?;
break;
}
}
@ -1728,60 +1669,42 @@ impl SymbolResolver for Resolver {
) -> Option<ValueEnum<'ctx>> {
if let Some(def_id) = self.0.id_to_def.read().get(&id) {
let top_levels = ctx.top_level.definitions.read();
if let TopLevelDef::Variable { resolver, .. } = &*top_levels[def_id.0].read() {
if matches!(&*top_levels[def_id.0].read(), TopLevelDef::Variable { .. }) {
let module_val = &self.0.module;
let Ok((obj, idx)) = Python::with_gil(
|py| -> PyResult<Result<(BasicValueEnum<'ctx>, Option<usize>), String>> {
let module_val = (**module_val).bind(py);
let ret = Python::with_gil(|py| -> PyResult<Result<BasicValueEnum, String>> {
let module_val = module_val.as_ref(py);
let ty = self.0.get_obj_type(
py,
module_val,
&mut ctx.unifier,
&top_levels,
&ctx.primitives,
)?;
if let Err(ty) = ty {
return Ok(Err(ty));
}
let ty = ty.unwrap();
let obj =
self.0.get_obj_value(py, module_val, ctx, generator, ty)?.unwrap();
let (idx, _) = ctx.get_attr_index(ty, id);
Ok(Ok((obj, idx)))
},
)
.unwrap() else {
return None;
};
let Some(idx) = idx else {
// `idx` not found in the current resolver - try the resolver of the variable
return resolver.as_ref().and_then(|resolver| {
let resolver = &**resolver;
// TODO: Can we assume that if get_identifier_def returns a result,
// get_symbol_value will also return a value?
resolver
.get_identifier_def(id)
.ok()
.and_then(|_| resolver.get_symbol_value(id, ctx, generator))
});
};
let ret = unsafe {
ctx.builder.build_gep(
obj.into_pointer_value(),
&[
ctx.ctx.i32_type().const_zero(),
ctx.ctx.i32_type().const_int(idx as u64, false),
],
id.to_string().as_str(),
)
}
let ty = self.0.get_obj_type(
py,
module_val,
&mut ctx.unifier,
&top_levels,
&ctx.primitives,
)?;
if let Err(ty) = ty {
return Ok(Err(ty));
}
let ty = ty.unwrap();
let obj = self.0.get_obj_value(py, module_val, ctx, generator, ty)?.unwrap();
let (idx, _) = ctx.get_attr_index(ty, id);
let ret = unsafe {
ctx.builder.build_gep(
obj.into_pointer_value(),
&[
ctx.ctx.i32_type().const_zero(),
ctx.ctx.i32_type().const_int(idx as u64, false),
],
id.to_string().as_str(),
)
}
.unwrap();
Ok(Ok(ret.as_basic_value_enum()))
})
.unwrap();
return Some(ret.as_basic_value_enum().into());
if ret.is_err() {
return None;
}
return Some(ret.unwrap().into());
}
}
@ -1790,16 +1713,15 @@ impl SymbolResolver for Resolver {
id_to_val.get(&id).cloned()
}
.or_else(|| {
Python::with_gil(|py| -> PyResult<Option<PyValueHandle>> {
let obj = self.0.module.bind(py);
let mut sym_value: Option<PyValueHandle> = None;
let members = obj.getattr("__dict__").unwrap();
let members = members.downcast::<PyDict>().unwrap();
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
let obj: &PyAny = self.0.module.extract(py)?;
let mut sym_value: Option<(u64, PyObject)> = None;
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
for (key, val) in members {
let key: &str = key.extract()?;
if key == id.to_string() {
let id = self.0.helper.id_fn.bind(py).call1((&val,))?.extract()?;
sym_value = Some((id, Arc::new(val.as_unbound().into_py_any(py)?)));
let id = self.0.helper.id_fn.call1(py, (val,))?.extract(py)?;
sym_value = Some((id, val.extract()?));
break;
}
}
@ -1811,14 +1733,12 @@ impl SymbolResolver for Resolver {
.unwrap()
});
sym_value.map(|(id, v)| {
Python::with_gil(|_| {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: v,
store_obj: self.0.helper.store_obj.clone(),
resolver: self.0.clone(),
}))
})
ValueEnum::Static(Arc::new(PythonValue {
id,
value: v,
store_obj: self.0.helper.store_obj.clone(),
resolver: self.0.clone(),
}))
})
}
@ -1866,9 +1786,9 @@ impl SymbolResolver for Resolver {
let store = self.0.deferred_eval_store.store.read();
Python::with_gil(|py| -> PyResult<Result<(), String>> {
for (variables, constraints, name) in store.iter() {
let constraints = constraints.bind(py);
let constraints: &PyAny = constraints.as_ref(py);
for (i, var) in variables.iter().enumerate() {
if let Ok(constr) = &constraints.get_item(i) {
if let Ok(constr) = constraints.get_item(i) {
match self.0.get_pyty_obj_type(py, constr, unifier, defs, primitives)? {
Ok((ty, _)) => {
if !unifier.is_concrete(ty, &[]) {

View File

@ -1,6 +1,11 @@
use itertools::Either;
use nac3core::{
codegen::{CodeGenContext, expr::infer_and_call_function},
inkwell::{AddressSpace, AtomicOrdering, values::BasicValueEnum},
codegen::CodeGenContext,
inkwell::{
values::{BasicValueEnum, CallSiteValue},
AddressSpace, AtomicOrdering,
},
};
/// Functions for manipulating the timeline.
@ -283,27 +288,36 @@ pub struct ExternTimeFns {}
impl TimeFns for ExternTimeFns {
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
infer_and_call_function(
ctx,
"now_mu",
Some(ctx.ctx.i64_type().into()),
&[],
Some("now_mu"),
None,
)
.unwrap()
let now_mu = ctx.module.get_function("now_mu").unwrap_or_else(|| {
ctx.module.add_function("now_mu", ctx.ctx.i64_type().fn_type(&[], false), None)
});
ctx.builder
.build_call(now_mu, &[], "now_mu")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap()
}
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
assert_eq!(t.get_type(), ctx.ctx.i64_type().into());
infer_and_call_function(ctx, "at_mu", None, &[t], Some("at_mu"), None);
let at_mu = ctx.module.get_function("at_mu").unwrap_or_else(|| {
ctx.module.add_function(
"at_mu",
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
None,
)
});
ctx.builder.build_call(at_mu, &[t.into()], "at_mu").unwrap();
}
fn emit_delay_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, dt: BasicValueEnum<'ctx>) {
assert_eq!(dt.get_type(), ctx.ctx.i64_type().into());
infer_and_call_function(ctx, "delay_mu", None, &[dt], Some("delay_mu"), None);
let delay_mu = ctx.module.get_function("delay_mu").unwrap_or_else(|| {
ctx.module.add_function(
"delay_mu",
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
None,
)
});
ctx.builder.build_call(delay_mu, &[dt.into()], "delay_mu").unwrap();
}
}

View File

@ -2,7 +2,7 @@
name = "nac3ast"
version = "0.1.0"
authors = ["RustPython Team", "M-Labs"]
edition = "2024"
edition = "2021"
[features]
default = ["constant-optimization", "fold"]
@ -11,5 +11,5 @@ fold = []
[dependencies]
parking_lot = "0.12"
string-interner = "0.19"
string-interner = "0.18"
fxhash = "0.2"

View File

@ -6,7 +6,7 @@ pub use crate::location::Location;
use fxhash::FxBuildHasher;
use parking_lot::{Mutex, MutexGuard};
use std::{cell::RefCell, collections::HashMap, fmt, sync::LazyLock};
use string_interner::{DefaultBackend, StringInterner, symbol::SymbolU32};
use string_interner::{symbol::SymbolU32, DefaultBackend, StringInterner};
pub type Interner = StringInterner<DefaultBackend, FxBuildHasher>;
static INTERNER: LazyLock<Mutex<Interner>> =

View File

@ -1,6 +1,6 @@
use crate::StrRef;
use crate::constant;
use crate::fold::Fold;
use crate::StrRef;
pub(crate) trait Foldable<T, U> {
type Mapped;

View File

@ -2,7 +2,7 @@
name = "nac3core"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2024"
edition = "2021"
[features]
default = ["derive"]
@ -12,24 +12,22 @@ no-escape-analysis = []
[dependencies]
itertools = "0.14"
crossbeam = "0.8"
indexmap = "2.8"
indexmap = "2.7"
parking_lot = "0.12"
nac3core_derive = { path = "nac3core_derive", optional = true }
nac3parser = { path = "../nac3parser" }
strum = "0.27"
strum_macros = "0.27"
strum = "0.26"
strum_macros = "0.26"
[dependencies.inkwell]
git = "https://github.com/Derppening/inkwell"
tag = "0.5.0_llvm15-typed-ptr"
version = "0.5"
default-features = false
features = ["llvm16-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking", "typed-pointers"]
features = ["llvm14-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[dev-dependencies]
test-case = "3.3"
test-case = "1.2.0"
indoc = "2.0"
insta = "1.42"
function_name = "0.3"
insta = "=1.11.0"
[build-dependencies]
regex = "1.11"

View File

@ -28,8 +28,6 @@ fn main() {
"-fno-exceptions",
"-fno-rtti",
"-emit-llvm",
"-Xclang",
"-no-opaque-pointers",
"-S",
"-Wall",
"-Wextra",
@ -102,7 +100,6 @@ fn main() {
let mut llvm_as = Command::new("llvm-as-irrt")
.stdin(Stdio::piped())
.arg("-opaque-pointers=0")
.arg("-o")
.arg(out_dir.join("irrt.bc"))
.spawn()

View File

@ -1,4 +1,3 @@
#include "irrt/cc-builtins.hpp"
#include "irrt/exception.hpp"
#include "irrt/list.hpp"
#include "irrt/math.hpp"

View File

@ -1,34 +0,0 @@
#pragma once
extern "C" {
#define DEF_builtin_unary(RET, NAME, TY) \
RET __nac3_##NAME(TY v) { return __builtin_##NAME(v); }
#define DEF_builtin_binary(RET, NAME, TY1, TY2) \
RET __nac3_##NAME(TY1 v1, TY2 v2) { return __builtin_##NAME(v1, v2); }
DEF_builtin_unary(bool, isinf, double);
DEF_builtin_unary(bool, isnan, double);
DEF_builtin_unary(double, tan, double);
DEF_builtin_unary(double, asin, double);
DEF_builtin_unary(double, acos, double);
DEF_builtin_unary(double, atan, double);
DEF_builtin_unary(double, sinh, double);
DEF_builtin_unary(double, cosh, double);
DEF_builtin_unary(double, tanh, double);
DEF_builtin_unary(double, asinh, double);
DEF_builtin_unary(double, acosh, double);
DEF_builtin_unary(double, atanh, double);
DEF_builtin_unary(double, expm1, double);
DEF_builtin_unary(double, cbrt, double);
DEF_builtin_unary(double, erf, double);
DEF_builtin_unary(double, erfc, double);
#define __builtin_gamma __builtin_tgamma
DEF_builtin_unary(double, gamma, double);
#undef __builtin_gamma
DEF_builtin_binary(double, atan2, double, double);
DEF_builtin_binary(double, hypot, double, double);
DEF_builtin_binary(double, nextafter, double, double);
DEF_builtin_binary(double, ldexp, double, int);
}

View File

@ -34,6 +34,39 @@ DEF_nac3_int_exp_(int64_t);
DEF_nac3_int_exp_(uint32_t);
DEF_nac3_int_exp_(uint64_t);
int32_t __nac3_isinf(double x) {
return __builtin_isinf(x);
}
int32_t __nac3_isnan(double x) {
return __builtin_isnan(x);
}
double tgamma(double arg);
double __nac3_gamma(double z) {
// Handling for denormals
// | x | Python gamma(x) | C tgamma(x) |
// --- | ----------------- | --------------- | ----------- |
// (1) | nan | nan | nan |
// (2) | -inf | -inf | inf |
// (3) | inf | inf | inf |
// (4) | 0.0 | inf | inf |
// (5) | {-1.0, -2.0, ...} | inf | nan |
// (1)-(3)
if (__builtin_isinf(z) || __builtin_isnan(z)) {
return z;
}
double v = tgamma(z);
// (4)-(5)
return __builtin_isinf(v) || __builtin_isnan(v) ? __builtin_inf() : v;
}
double lgamma(double arg);
double __nac3_gammaln(double x) {
// libm's handling of value overflows differs from scipy:
// - scipy: gammaln(-inf) -> -inf
@ -43,7 +76,7 @@ double __nac3_gammaln(double x) {
return x;
}
return __builtin_lgamma(x);
return lgamma(x);
}
double j0(double x);

View File

@ -1,7 +1,7 @@
[package]
name = "nac3core_derive"
version = "0.1.0"
edition = "2024"
edition = "2021"
[lib]
proc-macro = true

View File

@ -2,8 +2,8 @@ use proc_macro::TokenStream;
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::{
Data, DataStruct, Expr, ExprField, ExprMethodCall, ExprPath, GenericArgument, Ident, LitStr,
Path, PathArguments, Type, TypePath, parse_macro_input, spanned::Spanned,
parse_macro_input, spanned::Spanned, Data, DataStruct, Expr, ExprField, ExprMethodCall,
ExprPath, GenericArgument, Ident, LitStr, Path, PathArguments, Type, TypePath,
};
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
@ -59,7 +59,11 @@ fn replace_top_level_receiver(expr: &mut Expr, ident: Ident) -> Option<&mut Expr
| Expr::Field(ExprField { base: operand, .. }) = expr
{
return if extract_dot_operand(operand).is_some() {
if replace_top_level_receiver(operand, ident).is_some() { Some(expr) } else { None }
if replace_top_level_receiver(operand, ident).is_some() {
Some(expr)
} else {
None
}
} else {
*operand = Box::new(Expr::Path(ExprPath {
attrs: Vec::default(),
@ -101,7 +105,7 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
abort!(
path,
format!(
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
quote!(#expr).to_string(),
)
)
@ -150,7 +154,7 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
abort!(
expr,
format!(
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
quote!(#expr).to_string(),
)
)
@ -220,9 +224,10 @@ pub fn derive(input: TokenStream) -> TokenStream {
let Data::Struct(DataStruct { fields, .. }) = &input.data else {
abort!(input, "Only structs with named fields are supported");
};
if let Err(err_span) = fields
.iter()
.try_for_each(|field| if field.ident.is_some() { Ok(()) } else { Err(field.span()) })
if let Err(err_span) =
fields
.iter()
.try_for_each(|field| if field.ident.is_some() { Ok(()) } else { Err(field.span()) })
{
abort!(err_span, "Only structs with named fields are supported");
};

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,26 +1,26 @@
use inkwell::{
FloatPredicate, IntPredicate, OptimizationLevel,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
FloatPredicate, IntPredicate, OptimizationLevel,
};
use itertools::Itertools;
use super::{
CodeGenContext, CodeGenerator,
expr::destructure_range,
extern_fns, irrt,
irrt::calculate_len_for_slice_range,
llvm_intrinsics,
macros::codegen_unreachable,
types::{ListType, RangeType, TupleType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, ListType, RangeType, TupleType},
values::{
ProxyValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ProxyValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
};
use crate::{
toplevel::{
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
helper::{arraylike_flatten_element_type, extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys,
},
typecheck::typedef::{Type, TypeEnum},
@ -99,21 +99,17 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
}
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 32 => {
debug_assert!(
[ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
n.into()
}
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
debug_assert!(
[ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
ctx.builder.build_int_truncate(n, llvm_i32, "trunc").map(Into::into).unwrap()
}
@ -159,11 +155,9 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
Ok(match n {
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32) => {
debug_assert!(
[ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
if ctx.unifier.unioned(n_ty, ctx.primitives.int32) {
ctx.builder.build_int_s_extend(n, llvm_i64, "sext").map(Into::into).unwrap()
@ -173,11 +167,9 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
}
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
debug_assert!(
[ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
n.into()
}
@ -230,11 +222,9 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
}
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 32 => {
debug_assert!(
[ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
n.into()
}
@ -303,11 +293,9 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
Ok(match n {
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32) => {
debug_assert!(
[ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
if ctx.unifier.unioned(n_ty, ctx.primitives.int32) {
ctx.builder.build_int_s_extend(n, llvm_i64, "sext").map(Into::into).unwrap()
@ -317,11 +305,9 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
}
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
debug_assert!(
[ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
n.into()
}
@ -373,17 +359,15 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
Ok(match n {
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32 | 64) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
if [ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.int64]
.iter()
@ -531,16 +515,14 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
}
BasicValueEnum::IntValue(n) => {
debug_assert!(
[
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
);
debug_assert!([
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
ctx.builder
.build_int_compare(IntPredicate::NE, n, n.get_type().const_zero(), FN_NAME)
@ -701,17 +683,15 @@ pub fn call_min<'ctx>(
match (m, n) {
(BasicValueEnum::IntValue(m), BasicValueEnum::IntValue(n)) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty, *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty, *ty)));
if [ctx.primitives.int32, ctx.primitives.int64]
.iter()
@ -746,18 +726,16 @@ pub fn call_numpy_minimum<'ctx, G: CodeGenerator + ?Sized>(
Ok(match (x1, x2) {
(BasicValueEnum::IntValue(x1), BasicValueEnum::IntValue(x2)) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty)));
call_min(ctx, (x1_ty, x1.into()), (x2_ty, x2.into()))
}
@ -822,17 +800,15 @@ pub fn call_max<'ctx>(
match (m, n) {
(BasicValueEnum::IntValue(m), BasicValueEnum::IntValue(n)) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty, *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty, *ty)));
if [ctx.primitives.int32, ctx.primitives.int64]
.iter()
@ -869,18 +845,16 @@ pub fn call_numpy_max_min<'ctx, G: CodeGenerator + ?Sized>(
Ok(match a {
BasicValueEnum::IntValue(_) | BasicValueEnum::FloatValue(_) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(a_ty, *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(a_ty, *ty)));
match fn_name {
"np_argmin" | "np_argmax" => llvm_int64.const_zero().into(),
@ -1012,18 +986,16 @@ pub fn call_numpy_maximum<'ctx, G: CodeGenerator + ?Sized>(
Ok(match (x1, x2) {
(BasicValueEnum::IntValue(x1), BasicValueEnum::IntValue(x2)) => {
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
ctx.primitives.float,
]
.iter()
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty)));
call_max(ctx, (x1_ty, x1.into()), (x2_ty, x2.into()))
}
@ -1129,17 +1101,15 @@ pub fn call_abs<'ctx, G: CodeGenerator + ?Sized>(
&|_ctx, elem_ty| elem_ty,
&|_generator, ctx, val_ty, val| match val {
BasicValueEnum::IntValue(n) => Some({
debug_assert!(
[
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(val_ty, *ty))
);
debug_assert!([
ctx.primitives.bool,
ctx.primitives.int32,
ctx.primitives.uint32,
ctx.primitives.int64,
ctx.primitives.uint64,
]
.iter()
.any(|ty| ctx.unifier.unioned(val_ty, *ty)));
if [ctx.primitives.int32, ctx.primitives.int64]
.iter()
@ -1224,7 +1194,7 @@ macro_rules! create_helper_call_numpy_unary_elementwise_float_to_bool {
BasicValueEnum::FloatValue(n) => {
debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float));
let ret = $on_scalar(ctx, n, Option::<&str>::None);
let ret = $on_scalar(generator, ctx, n);
Some(generator.bool_to_i8(ctx, ret).into())
}
_ => None,
@ -1293,55 +1263,55 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_tan,
"np_tan",
irrt::call_tan
extern_fns::call_tan
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arcsin,
"np_arcsin",
irrt::call_asin
extern_fns::call_asin
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arccos,
"np_arccos",
irrt::call_acos
extern_fns::call_acos
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arctan,
"np_arctan",
irrt::call_atan
extern_fns::call_atan
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_sinh,
"np_sinh",
irrt::call_sinh
extern_fns::call_sinh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_cosh,
"np_cosh",
irrt::call_cosh
extern_fns::call_cosh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_tanh,
"np_tanh",
irrt::call_tanh
extern_fns::call_tanh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arcsinh,
"np_arcsinh",
irrt::call_asinh
extern_fns::call_asinh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arccosh,
"np_arccosh",
irrt::call_acosh
extern_fns::call_acosh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arctanh,
"np_arctanh",
irrt::call_atanh
extern_fns::call_atanh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1357,7 +1327,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_expm1,
"np_expm1",
irrt::call_expm1
extern_fns::call_expm1
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1384,7 +1354,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_cbrt,
"np_cbrt",
irrt::call_cbrt
extern_fns::call_cbrt
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1401,17 +1371,17 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_erf,
"sp_spec_erf",
irrt::call_erf
extern_fns::call_erf
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_erfc,
"sp_spec_erfc",
irrt::call_erfc
extern_fns::call_erfc
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_gamma,
"sp_spec_gamma",
irrt::call_gamma
|ctx, val, _| irrt::call_gamma(ctx, val)
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_gammaln,
@ -1452,7 +1422,7 @@ pub fn call_numpy_arctan2<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_atan2(ctx, x1, x2, None).into())
Ok(extern_fns::call_atan2(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1590,7 +1560,7 @@ pub fn call_numpy_ldexp<'ctx, G: CodeGenerator + ?Sized>(
(BasicValueEnum::FloatValue(x1_scalar), BasicValueEnum::IntValue(x2_scalar)) => {
debug_assert_eq!(x1.get_dtype(), ctx.ctx.f64_type().into());
debug_assert_eq!(x2.get_dtype(), ctx.ctx.i32_type().into());
Ok(irrt::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
Ok(extern_fns::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1624,7 +1594,7 @@ pub fn call_numpy_hypot<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_hypot(ctx, x1, x2, None).into())
Ok(extern_fns::call_hypot(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1658,7 +1628,7 @@ pub fn call_numpy_nextafter<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_nextafter(ctx, x1, x2, None).into())
Ok(extern_fns::call_nextafter(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}

View File

@ -10,7 +10,7 @@ use crate::{
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{
FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier, into_var_map,
into_var_map, FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier,
},
},
};

View File

@ -6,12 +6,12 @@ use std::{
};
use inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
attributes::{Attribute, AttributeLoc},
types::{AnyType, BasicType, BasicTypeEnum},
values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue, StructValue},
AddressSpace, IntPredicate, OptimizationLevel,
};
use itertools::{Either, Itertools, izip};
use itertools::{izip, Either, Itertools};
use nac3parser::ast::{
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
@ -19,7 +19,6 @@ use nac3parser::ast::{
};
use super::{
CodeGenContext, CodeGenTask, CodeGenerator,
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
irrt::*,
@ -33,21 +32,20 @@ use super::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var,
},
types::{
ExceptionType, ListType, OptionType, RangeType, StringType, TupleType, ndarray::NDArrayType,
},
types::{ndarray::NDArrayType, ListType, RangeType},
values::{
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
UntypedArrayLikeAccessor,
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
},
CodeGenContext, CodeGenTask, CodeGenerator,
};
use crate::{
symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{
DefinitionId, TopLevelDef,
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
helper::{arraylike_flatten_element_type, PrimDef},
numpy::unpack_ndarray_var_tys,
DefinitionId, TopLevelDef,
},
typecheck::{
magic_methods::{Binop, BinopVariant, HasOpInfo},
@ -73,7 +71,7 @@ pub fn get_subst_key(
})
.unwrap_or_default();
vars.extend(fun_vars);
let sorted = vars.keys().filter(|id| filter.is_none_or(|v| v.contains(id))).sorted();
let sorted = vars.keys().filter(|id| filter.map_or(true, |v| v.contains(id))).sorted();
sorted
.map(|id| {
unifier.internal_stringify(
@ -124,7 +122,7 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
/// Checks the field and attributes of classes
/// Returns the index of attr in class fields otherwise returns the attribute value
pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> (Option<usize>, Option<Constant>) {
pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> (usize, Option<Constant>) {
let obj_id = match &*self.unifier.get_ty(ty) {
TypeEnum::TObj { obj_id, .. } => *obj_id,
TypeEnum::TModule { module_id, .. } => *module_id,
@ -134,16 +132,13 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
let def = &self.top_level.definitions.read()[obj_id.0];
let (index, value) = if let TopLevelDef::Class { fields, attributes, .. } = &*def.read() {
if let Some(field_index) = fields.iter().find_position(|x| x.0 == attr) {
(Some(field_index.0), None)
(field_index.0, None)
} else {
let attribute_index = attributes.iter().find_position(|x| x.0 == attr);
(
attribute_index.map(|(idx, _)| idx),
attribute_index.map(|(_, (_, _, k))| k.clone()),
)
let attribute_index = attributes.iter().find_position(|x| x.0 == attr).unwrap();
(attribute_index.0, Some(attribute_index.1 .2.clone()))
}
} else if let TopLevelDef::Module { attributes, .. } = &*def.read() {
(attributes.iter().find_position(|x| x.0 == attr).map(|(idx, _)| idx), None)
(attributes.iter().find_position(|x| x.0 == attr).unwrap().0, None)
} else {
codegen_unreachable!(self)
};
@ -173,27 +168,65 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(),
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => {
StringType::new(self).construct_constant(self, v, None).as_abi_value(self).into()
let str_ptr = self
.builder
.build_global_string_ptr(v, "const")
.map(|v| v.as_pointer_value().into())
.unwrap();
let size = self.get_size_type().const_int(v.len() as u64, false);
let ty = self.get_llvm_type(generator, self.primitives.str).into_struct_type();
ty.const_named_struct(&[str_ptr, size.into()]).into()
}
SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec();
TupleType::new(self, &fields)
.construct_from_objects(self, vals, Some("tup_val"))
.as_abi_value(self)
.into()
let ty = self.ctx.struct_type(&fields, false);
let ptr = gen_var(self, ty.into(), Some("tuple")).unwrap();
let zero = self.ctx.i32_type().const_zero();
unsafe {
for (i, val) in vals.into_iter().enumerate() {
let p = self
.builder
.build_in_bounds_gep(
ptr,
&[zero, self.ctx.i32_type().const_int(i as u64, false)],
"elemptr",
)
.unwrap();
self.builder.build_store(p, val).unwrap();
}
}
self.builder.build_load(ptr, "tup_val").unwrap()
}
SymbolValue::OptionSome(v) => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
{
*params.iter().next().unwrap().1
}
_ => codegen_unreachable!(self, "must be option type"),
};
let val = self.gen_symbol_val(generator, v, ty);
OptionType::from_unifier_type(generator, self, ty)
.construct_some_value(generator, self, &val, None)
.as_abi_value(self)
.into()
let ptr = generator
.gen_var_alloc(self, val.get_type(), Some("default_opt_some"))
.unwrap();
self.builder.build_store(ptr, val).unwrap();
ptr.into()
}
SymbolValue::OptionNone => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
{
*params.iter().next().unwrap().1
}
_ => codegen_unreachable!(self, "must be option type"),
};
let actual_ptr_type =
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::default());
actual_ptr_type.const_null().into()
}
SymbolValue::OptionNone => OptionType::from_unifier_type(generator, self, ty)
.construct_empty(generator, self, None)
.as_abi_value(self)
.into(),
}
}
@ -288,10 +321,15 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
if let Some(v) = self.const_strings.get(v) {
Some(*v)
} else {
let val = StringType::new(self)
.construct_constant(self, v, None)
.as_abi_value(self)
.into();
let str_ptr = self
.builder
.build_global_string_ptr(v, "const")
.map(|v| v.as_pointer_value().into())
.unwrap();
let size = self.get_size_type().const_int(v.len() as u64, false);
let ty = self.get_llvm_type(generator, self.primitives.str);
let val =
ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
self.const_strings.insert(v.to_string(), val);
Some(val)
}
@ -581,35 +619,42 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
params: [Option<IntValue<'ctx>>; 3],
loc: Location,
) {
let llvm_i32 = self.ctx.i32_type();
let llvm_i64 = self.ctx.i64_type();
let llvm_exn = ExceptionType::get_instance(generator, self);
let zelf = if let Some(exception_val) = self.exception_val {
llvm_exn.map_pointer_value(exception_val, Some("exn"))
exception_val
} else {
let zelf = llvm_exn.alloca_var(generator, self, Some("exn"));
self.exception_val = Some(zelf.as_abi_value(self));
zelf
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type();
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into();
let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
*self.exception_val.insert(zelf)
};
let id = self.resolver.get_string_id(name);
zelf.store_name(self, llvm_i32.const_int(id as u64, false));
zelf.store_message(self, msg.into_struct_value());
zelf.store_params(
self,
params
.iter()
.map(|p| {
p.map_or(llvm_i64.const_zero(), |v| {
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
})
})
.collect_array()
.as_ref()
.unwrap(),
);
gen_raise(generator, self, Some(&zelf), loc);
let int32 = self.ctx.i32_type();
let zero = int32.const_zero();
unsafe {
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap();
let id = self.resolver.get_string_id(name);
self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap();
let ptr = self
.builder
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg")
.unwrap();
self.builder.build_store(ptr, msg).unwrap();
let i64_zero = self.ctx.i64_type().const_zero();
for (i, attr_ind) in [6, 7, 8].iter().enumerate() {
let ptr = self
.builder
.build_in_bounds_gep(
zelf,
&[zero, int32.const_int(*attr_ind, false)],
"exn.param",
)
.unwrap();
let val = params[i].map_or(i64_zero, |v| {
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
});
self.builder.build_store(ptr, val).unwrap();
}
}
gen_raise(generator, self, Some(&zelf.into()), loc);
}
pub fn make_assert<G: CodeGenerator + ?Sized>(
@ -785,10 +830,11 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let llvm_usize = ctx.get_size_type();
let definition = ctx.top_level.definitions.read().get(fun.1.0).cloned().unwrap();
let definition = ctx.top_level.definitions.read().get(fun.1 .0).cloned().unwrap();
let id;
let key;
let param_vals;
let is_extern;
let vararg_arg;
// Ensure that the function object only contains up to 1 vararg parameter
@ -807,6 +853,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
if let Some(callback) = codegen_callback {
return callback.run(ctx, obj, fun, params, generator);
}
is_extern = instance_to_stmt.is_empty();
vararg_arg = fun.0.args.iter().find(|arg| arg.is_vararg);
let old_key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), fun.0, None);
let mut keys = fun.0.args.clone();
@ -866,10 +913,9 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
} else {
mapping.insert(
k.name,
vec![
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty)
.into(),
],
vec![ctx
.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty)
.into()],
);
}
}
@ -939,7 +985,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
instance_to_symbol.get(&key).cloned().ok_or_else(String::new)
}
TopLevelDef::Class { .. } => {
return Ok(Some(generator.gen_constructor(ctx, fun.0, &def, params)?));
return Ok(Some(generator.gen_constructor(ctx, fun.0, &def, params)?))
}
TopLevelDef::Variable { .. } | TopLevelDef::Module { .. } => unreachable!(),
}
@ -960,11 +1006,22 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
} else {
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
};
let has_sret = ret_type.is_some_and(|ret_type| need_sret(ret_type));
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ret_type));
let mut byrefs = Vec::new();
let mut params = args
.iter()
.filter(|arg| !arg.is_vararg)
.map(|arg| ctx.get_llvm_abi_type(generator, arg.ty).into())
.enumerate()
.filter(|(_, arg)| !arg.is_vararg)
.map(|(i, arg)| {
match ctx.get_llvm_abi_type(generator, arg.ty) {
BasicTypeEnum::StructType(ty) if is_extern => {
byrefs.push((i, ty));
ty.ptr_type(AddressSpace::default()).into()
}
x => x,
}
.into()
})
.collect_vec();
if has_sret {
params.insert(0, ret_type.unwrap().ptr_type(AddressSpace::default()).into());
@ -978,7 +1035,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
_ => ctx.ctx.void_type().fn_type(&params, is_vararg),
};
let fun_val = ctx.module.add_function(&symbol, fun_ty, None);
if has_sret {
let offset = if has_sret {
fun_val.add_attribute(
AttributeLoc::Param(0),
ctx.ctx.create_type_attribute(
@ -986,8 +1043,23 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
ret_type.unwrap().as_any_type_enum(),
),
);
}
1
} else {
0
};
// The attribute ID used to mark arguments of a structure type.
// Structure-Typed parameters of extern functions must **not** be marked as `byval`, as
// `byval` explicitly specifies that the argument is to be passed on the stack, which breaks
// on most ABIs where the first several arguments are expected to be passed in registers.
let passing_attr_id =
Attribute::get_named_enum_kind_id(if is_extern { "byref" } else { "byval" });
for (i, ty) in byrefs {
fun_val.add_attribute(
AttributeLoc::Param((i as u32) + offset),
ctx.ctx.create_type_attribute(passing_attr_id, ty.as_any_type_enum()),
);
}
fun_val
});
@ -1247,7 +1319,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op: Binop,
right: (&Option<Type>, BasicValueEnum<'ctx>),
loc: Location,
) -> Result<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
let (left_ty, left_val) = left;
let (right_ty, right_val) = right;
@ -1258,14 +1330,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
// which would be unchanged until further unification, which we would never do
// when doing code generation for function instances
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, true))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, true).into()))
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, false))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, false).into()))
} else if [Operator::LShift, Operator::RShift].contains(&op.base) {
let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1);
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed).into()))
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
Ok(ctx.gen_float_ops(op.base, left_val, right_val))
Ok(Some(ctx.gen_float_ops(op.base, left_val, right_val).into()))
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int
assert_eq!(op.base, Operator::Pow);
@ -1275,7 +1347,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
right_val.into_int_value(),
Some("f_pow_i"),
);
Ok(res.into())
Ok(Some(res.into()))
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
{
@ -1305,10 +1377,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
debug_assert!(ctx.unifier.unioned(elem_ty1, elem_ty2));
let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty1);
let sizeof_elem = ctx
.builder
.build_int_truncate_or_bit_cast(llvm_elem_ty.size_of().unwrap(), llvm_usize, "")
.unwrap();
let sizeof_elem = llvm_elem_ty.size_of().unwrap();
let lhs =
ListValue::from_pointer_value(left_val.into_pointer_value(), llvm_usize, None);
@ -1323,14 +1392,34 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
let new_list =
ListType::new(ctx, &llvm_elem_ty).construct(generator, ctx, size, None);
let lhs_size = lhs.load_size(ctx, None);
let lhs_size = ctx
.builder
.build_int_z_extend_or_bit_cast(
lhs.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let lhs_len = ctx.builder.build_int_mul(lhs_size, sizeof_elem, "").unwrap();
let rhs_size = rhs.load_size(ctx, None);
let rhs_size = ctx
.builder
.build_int_z_extend_or_bit_cast(
rhs.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let rhs_len = ctx.builder.build_int_mul(rhs_size, sizeof_elem, "").unwrap();
let list_ptr = new_list.data().base_ptr(ctx, generator);
call_memcpy_generic(ctx, list_ptr, lhs.data().base_ptr(ctx, generator), lhs_len);
call_memcpy_generic(
ctx,
list_ptr,
lhs.data().base_ptr(ctx, generator),
lhs_len,
ctx.ctx.bool_type().const_zero(),
);
let list_ptr = unsafe {
new_list.data().ptr_offset_unchecked(
@ -1340,9 +1429,15 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
None,
)
};
call_memcpy_generic(ctx, list_ptr, rhs.data().base_ptr(ctx, generator), rhs_len);
call_memcpy_generic(
ctx,
list_ptr,
rhs.data().base_ptr(ctx, generator),
rhs_len,
ctx.ctx.bool_type().const_zero(),
);
Ok(new_list.as_abi_value(ctx).into())
Ok(Some(new_list.as_abi_value(ctx).into()))
}
Operator::Mult => {
@ -1380,10 +1475,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
let int_val = call_int_smax(ctx, int_val, llvm_usize.const_zero(), None);
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
let sizeof_elem = ctx
.builder
.build_int_truncate_or_bit_cast(elem_llvm_ty.size_of().unwrap(), llvm_usize, "")
.unwrap();
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
let new_list = ListType::new(ctx, &elem_llvm_ty).construct(
generator,
@ -1407,7 +1499,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
new_list.data().ptr_offset_unchecked(ctx, generator, &offset, None)
};
let list_size = list_val.load_size(ctx, None);
let list_size = ctx
.builder
.build_int_z_extend_or_bit_cast(
list_val.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let memcpy_sz =
ctx.builder.build_int_mul(list_size, sizeof_elem, "").unwrap();
@ -1417,6 +1516,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
ptr,
list_val.data().base_ptr(ctx, generator),
memcpy_sz,
ctx.ctx.bool_type().const_zero(),
);
Ok(())
@ -1424,7 +1524,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
llvm_usize.const_int(1, false),
)?;
Ok(new_list.as_abi_value(ctx).into())
Ok(Some(new_list.as_abi_value(ctx).into()))
}
_ => todo!("Operator not supported"),
@ -1463,7 +1563,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
let result = left
.matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out))
.split_unsized(generator, ctx);
Ok(result.to_basic_value_enum())
Ok(Some(result.to_basic_value_enum().into()))
} else {
// For other operations, they are all elementwise operations.
@ -1494,12 +1594,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op,
(&Some(ty2_dtype), right_value),
ctx.current_loc,
)?;
)?
.unwrap()
.to_basic_value_enum(ctx, generator, common_dtype)?;
Ok(result)
})
.unwrap();
Ok(result.as_abi_value(ctx).into())
Ok(Some(result.as_abi_value(ctx).into()))
}
} else {
let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap());
@ -1548,7 +1650,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
(&signature, fun_id),
vec![(None, right_val.into())],
)
.map(Option::unwrap)
.map(|f| f.map(Into::into))
}
}
@ -1586,7 +1688,6 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
(&right.custom, right_val),
loc,
)
.map(|res| Some(res.into()))
}
/// Generates LLVM IR for a unary operator expression using the [`Type`] and
@ -1596,19 +1697,18 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, '_>,
op: ast::Unaryop,
operand: (&Option<Type>, BasicValueEnum<'ctx>),
) -> Result<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
let (ty, val) = operand;
let ty = ctx.unifier.get_representative(ty.unwrap());
Ok(if ty == ctx.primitives.bool {
Ok(Some(if ty == ctx.primitives.bool {
let val = val.into_int_value();
if op == ast::Unaryop::Not {
let not = ctx
.builder
.build_int_compare(IntPredicate::EQ, val, val.get_type().const_zero(), "not")
.unwrap();
let not = ctx.builder.build_not(val, "not").unwrap();
let not_bool =
ctx.builder.build_and(not, not.get_type().const_int(1, false), "").unwrap();
generator.bool_to_int_type(ctx, not, val.get_type()).into()
not_bool.into()
} else {
let llvm_i32 = ctx.ctx.i32_type();
@ -1621,6 +1721,7 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx.builder.build_int_z_extend(val, llvm_i32, "").map(Into::into).unwrap(),
),
)?
.unwrap()
}
} else if [
ctx.primitives.int32,
@ -1674,13 +1775,10 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
if op == ast::Unaryop::Invert {
ast::Unaryop::Not
} else {
let ndims = extract_ndims(&ctx.unifier, ty);
codegen_unreachable!(
ctx,
"ufunc {} not supported for ndarray[bool, {}]",
"ufunc {} not supported for ndarray[bool, N]",
op.op_info().method_name,
ndims,
)
}
} else {
@ -1692,14 +1790,16 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx,
NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() },
|generator, ctx, scalar| {
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))?
.map(|val| val.to_basic_value_enum(ctx, generator, ndarray_dtype))
.unwrap()
},
)?;
mapped_ndarray.as_abi_value(ctx).into()
} else {
unimplemented!()
})
}))
}
/// Generates LLVM IR for a unary operator expression.
@ -1719,7 +1819,6 @@ pub fn gen_unaryop_expr<'ctx, G: CodeGenerator>(
};
gen_unaryop_expr_with_values(generator, ctx, op, (&operand.custom, val))
.map(|res| Some(res.into()))
}
/// Generates LLVM IR for a comparison operator expression using the [`Type`] and
@ -1730,7 +1829,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
left: (Option<Type>, BasicValueEnum<'ctx>),
ops: &[ast::Cmpop],
comparators: &[(Option<Type>, BasicValueEnum<'ctx>)],
) -> Result<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
debug_assert_eq!(comparators.len(), ops.len());
if comparators.len() == 1 {
@ -1772,13 +1871,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
(Some(left_ty_dtype), left_scalar),
&[op],
&[(Some(right_ty_dtype), right_scalar)],
)?
.unwrap()
.to_basic_value_enum(
ctx,
generator,
ctx.primitives.bool,
)?;
Ok(generator.bool_to_i8(ctx, val.into_int_value()).into())
},
)?;
return Ok(result_ndarray.as_abi_value(ctx).into());
return Ok(Some(result_ndarray.as_abi_value(ctx).into()));
}
}
@ -1862,19 +1967,41 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
} else if left_ty == ctx.primitives.str {
assert!(ctx.unifier.unioned(left_ty, right_ty));
let llvm_str = StringType::new(ctx);
let lhs = lhs.into_struct_value();
let rhs = rhs.into_struct_value();
let lhs = llvm_str.map_struct_value(lhs.into_struct_value(), None);
let rhs = llvm_str.map_struct_value(rhs.into_struct_value(), None);
let llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = ctx.get_size_type();
let result = call_string_eq(ctx, lhs, rhs);
let plhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
ctx.builder.build_store(plhs, lhs).unwrap();
let prhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
ctx.builder.build_store(prhs, rhs).unwrap();
let lhs_ptr = ctx.build_in_bounds_gep_and_load(
plhs,
&[llvm_usize.const_zero(), llvm_i32.const_zero()],
None,
).into_pointer_value();
let lhs_len = ctx.build_in_bounds_gep_and_load(
plhs,
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
None,
).into_int_value();
let rhs_ptr = ctx.build_in_bounds_gep_and_load(
prhs,
&[llvm_usize.const_zero(), llvm_i32.const_zero()],
None,
).into_pointer_value();
let rhs_len = ctx.build_in_bounds_gep_and_load(
prhs,
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
None,
).into_int_value();
let result = call_string_eq(ctx, lhs_ptr, lhs_len, rhs_ptr, rhs_len);
if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), result.into()),
)?.into_int_value()
ctx.builder.build_not(result, "").unwrap()
} else {
result
}
@ -1977,6 +2104,9 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[Cmpop::Eq],
&[(Some(right_elem_ty), right)],
)?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
.unwrap()
.into_int_value();
gen_if_callback(
@ -2025,6 +2155,8 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
Unaryop::Not,
(&Some(ctx.primitives.bool), acc.into()),
)?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
.into_int_value()
} else {
acc
@ -2112,6 +2244,11 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[op],
&[(Some(right_ty), right_elem)],
)
.transpose()
.unwrap()
.and_then(|v| {
v.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
})
.map(BasicValueEnum::into_int_value)?;
Ok(ctx.builder.build_not(
@ -2148,12 +2285,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
// Invert the final value if __ne__
if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), cmp_phi.into()),
)?.into_int_value()
ctx.builder.build_not(cmp_phi, "").unwrap()
} else {
cmp_phi
}
@ -2178,9 +2310,12 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
};
Ok(prev?.map(|v| ctx.builder.build_and(v, current, "cmp").unwrap()).or(Some(current)))
})?.unwrap();
})?;
Ok(cmp_val.into())
Ok(Some(match cmp_val {
Some(v) => v.into(),
None => return Ok(None),
}))
}
/// Generates LLVM IR for a comparison operator expression.
@ -2227,7 +2362,6 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
ops,
comparator_vals.as_slice(),
)
.map(|res| Some(res.into()))
}
/// See [`CodeGenerator::gen_expr`].
@ -2257,13 +2391,16 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
const_val.into()
}
ExprKind::Name { id, .. } if id == &"none".into() => {
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
TypeEnum::TObj { obj_id, .. }
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
match (
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
) {
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
if *obj_id == *opt_id =>
{
OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
.construct_empty(generator, ctx, None)
.as_abi_value(ctx)
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::default())
.const_null()
.into()
}
_ => codegen_unreachable!(ctx, "must be option type"),
@ -2464,7 +2601,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
let (index, _) = ctx.get_attr_index(value.custom.unwrap(), *attr);
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
))) as Result<_, String>
},
@ -2481,7 +2618,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
}
ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
))
}
@ -2566,7 +2703,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
}
ExprKind::UnaryOp { op, operand } => return gen_unaryop_expr(generator, ctx, *op, operand),
ExprKind::Compare { left, ops, comparators } => {
return gen_cmpop_expr(generator, ctx, left, ops, comparators);
return gen_cmpop_expr(generator, ctx, left, ops, comparators)
}
ExprKind::IfExp { test, body, orelse } => {
let test = match generator.gen_expr(ctx, test)? {
@ -2748,12 +2885,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
};
}
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let option = OptionType::from_pointer_type(
ptr.get_type(),
ctx.get_size_type(),
)
.map_pointer_value(ptr, None);
let not_null = option.is_some(ctx);
let not_null =
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
ctx.make_assert(
generator,
not_null,
@ -2762,7 +2895,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
[None, None, None],
expr.location,
);
return Ok(Some(unsafe { option.load(ctx).into() }));
return Ok(Some(
ctx.builder
.build_load(ptr, "unwrap_some_load")
.map(Into::into)
.unwrap(),
));
}
ValueEnum::Dynamic(_) => {
codegen_unreachable!(ctx, "option must be static or ptr")
@ -3010,8 +3148,9 @@ pub fn create_and_call_function<'ctx>(
value_name: Option<&str>,
configure: Option<&dyn Fn(&FunctionValue<'ctx>)>,
) -> Option<BasicValueEnum<'ctx>> {
let param_tys = params.iter().map(|(ty, _)| ty).copied().collect_vec();
let arg_values = params.iter().map(|(_, value)| value).copied().collect_vec();
let param_tys = params.iter().map(|(ty, _)| ty).copied().map(BasicTypeEnum::into).collect_vec();
let arg_values =
params.iter().map(|(_, value)| value).copied().map(BasicValueEnum::into).collect_vec();
create_fn_and_call(
ctx,

View File

@ -1,9 +1,10 @@
use inkwell::{
attributes::{Attribute, AttributeLoc},
values::{BasicValueEnum, FloatValue},
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
};
use itertools::Either;
use super::{CodeGenContext, expr::infer_and_call_function};
use super::CodeGenContext;
/// Macro to generate extern function
/// Both function return type and function parameter type are `FloatValue`
@ -36,8 +37,8 @@ macro_rules! generate_extern_fn {
($fn_name:ident, $extern_fn:literal $(,$args:ident)* $(,$attributes:literal)*) => {
#[doc = concat!("Invokes the [`", stringify!($extern_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($llvm_name), ") function." )]
pub fn $fn_name<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
$($args: FloatValue<'ctx>,)*
ctx: &CodeGenContext<'ctx, '_>
$(,$args: FloatValue<'ctx>)*,
name: Option<&str>,
) -> FloatValue<'ctx> {
const FN_NAME: &str = $extern_fn;
@ -45,35 +46,99 @@ macro_rules! generate_extern_fn {
let llvm_f64 = ctx.ctx.f64_type();
$(debug_assert_eq!($args.get_type(), llvm_f64);)*
infer_and_call_function(
ctx,
FN_NAME,
Some(llvm_f64.into()),
&[$($args.into()),*],
name,
Some(&|func| {
for attr in [$($attributes),*] {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
}
})
)
.map(BasicValueEnum::into_float_value)
.unwrap()
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[$($args.get_type().into()),*], false);
let func = ctx.module.add_function(FN_NAME, fn_type, None);
for attr in [$($attributes),*] {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
}
func
});
ctx.builder
.build_call(extern_fn, &[$($args.into()),*], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
};
}
generate_extern_fn!("unary", call_tan, "tan");
generate_extern_fn!("unary", call_asin, "asin");
generate_extern_fn!("unary", call_acos, "acos");
generate_extern_fn!("unary", call_atan, "atan");
generate_extern_fn!("unary", call_sinh, "sinh");
generate_extern_fn!("unary", call_cosh, "cosh");
generate_extern_fn!("unary", call_tanh, "tanh");
generate_extern_fn!("unary", call_asinh, "asinh");
generate_extern_fn!("unary", call_acosh, "acosh");
generate_extern_fn!("unary", call_atanh, "atanh");
generate_extern_fn!("unary", call_expm1, "expm1");
generate_extern_fn!(
"unary",
call_cbrt,
"cbrt",
"mustprogress",
"nofree",
"nosync",
"nounwind",
"readonly",
"willreturn"
);
generate_extern_fn!("unary", call_erf, "erf", "nounwind");
generate_extern_fn!("unary", call_erfc, "erfc", "nounwind");
generate_extern_fn!("unary", call_j1, "j1", "nounwind");
generate_extern_fn!("binary", call_atan2, "atan2");
generate_extern_fn!("binary", call_hypot, "hypot", "nounwind");
generate_extern_fn!("binary", call_nextafter, "nextafter", "nounwind");
/// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function.
pub fn call_ldexp<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
arg: FloatValue<'ctx>,
exp: IntValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
const FN_NAME: &str = "ldexp";
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i32 = ctx.ctx.i32_type();
debug_assert_eq!(arg.get_type(), llvm_f64);
debug_assert_eq!(exp.get_type(), llvm_i32);
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_i32.into()], false);
let func = ctx.module.add_function(FN_NAME, fn_type, None);
for attr in ["mustprogress", "nofree", "nounwind", "willreturn"] {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
}
func
});
ctx.builder
.build_call(extern_fn, &[arg.into(), exp.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Macro to generate `np_linalg` and `sp_linalg` functions
/// The function takes as input `NDArray` and returns ()
///
/// Arguments:
/// * `$fn_name:ident`: The identifier of the rust function to be generated
/// * `$extern_fn:ident`: Name of underlying extern function
/// * `$extern_fn:literal`: Name of underlying extern function
/// * (2/3/4): Number of `NDArray` that function takes as input
///
/// Note:
@ -81,51 +146,48 @@ generate_extern_fn!("unary", call_j1, "j1", "nounwind");
/// It is the responsibility of caller to ensure that output `NDArray` is properly allocated on stack
/// The function changes the content of the output `NDArray` in-place
macro_rules! generate_linalg_extern_fn {
($fn_name:ident, $extern_fn:ident, 2) => {
($fn_name:ident, $extern_fn:literal, 2) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2);
};
($fn_name:ident, $extern_fn:ident, 3) => {
($fn_name:ident, $extern_fn:literal, 3) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3);
};
($fn_name:ident, $extern_fn:ident, 4) => {
($fn_name:ident, $extern_fn:literal, 4) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3, mat4);
};
($fn_name:ident, $extern_fn:ident $(,$input_matrix:ident)*) => {
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), "` function." )]
($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => {
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )]
pub fn $fn_name<'ctx>(
ctx: &mut CodeGenContext<'ctx, '_>,
$($input_matrix: BasicValueEnum<'ctx>,)*
ctx: &mut CodeGenContext<'ctx, '_>
$(,$input_matrix: BasicValueEnum<'ctx>)*,
name: Option<&str>,
) {
const FN_NAME: &str = stringify!($extern_fn);
){
const FN_NAME: &str = $extern_fn;
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let fn_type = ctx.ctx.void_type().fn_type(&[$($input_matrix.get_type().into()),*], false);
infer_and_call_function(
ctx,
FN_NAME,
None,
&[$($input_matrix.into(),)*],
name,
Some(&|func| {
let func = ctx.module.add_function(FN_NAME, fn_type, None);
for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(
Attribute::get_named_enum_kind_id("nounwind"),
0,
),
)
}),
);
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
}
func
});
ctx.builder.build_call(extern_fn, &[$($input_matrix.into(),)*], name.unwrap_or_default()).unwrap();
}
};
}
generate_linalg_extern_fn!(call_np_linalg_cholesky, np_linalg_cholesky, 2);
generate_linalg_extern_fn!(call_np_linalg_qr, np_linalg_qr, 3);
generate_linalg_extern_fn!(call_np_linalg_svd, np_linalg_svd, 4);
generate_linalg_extern_fn!(call_np_linalg_inv, np_linalg_inv, 2);
generate_linalg_extern_fn!(call_np_linalg_pinv, np_linalg_pinv, 2);
generate_linalg_extern_fn!(call_np_linalg_matrix_power, np_linalg_matrix_power, 3);
generate_linalg_extern_fn!(call_np_linalg_det, np_linalg_det, 2);
generate_linalg_extern_fn!(call_sp_linalg_lu, sp_linalg_lu, 3);
generate_linalg_extern_fn!(call_sp_linalg_schur, sp_linalg_schur, 3);
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, sp_linalg_hessenberg, 3);
generate_linalg_extern_fn!(call_np_linalg_cholesky, "np_linalg_cholesky", 2);
generate_linalg_extern_fn!(call_np_linalg_qr, "np_linalg_qr", 3);
generate_linalg_extern_fn!(call_np_linalg_svd, "np_linalg_svd", 4);
generate_linalg_extern_fn!(call_np_linalg_inv, "np_linalg_inv", 2);
generate_linalg_extern_fn!(call_np_linalg_pinv, "np_linalg_pinv", 2);
generate_linalg_extern_fn!(call_np_linalg_matrix_power, "np_linalg_matrix_power", 3);
generate_linalg_extern_fn!(call_np_linalg_det, "np_linalg_det", 2);
generate_linalg_extern_fn!(call_sp_linalg_lu, "sp_linalg_lu", 3);
generate_linalg_extern_fn!(call_sp_linalg_schur, "sp_linalg_schur", 3);
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, "sp_linalg_hessenberg", 3);

View File

@ -7,7 +7,7 @@ use inkwell::{
use nac3parser::ast::{Expr, Stmt, StrRef};
use super::{CodeGenContext, bool_to_int_type, expr::*, stmt::*, values::ArraySliceValue};
use super::{bool_to_i1, bool_to_i8, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext};
use crate::{
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef},
@ -248,32 +248,22 @@ pub trait CodeGenerator {
gen_block(self, ctx, stmts)
}
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
/// See [`bool_to_i1`].
fn bool_to_i1<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
self.bool_to_int_type(ctx, bool_value, ctx.ctx.bool_type())
bool_to_i1(&ctx.builder, bool_value)
}
/// Converts the value of a boolean-like value `bool_value` into an `i8`.
/// See [`bool_to_i8`].
fn bool_to_i8<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
self.bool_to_int_type(ctx, bool_value, ctx.ctx.i8_type())
}
/// See [`bool_to_int_type`].
fn bool_to_int_type<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>,
ty: IntType<'ctx>,
) -> IntValue<'ctx> {
bool_to_int_type(&ctx.builder, bool_value, ty)
bool_to_i8(&ctx.builder, ctx.ctx, bool_value)
}
}
@ -308,6 +298,10 @@ impl CodeGenerator for DefaultCodeGenerator {
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
// it should be unsigned, but we don't really need unsigned and this could save us from
// having to do a bit cast...
if self.size_t == 32 { ctx.i32_type() } else { ctx.i64_type() }
if self.size_t == 32 {
ctx.i32_type()
} else {
ctx.i64_type()
}
}
}

View File

@ -1,131 +0,0 @@
use inkwell::{
attributes::{Attribute, AttributeLoc},
values::{BasicValueEnum, FloatValue, IntValue},
};
use crate::codegen::{CodeGenContext, expr::infer_and_call_function};
/// Generates a call to [`isinf`](https://en.cppreference.com/w/c/numeric/math/isinf) in IR. Returns
/// an `i1` representing the result.
pub fn call_isinf<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type();
let llvm_f64 = ctx.ctx.f64_type();
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(ctx, "__nac3_isinf", Some(llvm_i1.into()), &[v.into()], name, None)
.map(BasicValueEnum::into_int_value)
.unwrap()
}
/// Generates a call to [`isnan`](https://en.cppreference.com/w/c/numeric/math/isnan) in IR. Returns
/// an `i1` representing the result.
pub fn call_isnan<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type();
let llvm_f64 = ctx.ctx.f64_type();
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(ctx, "__nac3_isnan", Some(llvm_i1.into()), &[v.into()], name, None)
.map(BasicValueEnum::into_int_value)
.unwrap()
}
/// Macro to generate N-ary functions accepting an arbitrary number of `f64` as arguments and
/// returning `f64`.
///
/// Arguments:
///
/// - `$fn_name:ident`: The name of the Rust function to be generated.
/// - `$builtin_fn:ident`: The name of the builtin function to be invoked in the body of the
/// generated function. The corresponding function in IRRT must be prefixed with `__nac3_`.
/// - `$(,$args:ident)*`: The parameter name(s) to the IRRT function.
macro_rules! generate_f64_nary_fn {
($fn_name:ident, $builtin_fn:ident $(,$args:ident)* $(,)?) => {
#[doc = concat!("Generates a call to [`", stringify!($builtin_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($builtin_fn), ") in IR." )]
pub fn $fn_name<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
$($args: FloatValue<'ctx>,)*
name: Option<&str>,
) -> FloatValue<'ctx> {
const FN_NAME: &str = concat!("__nac3_", stringify!($builtin_fn));
let llvm_f64 = ctx.ctx.f64_type();
$(debug_assert_eq!($args.get_type(), llvm_f64);)*
infer_and_call_function(
ctx,
FN_NAME,
Some(llvm_f64.into()),
&[$($args.into()),*],
name,
Some(&|func| {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0)
);
}),
)
.map(BasicValueEnum::into_float_value)
.unwrap()
}
};
}
generate_f64_nary_fn!(call_tan, tan, arg);
generate_f64_nary_fn!(call_asin, asin, arg);
generate_f64_nary_fn!(call_acos, acos, arg);
generate_f64_nary_fn!(call_atan, atan, arg);
generate_f64_nary_fn!(call_sinh, sinh, arg);
generate_f64_nary_fn!(call_cosh, cosh, arg);
generate_f64_nary_fn!(call_tanh, tanh, arg);
generate_f64_nary_fn!(call_asinh, asinh, arg);
generate_f64_nary_fn!(call_acosh, acosh, arg);
generate_f64_nary_fn!(call_atanh, atanh, arg);
generate_f64_nary_fn!(call_expm1, expm1, arg);
generate_f64_nary_fn!(call_cbrt, cbrt, arg);
generate_f64_nary_fn!(call_erf, erf, arg);
generate_f64_nary_fn!(call_erfc, erfc, arg);
generate_f64_nary_fn!(call_gamma, gamma, z);
generate_f64_nary_fn!(call_atan2, atan2, y, x);
generate_f64_nary_fn!(call_hypot, hypot, x, y);
generate_f64_nary_fn!(call_nextafter, nextafter, from, to);
/// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function.
pub fn call_ldexp<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
arg: FloatValue<'ctx>,
exp: IntValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
const FN_NAME: &str = "__nac3_ldexp";
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i32 = ctx.ctx.i32_type();
debug_assert_eq!(arg.get_type(), llvm_f64);
debug_assert_eq!(exp.get_type(), llvm_i32);
infer_and_call_function(
ctx,
FN_NAME,
Some(llvm_f64.into()),
&[arg.into(), exp.into()],
name,
Some(&|func| {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0),
);
}),
)
.map(BasicValueEnum::into_float_value)
.unwrap()
}

View File

@ -1,16 +1,15 @@
use inkwell::{
AddressSpace, IntPredicate,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
values::{BasicValueEnum, CallSiteValue, IntValue},
AddressSpace, IntPredicate,
};
use itertools::Either;
use super::calculate_len_for_slice_range;
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
macros::codegen_unreachable,
stmt::gen_if_callback,
values::{ArrayLikeValue, ListValue},
CodeGenContext, CodeGenerator,
};
/// This function handles 'end' **inclusively**.
@ -37,6 +36,25 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
assert_eq!(src_idx.2.get_type(), llvm_i32);
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", llvm_pi8);
let slice_assign_fun = {
let ty_vec = vec![
llvm_i32.into(), // dest start idx
llvm_i32.into(), // dest end idx
llvm_i32.into(), // dest step
elem_ptr_type.into(), // dest arr ptr
llvm_i32.into(), // dest arr len
llvm_i32.into(), // src start idx
llvm_i32.into(), // src end idx
llvm_i32.into(), // src step
elem_ptr_type.into(), // src arr ptr
llvm_i32.into(), // src arr len
llvm_i32.into(), // size
];
ctx.module.get_function(fun_symbol).unwrap_or_else(|| {
let fn_t = llvm_i32.fn_type(ty_vec.as_slice(), false);
ctx.module.add_function(fun_symbol, fn_t, None)
})
};
let zero = llvm_i32.const_zero();
let one = llvm_i32.const_int(1, false);
@ -109,7 +127,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
);
let new_len = {
let args = [
let args = vec![
dest_idx.0.into(), // dest start idx
dest_idx.1.into(), // dest end idx
dest_idx.2.into(), // dest step
@ -132,35 +150,25 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
}
.into(),
];
infer_and_call_function(
ctx,
fun_symbol,
Some(llvm_i32.into()),
&args,
Some("slice_assign"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
ctx.builder
.build_call(slice_assign_fun, args.as_slice(), "slice_assign")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
};
// update length
gen_if_callback(
generator,
ctx,
|_, ctx| {
Ok(ctx
.builder
.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update")
.unwrap())
},
|_, ctx| {
let new_len =
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap();
dest_arr.store_size(ctx, new_len);
Ok(())
},
|_, _| Ok(()),
)
.unwrap();
let need_update =
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update").unwrap();
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let update_bb = ctx.ctx.append_basic_block(current, "update");
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
ctx.builder.position_at_end(update_bb);
let new_len =
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap();
dest_arr.store_size(ctx, new_len);
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
ctx.builder.position_at_end(cont_bb);
}

View File

@ -1,10 +1,10 @@
use inkwell::{
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
IntPredicate,
values::{BasicValueEnum, FloatValue, IntValue},
};
use itertools::Either;
use crate::codegen::{
expr::infer_and_call_function,
macros::codegen_unreachable,
{CodeGenContext, CodeGenerator},
};
@ -18,16 +18,18 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
exp: IntValue<'ctx>,
signed: bool,
) -> IntValue<'ctx> {
let base_type = base.get_type();
let symbol = match (base_type.get_bit_width(), exp.get_type().get_bit_width(), signed) {
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width(), signed) {
(32, 32, true) => "__nac3_int_exp_int32_t",
(64, 64, true) => "__nac3_int_exp_int64_t",
(32, 32, false) => "__nac3_int_exp_uint32_t",
(64, 64, false) => "__nac3_int_exp_uint64_t",
_ => codegen_unreachable!(ctx),
};
let base_type = base.get_type();
let pow_fun = ctx.module.get_function(symbol).unwrap_or_else(|| {
let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false);
ctx.module.add_function(symbol, fn_type, None)
});
// throw exception when exp < 0
let ge_zero = ctx
.builder
@ -46,17 +48,85 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
[None, None, None],
ctx.current_loc,
);
ctx.builder
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
infer_and_call_function(
ctx,
symbol,
Some(base_type.into()),
&[base.into(), exp.into()],
Some("call_int_pow"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
/// Generates a call to `isinf` in IR. Returns an `i1` representing the result.
pub fn call_isinf<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_f64 = ctx.ctx.f64_type();
assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_isinf", fn_type, None)
});
let ret = ctx
.builder
.build_call(intrinsic_fn, &[v.into()], "isinf")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
generator.bool_to_i1(ctx, ret)
}
/// Generates a call to `isnan` in IR. Returns an `i1` representing the result.
pub fn call_isnan<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_f64 = ctx.ctx.f64_type();
assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_isnan", fn_type, None)
});
let ret = ctx
.builder
.build_call(intrinsic_fn, &[v.into()], "isnan")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
generator.bool_to_i1(ctx, ret)
}
/// Generates a call to `gamma` in IR. Returns an `f64` representing the result.
pub fn call_gamma<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
let llvm_f64 = ctx.ctx.f64_type();
assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_gamma").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_gamma", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "gamma")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
@ -65,16 +135,17 @@ pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(
ctx,
"__nac3_gammaln",
Some(llvm_f64.into()),
&[v.into()],
Some("gammaln"),
None,
)
.map(BasicValueEnum::into_float_value)
.unwrap()
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_gammaln", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "gammaln")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `j0` in IR. Returns an `f64` representing the result.
@ -83,7 +154,15 @@ pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> Flo
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(ctx, "__nac3_j0", Some(llvm_f64.into()), &[v.into()], Some("j0"), None)
.map(BasicValueEnum::into_float_value)
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_j0", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "j0")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}

View File

@ -1,24 +1,22 @@
use inkwell::{
IntPredicate,
attributes::{Attribute, AttributeLoc},
context::Context,
memory_buffer::MemoryBuffer,
module::Module,
values::{BasicValue, BasicValueEnum, IntValue},
IntPredicate,
};
use nac3parser::ast::Expr;
use super::{CodeGenContext, CodeGenerator};
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
pub use cc_builtins::*;
pub use list::*;
pub use math::*;
pub use range::*;
pub use slice::*;
pub use string::*;
mod cc_builtins;
mod list;
mod math;
pub mod ndarray;

View File

@ -1,10 +1,10 @@
use inkwell::{types::BasicTypeEnum, values::IntValue};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ListValue, ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ListValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_array_set_and_validate_list_shape`.

View File

@ -1,13 +1,15 @@
use inkwell::{
AddressSpace,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace,
};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
expr::{create_and_call_function, infer_and_call_function},
irrt::get_usize_dependent_function_name,
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
types::ProxyType,
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_util_assert_shape_no_negative`.
@ -19,17 +21,24 @@ pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator +
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) {
let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!(shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_shape_no_negative");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[shape.size(ctx, generator).into(), shape.base_ptr(ctx, generator).into()],
&[
(llvm_usize.into(), shape.size(ctx, generator).into()),
(llvm_pusize.into(), shape.base_ptr(ctx, generator).into()),
],
None,
None,
);
@ -46,22 +55,29 @@ pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator +
output_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) {
let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!(ndarray_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(output_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(ndarray_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(output_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_output_shape_same");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[
ndarray_shape.size(ctx, generator).into(),
ndarray_shape.base_ptr(ctx, generator).into(),
output_shape.size(ctx, generator).into(),
output_shape.base_ptr(ctx, generator).into(),
(llvm_usize.into(), ndarray_shape.size(ctx, generator).into()),
(llvm_pusize.into(), ndarray_shape.base_ptr(ctx, generator).into()),
(llvm_usize.into(), output_shape.size(ctx, generator).into()),
(llvm_pusize.into(), output_shape.base_ptr(ctx, generator).into()),
],
None,
None,
@ -77,14 +93,15 @@ pub fn call_nac3_ndarray_size<'ctx>(
ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_size");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
Some("size"),
None,
)
@ -101,14 +118,15 @@ pub fn call_nac3_ndarray_nbytes<'ctx>(
ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_nbytes");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
Some("nbytes"),
None,
)
@ -125,14 +143,15 @@ pub fn call_nac3_ndarray_len<'ctx>(
ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_len");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
Some("len"),
None,
)
@ -148,14 +167,15 @@ pub fn call_nac3_ndarray_is_c_contiguous<'ctx>(
ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_is_c_contiguous");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_i1.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
Some("is_c_contiguous"),
None,
)
@ -174,16 +194,20 @@ pub fn call_nac3_ndarray_get_nth_pelement<'ctx>(
let llvm_i8 = ctx.ctx.i8_type();
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
assert_eq!(index.get_type(), llvm_usize);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_nth_pelement");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_pi8.into()),
&[ndarray.as_abi_value(ctx).into(), index.into()],
&[
(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into()),
(llvm_usize.into(), index.into()),
],
Some("pelement"),
None,
)
@ -205,16 +229,24 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
let llvm_i8 = ctx.ctx.i8_type();
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
let llvm_ndarray = ndarray.get_type();
assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_pelement_by_indices");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_pi8.into()),
&[ndarray.as_abi_value(ctx).into(), indices.base_ptr(ctx, generator).into()],
&[
(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into()),
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
],
Some("pelement"),
None,
)
@ -229,9 +261,18 @@ pub fn call_nac3_ndarray_set_strides_by_shape<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
ndarray: NDArrayValue<'ctx>,
) {
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_set_strides_by_shape");
infer_and_call_function(ctx, &name, None, &[ndarray.as_abi_value(ctx).into()], None, None);
create_and_call_function(
ctx,
&name,
None,
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
None,
None,
);
}
/// Generates a call to `__nac3_ndarray_copy_data`.

View File

@ -1,14 +1,14 @@
use inkwell::values::IntValue;
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
types::{ProxyType, ndarray::ShapeEntryType},
types::{ndarray::ShapeEntryType, ProxyType},
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator,
ndarray::NDArrayValue,
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
TypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_broadcast_to`.
@ -55,13 +55,11 @@ pub fn call_nac3_ndarray_broadcast_shapes<'ctx, G, Shape>(
let llvm_usize = ctx.get_size_type();
assert_eq!(num_shape_entries.get_type(), llvm_usize);
assert!(
ShapeEntryType::is_representable(
shape_entries.base_ptr(ctx, generator).get_type(),
llvm_usize,
)
.is_ok()
);
assert!(ShapeEntryType::is_representable(
shape_entries.base_ptr(ctx, generator).get_type(),
llvm_usize,
)
.is_ok());
assert_eq!(dst_ndims.get_type(), llvm_usize);
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());

View File

@ -1,8 +1,8 @@
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ArrayLikeValue, ArraySliceValue, ProxyValue, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_index`.

View File

@ -1,13 +1,18 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::{
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
expr::{create_and_call_function, infer_and_call_function},
irrt::get_usize_dependent_function_name,
types::ProxyType,
values::{
ProxyValue, TypedArrayLikeAccessor,
ndarray::{NDArrayValue, NDIterValue},
ProxyValue, TypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_nditer_initialize`.
@ -21,19 +26,23 @@ pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) {
let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
None,
&[
iter.as_abi_value(ctx).into(),
ndarray.as_abi_value(ctx).into(),
indices.base_ptr(ctx, generator).into(),
(iter.get_type().as_abi_type().into(), iter.as_abi_value(ctx).into()),
(ndarray.get_type().as_abi_type().into(), ndarray.as_abi_value(ctx).into()),
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
],
None,
None,

View File

@ -1,8 +1,8 @@
use inkwell::values::IntValue;
use inkwell::{types::BasicTypeEnum, values::IntValue};
use crate::codegen::{
CodeGenContext, CodeGenerator, expr::infer_and_call_function,
irrt::get_usize_dependent_function_name, values::TypedArrayLikeAccessor,
expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_matmul_calculate_shapes`.
@ -22,12 +22,26 @@ pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized
) {
let llvm_usize = ctx.get_size_type();
assert_eq!(a_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(b_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(final_ndims.get_type(), llvm_usize);
assert_eq!(new_a_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(new_b_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(a_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(b_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(new_a_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(new_b_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(dst_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_matmul_calculate_shapes");

View File

@ -1,10 +1,10 @@
use inkwell::values::IntValue;
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ArrayLikeValue, ArraySliceValue},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_reshape_resolve_and_check_new_shape`.

View File

@ -1,10 +1,10 @@
use inkwell::{AddressSpace, values::IntValue};
use inkwell::{values::IntValue, AddressSpace};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_transpose`.

View File

@ -1,9 +1,10 @@
use inkwell::{
values::{BasicValueEnum, CallSiteValue, IntValue},
IntPredicate,
values::{BasicValueEnum, IntValue},
};
use itertools::Either;
use crate::codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function};
use crate::codegen::{CodeGenContext, CodeGenerator};
/// Invokes the `__nac3_range_slice_len` in IRRT.
///
@ -22,10 +23,16 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
const SYMBOL: &str = "__nac3_range_slice_len";
let llvm_i32 = ctx.ctx.i32_type();
assert_eq!(start.get_type(), llvm_i32);
assert_eq!(end.get_type(), llvm_i32);
assert_eq!(step.get_type(), llvm_i32);
let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
let fn_t = llvm_i32.fn_type(&[llvm_i32.into(), llvm_i32.into(), llvm_i32.into()], false);
ctx.module.add_function(SYMBOL, fn_t, None)
});
// assert step != 0, throw exception if not
let not_zero = ctx
.builder
@ -40,14 +47,10 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
ctx.current_loc,
);
infer_and_call_function(
ctx,
SYMBOL,
Some(llvm_i32.into()),
&[start.into(), end.into(), step.into()],
Some("calc_len"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
ctx.builder
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}

View File

@ -1,9 +1,10 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue};
use itertools::Either;
use nac3parser::ast::Expr;
use crate::{
codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function},
codegen::{CodeGenContext, CodeGenerator},
typecheck::typedef::Type,
};
@ -16,26 +17,23 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
length: IntValue<'ctx>,
) -> Result<Option<IntValue<'ctx>>, String> {
const SYMBOL: &str = "__nac3_slice_index_bound";
let llvm_i32 = ctx.ctx.i32_type();
assert_eq!(length.get_type(), llvm_i32);
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
let i32_t = ctx.ctx.i32_type();
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into()], false);
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = if let Some(v) = generator.gen_expr(ctx, i)? {
v.to_basic_value_enum(ctx, generator, i.custom.unwrap())?
} else {
return Ok(None);
};
Ok(Some(
infer_and_call_function(
ctx,
SYMBOL,
Some(llvm_i32.into()),
&[i, length.into()],
Some("bounded_ind"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap(),
ctx.builder
.build_call(func, &[i.into(), length.into()], "bounded_ind")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap(),
))
}

View File

@ -1,31 +1,45 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue};
use itertools::Either;
use super::get_usize_dependent_function_name;
use crate::codegen::{CodeGenContext, expr::infer_and_call_function, values::StringValue};
use crate::codegen::CodeGenContext;
/// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal.
pub fn call_string_eq<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
str1: StringValue<'ctx>,
str2: StringValue<'ctx>,
str1_ptr: PointerValue<'ctx>,
str1_len: IntValue<'ctx>,
str2_ptr: PointerValue<'ctx>,
str2_len: IntValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type();
let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq");
infer_and_call_function(
ctx,
&func_name,
Some(llvm_i1.into()),
&[
str1.extract_ptr(ctx).into(),
str1.extract_len(ctx).into(),
str2.extract_ptr(ctx).into(),
str2.extract_len(ctx).into(),
],
Some("str_eq_call"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
let func = ctx.module.get_function(&func_name).unwrap_or_else(|| {
ctx.module.add_function(
&func_name,
llvm_i1.fn_type(
&[
str1_ptr.get_type().into(),
str1_len.get_type().into(),
str2_ptr.get_type().into(),
str2_len.get_type().into(),
],
false,
),
None,
)
});
ctx.builder
.build_call(
func,
&[str1_ptr.into(), str1_len.into(), str2_ptr.into(), str2_len.into()],
"str_eq_call",
)
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
intrinsics::Intrinsic,
types::AnyTypeEnum::IntType,
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue},
AddressSpace,
};
use itertools::Either;
@ -98,29 +98,41 @@ pub fn call_stackrestore<'ctx>(ctx: &CodeGenContext<'ctx, '_>, ptr: PointerValue
/// * `dest` - The pointer to the destination. Must be a pointer to an integer type.
/// * `src` - The pointer to the source. Must be a pointer to an integer type.
/// * `len` - The number of bytes to copy.
/// * `is_volatile` - Whether the `memcpy` operation should be `volatile`.
pub fn call_memcpy<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
const FN_NAME: &str = "llvm.memcpy";
debug_assert!(dest.get_type().get_element_type().is_int_type());
debug_assert!(src.get_type().get_element_type().is_int_type());
debug_assert_eq!(
dest.get_type().get_element_type().into_int_type().get_bit_width(),
src.get_type().get_element_type().into_int_type().get_bit_width(),
);
debug_assert_eq!(len.get_type(), ctx.get_size_type());
debug_assert!(matches!(len.get_type().get_bit_width(), 32 | 64));
debug_assert_eq!(is_volatile.get_type().get_bit_width(), 1);
let llvm_dest_t = dest.get_type();
let llvm_src_t = src.get_type();
let llvm_len_t = len.get_type();
let target_data =
ctx.registry.llvm_options.create_target_machine().map(|tm| tm.get_target_data()).unwrap();
let dest_alignment = target_data.get_abi_alignment(&llvm_dest_t);
let src_alignment = target_data.get_abi_alignment(&llvm_src_t);
let intrinsic_fn = Intrinsic::find(FN_NAME)
.and_then(|intrinsic| {
intrinsic.get_declaration(
&ctx.module,
&[llvm_dest_t.into(), llvm_src_t.into(), llvm_len_t.into()],
)
})
.unwrap();
ctx.builder.build_memcpy(dest, dest_alignment, src, src_alignment, len).unwrap();
ctx.builder
.build_call(intrinsic_fn, &[dest.into(), src.into(), len.into(), is_volatile.into()], "")
.unwrap();
}
/// Invokes the `llvm.memcpy` intrinsic.
@ -132,6 +144,7 @@ pub fn call_memcpy_generic<'ctx>(
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
@ -156,7 +169,7 @@ pub fn call_memcpy_generic<'ctx>(
.unwrap()
};
call_memcpy(ctx, dest, src, len);
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Invokes the `llvm.memcpy` intrinsic.
@ -170,10 +183,11 @@ pub fn call_memcpy_generic_array<'ctx>(
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let llvm_usize = ctx.get_size_type();
let llvm_sizeof_expr_t = llvm_i8.size_of().get_type();
let dest_elem_t = dest.get_type().get_element_type();
let src_elem_t = src.get_type().get_element_type();
@ -195,13 +209,10 @@ pub fn call_memcpy_generic_array<'ctx>(
.unwrap()
};
let sizeof_elem = ctx
.builder
.build_int_truncate_or_bit_cast(src_elem_t.size_of().unwrap(), llvm_usize, "")
.unwrap();
let len = ctx.builder.build_int_mul(len, sizeof_elem, "").unwrap();
let len = ctx.builder.build_int_z_extend_or_bit_cast(len, llvm_sizeof_expr_t, "").unwrap();
let len = ctx.builder.build_int_mul(len, src_elem_t.size_of().unwrap(), "").unwrap();
call_memcpy(ctx, dest, src, len);
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Macro to find and generate build call for llvm intrinsic (body of llvm intrinsic function)

View File

@ -2,15 +2,14 @@ use std::{
cell::OnceCell,
collections::{HashMap, HashSet},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
};
use crossbeam::channel::{Receiver, Sender, unbounded};
use crossbeam::channel::{unbounded, Receiver, Sender};
use inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
builder::Builder,
@ -23,6 +22,7 @@ use inkwell::{
targets::{CodeModel, RelocMode, Target, TargetMachine, TargetTriple},
types::{AnyType, BasicType, BasicTypeEnum, IntType},
values::{BasicValueEnum, FunctionValue, IntValue, PhiValue, PointerValue},
AddressSpace, IntPredicate, OptimizationLevel,
};
use itertools::Itertools;
use parking_lot::{Condvar, Mutex};
@ -32,9 +32,9 @@ use nac3parser::ast::{Location, Stmt, StrRef};
use crate::{
symbol_resolver::{StaticValue, SymbolResolver},
toplevel::{
TopLevelContext, TopLevelDef,
helper::{PrimDef, extract_ndims},
helper::{extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys,
TopLevelContext, TopLevelDef,
},
typecheck::{
type_inferencer::{CodeLocation, PrimitiveStore},
@ -43,10 +43,7 @@ use crate::{
};
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
pub use generator::{CodeGenerator, DefaultCodeGenerator};
use types::{
ExceptionType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
ndarray::NDArrayType,
};
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
pub mod builtin_fns;
pub mod concrete_type;
@ -541,7 +538,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
if PrimDef::contains_id(*obj_id) {
return match &*unifier.get_ty_immutable(ty) {
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
let element_type = get_llvm_type(
get_llvm_type(
ctx,
module,
generator,
@ -549,9 +546,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
top_level,
type_cache,
*params.iter().next().unwrap().1,
);
OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into()
)
.ptr_type(AddressSpace::default())
.into()
}
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
@ -789,11 +786,34 @@ pub fn gen_func_impl<
(primitives.float, context.f64_type().into()),
(primitives.bool, context.i8_type().into()),
(primitives.str, {
StringType::new_with_generator(generator, context).as_abi_type().into()
let name = "str";
match module.get_struct_type(name) {
None => {
let str_type = context.opaque_struct_type("str");
let fields = [
context.i8_type().ptr_type(AddressSpace::default()).into(),
generator.get_size_type(context).into(),
];
str_type.set_body(&fields, false);
str_type.into()
}
Some(t) => t.as_basic_type_enum(),
}
}),
(primitives.range, RangeType::new_with_generator(generator, context).as_abi_type().into()),
(primitives.exception, {
ExceptionType::new_with_generator(generator, context).as_abi_type().into()
let name = "Exception";
if let Some(t) = module.get_struct_type(name) {
t.ptr_type(AddressSpace::default()).as_basic_type_enum()
} else {
let exception = context.opaque_struct_type("Exception");
let int32 = context.i32_type().into();
let int64 = context.i64_type().into();
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
exception.set_body(&fields, false);
exception.ptr_type(AddressSpace::default()).as_basic_type_enum()
}
}),
]
.iter()
@ -832,7 +852,7 @@ pub fn gen_func_impl<
))
};
let has_sret = ret_type.is_some_and(|ty| need_sret(ty));
let has_sret = ret_type.map_or(false, |ty| need_sret(ty));
let mut params = args
.iter()
.filter(|arg| !arg.is_vararg)
@ -913,7 +933,7 @@ pub fn gen_func_impl<
let param_val = param.into_int_value();
if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 {
bool_to_int_type(&builder, param_val, context.i8_type())
bool_to_i8(&builder, context, param_val)
} else {
param_val
}
@ -1028,7 +1048,8 @@ pub fn gen_func_impl<
);
let generator_llvm_usize = generator.get_size_type(context);
assert_eq!(
generator_llvm_usize, target_llvm_usize,
generator_llvm_usize,
target_llvm_usize,
"CodeGenerator (size_t = {generator_llvm_usize}) is not compatible with CodeGen Target (size_t = {target_llvm_usize})",
);
@ -1082,29 +1103,43 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
})
}
/// Converts the value of a boolean-like value `value` into an arbitrary [`IntType`].
///
/// This has the same semantics as `(ty)(value != 0)` in C.
///
/// The returned value is guaranteed to either be `0` or `1`, except for `ty == i1` where only the
/// least-significant bit would be guaranteed to be `0` or `1`.
fn bool_to_int_type<'ctx>(
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
fn bool_to_i1<'ctx>(builder: &Builder<'ctx>, bool_value: IntValue<'ctx>) -> IntValue<'ctx> {
if bool_value.get_type().get_bit_width() == 1 {
bool_value
} else {
builder
.build_int_compare(
IntPredicate::NE,
bool_value,
bool_value.get_type().const_zero(),
"tobool",
)
.unwrap()
}
}
/// Converts the value of a boolean-like value `bool_value` into an `i8`.
fn bool_to_i8<'ctx>(
builder: &Builder<'ctx>,
value: IntValue<'ctx>,
ty: IntType<'ctx>,
ctx: &'ctx Context,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
// i1 -> i1 : %value ; no-op
// i1 -> i<N> : zext i1 %value to i<N> ; guaranteed to be 0 or 1 - see docs
// i<M> -> i<N>: zext i1 (icmp eq i<M> %value, 0) to i<N> ; same as i<M> -> i1 -> i<N>
match (value.get_type().get_bit_width(), ty.get_bit_width()) {
(1, 1) => value,
(1, _) => builder.build_int_z_extend(value, ty, "frombool").unwrap(),
_ => bool_to_int_type(
let value_bits = bool_value.get_type().get_bit_width();
match value_bits {
8 => bool_value,
1 => builder.build_int_z_extend(bool_value, ctx.i8_type(), "frombool").unwrap(),
_ => bool_to_i8(
builder,
ctx,
builder
.build_int_compare(IntPredicate::NE, value, value.get_type().const_zero(), "tobool")
.build_int_compare(
IntPredicate::NE,
bool_value,
bool_value.get_type().const_zero(),
"",
)
.unwrap(),
ty,
),
}
}

View File

@ -1,23 +1,23 @@
use inkwell::{
IntPredicate,
values::{BasicValue, BasicValueEnum, PointerValue},
IntPredicate,
};
use nac3parser::ast::StrRef;
use super::{
CodeGenContext, CodeGenerator,
macros::codegen_unreachable,
stmt::gen_for_callback,
types::ndarray::{NDArrayType, NDIterType},
values::{ProxyValue, ndarray::shape::parse_numpy_int_sequence},
values::{ndarray::shape::parse_numpy_int_sequence, ProxyValue},
CodeGenContext, CodeGenerator,
};
use crate::{
symbol_resolver::ValueEnum,
toplevel::{
DefinitionId,
helper::{arraylike_flatten_element_type, extract_ndims},
numpy::unpack_ndarray_var_tys,
DefinitionId,
},
typecheck::typedef::{FunSignature, Type},
};

View File

@ -1,36 +0,0 @@
---
source: nac3core/src/codegen/test.rs
expression: "module.print_to_string().to_str().map(str::trim).unwrap()"
---
; ModuleID = 'test'
source_filename = "test"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
init:
%add = add i32 %1, %0, !dbg !9
%cmp = icmp eq i32 %add, 1, !dbg !10
%. = select i1 %cmp, i32 %0, i32 0, !dbg !11
ret i32 %., !dbg !12
}
attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 2, !"Dwarf Version", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: "unknown", directory: "")
!4 = distinct !DISubprogram(name: "testing", linkageName: "testing", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
!6 = !{!7}
!7 = !DIBasicType(name: "_", flags: DIFlagPublic)
!8 = !{}
!9 = !DILocation(line: 1, column: 9, scope: !4)
!10 = !DILocation(line: 2, column: 15, scope: !4)
!11 = !DILocation(line: 2, scope: !4)
!12 = !DILocation(line: 3, column: 8, scope: !4)

View File

@ -1,42 +0,0 @@
---
source: nac3core/src/codegen/test.rs
expression: "module.print_to_string().to_str().map(str::trim).unwrap()"
---
; ModuleID = 'test'
source_filename = "test"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
init:
%add.i = shl i32 %0, 1, !dbg !10
%mul = add i32 %add.i, 2, !dbg !10
ret i32 %mul, !dbg !10
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
init:
%add = add i32 %0, 1, !dbg !12
ret i32 %add, !dbg !12
}
attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2, !4}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 2, !"Dwarf Version", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: "unknown", directory: "")
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!5 = distinct !DISubprogram(name: "testing", linkageName: "testing", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
!7 = !{!8}
!8 = !DIBasicType(name: "_", flags: DIFlagPublic)
!9 = !{}
!10 = !DILocation(line: 2, column: 12, scope: !5)
!11 = distinct !DISubprogram(name: "foo.0", linkageName: "foo.0", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
!12 = !DILocation(line: 1, column: 12, scope: !11)

View File

@ -1,35 +1,35 @@
use inkwell::{
IntPredicate,
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
builder::Builder,
types::{BasicType, BasicTypeEnum},
values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue},
IntPredicate,
};
use itertools::{Itertools, izip};
use itertools::{izip, Itertools};
use nac3parser::ast::{
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
};
use super::{
CodeGenContext, CodeGenerator,
expr::{destructure_range, gen_binop_expr},
gen_in_range_check,
irrt::{handle_slice_indices, list_slice_assignment},
macros::codegen_unreachable,
types::{ExceptionType, RangeType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, RangeType},
values::{
ArrayLikeIndexer, ArraySliceValue, ExceptionValue, ListValue, ProxyValue,
ndarray::{RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArraySliceValue, ListValue, ProxyValue,
},
CodeGenContext, CodeGenerator,
};
use crate::{
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
magic_methods::Binop,
typedef::{FunSignature, Type, TypeEnum, iter_type_vars},
typedef::{iter_type_vars, FunSignature, Type, TypeEnum},
},
};
@ -133,7 +133,7 @@ pub fn gen_store_target<'ctx, G: CodeGenerator>(
ptr,
&[
ctx.ctx.i32_type().const_zero(),
ctx.ctx.i32_type().const_int(index.unwrap() as u64, false),
ctx.ctx.i32_type().const_int(index as u64, false),
],
name.unwrap_or(""),
)
@ -234,7 +234,7 @@ pub fn gen_assign_target_list<'ctx, G: CodeGenerator>(
let a = starred_target_index; // Number of RHS values before the starred target
let b = tuple_tys.len() - (targets.len() - 1 - starred_target_index); // Number of RHS values after the starred target
// Thus `tuple[a..b]` is assigned to the starred target.
// Thus `tuple[a..b]` is assigned to the starred target.
// Handle assignment before the starred target
for (target, val, val_ty) in
@ -1337,19 +1337,43 @@ pub fn exn_constructor<'ctx>(
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
exception: Option<&ExceptionValue<'ctx>>,
exception: Option<&BasicValueEnum<'ctx>>,
loc: Location,
) {
if let Some(exception) = exception {
exception.store_location(generator, ctx, loc);
unsafe {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let exception = exception.into_pointer_value();
let file_ptr = ctx
.builder
.build_in_bounds_gep(exception, &[zero, int32.const_int(1, false)], "file_ptr")
.unwrap();
let filename = ctx.gen_string(generator, loc.file.0);
ctx.builder.build_store(file_ptr, filename).unwrap();
let row_ptr = ctx
.builder
.build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr")
.unwrap();
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false)).unwrap();
let col_ptr = ctx
.builder
.build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr")
.unwrap();
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false)).unwrap();
let current_fun = ctx.builder.get_insert_block().and_then(BasicBlock::get_parent).unwrap();
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
exception.store_func(ctx, fun_name);
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
let name_ptr = ctx
.builder
.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr")
.unwrap();
ctx.builder.build_store(name_ptr, fun_name).unwrap();
}
let raise = get_builtins(generator, ctx, "__nac3_raise");
let exception = *exception;
ctx.build_call_or_invoke(raise, &[exception.as_abi_value(ctx).into()], "raise");
ctx.build_call_or_invoke(raise, &[exception], "raise");
} else {
let resume = get_builtins(generator, ctx, "__nac3_resume");
ctx.build_call_or_invoke(resume, &[], "resume");
@ -1468,10 +1492,10 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
ctx.outer_catch_clauses = old_clauses;
ctx.unwind_target = old_unwind;
ctx.return_target = old_return;
ctx.loop_target = old_loop_target.or(ctx.loop_target);
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
let old_unwind = if finalbody.is_empty() {
old_unwind
None
} else {
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
ctx.builder.position_at_end(final_landingpad);
@ -1592,7 +1616,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
}
ctx.unwind_target = old_unwind;
ctx.loop_target = old_loop_target.or(ctx.loop_target);
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
ctx.return_target = old_return;
ctx.builder.position_at_end(landingpad);
@ -1688,259 +1712,13 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
}
/// See [`CodeGenerator::gen_with`].
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn gen_with<G: CodeGenerator>(
_: &mut G,
_: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String> {
let StmtKind::With { items, body, .. } = &stmt.node else { codegen_unreachable!(ctx) };
let mut exits = vec![];
let mut enters = vec![];
// prepare enters and exits
for item in items {
// evaluate the expression first
let expr_ty = item.context_expr.custom.unwrap();
let expr = generator.gen_expr(ctx, &item.context_expr)?.unwrap();
// get the __enter__ method signature and ID
let TypeEnum::TObj { obj_id, fields, .. } = &*ctx.unifier.get_ty(expr_ty) else {
codegen_unreachable!(ctx)
};
let top_level_defs = ctx.top_level.definitions.read();
let def = top_level_defs[obj_id.0].read();
let TopLevelDef::Class { methods, .. } = &*def else { codegen_unreachable!(ctx) };
let enter_fun_id = methods
.iter()
.find(|method| method.0 == "__enter__".into())
.map(|method| method.2)
.unwrap();
let enter = fields.get(&"__enter__".into()).copied().unwrap();
let TypeEnum::TFunc(enter_signature) = &*ctx.unifier.get_ty(enter.0) else {
codegen_unreachable!(ctx)
};
enters.push((
expr_ty,
expr.clone(),
enter_signature.clone(),
enter_fun_id,
item.optional_vars.clone(),
));
// save __exit__() data to be called later in final stage
let exit_fun_id = methods
.iter()
.find(|method| method.0 == "__exit__".into())
.map(|method| method.2)
.unwrap();
let exit = fields.get(&"__exit__".into()).copied().unwrap();
let TypeEnum::TFunc(exit_signature) = &*ctx.unifier.get_ty(exit.0) else {
codegen_unreachable!(ctx)
};
// stack the exits as the exit order is opposite of enter
// would be best to reuse try...finally but re-building Stmt vec seems infeasible
exits.push((expr_ty, expr, exit_signature.clone(), exit_fun_id));
}
let body_gen_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut G|
-> Result<(), String> {
for enter in &enters {
// call __enter__()
let enter_ret = generator.gen_call(
ctx,
Some((enter.0, enter.1.clone())),
(&enter.2, enter.3),
Vec::default(),
)?;
// deal with assignments (`as`)
if let Some(optional_vars) = &enter.4 {
generator.gen_assign(ctx, optional_vars, enter_ret.unwrap().into(), enter.2.ret)?;
}
}
// generate the `with` body
generator.gen_block(ctx, body.iter())
};
let exit_gen_lambda =
|ctx: &mut CodeGenContext<'ctx, 'a>, generator: &mut G| -> Result<(), String> {
// call __exit__()s in the reverse order
for exit in exits.iter().rev() {
generator.gen_call(
ctx,
Some((exit.0, exit.1.clone())),
(&exit.2, exit.3),
Vec::default(),
)?;
}
Ok(())
};
// copied and trimmed from gen_try, to cover try (setup, enter)..finally (exit)
let personality_symbol = ctx.top_level.personality_symbol.as_ref().unwrap();
let personality = ctx.module.get_function(personality_symbol).unwrap_or_else(|| {
let ty = ctx.ctx.i32_type().fn_type(&[], true);
ctx.module.add_function(personality_symbol, ty, None)
});
let exception_type = ctx.get_llvm_type(generator, ctx.primitives.exception);
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::default());
let current_block = ctx.builder.get_insert_block().unwrap();
let current_fun = current_block.get_parent().unwrap();
let landingpad = ctx.ctx.append_basic_block(current_fun, "with.landingpad");
let dispatcher = ctx.ctx.append_basic_block(current_fun, "with.dispatch");
let dispatcher_end = dispatcher;
ctx.builder.position_at_end(dispatcher);
let exn = ctx.builder.build_phi(exception_type, "exn").unwrap();
ctx.builder.position_at_end(current_block);
let mut old_loop_target = None;
let final_state =
generator.gen_var_alloc(ctx, ptr_type.into(), Some("with.final_state.addr"))?;
let mut final_data = Some((final_state, Vec::new(), Vec::new()));
if let Some((continue_target, break_target)) = ctx.loop_target {
let break_proxy = ctx.ctx.append_basic_block(current_fun, "with.break");
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "with.continue");
final_proxy(ctx, break_target, break_proxy, final_data.as_mut().unwrap());
final_proxy(ctx, continue_target, continue_proxy, final_data.as_mut().unwrap());
old_loop_target = ctx.loop_target.replace((continue_proxy, break_proxy));
}
let return_proxy = ctx.ctx.append_basic_block(current_fun, "with.return");
if let Some(return_target) = ctx.return_target {
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
} else {
let return_target = ctx.ctx.append_basic_block(current_fun, "with.return_target");
ctx.builder.position_at_end(return_target);
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret").unwrap());
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue)).unwrap();
ctx.builder.position_at_end(current_block);
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
}
let old_return = ctx.return_target.replace(return_proxy);
let cleanup = ctx.ctx.append_basic_block(current_fun, "with.cleanup");
// replace unwind target, clauses stay the same
let old_unwind = ctx.unwind_target.replace(landingpad);
body_gen_lambda(ctx, generator)?;
let body = ctx.builder.get_insert_block().unwrap();
// reset old_unwind
ctx.unwind_target = old_unwind;
ctx.return_target = old_return;
ctx.loop_target = old_loop_target.or(ctx.loop_target);
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "with.catch.final");
ctx.builder.position_at_end(final_landingpad);
ctx.builder
.build_landing_pad(
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
personality,
&[],
true,
"with.catch.final",
)
.unwrap();
ctx.builder.build_unconditional_branch(cleanup).unwrap();
ctx.builder.position_at_end(body);
let old_unwind = ctx.unwind_target.replace(final_landingpad);
let mut final_proxy_lambda =
|ctx: &mut CodeGenContext<'ctx, 'a>, target: BasicBlock<'ctx>, block: BasicBlock<'ctx>| {
final_proxy(ctx, target, block, final_data.as_mut().unwrap());
};
let redirect = &mut final_proxy_lambda
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>);
let resume = get_builtins(generator, ctx, "__nac3_resume");
let end_catch = get_builtins(generator, ctx, "__nac3_end_catch");
if let Some((continue_target, break_target)) = ctx.loop_target.take() {
let break_proxy = ctx.ctx.append_basic_block(current_fun, "with.break");
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "with.continue");
ctx.builder.position_at_end(break_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
ctx.builder.position_at_end(continue_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
ctx.builder.position_at_end(body);
redirect(ctx, break_target, break_proxy);
redirect(ctx, continue_target, continue_proxy);
ctx.loop_target = Some((continue_proxy, break_proxy));
old_loop_target = Some((continue_target, break_target));
}
let return_proxy = ctx.ctx.append_basic_block(current_fun, "with.return");
ctx.builder.position_at_end(return_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
let return_target = ctx.return_target.take().unwrap_or_else(|| {
let doreturn = ctx.ctx.append_basic_block(current_fun, "with.doreturn");
ctx.builder.position_at_end(doreturn);
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret").unwrap());
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue)).unwrap();
doreturn
});
redirect(ctx, return_target, return_proxy);
ctx.return_target = Some(return_proxy);
let old_return = Some(return_target);
ctx.unwind_target = old_unwind;
ctx.loop_target = old_loop_target.or(ctx.loop_target);
ctx.return_target = old_return;
ctx.builder.position_at_end(landingpad);
let landingpad_value = ctx
.builder
.build_landing_pad(
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
personality,
&Vec::new(),
true,
"try.landingpad",
)
.map(BasicValueEnum::into_struct_value)
.unwrap();
let exn_val = ctx.builder.build_extract_value(landingpad_value, 1, "exn").unwrap();
ctx.builder.build_unconditional_branch(dispatcher).unwrap();
exn.add_incoming(&[(&exn_val, landingpad)]);
if dispatcher_end.get_terminator().is_none() {
ctx.builder.position_at_end(dispatcher_end);
ctx.builder.build_unconditional_branch(cleanup).unwrap();
}
// exception path
ctx.builder.position_at_end(cleanup);
exit_gen_lambda(ctx, generator)?;
ctx.build_call_or_invoke(resume, &[], "resume");
ctx.builder.build_unreachable().unwrap();
// normal path
let (final_state, mut final_targets, final_paths) = final_data.unwrap();
let tail = ctx.ctx.append_basic_block(current_fun, "with.tail");
final_targets.push(tail);
let finalizer = ctx.ctx.append_basic_block(current_fun, "with.exits");
ctx.builder.position_at_end(finalizer);
exit_gen_lambda(ctx, generator)?;
let dest = ctx.builder.build_load(final_state, "final_dest").unwrap();
ctx.builder.build_indirect_branch(dest, &final_targets).unwrap();
for block in &final_paths {
if block.get_terminator().is_none() {
ctx.builder.position_at_end(*block);
ctx.builder.build_unconditional_branch(finalizer).unwrap();
}
}
for block in &[body] {
if block.get_terminator().is_none() {
ctx.builder.position_at_end(*block);
unsafe {
ctx.builder.build_store(final_state, tail.get_address().unwrap()).unwrap();
}
ctx.builder.build_unconditional_branch(finalizer).unwrap();
}
}
ctx.builder.position_at_end(tail);
Ok(())
// TODO: Implement with statement after finishing exceptions
Err(format!("With statement with custom types is not yet supported (at {})", stmt.location))
}
/// Generates IR for a `return` statement.
@ -2082,8 +1860,6 @@ pub fn gen_stmt<G: CodeGenerator>(
} else {
return Ok(());
};
let exc = ExceptionType::get_instance(generator, ctx)
.map_pointer_value(exc.into_pointer_value(), None);
gen_raise(generator, ctx, Some(&exc), stmt.location);
} else {
gen_raise(generator, ctx, None, stmt.location);

View File

@ -3,30 +3,29 @@ use std::{
sync::Arc,
};
use function_name::named;
use indexmap::IndexMap;
use indoc::indoc;
use inkwell::{
OptimizationLevel,
targets::{InitializationConfig, Target},
OptimizationLevel,
};
use nac3parser::{
ast::{FileName, StrRef, fold::Fold},
ast::{fold::Fold, FileName, StrRef},
parser::parse_program,
};
use parking_lot::RwLock;
use super::{
concrete_type::ConcreteTypeStore,
types::{ndarray::NDArrayType, ListType, ProxyType, RangeType},
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator,
DefaultCodeGenerator, WithCall, WorkerRegistry,
concrete_type::ConcreteTypeStore,
types::{ListType, ProxyType, RangeType, ndarray::NDArrayType},
};
use crate::{
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::{
DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
composer::{ComposerConfig, TopLevelComposer},
DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
},
typecheck::{
type_inferencer::{FunctionData, IdentifierInfo, Inferencer, PrimitiveStore},
@ -90,7 +89,6 @@ impl SymbolResolver for Resolver {
}
#[test]
#[named]
fn test_primitives() {
let source = indoc! { "
c = a + b
@ -183,10 +181,60 @@ fn test_primitives() {
id: 0,
};
let f = Arc::new(WithCall::new(Box::new(|module| {
insta::assert_snapshot!(
function_name!(),
module.print_to_string().to_str().map(str::trim).unwrap()
);
// the following IR is equivalent to
// ```
// ; ModuleID = 'test.ll'
// source_filename = "test"
//
// ; Function Attrs: norecurse nounwind readnone
// define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 {
// init:
// %add = add i32 %1, %0
// %cmp = icmp eq i32 %add, 1
// %ifexpr = select i1 %cmp, i32 %0, i32 0
// ret i32 %ifexpr
// }
//
// attributes #0 = { norecurse nounwind readnone }
// ```
// after O2 optimization
let expected = indoc! {"
; ModuleID = 'test'
source_filename = \"test\"
target datalayout = \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128\"
target triple = \"x86_64-unknown-linux-gnu\"
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
init:
%add = add i32 %1, %0, !dbg !9
%cmp = icmp eq i32 %add, 1, !dbg !10
%. = select i1 %cmp, i32 %0, i32 0, !dbg !11
ret i32 %., !dbg !12
}
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
!6 = !{!7}
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!8 = !{}
!9 = !DILocation(line: 1, column: 9, scope: !4)
!10 = !DILocation(line: 2, column: 15, scope: !4)
!11 = !DILocation(line: 0, scope: !4)
!12 = !DILocation(line: 3, column: 8, scope: !4)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
})));
Target::initialize_all(&InitializationConfig::default());
@ -201,7 +249,6 @@ fn test_primitives() {
}
#[test]
#[named]
fn test_simple_call() {
let source_1 = indoc! { "
a = foo(a)
@ -336,10 +383,48 @@ fn test_simple_call() {
id: 0,
};
let f = Arc::new(WithCall::new(Box::new(|module| {
insta::assert_snapshot!(
function_name!(),
module.print_to_string().to_str().map(str::trim).unwrap()
);
let expected = indoc! {"
; ModuleID = 'test'
source_filename = \"test\"
target datalayout = \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128\"
target triple = \"x86_64-unknown-linux-gnu\"
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
init:
%add.i = shl i32 %0, 1, !dbg !10
%mul = add i32 %add.i, 2, !dbg !10
ret i32 %mul, !dbg !10
}
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
init:
%add = add i32 %0, 1, !dbg !12
ret i32 %add, !dbg !12
}
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2, !4}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
!7 = !{!8}
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!9 = !{}
!10 = !DILocation(line: 2, column: 12, scope: !5)
!11 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
!12 = !DILocation(line: 1, column: 12, scope: !11)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
})));
Target::initialize_all(&InitializationConfig::default());

View File

@ -1,264 +0,0 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
ProxyType,
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
};
use crate::{
codegen::{CodeGenContext, CodeGenerator, values::ExceptionValue},
typecheck::typedef::{Type, TypeEnum},
};
/// Proxy type for an `Exception` in LLVM.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ExceptionType<'ctx> {
ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
}
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
pub struct ExceptionStructFields<'ctx> {
/// The ID of the exception name.
#[value_type(i32_type())]
pub name: StructField<'ctx, IntValue<'ctx>>,
/// The file where the exception originated from.
#[value_type(get_struct_type("str").unwrap())]
pub file: StructField<'ctx, StructValue<'ctx>>,
/// The line number where the exception originated from.
#[value_type(i32_type())]
pub line: StructField<'ctx, IntValue<'ctx>>,
/// The column number where the exception originated from.
#[value_type(i32_type())]
pub col: StructField<'ctx, IntValue<'ctx>>,
/// The function name where the exception originated from.
#[value_type(get_struct_type("str").unwrap())]
pub func: StructField<'ctx, StructValue<'ctx>>,
/// The exception message.
#[value_type(get_struct_type("str").unwrap())]
pub message: StructField<'ctx, StructValue<'ctx>>,
#[value_type(i64_type())]
pub param0: StructField<'ctx, IntValue<'ctx>>,
#[value_type(i64_type())]
pub param1: StructField<'ctx, IntValue<'ctx>>,
#[value_type(i64_type())]
pub param2: StructField<'ctx, IntValue<'ctx>>,
}
impl<'ctx> ExceptionType<'ctx> {
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use]
fn fields(
ctx: impl AsContextRef<'ctx>,
llvm_usize: IntType<'ctx>,
) -> ExceptionStructFields<'ctx> {
ExceptionStructFields::new(ctx, llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of an `Exception`.
#[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
const NAME: &str = "Exception";
assert!(ctx.get_struct_type("str").is_some());
if let Some(t) = ctx.get_struct_type(NAME) {
t.ptr_type(AddressSpace::default())
} else {
let exn_ty = ctx.opaque_struct_type(NAME);
let field_tys =
Self::fields(ctx, llvm_usize).into_iter().map(|field| field.1).collect_vec();
exn_ty.set_body(&field_tys, false);
exn_ty.ptr_type(AddressSpace::default())
}
}
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
let llvm_str = Self::llvm_type(ctx, llvm_usize);
Self { ty: llvm_str, llvm_usize }
}
/// Creates an instance of [`ExceptionType`].
#[must_use]
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
Self::new_impl(ctx.ctx, ctx.get_size_type())
}
/// Creates an instance of [`ExceptionType`].
#[must_use]
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
) -> Self {
Self::new_impl(ctx, generator.get_size_type(ctx))
}
/// Creates an [`ExceptionType`] from a [unifier type][Type].
#[must_use]
pub fn from_unifier_type(ctx: &mut CodeGenContext<'ctx, '_>, ty: Type) -> Self {
// Check unifier type
assert!(
matches!(&*ctx.unifier.get_ty_immutable(ty), TypeEnum::TObj { obj_id, .. } if *obj_id == ctx.primitives.exception.obj_id(&ctx.unifier).unwrap())
);
Self::new_impl(ctx.ctx, ctx.get_size_type())
}
/// Creates an [`ExceptionType`] from a [`StructType`] representing an `Exception`.
#[must_use]
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
}
/// Creates an [`ExceptionType`] from a [`PointerType`] representing an `Exception`.
#[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize }
}
/// Returns an instance of [`ExceptionType`] by obtaining the LLVM representation of the builtin
/// `Exception` type.
#[must_use]
pub fn get_instance<G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
) -> Self {
Self::from_pointer_type(
ctx.get_llvm_type(generator, ctx.primitives.exception).into_pointer_type(),
ctx.get_size_type(),
)
}
/// Allocates an instance of [`ExceptionValue`] as if by calling `alloca` on the base type.
///
/// See [`ProxyType::raw_alloca`].
#[must_use]
pub fn alloca(
&self,
ctx: &mut CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
self.raw_alloca(ctx, name),
self.llvm_usize,
name,
)
}
/// Allocates an instance of [`ExceptionValue`] as if by calling `alloca` on the base type.
///
/// See [`ProxyType::raw_alloca_var`].
#[must_use]
pub fn alloca_var<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
self.raw_alloca_var(generator, ctx, name),
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`ExceptionValue`].
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
value: StructValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_struct_value(
generator,
ctx,
value,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`ExceptionValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
}
}
impl<'ctx> ProxyType<'ctx> for ExceptionType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>;
type Value = ExceptionValue<'ctx>;
fn is_representable(
llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize)
} else {
Err(format!("Expected pointer type, got {llvm_ty:?}"))
}
}
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
let ctx = ty.get_context();
let llvm_ty = ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
};
check_struct_type_matches_fields(Self::fields(ctx, llvm_usize), llvm_ty, "exception", &[])
}
fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
}
fn as_base_type(&self) -> Self::Base {
self.ty
}
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for ExceptionType<'ctx> {
type StructFields = ExceptionStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
}
impl<'ctx> From<ExceptionType<'ctx>> for PointerType<'ctx> {
fn from(value: ExceptionType<'ctx>) -> Self {
value.as_base_type()
}
}

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
context::Context,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace, IntPredicate, OptimizationLevel,
};
use itertools::Itertools;
@ -11,14 +11,14 @@ use nac3core_derive::StructFields;
use super::ProxyType;
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
types::structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
},
values::ListValue,
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum, iter_type_vars},
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
};
/// Proxy type for a `list` type in LLVM.

View File

@ -25,19 +25,13 @@ use super::{
values::{ArraySliceValue, ProxyValue},
{CodeGenContext, CodeGenerator},
};
pub use exception::*;
pub use list::*;
pub use option::*;
pub use range::*;
pub use string::*;
pub use tuple::*;
mod exception;
mod list;
pub mod ndarray;
mod option;
mod range;
mod string;
pub mod structure;
mod tuple;
pub mod utils;

View File

@ -1,18 +1,19 @@
use inkwell::{
AddressSpace,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
stmt::gen_if_else_expr_callback,
types::{ListType, ProxyType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, ListType, ProxyType},
values::{
ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, TypedArrayLikeAdapter,
TypedArrayLikeMutator, ndarray::NDArrayValue,
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
TypedArrayLikeAdapter, TypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
},
toplevel::helper::{arraylike_flatten_element_type, arraylike_get_ndims},
typecheck::typedef::{Type, TypeEnum},

View File

@ -1,20 +1,20 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use crate::codegen::{
CodeGenContext, CodeGenerator,
types::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType},
ProxyType,
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
},
values::ndarray::ShapeEntryValue,
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
context::Context,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
@ -10,15 +10,15 @@ use nac3core_derive::StructFields;
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
types::{
ProxyType,
structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
},
ProxyType,
},
values::ndarray::ContiguousNDArrayValue,
CodeGenContext, CodeGenerator,
},
toplevel::numpy::unpack_ndarray_var_tys,
typecheck::typedef::Type,

View File

@ -1,12 +1,12 @@
use inkwell::{
IntPredicate,
values::{BasicValueEnum, IntValue},
IntPredicate,
};
use super::NDArrayType;
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt, types::ProxyType, values::TypedArrayLikeAccessor,
irrt, types::ProxyType, values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
},
typecheck::typedef::Type,
};

View File

@ -1,23 +1,23 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use crate::codegen::{
CodeGenContext, CodeGenerator,
types::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType},
ProxyType,
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
},
values::{
ArrayLikeIndexer, ArraySliceValue,
ndarray::{NDIndexValue, RustNDIndex},
ArrayLikeIndexer, ArraySliceValue,
},
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]

View File

@ -2,16 +2,16 @@ use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::gen_for_callback,
types::{
ProxyType,
ndarray::{NDArrayType, NDIterType},
ProxyType,
},
values::{
ArrayLikeValue, ProxyValue,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ArrayLikeValue, ProxyValue,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayType<'ctx> {

View File

@ -1,20 +1,20 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{BasicValue, IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType},
ProxyType,
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
};
use crate::{
codegen::{
values::{TypedArrayLikeMutator, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, TypedArrayLikeMutator},
{CodeGenContext, CodeGenerator},
},
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
@ -10,14 +10,15 @@ use nac3core_derive::StructFields;
use super::ProxyType;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
types::structure::{
StructField, StructFields, StructProxyType, check_struct_type_matches_fields,
check_struct_type_matches_fields, StructField, StructFields, StructProxyType,
},
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
ndarray::{NDArrayValue, NDIterValue},
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
},
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]

View File

@ -1,188 +0,0 @@
use inkwell::{
AddressSpace,
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType},
values::{BasicValue, BasicValueEnum, PointerValue},
};
use super::ProxyType;
use crate::{
codegen::{CodeGenContext, CodeGenerator, values::OptionValue},
typecheck::typedef::{Type, TypeEnum, iter_type_vars},
};
/// Proxy type for an `Option` type in LLVM.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct OptionType<'ctx> {
ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
}
impl<'ctx> OptionType<'ctx> {
/// Creates an LLVM type corresponding to the expected structure of an `Option`.
#[must_use]
fn llvm_type(element_type: &impl BasicType<'ctx>) -> PointerType<'ctx> {
element_type.ptr_type(AddressSpace::default())
}
fn new_impl(element_type: &impl BasicType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
let llvm_option = Self::llvm_type(element_type);
Self { ty: llvm_option, llvm_usize }
}
/// Creates an instance of [`OptionType`].
#[must_use]
pub fn new(ctx: &CodeGenContext<'ctx, '_>, element_type: &impl BasicType<'ctx>) -> Self {
Self::new_impl(element_type, ctx.get_size_type())
}
/// Creates an instance of [`OptionType`].
#[must_use]
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
element_type: &impl BasicType<'ctx>,
) -> Self {
Self::new_impl(element_type, generator.get_size_type(ctx))
}
/// Creates an [`OptionType`] from a [unifier type][Type].
#[must_use]
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &mut CodeGenContext<'ctx, '_>,
ty: Type,
) -> Self {
// Check unifier type and extract `element_type`
let elem_type = match &*ctx.unifier.get_ty_immutable(ty) {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
{
iter_type_vars(params).next().unwrap().ty
}
_ => panic!("Expected `option` type, but got {}", ctx.unifier.stringify(ty)),
};
let llvm_usize = ctx.get_size_type();
let llvm_elem_type = ctx.get_llvm_type(generator, elem_type);
Self::new_impl(&llvm_elem_type, llvm_usize)
}
/// Creates an [`OptionType`] from a [`PointerType`].
#[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize }
}
/// Returns the element type of this `Option` type.
#[must_use]
pub fn element_type(&self) -> BasicTypeEnum<'ctx> {
BasicTypeEnum::try_from(self.ty.get_element_type()).unwrap()
}
/// Allocates an [`OptionValue`] on the stack.
///
/// The returned value will be `Some(v)` if [`value` contains a value][Option::is_some],
/// otherwise `none` will be returned.
#[must_use]
pub fn construct<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
value: Option<BasicValueEnum<'ctx>>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
let ptr = if let Some(v) = value {
let pvar = self.raw_alloca_var(generator, ctx, name);
ctx.builder.build_store(pvar, v).unwrap();
pvar
} else {
self.ty.const_null()
};
self.map_pointer_value(ptr, name)
}
/// Allocates an [`OptionValue`] on the stack.
///
/// The returned value will always be `none`.
#[must_use]
pub fn construct_empty<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
self.construct(generator, ctx, None, name)
}
/// Allocates an [`OptionValue`] on the stack.
///
/// The returned value will be set to `Some(value)`.
#[must_use]
pub fn construct_some_value<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
value: &impl BasicValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
self.construct(generator, ctx, Some(value.as_basic_value_enum()), name)
}
/// Converts an existing value into a [`OptionValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
}
}
impl<'ctx> ProxyType<'ctx> for OptionType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>;
type Value = OptionValue<'ctx>;
fn is_representable(
llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize)
} else {
Err(format!("Expected pointer type, got {llvm_ty:?}"))
}
}
fn has_same_repr(ty: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
BasicTypeEnum::try_from(ty.get_element_type())
.map_err(|()| format!("Expected `ty` to be a BasicTypeEnum, got {ty}"))?;
Ok(())
}
fn alloca_type(&self) -> impl BasicType<'ctx> {
self.element_type()
}
fn as_base_type(&self) -> Self::Base {
self.ty
}
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> From<OptionType<'ctx>> for PointerType<'ctx> {
fn from(value: OptionType<'ctx>) -> Self {
value.as_base_type()
}
}

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
context::Context,
types::{AnyTypeEnum, ArrayType, BasicType, BasicTypeEnum, IntType, PointerType},
values::{ArrayValue, PointerValue},
AddressSpace,
};
use super::ProxyType;

View File

@ -1,177 +0,0 @@
use inkwell::{
AddressSpace,
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{GlobalValue, IntValue, PointerValue, StructValue},
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
ProxyType,
structure::{StructField, StructFields, check_struct_type_matches_fields},
};
use crate::codegen::{CodeGenContext, CodeGenerator, values::StringValue};
/// Proxy type for a `str` type in LLVM.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct StringType<'ctx> {
ty: StructType<'ctx>,
llvm_usize: IntType<'ctx>,
}
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
pub struct StringStructFields<'ctx> {
/// Pointer to the first character of the string.
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
pub ptr: StructField<'ctx, PointerValue<'ctx>>,
/// Length of the string.
#[value_type(usize)]
pub len: StructField<'ctx, IntValue<'ctx>>,
}
impl<'ctx> StringType<'ctx> {
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use]
fn fields(llvm_usize: IntType<'ctx>) -> StringStructFields<'ctx> {
StringStructFields::new(llvm_usize.get_context(), llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of a `str`.
#[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> StructType<'ctx> {
const NAME: &str = "str";
if let Some(t) = ctx.get_struct_type(NAME) {
t
} else {
let str_ty = ctx.opaque_struct_type(NAME);
let field_tys = Self::fields(llvm_usize).into_iter().map(|field| field.1).collect_vec();
str_ty.set_body(&field_tys, false);
str_ty
}
}
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
let llvm_str = Self::llvm_type(ctx, llvm_usize);
Self { ty: llvm_str, llvm_usize }
}
/// Creates an instance of [`StringType`].
#[must_use]
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
Self::new_impl(ctx.ctx, ctx.get_size_type())
}
/// Creates an instance of [`StringType`].
#[must_use]
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
) -> Self {
Self::new_impl(ctx, generator.get_size_type(ctx))
}
/// Creates an [`StringType`] from a [`StructType`] representing a `str`.
#[must_use]
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ty, llvm_usize).is_ok());
Self { ty, llvm_usize }
}
/// Creates an [`StringType`] from a [`PointerType`] representing a `str`.
#[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
Self::from_struct_type(ptr_ty.get_element_type().into_struct_type(), llvm_usize)
}
/// Returns the fields present in this [`StringType`].
#[must_use]
pub fn get_fields(&self) -> StringStructFields<'ctx> {
Self::fields(self.llvm_usize)
}
/// Constructs a global constant string.
#[must_use]
pub fn construct_constant(
&self,
ctx: &CodeGenContext<'ctx, '_>,
v: &str,
name: Option<&'ctx str>,
) -> StringValue<'ctx> {
let str_ptr = ctx
.builder
.build_global_string_ptr(v, "const")
.map(GlobalValue::as_pointer_value)
.unwrap();
let size = ctx.get_size_type().const_int(v.len() as u64, false);
self.map_struct_value(
self.as_abi_type().const_named_struct(&[str_ptr.into(), size.into()]),
name,
)
}
/// Converts an existing value into a [`StringValue`].
#[must_use]
pub fn map_struct_value(
&self,
value: StructValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name)
}
/// Converts an existing value into a [`StringValue`].
#[must_use]
pub fn map_pointer_value(
&self,
ctx: &CodeGenContext<'ctx, '_>,
value: PointerValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(ctx, value, self.llvm_usize, name)
}
}
impl<'ctx> ProxyType<'ctx> for StringType<'ctx> {
type ABI = StructType<'ctx>;
type Base = StructType<'ctx>;
type Value = StringValue<'ctx>;
fn is_representable(
llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize)
} else {
Err(format!("Expected structure type, got {llvm_ty:?}"))
}
}
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
check_struct_type_matches_fields(Self::fields(llvm_usize), ty, "str", &[])
}
fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type()
}
fn as_base_type(&self) -> Self::Base {
self.ty
}
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> From<StringType<'ctx>> for StructType<'ctx> {
fn from(value: StringType<'ctx>) -> Self {
value.as_base_type()
}
}

View File

@ -1,10 +1,10 @@
use std::marker::PhantomData;
use inkwell::{
AddressSpace,
context::AsContextRef,
types::{BasicTypeEnum, IntType, PointerType, StructType},
values::{AggregateValueEnum, BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;

View File

@ -7,7 +7,7 @@ use itertools::Itertools;
use super::ProxyType;
use crate::{
codegen::{CodeGenContext, CodeGenerator, values::TupleValue},
codegen::{values::TupleValue, CodeGenContext, CodeGenerator},
typecheck::typedef::{Type, TypeEnum},
};
@ -110,13 +110,20 @@ impl<'ctx> TupleType<'ctx> {
/// The caller must ensure that the index is valid.
#[must_use]
pub unsafe fn type_at_index_unchecked(&self, index: u32) -> BasicTypeEnum<'ctx> {
unsafe { self.ty.get_field_type_at_index_unchecked(index) }
self.ty.get_field_type_at_index_unchecked(index)
}
/// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value.
#[must_use]
pub fn construct(&self, name: Option<&'ctx str>) -> <Self as ProxyType<'ctx>>::Value {
self.map_struct_value(self.as_abi_type().const_zero(), name)
pub fn construct(
&self,
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
self.map_struct_value(
Self::llvm_type(ctx.ctx, &self.ty.get_field_types()).const_zero(),
name,
)
}
/// Constructs a [`TupleValue`] from `objects`. The resulting tuple preserves the order of
@ -131,15 +138,14 @@ impl<'ctx> TupleType<'ctx> {
let values = objects.into_iter().collect_vec();
assert_eq!(values.len(), self.num_elements() as usize);
assert!(
values.iter().enumerate().all(|(i, v)| {
v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) }
})
);
assert!(values
.iter()
.enumerate()
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } }));
let mut value = self.construct(name);
let mut value = self.construct(ctx, name);
for (i, val) in values.into_iter().enumerate() {
value.insert_element(ctx, i as u32, val);
value.store_element(ctx, i as u32, val);
}
value

View File

@ -1,23 +1,23 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context, ContextRef},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use crate::codegen::{
CodeGenContext, CodeGenerator,
types::{
ProxyType,
structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
},
ProxyType,
},
values::utils::SliceValue,
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]

View File

@ -1,7 +1,7 @@
use inkwell::{
IntPredicate,
types::AnyTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue},
IntPredicate,
};
use crate::codegen::{CodeGenContext, CodeGenerator};

View File

@ -1,188 +0,0 @@
use inkwell::{
types::IntType,
values::{IntValue, PointerValue, StructValue},
};
use itertools::Itertools;
use nac3parser::ast::Location;
use super::{ProxyValue, StringValue, structure::StructProxyValue};
use crate::codegen::{
CodeGenContext, CodeGenerator,
types::{
ExceptionType,
structure::{StructField, StructProxyType},
},
};
/// Proxy type for accessing an `Exception` value in LLVM.
#[derive(Copy, Clone)]
pub struct ExceptionValue<'ctx> {
value: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
}
impl<'ctx> ExceptionValue<'ctx> {
/// Creates an [`ExceptionValue`] from a [`StructValue`].
#[must_use]
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
let pval = generator
.gen_var_alloc(
ctx,
val.get_type().into(),
name.map(|name| format!("{name}.addr")).as_deref(),
)
.unwrap();
ctx.builder.build_store(pval, val).unwrap();
Self::from_pointer_value(pval, llvm_usize, name)
}
/// Creates an [`ExceptionValue`] from a [`PointerValue`].
#[must_use]
pub fn from_pointer_value(
ptr: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name }
}
fn name_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().name
}
/// Stores the ID of the exception name into this instance.
pub fn store_name(&self, ctx: &CodeGenContext<'ctx, '_>, name: IntValue<'ctx>) {
debug_assert_eq!(name.get_type(), ctx.ctx.i32_type());
self.name_field().store(ctx, self.value, name, self.name);
}
fn file_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
self.get_type().get_fields().file
}
/// Stores the file name of the exception source into this instance.
pub fn store_file(&self, ctx: &CodeGenContext<'ctx, '_>, file: StructValue<'ctx>) {
debug_assert!(StringValue::is_instance(file, self.llvm_usize).is_ok());
self.file_field().store(ctx, self.value, file, self.name);
}
fn line_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().line
}
fn col_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().col
}
/// Stores the [location][Location] of the exception source into this instance.
pub fn store_location<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
location: Location,
) {
let llvm_i32 = ctx.ctx.i32_type();
let filename = ctx.gen_string(generator, location.file.0);
self.store_file(ctx, filename);
self.line_field().store(
ctx,
self.value,
llvm_i32.const_int(location.row as u64, false),
self.name,
);
self.col_field().store(
ctx,
self.value,
llvm_i32.const_int(location.column as u64, false),
self.name,
);
}
fn func_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
self.get_type().get_fields().func
}
/// Stores the function name of the exception source into this instance.
pub fn store_func(&self, ctx: &CodeGenContext<'ctx, '_>, func: StructValue<'ctx>) {
debug_assert!(StringValue::is_instance(func, self.llvm_usize).is_ok());
self.func_field().store(ctx, self.value, func, self.name);
}
fn message_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
self.get_type().get_fields().message
}
/// Stores the exception message into this instance.
pub fn store_message(&self, ctx: &CodeGenContext<'ctx, '_>, message: StructValue<'ctx>) {
debug_assert!(StringValue::is_instance(message, self.llvm_usize).is_ok());
self.message_field().store(ctx, self.value, message, self.name);
}
fn param0_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().param0
}
fn param1_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().param1
}
fn param2_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().param2
}
/// Stores the parameters of the exception into this instance.
///
/// If the parameter does not exist, pass `i64 0` in the parameter slot.
pub fn store_params(&self, ctx: &CodeGenContext<'ctx, '_>, params: &[IntValue<'ctx>; 3]) {
debug_assert!(params.iter().all(|p| p.get_type() == ctx.ctx.i64_type()));
[self.param0_field(), self.param1_field(), self.param2_field()]
.into_iter()
.zip_eq(params)
.for_each(|(field, param)| {
field.store(ctx, self.value, *param, self.name);
});
}
}
impl<'ctx> ProxyValue<'ctx> for ExceptionValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>;
type Type = ExceptionType<'ctx>;
fn get_type(&self) -> Self::Type {
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
}
fn as_base_value(&self) -> Self::Base {
self.value
}
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
}
impl<'ctx> StructProxyValue<'ctx> for ExceptionValue<'ctx> {}
impl<'ctx> From<ExceptionValue<'ctx>> for PointerValue<'ctx> {
fn from(value: ExceptionValue<'ctx>) -> Self {
value.as_base_value()
}
}

View File

@ -1,17 +1,17 @@
use inkwell::{
AddressSpace, IntPredicate,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace, IntPredicate,
};
use super::{
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator, structure::StructProxyValue,
structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue,
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
};
use crate::codegen::{
types::{
ListType, ProxyType,
structure::{StructField, StructProxyType},
ListType, ProxyType,
},
{CodeGenContext, CodeGenerator},
};

View File

@ -1,21 +1,15 @@
use inkwell::{types::IntType, values::BasicValue};
use super::{CodeGenContext, types::ProxyType};
use super::{types::ProxyType, CodeGenContext};
pub use array::*;
pub use exception::*;
pub use list::*;
pub use option::*;
pub use range::*;
pub use string::*;
pub use tuple::*;
mod array;
mod exception;
mod list;
pub mod ndarray;
mod option;
mod range;
mod string;
pub mod structure;
mod tuple;
pub mod utils;

View File

@ -5,17 +5,18 @@ use inkwell::{
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
types::{
ProxyType,
ndarray::{NDArrayType, ShapeEntryType},
structure::{StructField, StructProxyType},
ProxyType,
},
values::{
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
TypedArrayLikeAdapter, TypedArrayLikeMutator, ndarray::NDArrayValue,
structure::StructProxyValue,
ndarray::NDArrayValue, structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue,
ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
TypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]
@ -167,11 +168,9 @@ fn broadcast_shapes<'ctx, G, Shape>(
let llvm_usize = ctx.get_size_type();
let llvm_shape_ty = ShapeEntryType::new(ctx);
assert!(
in_shape_entries
.iter()
.all(|entry| entry.0.element_type(ctx, generator) == llvm_usize.into())
);
assert!(in_shape_entries
.iter()
.all(|entry| entry.0.element_type(ctx, generator) == llvm_usize.into()));
assert_eq!(broadcast_shape.element_type(ctx, generator), llvm_usize.into());
// Prepare input shape entries to be passed to `call_nac3_ndarray_broadcast_shapes`.

View File

@ -1,18 +1,18 @@
use inkwell::{
AddressSpace,
types::{BasicType, BasicTypeEnum, IntType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use super::NDArrayValue;
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::gen_if_callback,
types::{
ndarray::{ContiguousNDArrayType, NDArrayType},
structure::{StructField, StructProxyType},
},
values::{ArrayLikeValue, ProxyValue, structure::StructProxyValue},
values::{structure::StructProxyValue, ArrayLikeValue, ProxyValue},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]

View File

@ -2,9 +2,9 @@ use inkwell::values::{BasicValue, BasicValueEnum};
use super::{NDArrayValue, NDIterValue, ScalarOrNDArray};
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::{BreakContinueHooks, gen_for_callback},
stmt::{gen_for_callback, BreakContinueHooks},
types::ndarray::NDIterType,
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{
AddressSpace,
types::IntType,
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
@ -9,15 +9,16 @@ use nac3parser::ast::{Expr, ExprKind};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
types::{
ndarray::{NDArrayType, NDIndexType},
structure::{StructField, StructProxyType},
utils::SliceType,
},
values::{
ProxyValue, ndarray::NDArrayValue, structure::StructProxyValue, utils::RustSlice,
ndarray::NDArrayValue, structure::StructProxyValue, utils::RustSlice, ProxyValue,
},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::Type,
};

View File

@ -1,11 +1,11 @@
use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
use crate::codegen::{
CodeGenContext, CodeGenerator,
values::{
ProxyValue,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ProxyValue,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -5,7 +5,6 @@ use nac3parser::ast::Operator;
use super::{NDArrayOut, NDArrayValue, RustNDIndex};
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
expr::gen_binop_expr_with_values,
irrt,
stmt::gen_for_callback_incrementing,
@ -14,6 +13,7 @@ use crate::{
ArrayLikeValue, ArraySliceValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
},
toplevel::helper::arraylike_flatten_element_type,
typecheck::{magic_methods::Binop, typedef::Type},
@ -213,7 +213,9 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
Binop::normal(Operator::Mult),
(&Some(rhs_dtype), b_kj),
ctx.current_loc,
)?;
)?
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
// dst_[...]ij += x
let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap();
@ -224,7 +226,9 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
Binop::normal(Operator::Add),
(&Some(dst_dtype), x),
ctx.current_loc,
)?;
)?
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
ctx.builder.build_store(pdst_ij, dst_ij).unwrap();
Ok(())

View File

@ -1,28 +1,29 @@
use std::iter::repeat_n;
use inkwell::{
AddressSpace, IntPredicate,
types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace, IntPredicate,
};
use itertools::Itertools;
use super::{
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, TypedArrayLikeAccessor,
TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator, structure::StructProxyValue,
structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue,
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator,
};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
llvm_intrinsics::{call_int_umin, call_memcpy_generic_array},
stmt::gen_for_callback_incrementing,
type_aligned_alloca,
types::{
TupleType,
ndarray::NDArrayType,
structure::{StructField, StructProxyType},
TupleType,
},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum},
};
@ -225,7 +226,13 @@ impl<'ctx> NDArrayValue<'ctx> {
) {
let num_items = self.load_ndims(ctx);
call_memcpy_generic_array(ctx, self.shape().base_ptr(ctx, generator), shape, num_items);
call_memcpy_generic_array(
ctx,
self.shape().base_ptr(ctx, generator),
shape,
num_items,
ctx.ctx.bool_type().const_zero(),
);
}
/// Copy shape dimensions from an ndarray.
@ -251,7 +258,13 @@ impl<'ctx> NDArrayValue<'ctx> {
) {
let num_items = self.load_ndims(ctx);
call_memcpy_generic_array(ctx, self.strides().base_ptr(ctx, generator), strides, num_items);
call_memcpy_generic_array(
ctx,
self.strides().base_ptr(ctx, generator),
strides,
num_items,
ctx.ctx.bool_type().const_zero(),
);
}
/// Copy strides dimensions from an ndarray.

View File

@ -1,18 +1,19 @@
use inkwell::{
AddressSpace,
types::{BasicType, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace,
};
use super::NDArrayValue;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
stmt::{BreakContinueHooks, gen_for_callback},
irrt,
stmt::{gen_for_callback, BreakContinueHooks},
types::{
ndarray::NDIterType,
structure::{StructField, StructProxyType},
},
values::{ArraySliceValue, ProxyValue, TypedArrayLikeAdapter, structure::StructProxyValue},
values::{structure::StructProxyValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]

View File

@ -2,13 +2,13 @@ use inkwell::values::{BasicValueEnum, IntValue};
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
stmt::gen_for_callback_incrementing,
types::{ListType, TupleType},
values::{
ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
TypedArrayLikeMutator, UntypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum},
};
@ -29,7 +29,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
(input_seq_ty, input_seq): (Type, BasicValueEnum<'ctx>),
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> + use<'ctx, G> {
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> {
let llvm_usize = ctx.get_size_type();
let zero = llvm_usize.const_zero();
let one = llvm_usize.const_int(1, false);
@ -106,7 +106,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
for i in 0..input_seq.get_type().num_elements() {
// Get the i-th element off of the tuple and load it into `result`.
let int = input_seq.extract_element(ctx, i).into_int_value();
let int = input_seq.load_element(ctx, i).into_int_value();
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
unsafe {

View File

@ -4,13 +4,14 @@ use inkwell::values::{IntValue, PointerValue};
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
stmt::gen_if_callback,
types::ndarray::NDArrayType,
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
ndarray::{NDArrayValue, RustNDIndex},
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -1,75 +0,0 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue},
};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, types::OptionType};
/// Proxy type for accessing a `Option` value in LLVM.
#[derive(Copy, Clone)]
pub struct OptionValue<'ctx> {
value: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
}
impl<'ctx> OptionValue<'ctx> {
/// Creates an [`OptionValue`] from a [`PointerValue`].
#[must_use]
pub fn from_pointer_value(
ptr: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name }
}
/// Returns an `i1` indicating if this `Option` instance does not hold a value.
#[must_use]
pub fn is_none(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
ctx.builder.build_is_null(self.value, "").unwrap()
}
/// Returns an `i1` indicating if this `Option` instance contains a value.
#[must_use]
pub fn is_some(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
ctx.builder.build_is_not_null(self.value, "").unwrap()
}
/// Loads the value present in this `Option` instance.
///
/// # Safety
///
/// The caller must ensure that this `option` value [contains a value][Self::is_some].
#[must_use]
pub unsafe fn load(&self, ctx: &CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
ctx.builder.build_load(self.value, "").unwrap()
}
}
impl<'ctx> ProxyValue<'ctx> for OptionValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>;
type Type = OptionType<'ctx>;
fn get_type(&self) -> Self::Type {
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
}
fn as_base_value(&self) -> Self::Base {
self.value
}
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
}
impl<'ctx> From<OptionValue<'ctx>> for PointerValue<'ctx> {
fn from(value: OptionValue<'ctx>) -> Self {
value.as_base_value()
}
}

View File

@ -4,7 +4,7 @@ use inkwell::{
};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, CodeGenerator, types::RangeType};
use crate::codegen::{types::RangeType, CodeGenContext, CodeGenerator};
/// Proxy type for accessing a `range` value in LLVM.
#[derive(Copy, Clone)]

View File

@ -1,87 +0,0 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
};
use crate::codegen::{
CodeGenContext,
types::{StringType, structure::StructField},
values::ProxyValue,
};
/// Proxy type for accessing a `str` value in LLVM.
#[derive(Copy, Clone)]
pub struct StringValue<'ctx> {
value: StructValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
}
impl<'ctx> StringValue<'ctx> {
/// Creates an [`StringValue`] from a [`StructValue`].
#[must_use]
pub fn from_struct_value(
val: StructValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(val, llvm_usize).is_ok());
Self { value: val, llvm_usize, name }
}
/// Creates an [`StringValue`] from a [`PointerValue`].
#[must_use]
pub fn from_pointer_value(
ctx: &CodeGenContext<'ctx, '_>,
ptr: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
let val = ctx.builder.build_load(ptr, "").map(BasicValueEnum::into_struct_value).unwrap();
Self::from_struct_value(val, llvm_usize, name)
}
fn ptr_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().ptr
}
/// Returns the pointer to the beginning of the string.
pub fn extract_ptr(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.ptr_field().extract_value(ctx, self.value)
}
fn len_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().len
}
/// Returns the length of the string.
pub fn extract_len(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.len_field().extract_value(ctx, self.value)
}
}
impl<'ctx> ProxyValue<'ctx> for StringValue<'ctx> {
type ABI = StructValue<'ctx>;
type Base = StructValue<'ctx>;
type Type = StringType<'ctx>;
fn get_type(&self) -> Self::Type {
Self::Type::from_struct_type(self.value.get_type(), self.llvm_usize)
}
fn as_base_value(&self) -> Self::Base {
self.value
}
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
}
impl<'ctx> From<StringValue<'ctx>> for StructValue<'ctx> {
fn from(value: StringValue<'ctx>) -> Self {
value.as_base_value()
}
}

View File

@ -1,7 +1,7 @@
use inkwell::values::{BasicValueEnum, PointerValue, StructValue};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, types::structure::StructProxyType};
use crate::codegen::{types::structure::StructProxyType, CodeGenContext};
/// An LLVM value that is used to represent a corresponding structure-like value in NAC3.
pub trait StructProxyValue<'ctx>:

View File

@ -4,7 +4,7 @@ use inkwell::{
};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, types::TupleType};
use crate::codegen::{types::TupleType, CodeGenContext};
#[derive(Copy, Clone)]
pub struct TupleValue<'ctx> {
@ -45,7 +45,7 @@ impl<'ctx> TupleValue<'ctx> {
}
/// Stores a value into the tuple element at the given `index`.
pub fn insert_element(
pub fn store_element(
&mut self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
@ -63,11 +63,7 @@ impl<'ctx> TupleValue<'ctx> {
}
/// Loads a value from the tuple element at the given `index`.
pub fn extract_element(
&self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
) -> BasicValueEnum<'ctx> {
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
ctx.builder
.build_extract_value(
self.value,

View File

@ -7,12 +7,12 @@ use nac3parser::ast::Expr;
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
types::{
structure::{StructField, StructProxyType},
utils::SliceType,
},
values::{ProxyValue, structure::StructProxyValue},
values::{structure::StructProxyValue, ProxyValue},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::Type,
};

View File

@ -6,14 +6,14 @@ use std::{
};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
use itertools::{Itertools, izip};
use itertools::{izip, Itertools};
use parking_lot::RwLock;
use nac3parser::ast::{Constant, Expr, Location, StrRef};
use crate::{
codegen::{CodeGenContext, CodeGenerator},
toplevel::{DefinitionId, TopLevelDef, type_annotation::TypeAnnotation},
toplevel::{type_annotation::TypeAnnotation, DefinitionId, TopLevelDef},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, Unifier, VarMap},

View File

@ -1,13 +1,13 @@
use std::iter::once;
use indexmap::IndexMap;
use inkwell::{IntPredicate, values::BasicValue};
use inkwell::{values::BasicValue, IntPredicate};
use strum::IntoEnumIterator;
use super::{
helper::{
PrimDef, PrimDefDetails, arraylike_flatten_element_type, debug_assert_prim_is_allowed,
extract_ndims, make_exception_fields,
arraylike_flatten_element_type, debug_assert_prim_is_allowed, extract_ndims,
make_exception_fields, PrimDef, PrimDefDetails,
},
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
*,
@ -17,14 +17,14 @@ use crate::{
builtin_fns,
numpy::*,
stmt::{exn_constructor, gen_if_callback},
types::{RangeType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, RangeType},
values::{
ndarray::{shape::parse_numpy_int_sequence, ScalarOrNDArray},
ProxyValue,
ndarray::{ScalarOrNDArray, shape::parse_numpy_int_sequence},
},
},
symbol_resolver::SymbolValue,
typecheck::typedef::{TypeVar, VarMap, into_var_map, iter_type_vars},
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},
};
type BuiltinInfo = Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>;
@ -36,7 +36,9 @@ pub fn get_exn_constructor(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> (TopLevelDef, TopLevelDef, Type, Type) {
let PrimitiveStore { int32, int64, str: string, .. } = *primitives;
let int32 = primitives.int32;
let int64 = primitives.int64;
let string = primitives.str;
let exception_fields = make_exception_fields(int32, int64, string);
let exn_cons_args = vec![
FuncArg {
@ -479,9 +481,7 @@ impl<'a> BuiltinBuilder<'a> {
assert_eq!(simple_name, &exp_simple_name.into());
}
_ => {
panic!(
"Class/function variant of the constructed TopLevelDef of PrimDef {prim:?} is different than what is defined by {prim:?}"
)
panic!("Class/function variant of the constructed TopLevelDef of PrimDef {prim:?} is different than what is defined by {prim:?}")
}
}
}

View File

@ -1,7 +1,7 @@
use std::rc::Rc;
use indexmap::IndexMap;
use nac3parser::ast::{ExprKind, Ident, fold::Fold};
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
use super::*;
use crate::{
@ -265,7 +265,8 @@ impl TopLevelComposer {
if self.keyword_list.contains(class_name) {
return Err(format!(
"cannot use keyword `{}` as a class name (at {})",
class_name, ast.location
class_name,
ast.location
));
}
let fully_qualified_class_name = if mod_path.is_empty() {
@ -276,7 +277,8 @@ impl TopLevelComposer {
if !defined_names.insert(fully_qualified_class_name.into()) {
return Err(format!(
"duplicate definition of class `{}` (at {})",
class_name, ast.location
class_name,
ast.location
));
}
@ -292,7 +294,7 @@ impl TopLevelComposer {
resolver.clone(),
fully_qualified_class_name,
Some(constructor_ty),
Some(ast.location),
Some(ast.location)
))),
None,
);
@ -319,7 +321,8 @@ impl TopLevelComposer {
if self.keyword_list.contains(method_name) {
return Err(format!(
"cannot use keyword `{}` as a method name (at {})",
method_name, b.location
method_name,
b.location
));
}
let global_class_method_name = Self::make_class_method_name(
@ -329,7 +332,8 @@ impl TopLevelComposer {
if !defined_names.insert(global_class_method_name.clone()) {
return Err(format!(
"class method `{}` defined twice (at {})",
global_class_method_name, b.location
global_class_method_name,
b.location
));
}
let method_def_id = self.definition_ast_list.len() + {
@ -376,11 +380,7 @@ impl TopLevelComposer {
self.definition_ast_list.push((def, Some(ast)));
}
let result_ty = if allow_no_constructor || contains_constructor {
Some(constructor_ty)
} else {
None
};
let result_ty = if allow_no_constructor || contains_constructor { Some(constructor_ty) } else { None };
Ok((class_name, DefinitionId(class_def_id), result_ty))
}
@ -393,7 +393,8 @@ impl TopLevelComposer {
if !defined_names.insert(global_fun_name.clone()) {
return Err(format!(
"top level function `{}` defined twice (at {})",
global_fun_name, ast.location
global_fun_name,
ast.location
));
}
@ -407,7 +408,7 @@ impl TopLevelComposer {
// dummy here, unify with correct type later
ty_to_be_unified,
resolver,
Some(ast.location),
Some(ast.location)
))
.into(),
Some(ast),
@ -431,10 +432,7 @@ impl TopLevelComposer {
// Make callers use `register_top_level_var` instead, as it provides more
// fine-grained control over which symbols to register, while also simplifying the
// usage of this function.
panic!(
"Registration of top-level Assign statements must use TopLevelComposer::register_top_level_var (at {})",
ast.location
);
panic!("Registration of top-level Assign statements must use TopLevelComposer::register_top_level_var (at {})", ast.location);
}
ast::StmtKind::AnnAssign { target, annotation, .. } => {
@ -463,9 +461,9 @@ impl TopLevelComposer {
/// Registers a top-level variable with the given `name` into the composer.
///
/// - `annotation` - The type annotation of the top-level variable, or [`None`] if no type
/// annotation is provided.
/// - `location` - The location of the top-level variable.
/// `annotation` - The type annotation of the top-level variable, or [`None`] if no type
/// annotation is provided.
/// `location` - The location of the top-level variable.
pub fn register_top_level_var(
&mut self,
name: Ident,
@ -1236,7 +1234,7 @@ impl TopLevelComposer {
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.is_some_and(|c| id == &c.into())
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.map_or(false, |c| id == &c.into())
) =>
{
(slice, true)
@ -1407,14 +1405,14 @@ impl TopLevelComposer {
);
if !ok {
return Err(HashSet::from([format!(
"method {class_method_name} has same name as ancestors' method, but incompatible type"
)]));
"method {class_method_name} has same name as ancestors' method, but incompatible type"),
]));
}
}
}
class_methods_def.clear();
class_methods_def
.extend(new_child_methods.iter().map(|f| (*f.0, f.1.0, f.1.1)).collect_vec());
.extend(new_child_methods.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
// handle class fields
let mut new_child_fields: IndexMap<StrRef, (Type, bool)> =
@ -1443,10 +1441,10 @@ impl TopLevelComposer {
class_fields_def.clear();
class_fields_def
.extend(new_child_fields.iter().map(|f| (*f.0, f.1.0, f.1.1)).collect_vec());
.extend(new_child_fields.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
class_attribute_def.clear();
class_attribute_def.extend(
new_child_attributes.iter().map(|f| (*f.0, f.1.0, f.1.1.clone())).collect_vec(),
new_child_attributes.iter().map(|f| (*f.0, f.1 .0, f.1 .1.clone())).collect_vec(),
);
Ok(())
}
@ -1523,7 +1521,8 @@ impl TopLevelComposer {
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{
// create constructor for these classes
let PrimitiveStore { str: string, int64, .. } = *primitives_ty;
let string = primitives_ty.str;
let int64 = primitives_ty.int64;
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg {
@ -1623,10 +1622,14 @@ impl TopLevelComposer {
)?;
for (f, _, _) in fields {
if !all_inited.contains(f) {
return Err(HashSet::from([format!(
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
f, class_name, body[0].location,
)]));
return Err(HashSet::from([
format!(
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
f,
class_name,
body[0].location,
),
]));
}
}
}
@ -1898,8 +1901,8 @@ impl TopLevelComposer {
let base_repr = inferencer.unifier.stringify(*base);
let subtype_repr = inferencer.unifier.stringify(*subtype);
return Err(HashSet::from([format!(
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
)]));
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
]));
}
};
let subtype_entry = defs[subtype_id.0].read();
@ -1913,8 +1916,8 @@ impl TopLevelComposer {
let base_repr = inferencer.unifier.stringify(*base);
let subtype_repr = inferencer.unifier.stringify(*subtype);
return Err(HashSet::from([format!(
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
)]));
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
]));
}
}
}
@ -1999,15 +2002,13 @@ impl TopLevelComposer {
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
ast::ExprKind::Name { id, .. } if self.core_config.kernel_ann.is_some_and(|c| id == &c.into()) || id == &self.core_config.kernel_invariant_ann.into()
ast::ExprKind::Name { id, .. } if self.core_config.kernel_ann.map_or(false, |c| id == &c.into())
) =>
{
slice
}
_ if self.core_config.kernel_ann.is_none() => ty_decl,
_ => unreachable!(
"Global variables should be annotated with Kernel[] or KernelInvariant[]"
), // ignore fields annotated otherwise
_ => unreachable!("Global variables should be annotated with Kernel[]"), // ignore fields annotated otherwise
};
let ty_annotation = parse_ast_to_type_annotation_kinds(

View File

@ -8,7 +8,7 @@ use nac3parser::ast::{Constant, ExprKind, Location};
use super::{numpy::unpack_ndarray_var_tys, *};
use crate::{
symbol_resolver::SymbolValue,
typecheck::typedef::{Mapping, TypeVarId, VarMap, into_var_map, iter_type_vars},
typecheck::typedef::{into_var_map, iter_type_vars, Mapping, TypeVarId, VarMap},
};
/// All primitive types and functions in nac3core.
@ -757,7 +757,7 @@ impl TopLevelComposer {
return Err(HashSet::from([format!(
"redundant type annotation for class fields at {}",
s.location
)]));
)]))
}
ast::StmtKind::Assign { targets, .. } => {
for t in targets {
@ -1038,10 +1038,7 @@ impl TopLevelComposer {
}
ast::ExprKind::Name { .. } | ast::ExprKind::Subscript { .. } => {
if has_base {
return Err(HashSet::from([format!(
"a class definition can only have at most one base class declaration and one generic declaration (at {})",
b.location
)]));
return Err(HashSet::from([format!("a class definition can only have at most one base class declaration and one generic declaration (at {})", b.location )]));
}
has_base = true;
// the function parse_ast_to make sure that no type var occurred in
@ -1236,9 +1233,7 @@ pub fn arraylike_get_ndims(unifier: &mut Unifier, ty: Type) -> u64 {
};
if values.len() > 1 {
todo!(
"Getting num of dimensions for ndarray with more than one ndim bound is unimplemented"
)
todo!("Getting num of dimensions for ndarray with more than one ndim bound is unimplemented")
}
u64::try_from(values[0].clone()).unwrap()

Some files were not shown because too many files have changed in this diff Show More