Compare commits

..

1 Commits

Author SHA1 Message Date
e134f4888b [meta] Add option to use mold as linker 2025-01-24 11:29:27 +08:00
67 changed files with 1875 additions and 3116 deletions

View File

@ -1,2 +0,0 @@
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

169
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "ahash" name = "ahash"
@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom 0.2.15", "getrandom",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
@ -127,9 +127,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.10" version = "1.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -142,9 +142,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.27" version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -152,9 +152,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.27" version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -200,9 +200,9 @@ dependencies = [
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.17" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -334,15 +334,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]] [[package]]
name = "fixedbitset" name = "fixedbitset"
version = "0.5.7" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "foldhash"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]] [[package]]
name = "fxhash" name = "fxhash"
@ -380,19 +374,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
] ]
[[package]] [[package]]
@ -407,14 +389,20 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"foldhash",
]
[[package]] [[package]]
name = "heck" name = "heck"
@ -449,9 +437,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.7.1" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.2", "hashbrown 0.15.2",
@ -510,9 +498,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.14.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [ dependencies = [
"either", "either",
] ]
@ -534,9 +522,9 @@ dependencies = [
[[package]] [[package]]
name = "lalrpop" name = "lalrpop"
version = "0.22.1" version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7047a26de42016abf8f181b46b398aef0b77ad46711df41847f6ed869a2a1d5b" checksum = "06093b57658c723a21da679530e061a8c25340fa5a6f98e313b542268c7e2a1f"
dependencies = [ dependencies = [
"ascii-canvas", "ascii-canvas",
"bit-set", "bit-set",
@ -556,9 +544,9 @@ dependencies = [
[[package]] [[package]]
name = "lalrpop-util" name = "lalrpop-util"
version = "0.22.1" version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d05b3fe34b8bd562c338db725dfa9beb9451a48f65f129ccb9538b48d2c93b" checksum = "feee752d43abd0f4807a921958ab4131f692a44d4d599733d4419c5d586176ce"
dependencies = [ dependencies = [
"regex-automata", "regex-automata",
"rustversion", "rustversion",
@ -646,7 +634,6 @@ dependencies = [
name = "nac3artiq" name = "nac3artiq"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"indexmap 2.7.1",
"itertools", "itertools",
"nac3core", "nac3core",
"nac3ld", "nac3ld",
@ -669,7 +656,7 @@ name = "nac3core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossbeam", "crossbeam",
"indexmap 2.7.1", "indexmap 2.7.0",
"indoc", "indoc",
"inkwell", "inkwell",
"insta", "insta",
@ -677,6 +664,7 @@ dependencies = [
"nac3core_derive", "nac3core_derive",
"nac3parser", "nac3parser",
"parking_lot", "parking_lot",
"rayon",
"regex", "regex",
"strum", "strum",
"strum_macros", "strum_macros",
@ -764,12 +752,12 @@ dependencies = [
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.7.1" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"indexmap 2.7.1", "indexmap 2.7.0",
] ]
[[package]] [[package]]
@ -992,7 +980,27 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom 0.2.15", "getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
] ]
[[package]] [[package]]
@ -1042,9 +1050,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -1061,9 +1069,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.19" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]] [[package]]
name = "same-file" name = "same-file"
@ -1082,9 +1090,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.25" version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
[[package]] [[package]]
name = "serde" name = "serde"
@ -1108,9 +1116,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.137" version = "1.0.135"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -1157,9 +1165,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.7.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
@ -1181,11 +1189,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "string-interner" name = "string-interner"
version = "0.18.0" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b" checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e"
dependencies = [ dependencies = [
"hashbrown 0.15.2", "cfg-if",
"hashbrown 0.14.5",
"serde", "serde",
] ]
@ -1263,13 +1272,13 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.16.0" version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
"getrandom 0.3.1", "getrandom",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.59.0",
@ -1354,7 +1363,7 @@ version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap 2.7.1", "indexmap 2.7.0",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
@ -1363,9 +1372,9 @@ dependencies = [
[[package]] [[package]]
name = "trybuild" name = "trybuild"
version = "1.0.103" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b812699e0c4f813b872b373a4471717d9eb550da14b311058a4d9cf4173cbca6" checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4"
dependencies = [ dependencies = [
"dissimilar", "dissimilar",
"glob", "glob",
@ -1437,9 +1446,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.16" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
@ -1509,15 +1518,6 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.9"
@ -1611,22 +1611,13 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.25" version = "0.6.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View File

@ -60,3 +60,16 @@ Several things to note:
- If `cargo fmt` or `cargo clippy` returns an error, the pre-commit hook will fail. You should fix all errors before trying to commit again. - If `cargo fmt` or `cargo clippy` returns an error, the pre-commit hook will fail. You should fix all errors before trying to commit again.
- If `cargo fmt` reformats some files, the pre-commit hook will also fail. You should review the changes and, if satisfied, try to commit again. - If `cargo fmt` reformats some files, the pre-commit hook will also fail. You should review the changes and, if satisfied, try to commit again.
### Use Mold Linker
The [Mold linker](https://github.com/rui314/mold) is available when using the Nix Flakes system, and can be enabled by adding the following to `.cargo/config.toml`:
```
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
```
Replace `x86_64-unknown-linux-gnu` with the target triple of your development platform.
**Note: The use of Mold linker is experimental and should be used for development purposes only.**

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1738680400, "lastModified": 1736798957,
"narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=", "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "799ba5bffed04ced7067a91798353d360788b30d", "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -41,9 +41,9 @@
lockFile = ./Cargo.lock; lockFile = ./Cargo.lock;
}; };
passthru.cargoLock = cargoLock; passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out pkgs.llvmPackages_14.bintools llvm-nac3 ]; nativeBuildInputs = [ pkgs.python3 pkgs.mold-wrapped (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ]; buildInputs = [ pkgs.python3 pkgs.mold-wrapped llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ]; checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) pkgs.mold-wrapped ];
checkPhase = checkPhase =
'' ''
echo "Checking nac3standalone demos..." echo "Checking nac3standalone demos..."
@ -68,6 +68,7 @@
mkdir -p $standalone/bin mkdir -p $standalone/bin
cp target/x86_64-unknown-linux-gnu/release/nac3standalone $standalone/bin cp target/x86_64-unknown-linux-gnu/release/nac3standalone $standalone/bin
''; '';
stdenv = [ pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv ];
} }
); );
python3-mimalloc = pkgs.python3 // rec { python3-mimalloc = pkgs.python3 // rec {
@ -85,8 +86,8 @@
name = "nac3artiq-instrumented"; name = "nac3artiq-instrumented";
src = self; src = self;
inherit (nac3artiq) cargoLock; inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_14.bintools llvm-nac3-instrumented ]; nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-instrumented ];
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ]; buildInputs = [ pkgs.python3 pkgs.mold-wrapped llvm-nac3-instrumented ];
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ]; cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
doCheck = false; doCheck = false;
configurePhase = configurePhase =
@ -99,6 +100,7 @@
mkdir -p $TARGET_DIR mkdir -p $TARGET_DIR
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
''; '';
stdenv = [ pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv ];
} }
); );
nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation { nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation {
@ -113,14 +115,13 @@
(pkgs.fetchFromGitHub { (pkgs.fetchFromGitHub {
owner = "m-labs"; owner = "m-labs";
repo = "artiq"; repo = "artiq";
rev = "554b0749ca5985bf4d006c4f29a05e83de0a226d"; rev = "28c9de3e251daa89a8c9fd79d5ab64a3ec03bac6";
sha256 = "sha256-3eSNHTSlmdzLMcEMIspxqjmjrcQe4aIGqIfRgquUg18="; sha256 = "sha256-vAvpbHc5B+1wtG8zqN7j9dQE1ON+i22v+uqA+tw6Gak=";
}) })
]; ];
buildInputs = [ buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ])) (python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ]))
pkgs.llvmPackages_14.llvm.out pkgs.llvmPackages_14.llvm.out
pkgs.llvmPackages_14.bintools
]; ];
phases = [ "buildPhase" "installPhase" ]; phases = [ "buildPhase" "installPhase" ];
buildPhase = buildPhase =
@ -148,8 +149,8 @@
name = "nac3artiq-pgo"; name = "nac3artiq-pgo";
src = self; src = self;
inherit (nac3artiq) cargoLock; inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_14.bintools llvm-nac3-pgo ]; nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-pgo ];
buildInputs = [ pkgs.python3 llvm-nac3-pgo ]; buildInputs = [ pkgs.python3 pkgs.mold-wrapped llvm-nac3-pgo ];
cargoBuildFlags = [ "--package" "nac3artiq" ]; cargoBuildFlags = [ "--package" "nac3artiq" ];
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ]; cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
installPhase = installPhase =
@ -158,6 +159,7 @@
mkdir -p $TARGET_DIR mkdir -p $TARGET_DIR
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
''; '';
stdenv = [ pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv ];
} }
); );
}; };
@ -169,10 +171,11 @@
buildInputs = with pkgs; [ buildInputs = with pkgs; [
# build dependencies # build dependencies
packages.x86_64-linux.llvm-nac3 packages.x86_64-linux.llvm-nac3
(pkgs.wrapClangMulti llvmPackages_14.clang) llvmPackages_14.llvm.out llvmPackages_14.bintools # for running nac3standalone demos (pkgs.wrapClangMulti llvmPackages_14.clang) llvmPackages_14.llvm.out # for running nac3standalone demos
packages.x86_64-linux.llvm-tools-irrt packages.x86_64-linux.llvm-tools-irrt
cargo cargo
rustc rustc
mold-wrapped
# runtime dependencies # runtime dependencies
lld_14 # 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 ])) (packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
@ -187,6 +190,7 @@
export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a
export DEMO_LINALG_STUB32=${packages.x86_64-linux.demo-linalg-stub32}/lib/liblinalg.a export DEMO_LINALG_STUB32=${packages.x86_64-linux.demo-linalg-stub32}/lib/liblinalg.a
''; '';
stdenv = [ pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv ];
}; };
devShells.x86_64-linux.msys2 = pkgs.mkShell { devShells.x86_64-linux.msys2 = pkgs.mkShell {
name = "nac3-dev-shell-msys2"; name = "nac3-dev-shell-msys2";

View File

@ -9,11 +9,10 @@ name = "nac3artiq"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
indexmap = "2.7" itertools = "0.13"
itertools = "0.14"
pyo3 = { version = "0.21", features = ["extension-module", "gil-refs"] } pyo3 = { version = "0.21", features = ["extension-module", "gil-refs"] }
parking_lot = "0.12" parking_lot = "0.12"
tempfile = "3.16" tempfile = "3.13"
nac3core = { path = "../nac3core" } nac3core = { path = "../nac3core" }
nac3ld = { path = "../nac3ld" } nac3ld = { path = "../nac3ld" }

View File

@ -16,7 +16,7 @@ __all__ = [
"rpc", "ms", "us", "ns", "rpc", "ms", "us", "ns",
"print_int32", "print_int64", "print_int32", "print_int64",
"Core", "TTLOut", "Core", "TTLOut",
"parallel", "legacy_parallel", "sequential" "parallel", "sequential"
] ]
@ -245,7 +245,7 @@ class Core:
embedding = EmbeddingMap() embedding = EmbeddingMap()
if allow_registration: if allow_registration:
compiler.analyze(registered_functions, registered_classes, special_ids, set()) compiler.analyze(registered_functions, registered_classes, set())
allow_registration = False allow_registration = False
if hasattr(method, "__self__"): if hasattr(method, "__self__"):
@ -336,11 +336,4 @@ class UnwrapNoneError(Exception):
artiq_builtin = True artiq_builtin = True
parallel = KernelContextManager() parallel = KernelContextManager()
legacy_parallel = KernelContextManager()
sequential = KernelContextManager() sequential = KernelContextManager()
special_ids = {
"parallel": id(parallel),
"legacy_parallel": id(legacy_parallel),
"sequential": id(sequential),
}

View File

@ -12,16 +12,16 @@ use pyo3::{
PyObject, PyResult, Python, PyObject, PyResult, Python,
}; };
use super::{symbol_resolver::InnerResolver, timeline::TimeFns, SpecialPythonId}; use super::{symbol_resolver::InnerResolver, timeline::TimeFns};
use nac3core::{ use nac3core::{
codegen::{ codegen::{
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}, llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave},
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
type_aligned_alloca, type_aligned_alloca,
types::{ndarray::NDArrayType, RangeType}, types::ndarray::NDArrayType,
values::{ values::{
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, RangeValue,
UntypedArrayLikeAccessor, UntypedArrayLikeAccessor,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
@ -41,10 +41,7 @@ use nac3core::{
numpy::unpack_ndarray_var_tys, numpy::unpack_ndarray_var_tys,
DefinitionId, GenCall, DefinitionId, GenCall,
}, },
typecheck::{ typecheck::typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
type_inferencer::PrimitiveStore,
typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
},
}; };
/// The parallelism mode within a block. /// The parallelism mode within a block.
@ -86,9 +83,6 @@ pub struct ArtiqCodeGenerator<'a> {
/// The current parallel context refers to the nearest `with parallel` or `with legacy_parallel` /// 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. /// statement, which is used to determine when and how the timeline should be updated.
parallel_mode: ParallelMode, parallel_mode: ParallelMode,
/// Specially treated python IDs to identify `with parallel` and `with sequential` blocks.
special_ids: SpecialPythonId,
} }
impl<'a> ArtiqCodeGenerator<'a> { impl<'a> ArtiqCodeGenerator<'a> {
@ -96,7 +90,6 @@ impl<'a> ArtiqCodeGenerator<'a> {
name: String, name: String,
size_t: IntType<'_>, size_t: IntType<'_>,
timeline: &'a (dyn TimeFns + Sync), timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> { ) -> ArtiqCodeGenerator<'a> {
assert!(matches!(size_t.get_bit_width(), 32 | 64)); assert!(matches!(size_t.get_bit_width(), 32 | 64));
ArtiqCodeGenerator { ArtiqCodeGenerator {
@ -107,7 +100,6 @@ impl<'a> ArtiqCodeGenerator<'a> {
end: None, end: None,
timeline, timeline,
parallel_mode: ParallelMode::None, parallel_mode: ParallelMode::None,
special_ids,
} }
} }
@ -117,10 +109,9 @@ impl<'a> ArtiqCodeGenerator<'a> {
ctx: &Context, ctx: &Context,
target_machine: &TargetMachine, target_machine: &TargetMachine,
timeline: &'a (dyn TimeFns + Sync), timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> { ) -> ArtiqCodeGenerator<'a> {
let llvm_usize = ctx.ptr_sized_int_type(&target_machine.get_target_data(), None); 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 /// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
@ -266,140 +257,122 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
// - If there is a end variable, it indicates that we are (indirectly) inside a // - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value. // parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node { if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
let resolver = ctx.resolver.clone(); if id == &"parallel".into() || id == &"legacy_parallel".into() {
if let Some(static_value) = let old_start = self.start.take();
if let Some((_ptr, static_value, _counter)) = ctx.var_assignment.get(id) { let old_end = self.end.take();
static_value.clone() let old_parallel_mode = self.parallel_mode;
} 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;
let now = if let Some(old_start) = &old_start { let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum( 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(
ctx, ctx,
self, self,
end_expr.custom.unwrap(), old_start.custom.unwrap(),
)?; )?
} else {
self.timeline.emit_now_mu(ctx)
};
// inside a sequential block // Emulate variable allocation, as we need to use the CodeGenContext
if old_start.is_none() { // HashMap to store our variable due to lifetime limitation
self.timeline.emit_at_mu(ctx, end_val); // 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.gen_block(ctx, body.iter())?;
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.parallel_mode = old_parallel_mode; let current = ctx.builder.get_insert_block().unwrap();
self.end = old_end;
self.start = old_start;
if reset_position { // if the current block is terminated, move before the terminator
ctx.builder.position_at_end(current); // 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(()); // set duration
} else if python_id == self.special_ids.sequential { let end_expr = self.end.take().unwrap();
// For deep parallel, temporarily take away start to avoid function calls in let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
// the block from resetting the timeline. ctx,
// This does not affect legacy parallel, as the timeline will be reset after self,
// this block finishes execution. end_expr.custom.unwrap(),
let start = self.start.take(); )?;
self.gen_block(ctx, body.iter())?;
self.start = start;
// Reset the timeline when we are exiting the sequential block // inside a sequential block
// Legacy parallel does not need this, since it will be reset after codegen if old_start.is_none() {
// for this statement is completed self.timeline.emit_at_mu(ctx, end_val);
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
} }
// 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(());
} }
} }
} }
@ -416,7 +389,12 @@ fn gen_rpc_tag(
) -> Result<(), String> { ) -> Result<(), String> {
use nac3core::typecheck::typedef::TypeEnum::*; 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) { if ctx.unifier.unioned(ty, int32) {
buffer.push(b'i'); buffer.push(b'i');
@ -498,8 +476,8 @@ fn format_rpc_arg<'ctx>(
let (elem_ty, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, arg_ty); let (elem_ty, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, arg_ty);
let ndims = extract_ndims(&ctx.unifier, ndims); let ndims = extract_ndims(&ctx.unifier, ndims);
let dtype = ctx.get_llvm_type(generator, elem_ty); let dtype = ctx.get_llvm_type(generator, elem_ty);
let ndarray = NDArrayType::new(ctx, dtype, ndims) let ndarray =
.map_pointer_value(arg.into_pointer_value(), None); NDArrayType::new(ctx, dtype, ndims).map_value(arg.into_pointer_value(), None);
let ndims = llvm_usize.const_int(ndims, false); let ndims = llvm_usize.const_int(ndims, false);
@ -783,7 +761,7 @@ fn format_rpc_ret<'ctx>(
ctx.builder.build_unconditional_branch(head_bb).unwrap(); ctx.builder.build_unconditional_branch(head_bb).unwrap();
ctx.builder.position_at_end(tail_bb); ctx.builder.position_at_end(tail_bb);
ndarray.as_abi_value(ctx).into() ndarray.as_base_value().into()
} }
_ => { _ => {
@ -936,14 +914,47 @@ fn rpc_codegen_callback_fn<'ctx>(
} }
// call // call
infer_and_call_function( if is_async {
ctx, let rpc_send_async = ctx.module.get_function("rpc_send_async").unwrap_or_else(|| {
if is_async { "rpc_send_async" } else { "rpc_send" }, ctx.module.add_function(
None, "rpc_send_async",
&[service_id.into(), tag_ptr.into(), args_ptr.into()], ctx.ctx.void_type().fn_type(
Some("rpc.send"), &[
None, 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 // reclaim stack space used by arguments
call_stackrestore(ctx, stackptr); call_stackrestore(ctx, stackptr);
@ -1157,22 +1168,29 @@ fn polymorphic_print<'ctx>(
debug_assert!(!fmt.is_empty()); debug_assert!(!fmt.is_empty());
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8); debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
let llvm_i32 = ctx.ctx.i32_type(); let fn_name = if as_rtio { "rtio_log" } else { "core_log" };
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default()); 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 = ctx.gen_string(generator, fmt);
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value(); let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
create_fn_and_call( ctx.builder
ctx, .build_call(
if as_rtio { "rtio_log" } else { "core_log" }, print_fn,
if as_rtio { None } else { Some(llvm_i32.into()) }, &once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
&[llvm_pi8.into()], "",
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(), )
true, .unwrap();
None,
None,
);
}; };
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
@ -1365,7 +1383,7 @@ fn polymorphic_print<'ctx>(
let (dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty); let (dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty)
.map_pointer_value(value.into_pointer_value(), None); .map_value(value.into_pointer_value(), None);
let num_0 = llvm_usize.const_zero(); let num_0 = llvm_usize.const_zero();
@ -1413,7 +1431,7 @@ fn polymorphic_print<'ctx>(
fmt.push_str("range("); fmt.push_str("range(");
flush(ctx, generator, &mut fmt, &mut args); flush(ctx, generator, &mut fmt, &mut args);
let val = RangeType::new(ctx).map_pointer_value(value.into_pointer_value(), None); let val = RangeValue::from_pointer_value(value.into_pointer_value(), None);
let (start, stop, step) = destructure_range(ctx, val); let (start, stop, step) = destructure_range(ctx, val);

View File

@ -19,7 +19,6 @@ use std::{
sync::Arc, sync::Arc,
}; };
use indexmap::IndexMap;
use itertools::Itertools; use itertools::Itertools;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use pyo3::{ use pyo3::{
@ -163,13 +162,6 @@ pub struct PrimitivePythonId {
module: u64, module: u64,
} }
#[derive(Clone, Default)]
pub struct SpecialPythonId {
parallel: u64,
legacy_parallel: u64,
sequential: u64,
}
type TopLevelComponent = (Stmt, String, PyObject); type TopLevelComponent = (Stmt, String, PyObject);
// TopLevelComposer is unsendable as it holds the unification table, which is // TopLevelComposer is unsendable as it holds the unification table, which is
@ -187,7 +179,6 @@ struct Nac3 {
string_store: Arc<RwLock<HashMap<String, i32>>>, string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>, exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore, deferred_eval_store: DeferredEvaluationStore,
special_ids: SpecialPythonId,
/// LLVM-related options for code generation. /// LLVM-related options for code generation.
llvm_options: CodeGenLLVMOptions, llvm_options: CodeGenLLVMOptions,
} }
@ -806,7 +797,6 @@ impl Nac3 {
&context, &context,
&self.get_llvm_target_machine(), &self.get_llvm_target_machine(),
self.time_fns, self.time_fns,
self.special_ids.clone(),
)) ))
}) })
.collect(); .collect();
@ -823,7 +813,6 @@ impl Nac3 {
&context, &context,
&self.get_llvm_target_machine(), &self.get_llvm_target_machine(),
self.time_fns, self.time_fns,
self.special_ids.clone(),
); );
let module = context.create_module("main"); let module = context.create_module("main");
let target_machine = self.llvm_options.create_target_machine().unwrap(); let target_machine = self.llvm_options.create_target_machine().unwrap();
@ -1203,7 +1192,6 @@ impl Nac3 {
string_store: Arc::new(string_store.into()), string_store: Arc::new(string_store.into()),
exception_ids: Arc::default(), exception_ids: Arc::default(),
deferred_eval_store: DeferredEvaluationStore::new(), deferred_eval_store: DeferredEvaluationStore::new(),
special_ids: SpecialPythonId::default(),
llvm_options: CodeGenLLVMOptions { llvm_options: CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default, opt_level: OptimizationLevel::Default,
target: isa.get_llvm_target_options(), target: isa.get_llvm_target_options(),
@ -1215,12 +1203,11 @@ impl Nac3 {
&mut self, &mut self,
functions: &PySet, functions: &PySet,
classes: &PySet, classes: &PySet,
special_ids: &PyDict,
content_modules: &PySet, content_modules: &PySet,
) -> PyResult<()> { ) -> PyResult<()> {
let (modules, class_ids) = let (modules, class_ids) =
Python::with_gil(|py| -> PyResult<(IndexMap<u64, PyObject>, HashSet<u64>)> { Python::with_gil(|py| -> PyResult<(HashMap<u64, PyObject>, HashSet<u64>)> {
let mut modules: IndexMap<u64, PyObject> = IndexMap::new(); let mut modules: HashMap<u64, PyObject> = HashMap::new();
let mut class_ids: HashSet<u64> = HashSet::new(); let mut class_ids: HashSet<u64> = HashSet::new();
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?; let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
@ -1249,25 +1236,6 @@ impl Nac3 {
for module in modules.into_values() { for module in modules.into_values() {
self.register_module(&module, &class_ids)?; 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(()) Ok(())
} }

View File

@ -16,7 +16,7 @@ use pyo3::{
use super::PrimitivePythonId; use super::PrimitivePythonId;
use nac3core::{ use nac3core::{
codegen::{ codegen::{
types::{ndarray::NDArrayType, structure::StructProxyType, ProxyType}, types::{ndarray::NDArrayType, ProxyType},
values::ndarray::make_contiguous_strides, values::ndarray::make_contiguous_strides,
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
@ -1146,7 +1146,7 @@ impl InnerResolver {
if self.global_value_ids.read().contains_key(&id) { if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| { let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global( ctx.module.add_global(
llvm_ndarray.as_abi_type().get_element_type().into_struct_type(), llvm_ndarray.as_base_type().get_element_type().into_struct_type(),
Some(AddressSpace::default()), Some(AddressSpace::default()),
&id_str, &id_str,
) )
@ -1315,16 +1315,20 @@ impl InnerResolver {
.unwrap() .unwrap()
}; };
let ndarray = llvm_ndarray.get_struct_type().const_named_struct(&[ let ndarray = llvm_ndarray
ndarray_itemsize.into(), .as_base_type()
ndarray_ndims.into(), .get_element_type()
ndarray_shape.into(), .into_struct_type()
ndarray_strides.into(), .const_named_struct(&[
ndarray_data.into(), ndarray_itemsize.into(),
]); ndarray_ndims.into(),
ndarray_shape.into(),
ndarray_strides.into(),
ndarray_data.into(),
]);
let ndarray_global = ctx.module.add_global( let ndarray_global = ctx.module.add_global(
llvm_ndarray.as_abi_type().get_element_type().into_struct_type(), llvm_ndarray.as_base_type().get_element_type().into_struct_type(),
Some(AddressSpace::default()), Some(AddressSpace::default()),
&id_str, &id_str,
); );

View File

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

@ -11,5 +11,5 @@ fold = []
[dependencies] [dependencies]
parking_lot = "0.12" parking_lot = "0.12"
string-interner = "0.18" string-interner = "0.17"
fxhash = "0.2" fxhash = "0.2"

View File

@ -10,10 +10,11 @@ derive = ["dep:nac3core_derive"]
no-escape-analysis = [] no-escape-analysis = []
[dependencies] [dependencies]
itertools = "0.14" itertools = "0.13"
crossbeam = "0.8" crossbeam = "0.8"
indexmap = "2.7" indexmap = "2.6"
parking_lot = "0.12" parking_lot = "0.12"
rayon = "1.10"
nac3core_derive = { path = "nac3core_derive", optional = true } nac3core_derive = { path = "nac3core_derive", optional = true }
nac3parser = { path = "../nac3parser" } nac3parser = { path = "../nac3parser" }
strum = "0.26" strum = "0.26"
@ -30,4 +31,4 @@ indoc = "2.0"
insta = "=1.11.0" insta = "=1.11.0"
[build-dependencies] [build-dependencies]
regex = "1.11" regex = "1.10"

View File

@ -1,6 +1,6 @@
use inkwell::{ use inkwell::{
types::BasicTypeEnum, types::BasicTypeEnum,
values::{BasicValueEnum, IntValue}, values::{BasicValue, BasicValueEnum, IntValue},
FloatPredicate, IntPredicate, OptimizationLevel, FloatPredicate, IntPredicate, OptimizationLevel,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -11,10 +11,10 @@ use super::{
irrt::calculate_len_for_slice_range, irrt::calculate_len_for_slice_range,
llvm_intrinsics, llvm_intrinsics,
macros::codegen_unreachable, macros::codegen_unreachable,
types::{ndarray::NDArrayType, ListType, RangeType, TupleType}, types::{ndarray::NDArrayType, ListType, TupleType},
values::{ values::{
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray}, ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ProxyValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor, ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -47,14 +47,14 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
let range_ty = ctx.primitives.range; let range_ty = ctx.primitives.range;
Ok(if ctx.unifier.unioned(arg_ty, range_ty) { Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = RangeType::new(ctx).map_pointer_value(arg.into_pointer_value(), Some("range")); let arg = RangeValue::from_pointer_value(arg.into_pointer_value(), Some("range"));
let (start, end, step) = destructure_range(ctx, arg); let (start, end, step) = destructure_range(ctx, arg);
calculate_len_for_slice_range(generator, ctx, start, end, step) calculate_len_for_slice_range(generator, ctx, start, end, step)
} else { } else {
match &*ctx.unifier.get_ty_immutable(arg_ty) { match &*ctx.unifier.get_ty_immutable(arg_ty) {
TypeEnum::TTuple { .. } => { TypeEnum::TTuple { .. } => {
let tuple = TupleType::from_unifier_type(generator, ctx, arg_ty) let tuple = TupleType::from_unifier_type(generator, ctx, arg_ty)
.map_struct_value(arg.into_struct_value(), None); .map_value(arg.into_struct_value(), None);
llvm_i32.const_int(tuple.get_type().num_elements().into(), false) llvm_i32.const_int(tuple.get_type().num_elements().into(), false)
} }
@ -62,7 +62,7 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
{ {
let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty)
.map_pointer_value(arg.into_pointer_value(), None); .map_value(arg.into_pointer_value(), None);
ctx.builder ctx.builder
.build_int_truncate_or_bit_cast(ndarray.len(ctx), llvm_i32, "len") .build_int_truncate_or_bit_cast(ndarray.len(ctx), llvm_i32, "len")
.unwrap() .unwrap()
@ -72,7 +72,7 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
{ {
let list = ListType::from_unifier_type(generator, ctx, arg_ty) let list = ListType::from_unifier_type(generator, ctx, arg_ty)
.map_pointer_value(arg.into_pointer_value(), None); .map_value(arg.into_pointer_value(), None);
ctx.builder ctx.builder
.build_int_truncate_or_bit_cast(list.load_size(ctx, None), llvm_i32, "len") .build_int_truncate_or_bit_cast(list.load_size(ctx, None), llvm_i32, "len")
.unwrap() .unwrap()
@ -126,8 +126,7 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -138,7 +137,7 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, "int32", &[n_ty]), _ => unsupported_type(ctx, "int32", &[n_ty]),
@ -187,8 +186,7 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -199,7 +197,7 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, "int64", &[n_ty]), _ => unsupported_type(ctx, "int64", &[n_ty]),
@ -264,8 +262,7 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -276,7 +273,7 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, "uint32", &[n_ty]), _ => unsupported_type(ctx, "uint32", &[n_ty]),
@ -330,8 +327,7 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -342,7 +338,7 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, "uint64", &[n_ty]), _ => unsupported_type(ctx, "uint64", &[n_ty]),
@ -395,8 +391,7 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -407,7 +402,7 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, "float", &[n_ty]), _ => unsupported_type(ctx, "float", &[n_ty]),
@ -440,8 +435,7 @@ pub fn call_round<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -454,7 +448,7 @@ pub fn call_round<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[n_ty]), _ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -480,8 +474,7 @@ pub fn call_numpy_round<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -492,7 +485,7 @@ pub fn call_numpy_round<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[n_ty]), _ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -543,8 +536,7 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -558,7 +550,7 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[n_ty]), _ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -595,8 +587,7 @@ pub fn call_floor<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -609,7 +600,7 @@ pub fn call_floor<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[n_ty]), _ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -646,8 +637,7 @@ pub fn call_ceil<'ctx, G: CodeGenerator + ?Sized>(
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) => if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
let result = ndarray let result = ndarray
.map( .map(
@ -660,7 +650,7 @@ pub fn call_ceil<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[n_ty]), _ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -777,7 +767,7 @@ pub fn call_numpy_minimum<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]), _ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
@ -868,8 +858,7 @@ pub fn call_numpy_max_min<'ctx, G: CodeGenerator + ?Sized>(
{ {
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, a_ty); let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, a_ty);
let ndarray = let ndarray = NDArrayType::from_unifier_type(generator, ctx, a_ty).map_value(n, None);
NDArrayType::from_unifier_type(generator, ctx, a_ty).map_pointer_value(n, None);
let llvm_dtype = ndarray.get_type().element_type(); let llvm_dtype = ndarray.get_type().element_type();
let zero = llvm_usize.const_zero(); let zero = llvm_usize.const_zero();
@ -1037,7 +1026,7 @@ pub fn call_numpy_maximum<'ctx, G: CodeGenerator + ?Sized>(
) )
.unwrap(); .unwrap();
result.as_abi_value(ctx).into() result.as_base_value().into()
} }
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]), _ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
@ -1649,7 +1638,7 @@ pub fn call_np_linalg_cholesky<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1664,11 +1653,11 @@ pub fn call_np_linalg_cholesky<'ctx, G: CodeGenerator + ?Sized>(
let out_c = out.make_contiguous_ndarray(generator, ctx); let out_c = out.make_contiguous_ndarray(generator, ctx);
extern_fns::call_np_linalg_cholesky( extern_fns::call_np_linalg_cholesky(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
out_c.as_abi_value(ctx).into(), out_c.as_base_value().into(),
None, None,
); );
Ok(out.as_abi_value(ctx).into()) Ok(out.as_base_value().into())
} }
/// Invokes the `np_linalg_qr` linalg function /// Invokes the `np_linalg_qr` linalg function
@ -1683,7 +1672,7 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1710,20 +1699,20 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_qr( extern_fns::call_np_linalg_qr(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
q_c.as_abi_value(ctx).into(), q_c.as_base_value().into(),
r_c.as_abi_value(ctx).into(), r_c.as_base_value().into(),
None, None,
); );
let q = q.as_abi_value(ctx); let q = q.as_base_value().as_basic_value_enum();
let r = r.as_abi_value(ctx); let r = r.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[q.get_type(), r.get_type()]).construct_from_objects( let tuple = TupleType::new(ctx, &[q.get_type(), r.get_type()]).construct_from_objects(
ctx, ctx,
[q.into(), r.into()], [q, r],
None, None,
); );
Ok(tuple.as_abi_value(ctx).into()) Ok(tuple.as_base_value().into())
} }
/// Invokes the `np_linalg_svd` linalg function /// Invokes the `np_linalg_svd` linalg function
@ -1738,7 +1727,7 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1771,19 +1760,19 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_svd( extern_fns::call_np_linalg_svd(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
u_c.as_abi_value(ctx).into(), u_c.as_base_value().into(),
s_c.as_abi_value(ctx).into(), s_c.as_base_value().into(),
vh_c.as_abi_value(ctx).into(), vh_c.as_base_value().into(),
None, None,
); );
let u = u.as_abi_value(ctx); let u = u.as_base_value().as_basic_value_enum();
let s = s.as_abi_value(ctx); let s = s.as_base_value().as_basic_value_enum();
let vh = vh.as_abi_value(ctx); let vh = vh.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[u.get_type(), s.get_type(), vh.get_type()]) let tuple = TupleType::new(ctx, &[u.get_type(), s.get_type(), vh.get_type()])
.construct_from_objects(ctx, [u.into(), s.into(), vh.into()], None); .construct_from_objects(ctx, [u, s, vh], None);
Ok(tuple.as_abi_value(ctx).into()) Ok(tuple.as_base_value().into())
} }
/// Invokes the `np_linalg_inv` linalg function /// Invokes the `np_linalg_inv` linalg function
@ -1796,7 +1785,7 @@ pub fn call_np_linalg_inv<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1811,12 +1800,12 @@ pub fn call_np_linalg_inv<'ctx, G: CodeGenerator + ?Sized>(
let out_c = out.make_contiguous_ndarray(generator, ctx); let out_c = out.make_contiguous_ndarray(generator, ctx);
extern_fns::call_np_linalg_inv( extern_fns::call_np_linalg_inv(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
out_c.as_abi_value(ctx).into(), out_c.as_base_value().into(),
None, None,
); );
Ok(out.as_abi_value(ctx).into()) Ok(out.as_base_value().into())
} }
/// Invokes the `np_linalg_pinv` linalg function /// Invokes the `np_linalg_pinv` linalg function
@ -1831,7 +1820,7 @@ pub fn call_np_linalg_pinv<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1856,12 +1845,12 @@ pub fn call_np_linalg_pinv<'ctx, G: CodeGenerator + ?Sized>(
let out_c = out.make_contiguous_ndarray(generator, ctx); let out_c = out.make_contiguous_ndarray(generator, ctx);
extern_fns::call_np_linalg_pinv( extern_fns::call_np_linalg_pinv(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
out_c.as_abi_value(ctx).into(), out_c.as_base_value().into(),
None, None,
); );
Ok(out.as_abi_value(ctx).into()) Ok(out.as_base_value().into())
} }
/// Invokes the `sp_linalg_lu` linalg function /// Invokes the `sp_linalg_lu` linalg function
@ -1876,7 +1865,7 @@ pub fn call_sp_linalg_lu<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1903,20 +1892,20 @@ pub fn call_sp_linalg_lu<'ctx, G: CodeGenerator + ?Sized>(
let u_c = u.make_contiguous_ndarray(generator, ctx); let u_c = u.make_contiguous_ndarray(generator, ctx);
extern_fns::call_sp_linalg_lu( extern_fns::call_sp_linalg_lu(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
l_c.as_abi_value(ctx).into(), l_c.as_base_value().into(),
u_c.as_abi_value(ctx).into(), u_c.as_base_value().into(),
None, None,
); );
let l = l.as_abi_value(ctx); let l = l.as_base_value().as_basic_value_enum();
let u = u.as_abi_value(ctx); let u = u.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[l.get_type(), u.get_type()]).construct_from_objects( let tuple = TupleType::new(ctx, &[l.get_type(), u.get_type()]).construct_from_objects(
ctx, ctx,
[l.into(), u.into()], [l, u],
None, None,
); );
Ok(tuple.as_abi_value(ctx).into()) Ok(tuple.as_base_value().into())
} }
/// Invokes the `np_linalg_matrix_power` linalg function /// Invokes the `np_linalg_matrix_power` linalg function
@ -1964,13 +1953,13 @@ pub fn call_np_linalg_matrix_power<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_matrix_power( extern_fns::call_np_linalg_matrix_power(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
x2_c.as_abi_value(ctx).into(), x2_c.as_base_value().into(),
out_c.as_abi_value(ctx).into(), out_c.as_base_value().into(),
None, None,
); );
Ok(out.as_abi_value(ctx).into()) Ok(out.as_base_value().into())
} }
/// Invokes the `np_linalg_det` linalg function /// Invokes the `np_linalg_det` linalg function
@ -1985,7 +1974,7 @@ pub fn call_np_linalg_det<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]); unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -2004,8 +1993,8 @@ pub fn call_np_linalg_det<'ctx, G: CodeGenerator + ?Sized>(
let out_c = det.make_contiguous_ndarray(generator, ctx); let out_c = det.make_contiguous_ndarray(generator, ctx);
extern_fns::call_np_linalg_det( extern_fns::call_np_linalg_det(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
out_c.as_abi_value(ctx).into(), out_c.as_base_value().into(),
None, None,
); );
@ -2024,7 +2013,7 @@ pub fn call_sp_linalg_schur<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
assert_eq!(x1.get_type().ndims(), 2); assert_eq!(x1.get_type().ndims(), 2);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
@ -2046,20 +2035,20 @@ pub fn call_sp_linalg_schur<'ctx, G: CodeGenerator + ?Sized>(
let z_c = z.make_contiguous_ndarray(generator, ctx); let z_c = z.make_contiguous_ndarray(generator, ctx);
extern_fns::call_sp_linalg_schur( extern_fns::call_sp_linalg_schur(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
t_c.as_abi_value(ctx).into(), t_c.as_base_value().into(),
z_c.as_abi_value(ctx).into(), z_c.as_base_value().into(),
None, None,
); );
let t = t.as_abi_value(ctx); let t = t.as_base_value().as_basic_value_enum();
let z = z.as_abi_value(ctx); let z = z.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[t.get_type(), z.get_type()]).construct_from_objects( let tuple = TupleType::new(ctx, &[t.get_type(), z.get_type()]).construct_from_objects(
ctx, ctx,
[t.into(), z.into()], [t, z],
None, None,
); );
Ok(tuple.as_abi_value(ctx).into()) Ok(tuple.as_base_value().into())
} }
/// Invokes the `sp_linalg_hessenberg` linalg function /// Invokes the `sp_linalg_hessenberg` linalg function
@ -2072,7 +2061,7 @@ pub fn call_sp_linalg_hessenberg<'ctx, G: CodeGenerator + ?Sized>(
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) }; let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None); let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
assert_eq!(x1.get_type().ndims(), 2); assert_eq!(x1.get_type().ndims(), 2);
if !x1.get_type().element_type().is_float_type() { if !x1.get_type().element_type().is_float_type() {
@ -2094,18 +2083,18 @@ pub fn call_sp_linalg_hessenberg<'ctx, G: CodeGenerator + ?Sized>(
let q_c = q.make_contiguous_ndarray(generator, ctx); let q_c = q.make_contiguous_ndarray(generator, ctx);
extern_fns::call_sp_linalg_hessenberg( extern_fns::call_sp_linalg_hessenberg(
ctx, ctx,
x1_c.as_abi_value(ctx).into(), x1_c.as_base_value().into(),
h_c.as_abi_value(ctx).into(), h_c.as_base_value().into(),
q_c.as_abi_value(ctx).into(), q_c.as_base_value().into(),
None, None,
); );
let h = h.as_abi_value(ctx); let h = h.as_base_value().as_basic_value_enum();
let q = q.as_abi_value(ctx); let q = q.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[h.get_type(), q.get_type()]).construct_from_objects( let tuple = TupleType::new(ctx, &[h.get_type(), q.get_type()]).construct_from_objects(
ctx, ctx,
[h.into(), q.into()], [h, q],
None, None,
); );
Ok(tuple.as_abi_value(ctx).into()) Ok(tuple.as_base_value().into())
} }

View File

@ -32,9 +32,7 @@ use super::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var, gen_var,
}, },
types::{ types::{ndarray::NDArrayType, ListType},
ndarray::NDArrayType, ExceptionType, ListType, OptionType, RangeType, StringType, TupleType,
},
values::{ values::{
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray}, ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue, ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
@ -45,7 +43,7 @@ use super::{
use crate::{ use crate::{
symbol_resolver::{SymbolValue, ValueEnum}, symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{ toplevel::{
helper::{arraylike_flatten_element_type, extract_ndims, PrimDef}, helper::{arraylike_flatten_element_type, PrimDef},
numpy::unpack_ndarray_var_tys, numpy::unpack_ndarray_var_tys,
DefinitionId, TopLevelDef, DefinitionId, TopLevelDef,
}, },
@ -170,27 +168,65 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(), 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::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => { 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) => { SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec(); 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(); let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec();
TupleType::new(self, &fields) let ty = self.ctx.struct_type(&fields, false);
.construct_from_objects(self, vals, Some("tup_val")) let ptr = gen_var(self, ty.into(), Some("tuple")).unwrap();
.as_abi_value(self) let zero = self.ctx.i32_type().const_zero();
.into() 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) => { 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); let val = self.gen_symbol_val(generator, v, ty);
OptionType::from_unifier_type(generator, self, ty) let ptr = generator
.construct_some_value(generator, self, &val, None) .gen_var_alloc(self, val.get_type(), Some("default_opt_some"))
.as_abi_value(self) .unwrap();
.into() 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(),
} }
} }
@ -285,10 +321,15 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
if let Some(v) = self.const_strings.get(v) { if let Some(v) = self.const_strings.get(v) {
Some(*v) Some(*v)
} else { } else {
let val = StringType::new(self) let str_ptr = self
.construct_constant(self, v, None) .builder
.as_abi_value(self) .build_global_string_ptr(v, "const")
.into(); .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); self.const_strings.insert(v.to_string(), val);
Some(val) Some(val)
} }
@ -578,35 +619,42 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3],
loc: Location, 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 { let zelf = if let Some(exception_val) = self.exception_val {
llvm_exn.map_pointer_value(exception_val, Some("exn")) exception_val
} else { } else {
let zelf = llvm_exn.alloca_var(generator, self, Some("exn")); let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type();
self.exception_val = Some(zelf.as_abi_value(self)); let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into();
zelf let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
*self.exception_val.insert(zelf)
}; };
let int32 = self.ctx.i32_type();
let id = self.resolver.get_string_id(name); let zero = int32.const_zero();
zelf.store_name(self, llvm_i32.const_int(id as u64, false)); unsafe {
zelf.store_message(self, msg.into_struct_value()); let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap();
zelf.store_params( let id = self.resolver.get_string_id(name);
self, self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap();
params let ptr = self
.iter() .builder
.map(|p| { .build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg")
p.map_or(llvm_i64.const_zero(), |v| { .unwrap();
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").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() {
.collect_array() let ptr = self
.as_ref() .builder
.unwrap(), .build_in_bounds_gep(
); zelf,
gen_raise(generator, self, Some(&zelf), loc); &[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>( pub fn make_assert<G: CodeGenerator + ?Sized>(
@ -1103,7 +1151,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
{ {
let iter_val = let iter_val =
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range")); RangeValue::from_pointer_value(iter_val.into_pointer_value(), Some("range"));
let (start, stop, step) = destructure_range(ctx, iter_val); let (start, stop, step) = destructure_range(ctx, iter_val);
let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap(); let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap();
// add 1 to the length as the value is rounded to zero // add 1 to the length as the value is rounded to zero
@ -1259,7 +1307,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
emit_cont_bb(ctx, list); emit_cont_bb(ctx, list);
Ok(Some(list.as_abi_value(ctx).into())) Ok(Some(list.as_base_value().into()))
} }
/// Generates LLVM IR for a binary operator expression using the [`Type`] and /// Generates LLVM IR for a binary operator expression using the [`Type`] and
@ -1271,7 +1319,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op: Binop, op: Binop,
right: (&Option<Type>, BasicValueEnum<'ctx>), right: (&Option<Type>, BasicValueEnum<'ctx>),
loc: Location, loc: Location,
) -> Result<BasicValueEnum<'ctx>, String> { ) -> Result<Option<ValueEnum<'ctx>>, String> {
let (left_ty, left_val) = left; let (left_ty, left_val) = left;
let (right_ty, right_val) = right; let (right_ty, right_val) = right;
@ -1282,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 // which would be unchanged until further unification, which we would never do
// when doing code generation for function instances // when doing code generation for function instances
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) { 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) { } 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) { } else if [Operator::LShift, Operator::RShift].contains(&op.base) {
let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1); 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 { } 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 { } else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int // Pow is the only operator that would pass typecheck between float and int
assert_eq!(op.base, Operator::Pow); assert_eq!(op.base, Operator::Pow);
@ -1299,7 +1347,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
right_val.into_int_value(), right_val.into_int_value(),
Some("f_pow_i"), 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()) } 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()) || ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
{ {
@ -1389,7 +1437,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
ctx.ctx.bool_type().const_zero(), ctx.ctx.bool_type().const_zero(),
); );
Ok(new_list.as_abi_value(ctx).into()) Ok(Some(new_list.as_base_value().into()))
} }
Operator::Mult => { Operator::Mult => {
@ -1476,7 +1524,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
llvm_usize.const_int(1, false), llvm_usize.const_int(1, false),
)?; )?;
Ok(new_list.as_abi_value(ctx).into()) Ok(Some(new_list.as_base_value().into()))
} }
_ => todo!("Operator not supported"), _ => todo!("Operator not supported"),
@ -1515,7 +1563,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
let result = left let result = left
.matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out)) .matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out))
.split_unsized(generator, ctx); .split_unsized(generator, ctx);
Ok(result.to_basic_value_enum()) Ok(Some(result.to_basic_value_enum().into()))
} else { } else {
// For other operations, they are all elementwise operations. // For other operations, they are all elementwise operations.
@ -1546,12 +1594,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op, op,
(&Some(ty2_dtype), right_value), (&Some(ty2_dtype), right_value),
ctx.current_loc, ctx.current_loc,
)?; )?
.unwrap()
.to_basic_value_enum(ctx, generator, common_dtype)?;
Ok(result) Ok(result)
}) })
.unwrap(); .unwrap();
Ok(result.as_abi_value(ctx).into()) Ok(Some(result.as_base_value().into()))
} }
} else { } else {
let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap()); let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap());
@ -1600,8 +1650,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
(&signature, fun_id), (&signature, fun_id),
vec![(None, right_val.into())], vec![(None, right_val.into())],
) )
.map(Option::unwrap) .map(|f| f.map(Into::into))
.map(BasicValueEnum::into)
} }
} }
@ -1639,7 +1688,6 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
(&right.custom, right_val), (&right.custom, right_val),
loc, loc,
) )
.map(|res| Some(res.into()))
} }
/// Generates LLVM IR for a unary operator expression using the [`Type`] and /// Generates LLVM IR for a unary operator expression using the [`Type`] and
@ -1649,19 +1697,18 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
op: ast::Unaryop, op: ast::Unaryop,
operand: (&Option<Type>, BasicValueEnum<'ctx>), operand: (&Option<Type>, BasicValueEnum<'ctx>),
) -> Result<BasicValueEnum<'ctx>, String> { ) -> Result<Option<ValueEnum<'ctx>>, String> {
let (ty, val) = operand; let (ty, val) = operand;
let ty = ctx.unifier.get_representative(ty.unwrap()); 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(); let val = val.into_int_value();
if op == ast::Unaryop::Not { if op == ast::Unaryop::Not {
let not = ctx let not = ctx.builder.build_not(val, "not").unwrap();
.builder let not_bool =
.build_int_compare(IntPredicate::EQ, val, val.get_type().const_zero(), "not") ctx.builder.build_and(not, not.get_type().const_int(1, false), "").unwrap();
.unwrap();
generator.bool_to_int_type(ctx, not, val.get_type()).into() not_bool.into()
} else { } else {
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
@ -1674,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(), ctx.builder.build_int_z_extend(val, llvm_i32, "").map(Into::into).unwrap(),
), ),
)? )?
.unwrap()
} }
} else if [ } else if [
ctx.primitives.int32, ctx.primitives.int32,
@ -1719,7 +1767,7 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
let (ndarray_dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty); let (ndarray_dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty)
.map_pointer_value(val.into_pointer_value(), None); .map_value(val.into_pointer_value(), None);
// ndarray uses `~` rather than `not` to perform elementwise inversion, convert it before // ndarray uses `~` rather than `not` to perform elementwise inversion, convert it before
// passing it to the elementwise codegen function // passing it to the elementwise codegen function
@ -1727,13 +1775,10 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
if op == ast::Unaryop::Invert { if op == ast::Unaryop::Invert {
ast::Unaryop::Not ast::Unaryop::Not
} else { } else {
let ndims = extract_ndims(&ctx.unifier, ty);
codegen_unreachable!( codegen_unreachable!(
ctx, ctx,
"ufunc {} not supported for ndarray[bool, {}]", "ufunc {} not supported for ndarray[bool, N]",
op.op_info().method_name, op.op_info().method_name,
ndims,
) )
} }
} else { } else {
@ -1745,14 +1790,16 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx, ctx,
NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() }, NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() },
|generator, ctx, scalar| { |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() mapped_ndarray.as_base_value().into()
} else { } else {
unimplemented!() unimplemented!()
}) }))
} }
/// Generates LLVM IR for a unary operator expression. /// Generates LLVM IR for a unary operator expression.
@ -1772,7 +1819,6 @@ pub fn gen_unaryop_expr<'ctx, G: CodeGenerator>(
}; };
gen_unaryop_expr_with_values(generator, ctx, op, (&operand.custom, val)) 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 /// Generates LLVM IR for a comparison operator expression using the [`Type`] and
@ -1783,7 +1829,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
left: (Option<Type>, BasicValueEnum<'ctx>), left: (Option<Type>, BasicValueEnum<'ctx>),
ops: &[ast::Cmpop], ops: &[ast::Cmpop],
comparators: &[(Option<Type>, BasicValueEnum<'ctx>)], comparators: &[(Option<Type>, BasicValueEnum<'ctx>)],
) -> Result<BasicValueEnum<'ctx>, String> { ) -> Result<Option<ValueEnum<'ctx>>, String> {
debug_assert_eq!(comparators.len(), ops.len()); debug_assert_eq!(comparators.len(), ops.len());
if comparators.len() == 1 { if comparators.len() == 1 {
@ -1825,13 +1871,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
(Some(left_ty_dtype), left_scalar), (Some(left_ty_dtype), left_scalar),
&[op], &[op],
&[(Some(right_ty_dtype), right_scalar)], &[(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()) 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_base_value().into()));
} }
} }
@ -1915,19 +1967,41 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
} else if left_ty == ctx.primitives.str { } else if left_ty == ctx.primitives.str {
assert!(ctx.unifier.unioned(left_ty, right_ty)); 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 llvm_i32 = ctx.ctx.i32_type();
let rhs = llvm_str.map_struct_value(rhs.into_struct_value(), None); 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 { if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values( ctx.builder.build_not(result, "").unwrap()
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), result.into()),
)?.into_int_value()
} else { } else {
result result
} }
@ -2030,6 +2104,9 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[Cmpop::Eq], &[Cmpop::Eq],
&[(Some(right_elem_ty), right)], &[(Some(right_elem_ty), right)],
)? )?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
.unwrap()
.into_int_value(); .into_int_value();
gen_if_callback( gen_if_callback(
@ -2078,6 +2155,8 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
Unaryop::Not, Unaryop::Not,
(&Some(ctx.primitives.bool), acc.into()), (&Some(ctx.primitives.bool), acc.into()),
)? )?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
.into_int_value() .into_int_value()
} else { } else {
acc acc
@ -2165,6 +2244,11 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[op], &[op],
&[(Some(right_ty), right_elem)], &[(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)?; .map(BasicValueEnum::into_int_value)?;
Ok(ctx.builder.build_not( Ok(ctx.builder.build_not(
@ -2201,12 +2285,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
// Invert the final value if __ne__ // Invert the final value if __ne__
if *op == Cmpop::NotEq { if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values( ctx.builder.build_not(cmp_phi, "").unwrap()
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), cmp_phi.into()),
)?.into_int_value()
} else { } else {
cmp_phi cmp_phi
} }
@ -2231,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))) 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. /// Generates LLVM IR for a comparison operator expression.
@ -2280,7 +2362,6 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
ops, ops,
comparator_vals.as_slice(), comparator_vals.as_slice(),
) )
.map(|res| Some(res.into()))
} }
/// See [`CodeGenerator::gen_expr`]. /// See [`CodeGenerator::gen_expr`].
@ -2310,13 +2391,16 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
const_val.into() const_val.into()
} }
ExprKind::Name { id, .. } if id == &"none".into() => { ExprKind::Name { id, .. } if id == &"none".into() => {
match &*ctx.unifier.get_ty(expr.custom.unwrap()) { match (
TypeEnum::TObj { obj_id, .. } ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() => 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()) ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
.construct_empty(generator, ctx, None) .ptr_type(AddressSpace::default())
.as_abi_value(ctx) .const_null()
.into() .into()
} }
_ => codegen_unreachable!(ctx, "must be option type"), _ => codegen_unreachable!(ctx, "must be option type"),
@ -2409,7 +2493,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
); );
ctx.builder.build_store(elem_ptr, *v).unwrap(); ctx.builder.build_store(elem_ptr, *v).unwrap();
} }
arr_str_ptr.as_abi_value(ctx).into() arr_str_ptr.as_base_value().into()
} }
ExprKind::Tuple { elts, .. } => { ExprKind::Tuple { elts, .. } => {
let elements_val = elts let elements_val = elts
@ -2801,12 +2885,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
}; };
} }
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => { ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let option = OptionType::from_pointer_type( let not_null =
ptr.get_type(), ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
ctx.get_size_type(),
)
.map_pointer_value(ptr, None);
let not_null = option.is_some(ctx);
ctx.make_assert( ctx.make_assert(
generator, generator,
not_null, not_null,
@ -2815,7 +2895,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
[None, None, None], [None, None, None],
expr.location, 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(_) => { ValueEnum::Dynamic(_) => {
codegen_unreachable!(ctx, "option must be static or ptr") codegen_unreachable!(ctx, "option must be static or ptr")
@ -2903,7 +2988,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
v, v,
(start, end, step), (start, end, step),
); );
res_array_ret.as_abi_value(ctx).into() res_array_ret.as_base_value().into()
} else { } else {
let len = v.load_size(ctx, Some("len")); let len = v.load_size(ctx, Some("len"));
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? { let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
@ -2958,14 +3043,14 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
let ndarray_ty = value.custom.unwrap(); let ndarray_ty = value.custom.unwrap();
let ndarray = ndarray.to_basic_value_enum(ctx, generator, ndarray_ty)?; let ndarray = ndarray.to_basic_value_enum(ctx, generator, ndarray_ty)?;
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
.map_pointer_value(ndarray.into_pointer_value(), None); .map_value(ndarray.into_pointer_value(), None);
let indices = RustNDIndex::from_subscript_expr(generator, ctx, slice)?; let indices = RustNDIndex::from_subscript_expr(generator, ctx, slice)?;
let result = ndarray let result = ndarray
.index(generator, ctx, &indices) .index(generator, ctx, &indices)
.split_unsized(generator, ctx) .split_unsized(generator, ctx)
.to_basic_value_enum(); .to_basic_value_enum();
return Ok(Some(result.into())); return Ok(Some(ValueEnum::Dynamic(result)));
} }
TypeEnum::TTuple { .. } => { TypeEnum::TTuple { .. } => {
let index: u32 = let index: u32 =

View File

@ -1,9 +1,10 @@
use inkwell::{ use inkwell::{
attributes::{Attribute, AttributeLoc}, attributes::{Attribute, AttributeLoc},
values::{BasicValueEnum, FloatValue, IntValue}, values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
}; };
use itertools::Either;
use super::{expr::infer_and_call_function, CodeGenContext}; use super::CodeGenContext;
/// Macro to generate extern function /// Macro to generate extern function
/// Both function return type and function parameter type are `FloatValue` /// 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)*) => { ($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." )] #[doc = concat!("Invokes the [`", stringify!($extern_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($llvm_name), ") function." )]
pub fn $fn_name<'ctx>( pub fn $fn_name<'ctx>(
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>
$($args: FloatValue<'ctx>,)* $(,$args: FloatValue<'ctx>)*,
name: Option<&str>, name: Option<&str>,
) -> FloatValue<'ctx> { ) -> FloatValue<'ctx> {
const FN_NAME: &str = $extern_fn; const FN_NAME: &str = $extern_fn;
@ -45,23 +46,24 @@ macro_rules! generate_extern_fn {
let llvm_f64 = ctx.ctx.f64_type(); let llvm_f64 = ctx.ctx.f64_type();
$(debug_assert_eq!($args.get_type(), llvm_f64);)* $(debug_assert_eq!($args.get_type(), llvm_f64);)*
infer_and_call_function( let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
ctx, let fn_type = llvm_f64.fn_type(&[$($args.get_type().into()),*], false);
FN_NAME, let func = ctx.module.add_function(FN_NAME, fn_type, None);
Some(llvm_f64.into()), for attr in [$($attributes),*] {
&[$($args.into()),*], func.add_attribute(
name, AttributeLoc::Function,
Some(&|func| { ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
for attr in [$($attributes),*] { );
func.add_attribute( }
AttributeLoc::Function, func
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), });
);
} ctx.builder
}) .build_call(extern_fn, &[$($args.into()),*], name.unwrap_or_default())
) .map(CallSiteValue::try_as_basic_value)
.map(BasicValueEnum::into_float_value) .map(|v| v.map_left(BasicValueEnum::into_float_value))
.unwrap() .map(Either::unwrap_left)
.unwrap()
} }
}; };
} }
@ -110,23 +112,25 @@ pub fn call_ldexp<'ctx>(
debug_assert_eq!(arg.get_type(), llvm_f64); debug_assert_eq!(arg.get_type(), llvm_f64);
debug_assert_eq!(exp.get_type(), llvm_i32); debug_assert_eq!(exp.get_type(), llvm_i32);
infer_and_call_function( let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
ctx, let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_i32.into()], false);
FN_NAME, let func = ctx.module.add_function(FN_NAME, fn_type, None);
Some(llvm_f64.into()), for attr in ["mustprogress", "nofree", "nounwind", "willreturn"] {
&[arg.into(), exp.into()], func.add_attribute(
name, AttributeLoc::Function,
Some(&|func| { ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
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(BasicValueEnum::into_float_value) .map(CallSiteValue::try_as_basic_value)
.unwrap() .map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
} }
/// Macro to generate `np_linalg` and `sp_linalg` functions /// Macro to generate `np_linalg` and `sp_linalg` functions
@ -154,27 +158,25 @@ macro_rules! generate_linalg_extern_fn {
($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => { ($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => {
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )] #[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )]
pub fn $fn_name<'ctx>( pub fn $fn_name<'ctx>(
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>
$($input_matrix: BasicValueEnum<'ctx>,)* $(,$input_matrix: BasicValueEnum<'ctx>)*,
name: Option<&str>, name: Option<&str>,
){ ){
const FN_NAME: &str = $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( let func = ctx.module.add_function(FN_NAME, fn_type, None);
ctx, for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] {
FN_NAME, func.add_attribute(
None, AttributeLoc::Function,
&[$($input_matrix.into(),)*], ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
name, );
Some(&|func| { }
for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { func
func.add_attribute( });
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), ctx.builder.build_call(extern_fn, &[$($input_matrix.into(),)*], name.unwrap_or_default()).unwrap();
);
}
}),
);
} }
}; };
} }

View File

@ -7,7 +7,7 @@ use inkwell::{
use nac3parser::ast::{Expr, Stmt, StrRef}; use nac3parser::ast::{Expr, Stmt, StrRef};
use super::{bool_to_int_type, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext}; use super::{bool_to_i1, bool_to_i8, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext};
use crate::{ use crate::{
symbol_resolver::ValueEnum, symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef}, toplevel::{DefinitionId, TopLevelDef},
@ -248,32 +248,22 @@ pub trait CodeGenerator {
gen_block(self, ctx, stmts) 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>( fn bool_to_i1<'ctx>(
&self, &self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>, bool_value: IntValue<'ctx>,
) -> 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>( fn bool_to_i8<'ctx>(
&self, &self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>, bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
self.bool_to_int_type(ctx, bool_value, ctx.ctx.i8_type()) bool_to_i8(&ctx.builder, ctx.ctx, bool_value)
}
/// 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)
} }
} }

View File

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

View File

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

View File

@ -36,7 +36,7 @@ pub fn call_nac3_ndarray_array_set_and_validate_list_shape<'ctx, G: CodeGenerato
ctx, ctx,
&name, &name,
None, None,
&[list.as_abi_value(ctx).into(), ndims.into(), shape.base_ptr(ctx, generator).into()], &[list.as_base_value().into(), ndims.into(), shape.base_ptr(ctx, generator).into()],
None, None,
None, None,
); );
@ -65,7 +65,7 @@ pub fn call_nac3_ndarray_array_write_list_to_array<'ctx>(
ctx, ctx,
&name, &name,
None, None,
&[list.as_abi_value(ctx).into(), ndarray.as_abi_value(ctx).into()], &[list.as_base_value().into(), ndarray.as_base_value().into()],
None, None,
None, None,
); );

View File

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

View File

@ -30,7 +30,7 @@ pub fn call_nac3_ndarray_broadcast_to<'ctx>(
ctx, ctx,
&name, &name,
None, None,
&[src_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_abi_value(ctx).into()], &[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
None, None,
None, None,
); );
@ -55,9 +55,10 @@ pub fn call_nac3_ndarray_broadcast_shapes<'ctx, G, Shape>(
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
assert_eq!(num_shape_entries.get_type(), llvm_usize); assert_eq!(num_shape_entries.get_type(), llvm_usize);
assert!(ShapeEntryType::is_representable( assert!(ShapeEntryType::is_type(
shape_entries.base_ptr(ctx, generator).get_type(), generator,
llvm_usize, ctx.ctx,
shape_entries.base_ptr(ctx, generator).get_type()
) )
.is_ok()); .is_ok());
assert_eq!(dst_ndims.get_type(), llvm_usize); assert_eq!(dst_ndims.get_type(), llvm_usize);

View File

@ -25,8 +25,8 @@ pub fn call_nac3_ndarray_index<'ctx, G: CodeGenerator + ?Sized>(
&[ &[
indices.size(ctx, generator).into(), indices.size(ctx, generator).into(),
indices.base_ptr(ctx, generator).into(), indices.base_ptr(ctx, generator).into(),
src_ndarray.as_abi_value(ctx).into(), src_ndarray.as_base_value().into(),
dst_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_base_value().into(),
], ],
None, None,
None, None,

View File

@ -1,8 +1,13 @@
use inkwell::values::{BasicValueEnum, IntValue}; use inkwell::{
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::codegen::{ use crate::codegen::{
expr::infer_and_call_function, expr::{create_and_call_function, infer_and_call_function},
irrt::get_usize_dependent_function_name, irrt::get_usize_dependent_function_name,
types::ProxyType,
values::{ values::{
ndarray::{NDArrayValue, NDIterValue}, ndarray::{NDArrayValue, NDIterValue},
ProxyValue, TypedArrayLikeAccessor, ProxyValue, TypedArrayLikeAccessor,
@ -21,19 +26,23 @@ pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>, indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) { ) {
let llvm_usize = ctx.get_size_type(); 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"); let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize");
infer_and_call_function( create_and_call_function(
ctx, ctx,
&name, &name,
None, None,
&[ &[
iter.as_abi_value(ctx).into(), (iter.get_type().as_base_type().into(), iter.as_base_value().into()),
ndarray.as_abi_value(ctx).into(), (ndarray.get_type().as_base_type().into(), ndarray.as_base_value().into()),
indices.base_ptr(ctx, generator).into(), (llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
], ],
None, None,
None, None,
@ -54,7 +63,7 @@ pub fn call_nac3_nditer_has_element<'ctx>(
ctx, ctx,
&name, &name,
Some(ctx.ctx.bool_type().into()), Some(ctx.ctx.bool_type().into()),
&[iter.as_abi_value(ctx).into()], &[iter.as_base_value().into()],
None, None,
None, None,
) )
@ -68,5 +77,5 @@ pub fn call_nac3_nditer_has_element<'ctx>(
pub fn call_nac3_nditer_next<'ctx>(ctx: &CodeGenContext<'ctx, '_>, iter: NDIterValue<'ctx>) { pub fn call_nac3_nditer_next<'ctx>(ctx: &CodeGenContext<'ctx, '_>, iter: NDIterValue<'ctx>) {
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_next"); let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_next");
infer_and_call_function(ctx, &name, None, &[iter.as_abi_value(ctx).into()], None, None); infer_and_call_function(ctx, &name, None, &[iter.as_base_value().into()], None, None);
} }

View File

@ -1,4 +1,4 @@
use inkwell::values::IntValue; use inkwell::{types::BasicTypeEnum, values::IntValue};
use crate::codegen::{ use crate::codegen::{
expr::infer_and_call_function, irrt::get_usize_dependent_function_name, expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
@ -22,12 +22,26 @@ pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized
) { ) {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
assert_eq!(a_shape.element_type(ctx, generator), llvm_usize.into()); assert_eq!(
assert_eq!(b_shape.element_type(ctx, generator), llvm_usize.into()); BasicTypeEnum::try_from(a_shape.element_type(ctx, generator)).unwrap(),
assert_eq!(final_ndims.get_type(), llvm_usize); llvm_usize.into()
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!(
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into()); 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"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_matmul_calculate_shapes");

View File

@ -34,8 +34,8 @@ pub fn call_nac3_ndarray_transpose<'ctx, G: CodeGenerator + ?Sized>(
&name, &name,
None, None,
&[ &[
src_ndarray.as_abi_value(ctx).into(), src_ndarray.as_base_value().into(),
dst_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_base_value().into(),
axes.map_or(llvm_usize.const_zero(), |axes| axes.size(ctx, generator)).into(), axes.map_or(llvm_usize.const_zero(), |axes| axes.size(ctx, generator)).into(),
axes.map_or(llvm_usize.ptr_type(AddressSpace::default()).const_null(), |axes| { axes.map_or(llvm_usize.ptr_type(AddressSpace::default()).const_null(), |axes| {
axes.base_ptr(ctx, generator) axes.base_ptr(ctx, generator)

View File

@ -1,9 +1,10 @@
use inkwell::{ use inkwell::{
values::{BasicValueEnum, IntValue}, values::{BasicValueEnum, CallSiteValue, IntValue},
IntPredicate, IntPredicate,
}; };
use itertools::Either;
use crate::codegen::{expr::infer_and_call_function, CodeGenContext, CodeGenerator}; use crate::codegen::{CodeGenContext, CodeGenerator};
/// Invokes the `__nac3_range_slice_len` in IRRT. /// 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"; const SYMBOL: &str = "__nac3_range_slice_len";
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
assert_eq!(start.get_type(), llvm_i32); assert_eq!(start.get_type(), llvm_i32);
assert_eq!(end.get_type(), llvm_i32); assert_eq!(end.get_type(), llvm_i32);
assert_eq!(step.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 // assert step != 0, throw exception if not
let not_zero = ctx let not_zero = ctx
.builder .builder
@ -40,14 +47,10 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
ctx.current_loc, ctx.current_loc,
); );
infer_and_call_function( ctx.builder
ctx, .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
SYMBOL, .map(CallSiteValue::try_as_basic_value)
Some(llvm_i32.into()), .map(|v| v.map_left(BasicValueEnum::into_int_value))
&[start.into(), end.into(), step.into()], .map(Either::unwrap_left)
Some("calc_len"), .unwrap()
None,
)
.map(BasicValueEnum::into_int_value)
.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 nac3parser::ast::Expr;
use crate::{ use crate::{
codegen::{expr::infer_and_call_function, CodeGenContext, CodeGenerator}, codegen::{CodeGenContext, CodeGenerator},
typecheck::typedef::Type, typecheck::typedef::Type,
}; };
@ -16,26 +17,23 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
length: IntValue<'ctx>, length: IntValue<'ctx>,
) -> Result<Option<IntValue<'ctx>>, String> { ) -> Result<Option<IntValue<'ctx>>, String> {
const SYMBOL: &str = "__nac3_slice_index_bound"; const SYMBOL: &str = "__nac3_slice_index_bound";
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
let llvm_i32 = ctx.ctx.i32_type(); let i32_t = ctx.ctx.i32_type();
assert_eq!(length.get_type(), llvm_i32); 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)? { let i = if let Some(v) = generator.gen_expr(ctx, i)? {
v.to_basic_value_enum(ctx, generator, i.custom.unwrap())? v.to_basic_value_enum(ctx, generator, i.custom.unwrap())?
} else { } else {
return Ok(None); return Ok(None);
}; };
Ok(Some( Ok(Some(
infer_and_call_function( ctx.builder
ctx, .build_call(func, &[i.into(), length.into()], "bounded_ind")
SYMBOL, .map(CallSiteValue::try_as_basic_value)
Some(llvm_i32.into()), .map(|v| v.map_left(BasicValueEnum::into_int_value))
&[i, length.into()], .map(Either::unwrap_left)
Some("bounded_ind"), .unwrap(),
None,
)
.map(BasicValueEnum::into_int_value)
.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 super::get_usize_dependent_function_name;
use crate::codegen::{expr::infer_and_call_function, values::StringValue, CodeGenContext}; use crate::codegen::CodeGenContext;
/// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal. /// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal.
pub fn call_string_eq<'ctx>( pub fn call_string_eq<'ctx>(
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
str1: StringValue<'ctx>, str1_ptr: PointerValue<'ctx>,
str2: StringValue<'ctx>, str1_len: IntValue<'ctx>,
str2_ptr: PointerValue<'ctx>,
str2_len: IntValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type(); let llvm_i1 = ctx.ctx.bool_type();
let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq"); let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq");
infer_and_call_function( let func = ctx.module.get_function(&func_name).unwrap_or_else(|| {
ctx, ctx.module.add_function(
&func_name, &func_name,
Some(llvm_i1.into()), llvm_i1.fn_type(
&[ &[
str1.extract_ptr(ctx).into(), str1_ptr.get_type().into(),
str1.extract_len(ctx).into(), str1_len.get_type().into(),
str2.extract_ptr(ctx).into(), str2_ptr.get_type().into(),
str2.extract_len(ctx).into(), str2_len.get_type().into(),
], ],
Some("str_eq_call"), false,
None, ),
) None,
.map(BasicValueEnum::into_int_value) )
.unwrap() });
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

@ -43,9 +43,7 @@ use crate::{
}; };
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore}; use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
pub use generator::{CodeGenerator, DefaultCodeGenerator}; pub use generator::{CodeGenerator, DefaultCodeGenerator};
use types::{ use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
ndarray::NDArrayType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
};
pub mod builtin_fns; pub mod builtin_fns;
pub mod concrete_type; pub mod concrete_type;
@ -540,7 +538,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
if PrimDef::contains_id(*obj_id) { if PrimDef::contains_id(*obj_id) {
return match &*unifier.get_ty_immutable(ty) { return match &*unifier.get_ty_immutable(ty) {
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => { TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
let element_type = get_llvm_type( get_llvm_type(
ctx, ctx,
module, module,
generator, generator,
@ -548,9 +546,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
top_level, top_level,
type_cache, type_cache,
*params.iter().next().unwrap().1, *params.iter().next().unwrap().1,
); )
.ptr_type(AddressSpace::default())
OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into() .into()
} }
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
@ -564,7 +562,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
*params.iter().next().unwrap().1, *params.iter().next().unwrap().1,
); );
ListType::new_with_generator(generator, ctx, element_type).as_abi_type().into() ListType::new_with_generator(generator, ctx, element_type).as_base_type().into()
} }
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => { TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
@ -574,7 +572,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
ctx, module, generator, unifier, top_level, type_cache, dtype, ctx, module, generator, unifier, top_level, type_cache, dtype,
); );
NDArrayType::new_with_generator(generator, ctx, element_type, ndims).as_abi_type().into() NDArrayType::new_with_generator(generator, ctx, element_type, ndims).as_base_type().into()
} }
_ => unreachable!( _ => unreachable!(
@ -628,7 +626,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, *ty) get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, *ty)
}) })
.collect_vec(); .collect_vec();
TupleType::new_with_generator(generator, ctx, &fields).as_abi_type().into() TupleType::new_with_generator(generator, ctx, &fields).as_base_type().into()
} }
TVirtual { .. } => unimplemented!(), TVirtual { .. } => unimplemented!(),
_ => unreachable!("{}", ty_enum.get_type_name()), _ => unreachable!("{}", ty_enum.get_type_name()),
@ -788,9 +786,21 @@ pub fn gen_func_impl<
(primitives.float, context.f64_type().into()), (primitives.float, context.f64_type().into()),
(primitives.bool, context.i8_type().into()), (primitives.bool, context.i8_type().into()),
(primitives.str, { (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.range, RangeType::new(context).as_base_type().into()),
(primitives.exception, { (primitives.exception, {
let name = "Exception"; let name = "Exception";
if let Some(t) = module.get_struct_type(name) { if let Some(t) = module.get_struct_type(name) {
@ -923,7 +933,7 @@ pub fn gen_func_impl<
let param_val = param.into_int_value(); let param_val = param.into_int_value();
if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 { 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 { } else {
param_val param_val
} }
@ -1093,29 +1103,43 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
}) })
} }
/// Converts the value of a boolean-like value `value` into an arbitrary [`IntType`]. /// 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> {
/// This has the same semantics as `(ty)(value != 0)` in C. if bool_value.get_type().get_bit_width() == 1 {
/// bool_value
/// The returned value is guaranteed to either be `0` or `1`, except for `ty == i1` where only the } else {
/// least-significant bit would be guaranteed to be `0` or `1`. builder
fn bool_to_int_type<'ctx>( .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>, builder: &Builder<'ctx>,
value: IntValue<'ctx>, ctx: &'ctx Context,
ty: IntType<'ctx>, bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
// i1 -> i1 : %value ; no-op let value_bits = bool_value.get_type().get_bit_width();
// i1 -> i<N> : zext i1 %value to i<N> ; guaranteed to be 0 or 1 - see docs match value_bits {
// i<M> -> i<N>: zext i1 (icmp eq i<M> %value, 0) to i<N> ; same as i<M> -> i1 -> i<N> 8 => bool_value,
match (value.get_type().get_bit_width(), ty.get_bit_width()) { 1 => builder.build_int_z_extend(bool_value, ctx.i8_type(), "frombool").unwrap(),
(1, 1) => value, _ => bool_to_i8(
(1, _) => builder.build_int_z_extend(value, ty, "frombool").unwrap(),
_ => bool_to_int_type(
builder, builder,
ctx,
builder 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(), .unwrap(),
ty,
), ),
} }
} }

View File

@ -44,7 +44,7 @@ pub fn gen_ndarray_empty<'ctx>(
let ndarray = NDArrayType::new(context, llvm_dtype, ndims) let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
.construct_numpy_empty(generator, context, &shape, None); .construct_numpy_empty(generator, context, &shape, None);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.zeros`. /// Generates LLVM IR for `ndarray.zeros`.
@ -69,7 +69,7 @@ pub fn gen_ndarray_zeros<'ctx>(
let ndarray = NDArrayType::new(context, llvm_dtype, ndims) let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
.construct_numpy_zeros(generator, context, dtype, &shape, None); .construct_numpy_zeros(generator, context, dtype, &shape, None);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.ones`. /// Generates LLVM IR for `ndarray.ones`.
@ -94,7 +94,7 @@ pub fn gen_ndarray_ones<'ctx>(
let ndarray = NDArrayType::new(context, llvm_dtype, ndims) let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
.construct_numpy_ones(generator, context, dtype, &shape, None); .construct_numpy_ones(generator, context, dtype, &shape, None);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.full`. /// Generates LLVM IR for `ndarray.full`.
@ -127,7 +127,7 @@ pub fn gen_ndarray_full<'ctx>(
fill_value_arg, fill_value_arg,
None, None,
); );
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
pub fn gen_ndarray_array<'ctx>( pub fn gen_ndarray_array<'ctx>(
@ -166,7 +166,7 @@ pub fn gen_ndarray_array<'ctx>(
.construct_numpy_array(generator, context, (obj_ty, obj_arg), copy, None) .construct_numpy_array(generator, context, (obj_ty, obj_arg), copy, None)
.atleast_nd(generator, context, ndims); .atleast_nd(generator, context, ndims);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.eye`. /// Generates LLVM IR for `ndarray.eye`.
@ -225,7 +225,7 @@ pub fn gen_ndarray_eye<'ctx>(
let ndarray = NDArrayType::new(context, llvm_dtype, 2) let ndarray = NDArrayType::new(context, llvm_dtype, 2)
.construct_numpy_eye(generator, context, dtype, nrows, ncols, offset, None); .construct_numpy_eye(generator, context, dtype, nrows, ncols, offset, None);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.identity`. /// Generates LLVM IR for `ndarray.identity`.
@ -253,7 +253,7 @@ pub fn gen_ndarray_identity<'ctx>(
.unwrap(); .unwrap();
let ndarray = NDArrayType::new(context, llvm_dtype, 2) let ndarray = NDArrayType::new(context, llvm_dtype, 2)
.construct_numpy_identity(generator, context, dtype, n, None); .construct_numpy_identity(generator, context, dtype, n, None);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.copy`. /// Generates LLVM IR for `ndarray.copy`.
@ -272,9 +272,9 @@ pub fn gen_ndarray_copy<'ctx>(
obj.as_ref().unwrap().1.clone().to_basic_value_enum(context, generator, this_ty)?; obj.as_ref().unwrap().1.clone().to_basic_value_enum(context, generator, this_ty)?;
let this = NDArrayType::from_unifier_type(generator, context, this_ty) let this = NDArrayType::from_unifier_type(generator, context, this_ty)
.map_pointer_value(this_arg.into_pointer_value(), None); .map_value(this_arg.into_pointer_value(), None);
let ndarray = this.make_copy(generator, context); let ndarray = this.make_copy(generator, context);
Ok(ndarray.as_abi_value(context)) Ok(ndarray.as_base_value())
} }
/// Generates LLVM IR for `ndarray.fill`. /// Generates LLVM IR for `ndarray.fill`.
@ -295,7 +295,7 @@ pub fn gen_ndarray_fill<'ctx>(
let value_arg = args[0].1.clone().to_basic_value_enum(context, generator, value_ty)?; let value_arg = args[0].1.clone().to_basic_value_enum(context, generator, value_ty)?;
let this = NDArrayType::from_unifier_type(generator, context, this_ty) let this = NDArrayType::from_unifier_type(generator, context, this_ty)
.map_pointer_value(this_arg.into_pointer_value(), None); .map_value(this_arg.into_pointer_value(), None);
this.fill(generator, context, value_arg); this.fill(generator, context, value_arg);
Ok(()) Ok(())
} }
@ -316,10 +316,8 @@ pub fn ndarray_dot<'ctx, G: CodeGenerator + ?Sized>(
match (x1, x2) { match (x1, x2) {
(BasicValueEnum::PointerValue(n1), BasicValueEnum::PointerValue(n2)) => { (BasicValueEnum::PointerValue(n1), BasicValueEnum::PointerValue(n2)) => {
let a = let a = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(n1, None);
NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(n1, None); let b = NDArrayType::from_unifier_type(generator, ctx, x2_ty).map_value(n2, None);
let b =
NDArrayType::from_unifier_type(generator, ctx, x2_ty).map_pointer_value(n2, None);
// TODO: General `np.dot()` https://numpy.org/doc/stable/reference/generated/numpy.dot.html. // TODO: General `np.dot()` https://numpy.org/doc/stable/reference/generated/numpy.dot.html.
assert_eq!(a.get_type().ndims(), 1); assert_eq!(a.get_type().ndims(), 1);

View File

@ -17,10 +17,10 @@ use super::{
gen_in_range_check, gen_in_range_check,
irrt::{handle_slice_indices, list_slice_assignment}, irrt::{handle_slice_indices, list_slice_assignment},
macros::codegen_unreachable, macros::codegen_unreachable,
types::{ndarray::NDArrayType, ExceptionType, RangeType}, types::ndarray::NDArrayType,
values::{ values::{
ndarray::{RustNDIndex, ScalarOrNDArray}, ndarray::{RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArraySliceValue, ExceptionValue, ListValue, ProxyValue, ArrayLikeIndexer, ArraySliceValue, ListValue, ProxyValue, RangeValue,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -440,7 +440,7 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
// ``` // ```
let target = NDArrayType::from_unifier_type(generator, ctx, target_ty) let target = NDArrayType::from_unifier_type(generator, ctx, target_ty)
.map_pointer_value(target.into_pointer_value(), None); .map_value(target.into_pointer_value(), None);
let target = target.index(generator, ctx, &key); let target = target.index(generator, ctx, &key);
let value = ScalarOrNDArray::from_value(generator, ctx, (value_ty, value)) let value = ScalarOrNDArray::from_value(generator, ctx, (value_ty, value))
@ -511,7 +511,7 @@ pub fn gen_for<G: CodeGenerator>(
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
{ {
let iter_val = let iter_val =
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range")); RangeValue::from_pointer_value(iter_val.into_pointer_value(), Some("range"));
// Internal variable for loop; Cannot be assigned // Internal variable for loop; Cannot be assigned
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?; let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed // Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed
@ -1337,19 +1337,43 @@ pub fn exn_constructor<'ctx>(
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>( pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
exception: Option<&ExceptionValue<'ctx>>, exception: Option<&BasicValueEnum<'ctx>>,
loc: Location, loc: Location,
) { ) {
if let Some(exception) = exception { 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 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 fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
exception.store_func(ctx, fun_name); 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 raise = get_builtins(generator, ctx, "__nac3_raise");
let exception = *exception; 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 { } else {
let resume = get_builtins(generator, ctx, "__nac3_resume"); let resume = get_builtins(generator, ctx, "__nac3_resume");
ctx.build_call_or_invoke(resume, &[], "resume"); ctx.build_call_or_invoke(resume, &[], "resume");
@ -1836,8 +1860,6 @@ pub fn gen_stmt<G: CodeGenerator>(
} else { } else {
return Ok(()); 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); gen_raise(generator, ctx, Some(&exc), stmt.location);
} else { } else {
gen_raise(generator, ctx, None, stmt.location); gen_raise(generator, ctx, None, stmt.location);

View File

@ -447,18 +447,15 @@ fn test_classes_list_type_new() {
let llvm_usize = generator.get_size_type(&ctx); let llvm_usize = generator.get_size_type(&ctx);
let llvm_list = ListType::new_with_generator(&generator, &ctx, llvm_i32.into()); let llvm_list = ListType::new_with_generator(&generator, &ctx, llvm_i32.into());
assert!(ListType::is_representable(llvm_list.as_abi_type(), llvm_usize).is_ok()); assert!(ListType::is_representable(llvm_list.as_base_type(), llvm_usize).is_ok());
} }
#[test] #[test]
fn test_classes_range_type_new() { fn test_classes_range_type_new() {
let ctx = inkwell::context::Context::create(); let ctx = inkwell::context::Context::create();
let generator = DefaultCodeGenerator::new(String::new(), ctx.i64_type());
let llvm_usize = generator.get_size_type(&ctx); let llvm_range = RangeType::new(&ctx);
assert!(RangeType::is_representable(llvm_range.as_base_type()).is_ok());
let llvm_range = RangeType::new_with_generator(&generator, &ctx);
assert!(RangeType::is_representable(llvm_range.as_abi_type(), llvm_usize).is_ok());
} }
#[test] #[test]
@ -470,5 +467,5 @@ fn test_classes_ndarray_type_new() {
let llvm_usize = generator.get_size_type(&ctx); let llvm_usize = generator.get_size_type(&ctx);
let llvm_ndarray = NDArrayType::new_with_generator(&generator, &ctx, llvm_i32.into(), 2); let llvm_ndarray = NDArrayType::new_with_generator(&generator, &ctx, llvm_i32.into(), 2);
assert!(NDArrayType::is_representable(llvm_ndarray.as_abi_type(), llvm_usize).is_ok()); assert!(NDArrayType::is_representable(llvm_ndarray.as_base_type(), llvm_usize).is_ok());
} }

View File

@ -1,257 +0,0 @@
use inkwell::{
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType},
ProxyType,
};
use crate::{
codegen::{values::ExceptionValue, CodeGenContext, CodeGenerator},
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> {
assert!(ctx.get_struct_type("str").is_some());
let field_tys =
Self::fields(ctx, llvm_usize).into_iter().map(|field| field.1).collect_vec();
ctx.struct_type(&field_tys, false).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,7 +1,7 @@
use inkwell::{ use inkwell::{
context::Context, context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, IntPredicate, OptimizationLevel, AddressSpace, IntPredicate, OptimizationLevel,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -13,9 +13,8 @@ use crate::{
codegen::{ codegen::{
types::structure::{ types::structure::{
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields, check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
}, },
values::ListValue, values::{ListValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
typecheck::typedef::{iter_type_vars, Type, TypeEnum}, typecheck::typedef::{iter_type_vars, Type, TypeEnum},
@ -57,12 +56,47 @@ impl<'ctx> ListStructFields<'ctx> {
} }
impl<'ctx> ListType<'ctx> { impl<'ctx> ListType<'ctx> {
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
};
let fields = ListStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(
fields,
llvm_ty,
"list",
&[(fields.items.name(), &|ty| {
if ty.is_pointer_type() {
Ok(())
} else {
Err(format!("Expected T* for `list.items`, got {ty}"))
}
})],
)
}
/// Returns an instance of [`StructFields`] containing all field accessors for this type. /// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use] #[must_use]
fn fields(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> ListStructFields<'ctx> { fn fields(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> ListStructFields<'ctx> {
ListStructFields::new_typed(item, llvm_usize) ListStructFields::new_typed(item, llvm_usize)
} }
/// See [`ListType::fields`].
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self, _ctx: &impl AsContextRef<'ctx>) -> ListStructFields<'ctx> {
Self::fields(self.item.unwrap_or(self.llvm_usize.into()), self.llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of a `List`. /// Creates an LLVM type corresponding to the expected structure of a `List`.
#[must_use] #[must_use]
fn llvm_type( fn llvm_type(
@ -147,16 +181,10 @@ impl<'ctx> ListType<'ctx> {
Self::new_impl(ctx.ctx, llvm_elem_type, llvm_usize) Self::new_impl(ctx.ctx, llvm_elem_type, llvm_usize)
} }
/// Creates an [`ListType`] from a [`StructType`].
#[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 [`ListType`] from a [`PointerType`]. /// Creates an [`ListType`] from a [`PointerType`].
#[must_use] #[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
let ctx = ptr_ty.get_context(); let ctx = ptr_ty.get_context();
@ -295,27 +323,9 @@ impl<'ctx> ListType<'ctx> {
/// Converts an existing value into a [`ListValue`]. /// Converts an existing value into a [`ListValue`].
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
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 [`ListValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name) <Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
@ -323,64 +333,36 @@ impl<'ctx> ListType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for ListType<'ctx> { impl<'ctx> ProxyType<'ctx> for ListType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = ListValue<'ctx>; type Value = ListValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else { ) -> Result<(), String> {
return Err(format!("Expected struct type for `list` type, got {llvm_ty}")); Self::is_representable(llvm_ty, generator.get_size_type(ctx))
};
let fields = ListStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(
fields,
llvm_ty,
"list",
&[(fields.items.name(), &|ty| {
if ty.is_pointer_type() {
Ok(())
} else {
Err(format!("Expected T* for `list.items`, got {ty}"))
}
})],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for ListType<'ctx> {
type StructFields = ListStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.item.unwrap_or(self.llvm_usize.into()), self.llvm_usize)
}
} }
impl<'ctx> From<ListType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<ListType<'ctx>> for PointerType<'ctx> {

View File

@ -17,7 +17,8 @@
//! on the stack. //! on the stack.
use inkwell::{ use inkwell::{
types::{BasicType, IntType}, context::Context,
types::BasicType,
values::{IntValue, PointerValue}, values::{IntValue, PointerValue},
}; };
@ -25,43 +26,38 @@ use super::{
values::{ArraySliceValue, ProxyValue}, values::{ArraySliceValue, ProxyValue},
{CodeGenContext, CodeGenerator}, {CodeGenContext, CodeGenerator},
}; };
pub use exception::*;
pub use list::*; pub use list::*;
pub use option::*;
pub use range::*; pub use range::*;
pub use string::*;
pub use tuple::*; pub use tuple::*;
mod exception;
mod list; mod list;
pub mod ndarray; pub mod ndarray;
mod option;
mod range; mod range;
mod string;
pub mod structure; pub mod structure;
mod tuple; mod tuple;
pub mod utils; pub mod utils;
/// A LLVM type that is used to represent a corresponding type in NAC3. /// A LLVM type that is used to represent a corresponding type in NAC3.
pub trait ProxyType<'ctx>: Into<Self::Base> { pub trait ProxyType<'ctx>: Into<Self::Base> {
/// The ABI type of which values of this type possess. /// The LLVM type of which values of this type possess. This is usually a
type ABI: BasicType<'ctx>; /// [LLVM pointer type][PointerType] for any non-primitive types.
/// The LLVM type of which values of this type possess.
type Base: BasicType<'ctx>; type Base: BasicType<'ctx>;
/// The type of values represented by this type. /// The type of values represented by this type.
type Value: ProxyValue<'ctx, Type = Self>; type Value: ProxyValue<'ctx, Type = Self>;
/// Checks whether `llvm_ty` can be represented by this [`ProxyType`]. fn is_type<G: CodeGenerator + ?Sized>(
fn is_representable( generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String>; ) -> Result<(), String>;
/// Checks whether the type represented by `ty` expresses the same type represented by this /// Checks whether `llvm_ty` can be represented by this [`ProxyType`].
/// [`ProxyType`]. fn is_representable<G: CodeGenerator + ?Sized>(
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String>; generator: &G,
ctx: &'ctx Context,
llvm_ty: Self::Base,
) -> Result<(), String>;
/// Returns the type that should be used in `alloca` IR statements. /// Returns the type that should be used in `alloca` IR statements.
fn alloca_type(&self) -> impl BasicType<'ctx>; fn alloca_type(&self) -> impl BasicType<'ctx>;
@ -126,10 +122,4 @@ pub trait ProxyType<'ctx>: Into<Self::Base> {
/// Returns the [base type][Self::Base] of this proxy. /// Returns the [base type][Self::Base] of this proxy.
fn as_base_type(&self) -> Self::Base; fn as_base_type(&self) -> Self::Base;
/// Returns this proxy as its ABI type, i.e. the expected type representation if a value of this
/// [`ProxyType`] is being passed into or returned from a function.
///
/// See [`CodeGenContext::get_llvm_abi_type`].
fn as_abi_type(&self) -> Self::ABI;
} }

View File

@ -151,7 +151,7 @@ impl<'ctx> NDArrayType<'ctx> {
(list_ty, list), (list_ty, list),
name, name,
); );
Ok(Some(ndarray.as_abi_value(ctx))) Ok(Some(ndarray.as_base_value()))
}, },
|generator, ctx| { |generator, ctx| {
let ndarray = self.construct_numpy_array_from_list_copy_none_impl( let ndarray = self.construct_numpy_array_from_list_copy_none_impl(
@ -160,14 +160,14 @@ impl<'ctx> NDArrayType<'ctx> {
(list_ty, list), (list_ty, list),
name, name,
); );
Ok(Some(ndarray.as_abi_value(ctx))) Ok(Some(ndarray.as_base_value()))
}, },
) )
.unwrap() .unwrap()
.map(BasicValueEnum::into_pointer_value) .map(BasicValueEnum::into_pointer_value)
.unwrap(); .unwrap();
NDArrayType::new(ctx, dtype, ndims).map_pointer_value(ndarray, None) NDArrayType::new(ctx, dtype, ndims).map_value(ndarray, None)
} }
/// Implementation of `np_array(<ndarray>, copy=copy)`. /// Implementation of `np_array(<ndarray>, copy=copy)`.
@ -189,18 +189,18 @@ impl<'ctx> NDArrayType<'ctx> {
|_generator, _ctx| Ok(copy), |_generator, _ctx| Ok(copy),
|generator, ctx| { |generator, ctx| {
let ndarray = ndarray.make_copy(generator, ctx); // Force copy let ndarray = ndarray.make_copy(generator, ctx); // Force copy
Ok(Some(ndarray.as_abi_value(ctx))) Ok(Some(ndarray.as_base_value()))
}, },
|_generator, ctx| { |_generator, _ctx| {
// No need to copy. Return `ndarray` itself. // No need to copy. Return `ndarray` itself.
Ok(Some(ndarray.as_abi_value(ctx))) Ok(Some(ndarray.as_base_value()))
}, },
) )
.unwrap() .unwrap()
.map(BasicValueEnum::into_pointer_value) .map(BasicValueEnum::into_pointer_value)
.unwrap(); .unwrap();
ndarray.get_type().map_pointer_value(ndarray_val, name) ndarray.get_type().map_value(ndarray_val, name)
} }
/// Create a new ndarray like /// Create a new ndarray like
@ -222,7 +222,7 @@ impl<'ctx> NDArrayType<'ctx> {
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
{ {
let list = ListType::from_unifier_type(generator, ctx, object_ty) let list = ListType::from_unifier_type(generator, ctx, object_ty)
.map_pointer_value(object.into_pointer_value(), None); .map_value(object.into_pointer_value(), None);
self.construct_numpy_array_list_impl(generator, ctx, (object_ty, list), copy, name) self.construct_numpy_array_list_impl(generator, ctx, (object_ty, list), copy, name)
} }
@ -230,7 +230,7 @@ impl<'ctx> NDArrayType<'ctx> {
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
{ {
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
.map_pointer_value(object.into_pointer_value(), None); .map_value(object.into_pointer_value(), None);
self.construct_numpy_array_ndarray_impl(generator, ctx, ndarray, copy, name) self.construct_numpy_array_ndarray_impl(generator, ctx, ndarray, copy, name)
} }

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::{AsContextRef, Context}, context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -10,10 +10,10 @@ use nac3core_derive::StructFields;
use crate::codegen::{ use crate::codegen::{
types::{ types::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType}, structure::{check_struct_type_matches_fields, StructField, StructFields},
ProxyType, ProxyType,
}, },
values::ndarray::ShapeEntryValue, values::{ndarray::ShapeEntryValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -32,6 +32,28 @@ pub struct ShapeEntryStructFields<'ctx> {
} }
impl<'ctx> ShapeEntryType<'ctx> { impl<'ctx> ShapeEntryType<'ctx> {
/// Checks whether `llvm_ty` represents a [`ShapeEntryType`], returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ndarray_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
return Err(format!(
"Expected struct type for `ShapeEntry` type, got {llvm_ndarray_ty}"
));
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDArray",
&[],
)
}
/// Returns an instance of [`StructFields`] containing all field accessors for this type. /// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use] #[must_use]
fn fields( fn fields(
@ -41,6 +63,13 @@ impl<'ctx> ShapeEntryType<'ctx> {
ShapeEntryStructFields::new(ctx, llvm_usize) ShapeEntryStructFields::new(ctx, llvm_usize)
} }
/// See [`ShapeEntryStructFields::fields`].
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> ShapeEntryStructFields<'ctx> {
Self::fields(ctx, self.llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of a `ShapeEntry`. /// Creates an LLVM type corresponding to the expected structure of a `ShapeEntry`.
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
@ -71,16 +100,10 @@ impl<'ctx> ShapeEntryType<'ctx> {
Self::new_impl(ctx, generator.get_size_type(ctx)) Self::new_impl(ctx, generator.get_size_type(ctx))
} }
/// Creates a [`ShapeEntryType`] from a [`StructType`] representing an `ShapeEntry`.
#[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 a [`ShapeEntryType`] from a [`PointerType`] representing an `ShapeEntry`. /// Creates a [`ShapeEntryType`] from a [`PointerType`] representing an `ShapeEntry`.
#[must_use] #[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize } Self { ty: ptr_ty, llvm_usize }
} }
@ -116,27 +139,9 @@ impl<'ctx> ShapeEntryType<'ctx> {
/// Converts an existing value into a [`ShapeEntryValue`]. /// Converts an existing value into a [`ShapeEntryValue`].
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
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 [`ShapeEntryValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name) <Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
@ -144,58 +149,36 @@ impl<'ctx> ShapeEntryType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for ShapeEntryType<'ctx> { impl<'ctx> ProxyType<'ctx> for ShapeEntryType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = ShapeEntryValue<'ctx>; type Value = ShapeEntryValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ndarray_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else { ) -> Result<(), String> {
return Err(format!( Self::is_representable(llvm_ty, generator.get_size_type(ctx))
"Expected struct type for `ShapeEntry` type, got {llvm_ndarray_ty}"
));
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDArray",
&[],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for ShapeEntryType<'ctx> {
type StructFields = ShapeEntryStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
} }
impl<'ctx> From<ShapeEntryType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<ShapeEntryType<'ctx>> for PointerType<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::Context, context::Context,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -13,11 +13,10 @@ use crate::{
types::{ types::{
structure::{ structure::{
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields, check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
}, },
ProxyType, ProxyType,
}, },
values::ndarray::ContiguousNDArrayValue, values::{ndarray::ContiguousNDArrayValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
toplevel::numpy::unpack_ndarray_var_tys, toplevel::numpy::unpack_ndarray_var_tys,
@ -59,6 +58,36 @@ impl<'ctx> ContiguousNDArrayStructFields<'ctx> {
} }
impl<'ctx> ContiguousNDArrayType<'ctx> { impl<'ctx> ContiguousNDArrayType<'ctx> {
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!(
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
));
};
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(
fields,
llvm_ty,
"ContiguousNDArray",
&[(fields.data.name(), &|ty| {
if ty.is_pointer_type() {
Ok(())
} else {
Err(format!("Expected T* for `ContiguousNDArray.data`, got {ty}"))
}
})],
)
}
/// Returns an instance of [`StructFields`] containing all field accessors for this type. /// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use] #[must_use]
fn fields( fn fields(
@ -68,6 +97,13 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
ContiguousNDArrayStructFields::new_typed(item, llvm_usize) ContiguousNDArrayStructFields::new_typed(item, llvm_usize)
} }
/// See [`NDArrayType::fields`].
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self) -> ContiguousNDArrayStructFields<'ctx> {
Self::fields(self.item, self.llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of an `NDArray`. /// Creates an LLVM type corresponding to the expected structure of an `NDArray`.
#[must_use] #[must_use]
fn llvm_type( fn llvm_type(
@ -117,24 +153,14 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
Self::new_impl(ctx.ctx, llvm_dtype, ctx.get_size_type()) Self::new_impl(ctx.ctx, llvm_dtype, ctx.get_size_type())
} }
/// Creates an [`ContiguousNDArrayType`] from a [`StructType`] representing an `NDArray`.
#[must_use]
pub fn from_struct_type(
ty: StructType<'ctx>,
item: BasicTypeEnum<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Self {
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), item, llvm_usize)
}
/// Creates an [`ContiguousNDArrayType`] from a [`PointerType`] representing an `NDArray`. /// Creates an [`ContiguousNDArrayType`] from a [`PointerType`] representing an `NDArray`.
#[must_use] #[must_use]
pub fn from_pointer_type( pub fn from_type(
ptr_ty: PointerType<'ctx>, ptr_ty: PointerType<'ctx>,
item: BasicTypeEnum<'ctx>, item: BasicTypeEnum<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
) -> Self { ) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, item, llvm_usize } Self { ty: ptr_ty, item, llvm_usize }
} }
@ -178,28 +204,9 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
/// Converts an existing value into a [`ContiguousNDArrayValue`]. /// Converts an existing value into a [`ContiguousNDArrayValue`].
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
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.item,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`ContiguousNDArrayValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value( <Self as ProxyType<'ctx>>::Value::from_pointer_value(
@ -212,66 +219,36 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for ContiguousNDArrayType<'ctx> { impl<'ctx> ProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = ContiguousNDArrayValue<'ctx>; type Value = ContiguousNDArrayValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else { ) -> Result<(), String> {
return Err(format!( Self::is_representable(llvm_ty, generator.get_size_type(ctx))
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
));
};
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(
fields,
llvm_ty,
"ContiguousNDArray",
&[(fields.data.name(), &|ty| {
if ty.is_pointer_type() {
Ok(())
} else {
Err(format!("Expected T* for `ContiguousNDArray.data`, got {ty}"))
}
})],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
type StructFields = ContiguousNDArrayStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.item, self.llvm_usize)
}
} }
impl<'ctx> From<ContiguousNDArrayType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<ContiguousNDArrayType<'ctx>> for PointerType<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::{AsContextRef, Context}, context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -10,12 +10,12 @@ use nac3core_derive::StructFields;
use crate::codegen::{ use crate::codegen::{
types::{ types::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType}, structure::{check_struct_type_matches_fields, StructField, StructFields},
ProxyType, ProxyType,
}, },
values::{ values::{
ndarray::{NDIndexValue, RustNDIndex}, ndarray::{NDIndexValue, RustNDIndex},
ArrayLikeIndexer, ArraySliceValue, ArrayLikeIndexer, ArraySliceValue, ProxyValue,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -35,6 +35,25 @@ pub struct NDIndexStructFields<'ctx> {
} }
impl<'ctx> NDIndexType<'ctx> { impl<'ctx> NDIndexType<'ctx> {
/// Checks whether `llvm_ty` represents a `ndindex` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!(
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
));
};
let fields = NDIndexStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(fields, llvm_ty, "NDIndex", &[])
}
#[must_use] #[must_use]
fn fields( fn fields(
ctx: impl AsContextRef<'ctx>, ctx: impl AsContextRef<'ctx>,
@ -43,6 +62,11 @@ impl<'ctx> NDIndexType<'ctx> {
NDIndexStructFields::new(ctx, llvm_usize) NDIndexStructFields::new(ctx, llvm_usize)
} }
#[must_use]
pub fn get_fields(&self) -> NDIndexStructFields<'ctx> {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
let field_tys = let field_tys =
@ -71,13 +95,8 @@ impl<'ctx> NDIndexType<'ctx> {
} }
#[must_use] #[must_use]
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize) debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
}
#[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 } Self { ty: ptr_ty, llvm_usize }
} }
@ -148,26 +167,9 @@ impl<'ctx> NDIndexType<'ctx> {
} }
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
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,
)
}
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name) <Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
@ -175,55 +177,36 @@ impl<'ctx> NDIndexType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for NDIndexType<'ctx> { impl<'ctx> ProxyType<'ctx> for NDIndexType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = NDIndexValue<'ctx>; type Value = NDIndexValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else { ) -> Result<(), String> {
return Err(format!( Self::is_representable(llvm_ty, generator.get_size_type(ctx))
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
));
};
let fields = NDIndexStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(fields, llvm_ty, "NDIndex", &[])
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for NDIndexType<'ctx> {
type StructFields = NDIndexStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
} }
impl<'ctx> From<NDIndexType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<NDIndexType<'ctx>> for PointerType<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::{AsContextRef, Context}, context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{BasicValue, IntValue, PointerValue, StructValue}, values::{BasicValue, IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -9,12 +9,12 @@ use itertools::Itertools;
use nac3core_derive::StructFields; use nac3core_derive::StructFields;
use super::{ use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType}, structure::{check_struct_type_matches_fields, StructField, StructFields},
ProxyType, ProxyType,
}; };
use crate::{ use crate::{
codegen::{ codegen::{
values::{ndarray::NDArrayValue, TypedArrayLikeMutator}, values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeMutator},
{CodeGenContext, CodeGenerator}, {CodeGenContext, CodeGenerator},
}, },
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys}, toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},
@ -62,6 +62,26 @@ pub struct NDArrayStructFields<'ctx> {
} }
impl<'ctx> NDArrayType<'ctx> { impl<'ctx> NDArrayType<'ctx> {
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ndarray_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}"));
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDArray",
&[],
)
}
/// Returns an instance of [`StructFields`] containing all field accessors for this type. /// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use] #[must_use]
fn fields( fn fields(
@ -71,6 +91,13 @@ impl<'ctx> NDArrayType<'ctx> {
NDArrayStructFields::new(ctx, llvm_usize) NDArrayStructFields::new(ctx, llvm_usize)
} }
/// See [`NDArrayType::fields`].
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> NDArrayStructFields<'ctx> {
Self::fields(ctx, self.llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of an `NDArray`. /// Creates an LLVM type corresponding to the expected structure of an `NDArray`.
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
@ -176,26 +203,15 @@ impl<'ctx> NDArrayType<'ctx> {
Self::new_impl(ctx.ctx, llvm_dtype, ndims, ctx.get_size_type()) Self::new_impl(ctx.ctx, llvm_dtype, ndims, ctx.get_size_type())
} }
/// Creates an [`NDArrayType`] from a [`StructType`] representing an `NDArray`.
#[must_use]
pub fn from_struct_type(
ty: StructType<'ctx>,
dtype: BasicTypeEnum<'ctx>,
ndims: u64,
llvm_usize: IntType<'ctx>,
) -> Self {
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), dtype, ndims, llvm_usize)
}
/// Creates an [`NDArrayType`] from a [`PointerType`] representing an `NDArray`. /// Creates an [`NDArrayType`] from a [`PointerType`] representing an `NDArray`.
#[must_use] #[must_use]
pub fn from_pointer_type( pub fn from_type(
ptr_ty: PointerType<'ctx>, ptr_ty: PointerType<'ctx>,
dtype: BasicTypeEnum<'ctx>, dtype: BasicTypeEnum<'ctx>,
ndims: u64, ndims: u64,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
) -> Self { ) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
NDArrayType { ty: ptr_ty, dtype, ndims, llvm_usize } NDArrayType { ty: ptr_ty, dtype, ndims, llvm_usize }
} }
@ -415,29 +431,9 @@ impl<'ctx> NDArrayType<'ctx> {
/// Converts an existing value into a [`NDArrayValue`]. /// Converts an existing value into a [`NDArrayValue`].
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
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.dtype,
self.ndims,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`NDArrayValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value( <Self as ProxyType<'ctx>>::Value::from_pointer_value(
@ -451,56 +447,36 @@ impl<'ctx> NDArrayType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> { impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = NDArrayValue<'ctx>; type Value = NDArrayValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ndarray_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else { ) -> Result<(), String> {
return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}")); Self::is_representable(llvm_ty, generator.get_size_type(ctx))
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDArray",
&[],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for NDArrayType<'ctx> {
type StructFields = NDArrayStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
} }
impl<'ctx> From<NDArrayType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<NDArrayType<'ctx>> for PointerType<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::{AsContextRef, Context}, context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -11,9 +11,7 @@ use nac3core_derive::StructFields;
use super::ProxyType; use super::ProxyType;
use crate::codegen::{ use crate::codegen::{
irrt, irrt,
types::structure::{ types::structure::{check_struct_type_matches_fields, StructField, StructFields},
check_struct_type_matches_fields, StructField, StructFields, StructProxyType,
},
values::{ values::{
ndarray::{NDArrayValue, NDIterValue}, ndarray::{NDArrayValue, NDIterValue},
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
@ -46,12 +44,39 @@ pub struct NDIterStructFields<'ctx> {
} }
impl<'ctx> NDIterType<'ctx> { impl<'ctx> NDIterType<'ctx> {
/// Checks whether `llvm_ty` represents a `nditer` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let llvm_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ty else {
return Err(format!("Expected struct type for `NDIter` type, got {llvm_ty}"));
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDIter",
&[],
)
}
/// Returns an instance of [`StructFields`] containing all field accessors for this type. /// Returns an instance of [`StructFields`] containing all field accessors for this type.
#[must_use] #[must_use]
fn fields(ctx: impl AsContextRef<'ctx>, llvm_usize: IntType<'ctx>) -> NDIterStructFields<'ctx> { fn fields(ctx: impl AsContextRef<'ctx>, llvm_usize: IntType<'ctx>) -> NDIterStructFields<'ctx> {
NDIterStructFields::new(ctx, llvm_usize) NDIterStructFields::new(ctx, llvm_usize)
} }
/// See [`NDIterType::fields`].
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> NDIterStructFields<'ctx> {
Self::fields(ctx, self.llvm_usize)
}
/// Creates an LLVM type corresponding to the expected structure of an `NDIter`. /// Creates an LLVM type corresponding to the expected structure of an `NDIter`.
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
@ -82,16 +107,10 @@ impl<'ctx> NDIterType<'ctx> {
Self::new_impl(ctx, generator.get_size_type(ctx)) Self::new_impl(ctx, generator.get_size_type(ctx))
} }
/// Creates an [`NDIterType`] from a [`StructType`] representing an `NDIter`.
#[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 [`NDIterType`] from a [`PointerType`] representing an `NDIter`. /// Creates an [`NDIterType`] from a [`PointerType`] representing an `NDIter`.
#[must_use] #[must_use]
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize } Self { ty: ptr_ty, llvm_usize }
} }
@ -160,8 +179,7 @@ impl<'ctx> NDIterType<'ctx> {
let indices = let indices =
TypedArrayLikeAdapter::from(indices, |_, _, v| v.into_int_value(), |_, _, v| v.into()); TypedArrayLikeAdapter::from(indices, |_, _, v| v.into_int_value(), |_, _, v| v.into());
let nditer = let nditer = self.map_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
self.map_pointer_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, &indices); irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, &indices);
@ -169,30 +187,9 @@ impl<'ctx> NDIterType<'ctx> {
} }
#[must_use] #[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>( pub fn map_value(
&self, &self,
generator: &mut G, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
ctx: &mut CodeGenContext<'ctx, '_>,
value: StructValue<'ctx>,
parent: NDArrayValue<'ctx>,
indices: ArraySliceValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_struct_value(
generator,
ctx,
value,
parent,
indices,
self.llvm_usize,
name,
)
}
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
parent: NDArrayValue<'ctx>, parent: NDArrayValue<'ctx>,
indices: ArraySliceValue<'ctx>, indices: ArraySliceValue<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
@ -208,56 +205,36 @@ impl<'ctx> NDIterType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for NDIterType<'ctx> { impl<'ctx> ProxyType<'ctx> for NDIterType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = NDIterValue<'ctx>; type Value = NDIterValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let llvm_ty = ty.get_element_type(); llvm_ty: Self::Base,
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ty else { ) -> Result<(), String> {
return Err(format!("Expected struct type for `NDIter` type, got {llvm_ty}")); Self::is_representable(llvm_ty, generator.get_size_type(ctx))
};
check_struct_type_matches_fields(
Self::fields(ctx, llvm_usize),
llvm_ndarray_ty,
"NDIter",
&[],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for NDIterType<'ctx> {
type StructFields = NDIterStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
Self::fields(self.ty.get_context(), self.llvm_usize)
}
} }
impl<'ctx> From<NDIterType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<NDIterType<'ctx>> for PointerType<'ctx> {

View File

@ -1,188 +0,0 @@
use inkwell::{
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType},
values::{BasicValue, BasicValueEnum, PointerValue},
AddressSpace,
};
use super::ProxyType;
use crate::{
codegen::{values::OptionValue, CodeGenContext, CodeGenerator},
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
};
/// 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,167 +1,25 @@
use inkwell::{ use inkwell::{
context::Context, context::Context,
types::{AnyTypeEnum, ArrayType, BasicType, BasicTypeEnum, IntType, PointerType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{ArrayValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use super::ProxyType; use super::ProxyType;
use crate::{ use crate::codegen::{
codegen::{ values::{ProxyValue, RangeValue},
values::RangeValue, {CodeGenContext, CodeGenerator},
{CodeGenContext, CodeGenerator},
},
typecheck::typedef::{Type, TypeEnum},
}; };
/// Proxy type for a `range` type in LLVM. /// Proxy type for a `range` type in LLVM.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct RangeType<'ctx> { pub struct RangeType<'ctx> {
ty: PointerType<'ctx>, ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
} }
impl<'ctx> RangeType<'ctx> { impl<'ctx> RangeType<'ctx> {
/// Creates an LLVM type corresponding to the expected structure of a `Range`. /// Checks whether `llvm_ty` represents a `range` type, returning [Err] if it does not.
#[must_use] pub fn is_representable(llvm_ty: PointerType<'ctx>) -> Result<(), String> {
fn llvm_type(ctx: &'ctx Context) -> PointerType<'ctx> { let llvm_range_ty = llvm_ty.get_element_type();
// typedef int32_t Range[3];
let llvm_i32 = ctx.i32_type();
llvm_i32.array_type(3).ptr_type(AddressSpace::default())
}
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
let llvm_range = Self::llvm_type(ctx);
RangeType { ty: llvm_range, llvm_usize }
}
/// Creates an instance of [`RangeType`].
#[must_use]
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
Self::new_impl(ctx.ctx, ctx.get_size_type())
}
/// Creates an instance of [`RangeType`].
#[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 [`RangeType`] 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.range.obj_id(&ctx.unifier).unwrap())
);
Self::new(ctx)
}
/// Creates an [`RangeType`] from a [`ArrayType`].
#[must_use]
pub fn from_array_type(arr_ty: ArrayType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
Self::from_pointer_type(arr_ty.ptr_type(AddressSpace::default()), llvm_usize)
}
/// Creates an [`RangeType`] 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());
RangeType { ty: ptr_ty, llvm_usize }
}
/// Returns the type of all fields of this `range` type.
#[must_use]
pub fn value_type(&self) -> IntType<'ctx> {
self.as_abi_type().get_element_type().into_array_type().get_element_type().into_int_type()
}
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
///
/// See [`ProxyType::raw_alloca`].
#[must_use]
pub fn alloca<G: CodeGenerator + ?Sized>(
&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 [`RangeValue`] 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 [`RangeValue`].
#[must_use]
pub fn map_array_value<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
value: ArrayValue<'ctx>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_array_value(
generator,
ctx,
value,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`RangeValue`].
#[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 RangeType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>;
type Value = RangeValue<'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> {
let llvm_range_ty = ty.get_element_type();
let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else { let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else {
return Err(format!("Expected array type for `range` type, got {llvm_range_ty}")); return Err(format!("Expected array type for `range` type, got {llvm_range_ty}"));
}; };
@ -188,17 +46,106 @@ impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
Ok(()) Ok(())
} }
/// Creates an LLVM type corresponding to the expected structure of a `Range`.
#[must_use]
fn llvm_type(ctx: &'ctx Context) -> PointerType<'ctx> {
// typedef int32_t Range[3];
let llvm_i32 = ctx.i32_type();
llvm_i32.array_type(3).ptr_type(AddressSpace::default())
}
/// Creates an instance of [`RangeType`].
#[must_use]
pub fn new(ctx: &'ctx Context) -> Self {
let llvm_range = Self::llvm_type(ctx);
RangeType::from_type(llvm_range)
}
/// Creates an [`RangeType`] from a [`PointerType`].
#[must_use]
pub fn from_type(ptr_ty: PointerType<'ctx>) -> Self {
debug_assert!(Self::is_representable(ptr_ty).is_ok());
RangeType { ty: ptr_ty }
}
/// Returns the type of all fields of this `range` type.
#[must_use]
pub fn value_type(&self) -> IntType<'ctx> {
self.as_base_type().get_element_type().into_array_type().get_element_type().into_int_type()
}
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
///
/// See [`ProxyType::raw_alloca`].
#[must_use]
pub fn alloca<G: CodeGenerator + ?Sized>(
&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), name)
}
/// Allocates an instance of [`RangeValue`] 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),
name,
)
}
/// Converts an existing value into a [`RangeValue`].
#[must_use]
pub fn map_value(
&self,
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, name)
}
}
impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
type Base = PointerType<'ctx>;
type Value = RangeValue<'ctx>;
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>,
) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else {
Err(format!("Expected pointer type, got {llvm_ty:?}"))
}
}
fn is_representable<G: CodeGenerator + ?Sized>(
_: &G,
_: &'ctx Context,
llvm_ty: Self::Base,
) -> Result<(), String> {
Self::is_representable(llvm_ty)
}
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
} }
impl<'ctx> From<RangeType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<RangeType<'ctx>> for PointerType<'ctx> {

View File

@ -1,177 +0,0 @@
use inkwell::{
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{GlobalValue, IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields},
ProxyType,
};
use crate::codegen::{values::StringValue, CodeGenContext, CodeGenerator};
/// 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

@ -2,55 +2,13 @@ use std::marker::PhantomData;
use inkwell::{ use inkwell::{
context::AsContextRef, context::AsContextRef,
types::{BasicTypeEnum, IntType, PointerType, StructType}, types::{BasicTypeEnum, IntType, StructType},
values::{AggregateValueEnum, BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue}, values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
use super::ProxyType;
use crate::codegen::CodeGenContext; use crate::codegen::CodeGenContext;
/// A LLVM type that is used to represent a corresponding structure-like type in NAC3.
pub trait StructProxyType<'ctx>: ProxyType<'ctx, Base = PointerType<'ctx>> {
/// The concrete type of [`StructFields`].
type StructFields: StructFields<'ctx>;
/// Whether this [`StructProxyType`] has the same LLVM type representation as
/// [`llvm_ty`][StructType].
fn has_same_struct_repr(
llvm_ty: StructType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
Self::has_same_pointer_repr(llvm_ty.ptr_type(AddressSpace::default()), llvm_usize)
}
/// Whether this [`StructProxyType`] has the same LLVM type representation as
/// [`llvm_ty`][PointerType].
fn has_same_pointer_repr(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
Self::has_same_repr(llvm_ty, llvm_usize)
}
/// Returns the fields present in this [`StructProxyType`].
#[must_use]
fn get_fields(&self) -> Self::StructFields;
/// Returns the [`StructType`].
#[must_use]
fn get_struct_type(&self) -> StructType<'ctx> {
self.as_base_type().get_element_type().into_struct_type()
}
/// Returns the [`PointerType`] representing this type.
#[must_use]
fn get_pointer_type(&self) -> PointerType<'ctx> {
self.as_base_type()
}
}
/// Trait indicating that the structure is a field-wise representation of an LLVM structure. /// Trait indicating that the structure is a field-wise representation of an LLVM structure.
/// ///
/// # Usage /// # Usage
@ -203,38 +161,17 @@ where
/// Gets the value of this field for a given `obj`. /// Gets the value of this field for a given `obj`.
#[must_use] #[must_use]
pub fn extract_value(&self, ctx: &CodeGenContext<'ctx, '_>, obj: StructValue<'ctx>) -> Value { pub fn get_from_value(&self, obj: StructValue<'ctx>) -> Value {
Value::try_from( obj.get_field_at_index(self.index).and_then(|value| Value::try_from(value).ok()).unwrap()
ctx.builder
.build_extract_value(
obj,
self.index,
&format!("{}.{}", obj.get_name().to_str().unwrap(), self.name),
)
.unwrap(),
)
.unwrap()
} }
/// Sets the value of this field for a given `obj`. /// Sets the value of this field for a given `obj`.
#[must_use] pub fn set_for_value(&self, obj: StructValue<'ctx>, value: Value) {
pub fn insert_value( obj.set_field_at_index(self.index, value);
&self,
ctx: &CodeGenContext<'ctx, '_>,
obj: StructValue<'ctx>,
value: Value,
) -> StructValue<'ctx> {
let obj_name = obj.get_name().to_str().unwrap();
let new_obj_name = if obj_name.chars().all(char::is_numeric) { "" } else { obj_name };
ctx.builder
.build_insert_value(obj, value, self.index, new_obj_name)
.map(AggregateValueEnum::into_struct_value)
.unwrap()
} }
/// Loads the value of this field for a pointer-to-structure. /// Gets the value of this field for a pointer-to-structure.
pub fn load( pub fn get(
&self, &self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
pobj: PointerValue<'ctx>, pobj: PointerValue<'ctx>,
@ -250,8 +187,8 @@ where
.unwrap() .unwrap()
} }
/// Stores the value of this field for a pointer-to-structure. /// Sets the value of this field for a pointer-to-structure.
pub fn store( pub fn set(
&self, &self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
pobj: PointerValue<'ctx>, pobj: PointerValue<'ctx>,

View File

@ -1,13 +1,16 @@
use inkwell::{ use inkwell::{
context::Context, context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{BasicType, BasicTypeEnum, IntType, StructType},
values::{BasicValueEnum, PointerValue, StructValue}, values::BasicValueEnum,
}; };
use itertools::Itertools; use itertools::Itertools;
use super::ProxyType; use super::ProxyType;
use crate::{ use crate::{
codegen::{values::TupleValue, CodeGenContext, CodeGenerator}, codegen::{
values::{ProxyValue, TupleValue},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum}, typecheck::typedef::{Type, TypeEnum},
}; };
@ -18,6 +21,11 @@ pub struct TupleType<'ctx> {
} }
impl<'ctx> TupleType<'ctx> { impl<'ctx> TupleType<'ctx> {
/// Checks whether `llvm_ty` represents any tuple type, returning [Err] if it does not.
pub fn is_representable(_value: StructType<'ctx>) -> Result<(), String> {
Ok(())
}
/// Creates an LLVM type corresponding to the expected structure of a tuple. /// Creates an LLVM type corresponding to the expected structure of a tuple.
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, tys: &[BasicTypeEnum<'ctx>]) -> StructType<'ctx> { fn llvm_type(ctx: &'ctx Context, tys: &[BasicTypeEnum<'ctx>]) -> StructType<'ctx> {
@ -74,18 +82,12 @@ impl<'ctx> TupleType<'ctx> {
/// Creates an [`TupleType`] from a [`StructType`]. /// Creates an [`TupleType`] from a [`StructType`].
#[must_use] #[must_use]
pub fn from_struct_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { pub fn from_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(struct_ty, llvm_usize).is_ok()); debug_assert!(Self::is_representable(struct_ty).is_ok());
TupleType { ty: struct_ty, llvm_usize } TupleType { ty: struct_ty, llvm_usize }
} }
/// Creates an [`TupleType`] from a [`PointerType`].
#[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 number of elements present in this [`TupleType`]. /// Returns the number of elements present in this [`TupleType`].
#[must_use] #[must_use]
pub fn num_elements(&self) -> u32 { pub fn num_elements(&self) -> u32 {
@ -115,8 +117,12 @@ impl<'ctx> TupleType<'ctx> {
/// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value. /// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value.
#[must_use] #[must_use]
pub fn construct(&self, name: Option<&'ctx str>) -> <Self as ProxyType<'ctx>>::Value { pub fn construct(
self.map_struct_value(self.as_abi_type().const_zero(), name) &self,
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
self.map_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 /// Constructs a [`TupleValue`] from `objects`. The resulting tuple preserves the order of
@ -136,9 +142,9 @@ impl<'ctx> TupleType<'ctx> {
.enumerate() .enumerate()
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } })); .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() { 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 value
@ -146,44 +152,37 @@ impl<'ctx> TupleType<'ctx> {
/// Converts an existing value into a [`ListValue`]. /// Converts an existing value into a [`ListValue`].
#[must_use] #[must_use]
pub fn map_struct_value( pub fn map_value(
&self, &self,
value: StructValue<'ctx>, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name) <Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name)
} }
/// Converts an existing value into a [`TupleValue`].
#[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 TupleType<'ctx> { impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
type ABI = StructType<'ctx>;
type Base = StructType<'ctx>; type Base = StructType<'ctx>;
type Value = TupleValue<'ctx>; type Value = TupleValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected struct type, got {llvm_ty:?}")) Err(format!("Expected struct type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(_: Self::Base, _: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
Ok(()) _generator: &G,
_ctx: &'ctx Context,
llvm_ty: Self::Base,
) -> Result<(), String> {
Self::is_representable(llvm_ty)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
@ -193,10 +192,6 @@ impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
} }
impl<'ctx> From<TupleType<'ctx>> for StructType<'ctx> { impl<'ctx> From<TupleType<'ctx>> for StructType<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{ use inkwell::{
context::{AsContextRef, Context, ContextRef}, context::{AsContextRef, Context, ContextRef},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue, StructValue}, values::IntValue,
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -12,11 +12,10 @@ use crate::codegen::{
types::{ types::{
structure::{ structure::{
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields, check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
StructProxyType,
}, },
ProxyType, ProxyType,
}, },
values::utils::SliceValue, values::{utils::SliceValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -28,7 +27,7 @@ pub struct SliceType<'ctx> {
} }
#[derive(PartialEq, Eq, Clone, Copy, StructFields)] #[derive(PartialEq, Eq, Clone, Copy, StructFields)]
pub struct SliceStructFields<'ctx> { pub struct SliceFields<'ctx> {
#[value_type(bool_type())] #[value_type(bool_type())]
pub start_defined: StructField<'ctx, IntValue<'ctx>>, pub start_defined: StructField<'ctx, IntValue<'ctx>>,
#[value_type(usize)] #[value_type(usize)]
@ -43,14 +42,14 @@ pub struct SliceStructFields<'ctx> {
pub step: StructField<'ctx, IntValue<'ctx>>, pub step: StructField<'ctx, IntValue<'ctx>>,
} }
impl<'ctx> SliceStructFields<'ctx> { impl<'ctx> SliceFields<'ctx> {
/// Creates a new instance of [`SliceStructFields`] with a custom integer type for its range values. /// Creates a new instance of [`SliceFields`] with a custom integer type for its range values.
#[must_use] #[must_use]
pub fn new_sized(ctx: &impl AsContextRef<'ctx>, int_ty: IntType<'ctx>) -> Self { pub fn new_sized(ctx: &impl AsContextRef<'ctx>, int_ty: IntType<'ctx>) -> Self {
let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) }; let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) };
let mut counter = FieldIndexCounter::default(); let mut counter = FieldIndexCounter::default();
SliceStructFields { SliceFields {
start_defined: StructField::create(&mut counter, "start_defined", ctx.bool_type()), start_defined: StructField::create(&mut counter, "start_defined", ctx.bool_type()),
start: StructField::create(&mut counter, "start", int_ty), start: StructField::create(&mut counter, "start", int_ty),
stop_defined: StructField::create(&mut counter, "stop_defined", ctx.bool_type()), stop_defined: StructField::create(&mut counter, "stop_defined", ctx.bool_type()),
@ -62,10 +61,60 @@ impl<'ctx> SliceStructFields<'ctx> {
} }
impl<'ctx> SliceType<'ctx> { impl<'ctx> SliceType<'ctx> {
/// Checks whether `llvm_ty` represents a `slice` type, returning [Err] if it does not.
pub fn is_representable(
llvm_ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
let ctx = llvm_ty.get_context();
let fields = SliceFields::new(ctx, llvm_usize);
let llvm_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}"));
};
check_struct_type_matches_fields(
fields,
llvm_ty,
"Slice",
&[
(fields.start.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.start`, got {ty}"))
}
}),
(fields.stop.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.stop`, got {ty}"))
}
}),
(fields.step.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.step`, got {ty}"))
}
}),
],
)
}
// TODO: Move this into e.g. StructProxyType
#[must_use]
pub fn get_fields(&self) -> SliceFields<'ctx> {
SliceFields::new_sized(&self.int_ty.get_context(), self.int_ty)
}
/// Creates an LLVM type corresponding to the expected structure of a `Slice`. /// Creates an LLVM type corresponding to the expected structure of a `Slice`.
#[must_use] #[must_use]
fn llvm_type(ctx: &'ctx Context, int_ty: IntType<'ctx>) -> PointerType<'ctx> { fn llvm_type(ctx: &'ctx Context, int_ty: IntType<'ctx>) -> PointerType<'ctx> {
let field_tys = SliceStructFields::new_sized(&int_ty.get_context(), int_ty) let field_tys = SliceFields::new_sized(&int_ty.get_context(), int_ty)
.into_iter() .into_iter()
.map(|field| field.1) .map(|field| field.1)
.collect_vec(); .collect_vec();
@ -85,16 +134,6 @@ impl<'ctx> SliceType<'ctx> {
Self::new_impl(ctx.ctx, int_ty, ctx.get_size_type()) Self::new_impl(ctx.ctx, int_ty, ctx.get_size_type())
} }
/// Creates an instance of [`SliceType`] with `int_ty` as its backing integer type.
#[must_use]
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
int_ty: IntType<'ctx>,
) -> Self {
Self::new_impl(ctx, int_ty, generator.get_size_type(ctx))
}
/// Creates an instance of [`SliceType`] with `usize` as its backing integer type. /// Creates an instance of [`SliceType`] with `usize` as its backing integer type.
#[must_use] #[must_use]
pub fn new_usize(ctx: &CodeGenContext<'ctx, '_>) -> Self { pub fn new_usize(ctx: &CodeGenContext<'ctx, '_>) -> Self {
@ -110,24 +149,14 @@ impl<'ctx> SliceType<'ctx> {
Self::new_impl(ctx, generator.get_size_type(ctx), generator.get_size_type(ctx)) Self::new_impl(ctx, generator.get_size_type(ctx), generator.get_size_type(ctx))
} }
/// Creates an [`SliceType`] from a [`StructType`] representing a `slice`.
#[must_use]
pub fn from_struct_type(
ty: StructType<'ctx>,
int_ty: IntType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Self {
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), int_ty, llvm_usize)
}
/// Creates an [`SliceType`] from a [`PointerType`] representing a `slice`. /// Creates an [`SliceType`] from a [`PointerType`] representing a `slice`.
#[must_use] #[must_use]
pub fn from_pointer_type( pub fn from_type(
ptr_ty: PointerType<'ctx>, ptr_ty: PointerType<'ctx>,
int_ty: IntType<'ctx>, int_ty: IntType<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
) -> Self { ) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, int_ty).is_ok()); debug_assert!(Self::is_representable(ptr_ty, int_ty).is_ok());
Self { ty: ptr_ty, int_ty, llvm_usize } Self { ty: ptr_ty, int_ty, llvm_usize }
} }
@ -172,30 +201,11 @@ impl<'ctx> SliceType<'ctx> {
) )
} }
/// Converts an existing value into a [`SliceValue`].
#[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.int_ty,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`ContiguousNDArrayValue`]. /// Converts an existing value into a [`ContiguousNDArrayValue`].
#[must_use] #[must_use]
pub fn map_pointer_value( pub fn map_value(
&self, &self,
value: PointerValue<'ctx>, value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value { ) -> <Self as ProxyType<'ctx>>::Value {
<Self as ProxyType<'ctx>>::Value::from_pointer_value( <Self as ProxyType<'ctx>>::Value::from_pointer_value(
@ -208,80 +218,36 @@ impl<'ctx> SliceType<'ctx> {
} }
impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> { impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> {
type ABI = PointerType<'ctx>;
type Base = PointerType<'ctx>; type Base = PointerType<'ctx>;
type Value = SliceValue<'ctx>; type Value = SliceValue<'ctx>;
fn is_representable( fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>, llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Result<(), String> { ) -> Result<(), String> {
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
Self::has_same_repr(ty, llvm_usize) <Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else { } else {
Err(format!("Expected pointer type, got {llvm_ty:?}")) Err(format!("Expected pointer type, got {llvm_ty:?}"))
} }
} }
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_representable<G: CodeGenerator + ?Sized>(
let ctx = ty.get_context(); generator: &G,
ctx: &'ctx Context,
let fields = SliceStructFields::new(ctx, llvm_usize); llvm_ty: Self::Base,
) -> Result<(), String> {
let llvm_ty = ty.get_element_type(); Self::is_representable(llvm_ty, generator.get_size_type(ctx))
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}"));
};
check_struct_type_matches_fields(
fields,
llvm_ty,
"Slice",
&[
(fields.start.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.start`, got {ty}"))
}
}),
(fields.stop.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.stop`, got {ty}"))
}
}),
(fields.step.name(), &|ty| {
if ty.is_int_type() {
Ok(())
} else {
Err(format!("Expected int type for `Slice.step`, got {ty}"))
}
}),
],
)
} }
fn alloca_type(&self) -> impl BasicType<'ctx> { fn alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type() self.as_base_type().get_element_type().into_struct_type()
} }
fn as_base_type(&self) -> Self::Base { fn as_base_type(&self) -> Self::Base {
self.ty self.ty
} }
fn as_abi_type(&self) -> Self::ABI {
self.as_base_type()
}
}
impl<'ctx> StructProxyType<'ctx> for SliceType<'ctx> {
type StructFields = SliceStructFields<'ctx>;
fn get_fields(&self) -> Self::StructFields {
SliceStructFields::new_sized(&self.ty.get_context(), self.int_ty)
}
} }
impl<'ctx> From<SliceType<'ctx>> for PointerType<'ctx> { impl<'ctx> From<SliceType<'ctx>> for PointerType<'ctx> {

View File

@ -1,188 +0,0 @@
use inkwell::{
types::IntType,
values::{IntValue, PointerValue, StructValue},
};
use itertools::Itertools;
use nac3parser::ast::Location;
use super::{structure::StructProxyValue, ProxyValue, StringValue};
use crate::codegen::{
types::{
structure::{StructField, StructProxyType},
ExceptionType,
},
CodeGenContext, CodeGenerator,
};
/// 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,18 +1,14 @@
use inkwell::{ use inkwell::{
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType}, types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue}, values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace, IntPredicate, AddressSpace, IntPredicate,
}; };
use super::{ use super::{
structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
}; };
use crate::codegen::{ use crate::codegen::{
types::{ types::{structure::StructField, ListType, ProxyType},
structure::{StructField, StructProxyType},
ListType, ProxyType,
},
{CodeGenContext, CodeGenerator}, {CodeGenContext, CodeGenerator},
}; };
@ -25,24 +21,13 @@ pub struct ListValue<'ctx> {
} }
impl<'ctx> ListValue<'ctx> { impl<'ctx> ListValue<'ctx> {
/// Creates an [`ListValue`] from a [`PointerValue`]. /// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an
#[must_use] /// instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { ListType::is_representable(value.get_type(), llvm_usize)
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 [`ListValue`] from a [`PointerValue`]. /// Creates an [`ListValue`] from a [`PointerValue`].
@ -52,18 +37,24 @@ impl<'ctx> ListValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
ListValue { value: ptr, llvm_usize, name } ListValue { value: ptr, llvm_usize, name }
} }
fn items_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn items_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().items self.get_type().get_fields(&ctx.ctx).items
}
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
/// on the field.
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.items_field(ctx).ptr_by_gep(ctx, self.value, self.name)
} }
/// Stores the array of data elements `data` into this instance. /// Stores the array of data elements `data` into this instance.
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) { fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
self.items_field().store(ctx, self.value, data, self.name); self.items_field(ctx).set(ctx, self.value, data, self.name);
} }
/// Convenience method for creating a new array storing data elements with the given element /// Convenience method for creating a new array storing data elements with the given element
@ -101,15 +92,15 @@ impl<'ctx> ListValue<'ctx> {
ListDataProxy(self) ListDataProxy(self)
} }
fn len_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn len_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().len self.get_type().get_fields(&ctx.ctx).len
} }
/// Stores the `size` of this `list` into this instance. /// Stores the `size` of this `list` into this instance.
pub fn store_size(&self, ctx: &CodeGenContext<'ctx, '_>, size: IntValue<'ctx>) { pub fn store_size(&self, ctx: &CodeGenContext<'ctx, '_>, size: IntValue<'ctx>) {
debug_assert_eq!(size.get_type(), ctx.get_size_type()); debug_assert_eq!(size.get_type(), ctx.get_size_type());
self.len_field().store(ctx, self.value, size, self.name); self.len_field(ctx).set(ctx, self.value, size, self.name);
} }
/// Returns the size of this `list` as a value. /// Returns the size of this `list` as a value.
@ -118,7 +109,7 @@ impl<'ctx> ListValue<'ctx> {
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
self.len_field().load(ctx, self.value, name) self.len_field(ctx).get(ctx, self.value, name)
} }
/// Returns an instance of [`ListValue`] with the `items` pointer cast to `i8*`. /// Returns an instance of [`ListValue`] with the `items` pointer cast to `i8*`.
@ -128,7 +119,7 @@ impl<'ctx> ListValue<'ctx> {
let llvm_list_i8 = <Self as ProxyValue>::Type::new(ctx, &llvm_i8); let llvm_list_i8 = <Self as ProxyValue>::Type::new(ctx, &llvm_i8);
Self::from_pointer_value( Self::from_pointer_value(
ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_abi_type(), "").unwrap(), ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_base_type(), "").unwrap(),
self.llvm_usize, self.llvm_usize,
self.name, self.name,
) )
@ -136,25 +127,18 @@ impl<'ctx> ListValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = ListType<'ctx>; type Type = ListType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
ListType::from_pointer_type(self.as_base_value().get_type(), self.llvm_usize) ListType::from_type(self.as_base_value().get_type(), self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for ListValue<'ctx> {}
impl<'ctx> From<ListValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<ListValue<'ctx>> for PointerValue<'ctx> {
fn from(value: ListValue<'ctx>) -> Self { fn from(value: ListValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()
@ -179,7 +163,12 @@ impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
_: &G, _: &G,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
self.0.items_field().load(ctx, self.0.value, self.0.name) let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default();
ctx.builder
.build_load(self.0.pptr_to_data(ctx), var_name.as_str())
.map(BasicValueEnum::into_pointer_value)
.unwrap()
} }
fn size<G: CodeGenerator + ?Sized>( fn size<G: CodeGenerator + ?Sized>(

View File

@ -1,39 +1,44 @@
use inkwell::{types::IntType, values::BasicValue}; use inkwell::{context::Context, values::BasicValue};
use super::{types::ProxyType, CodeGenContext}; use super::types::ProxyType;
use crate::codegen::CodeGenerator;
pub use array::*; pub use array::*;
pub use exception::*;
pub use list::*; pub use list::*;
pub use option::*;
pub use range::*; pub use range::*;
pub use string::*;
pub use tuple::*; pub use tuple::*;
mod array; mod array;
mod exception;
mod list; mod list;
pub mod ndarray; pub mod ndarray;
mod option;
mod range; mod range;
mod string;
pub mod structure;
mod tuple; mod tuple;
pub mod utils; pub mod utils;
/// A LLVM type that is used to represent a non-primitive value in NAC3. /// A LLVM type that is used to represent a non-primitive value in NAC3.
pub trait ProxyValue<'ctx>: Into<Self::Base> { pub trait ProxyValue<'ctx>: Into<Self::Base> {
/// The ABI type of LLVM values represented by this instance. /// The type of LLVM values represented by this instance. This is usually the
type ABI: BasicValue<'ctx>; /// [LLVM pointer type][PointerValue].
/// The type of LLVM values represented by this instance.
type Base: BasicValue<'ctx>; type Base: BasicValue<'ctx>;
/// The type of this value. /// The type of this value.
type Type: ProxyType<'ctx, Value = Self>; type Type: ProxyType<'ctx, Value = Self>;
/// Checks whether `value` can be represented by this [`ProxyValue`]. /// Checks whether `value` can be represented by this [`ProxyValue`].
fn is_instance(value: impl BasicValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { fn is_instance<G: CodeGenerator + ?Sized>(
Self::Type::is_representable(value.as_basic_value_enum().get_type(), llvm_usize) generator: &G,
ctx: &'ctx Context,
value: impl BasicValue<'ctx>,
) -> Result<(), String> {
Self::Type::is_type(generator, ctx, value.as_basic_value_enum().get_type())
}
/// Checks whether `value` can be represented by this [`ProxyValue`].
fn is_representable<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
value: Self::Base,
) -> Result<(), String> {
Self::is_instance(generator, ctx, value.as_basic_value_enum())
} }
/// Returns the [type][ProxyType] of this value. /// Returns the [type][ProxyType] of this value.
@ -41,10 +46,4 @@ pub trait ProxyValue<'ctx>: Into<Self::Base> {
/// Returns the [base value][Self::Base] of this proxy. /// Returns the [base value][Self::Base] of this proxy.
fn as_base_value(&self) -> Self::Base; fn as_base_value(&self) -> Self::Base;
/// Returns this proxy as its ABI value, i.e. the expected value representation if a value
/// represented by this [`ProxyValue`] is being passed into or returned from a function.
///
/// See [`CodeGenContext::get_llvm_abi_type`].
fn as_abi_value(&self, ctx: &CodeGenContext<'ctx, '_>) -> Self::ABI;
} }

View File

@ -1,6 +1,6 @@
use inkwell::{ use inkwell::{
types::IntType, types::IntType,
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
}; };
use itertools::Itertools; use itertools::Itertools;
@ -8,13 +8,12 @@ use crate::codegen::{
irrt, irrt,
types::{ types::{
ndarray::{NDArrayType, ShapeEntryType}, ndarray::{NDArrayType, ShapeEntryType},
structure::{StructField, StructProxyType}, structure::StructField,
ProxyType, ProxyType,
}, },
values::{ values::{
ndarray::NDArrayValue, structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue, ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue,
ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator,
TypedArrayLikeMutator,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -27,24 +26,13 @@ pub struct ShapeEntryValue<'ctx> {
} }
impl<'ctx> ShapeEntryValue<'ctx> { impl<'ctx> ShapeEntryValue<'ctx> {
/// Creates an [`ShapeEntryValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `ShapeEntry`, returning [Err] if `value` is
#[must_use] /// not an instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { <Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
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 [`ShapeEntryValue`] from a [`PointerValue`]. /// Creates an [`ShapeEntryValue`] from a [`PointerValue`].
@ -54,50 +42,43 @@ impl<'ctx> ShapeEntryValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name } Self { value: ptr, llvm_usize, name }
} }
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().ndims self.get_type().get_fields(self.value.get_type().get_context()).ndims
} }
/// Stores the number of dimensions into this value. /// Stores the number of dimensions into this value.
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) { pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.ndims_field().store(ctx, self.value, value, self.name); self.ndims_field().set(ctx, self.value, value, self.name);
} }
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().shape self.get_type().get_fields(self.value.get_type().get_context()).shape
} }
/// Stores the shape into this value. /// Stores the shape into this value.
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) { pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.value, value, self.name); self.shape_field().set(ctx, self.value, value, self.name);
} }
} }
impl<'ctx> ProxyValue<'ctx> for ShapeEntryValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for ShapeEntryValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = ShapeEntryType<'ctx>; type Type = ShapeEntryType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize) Self::Type::from_type(self.value.get_type(), self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for ShapeEntryValue<'ctx> {}
impl<'ctx> From<ShapeEntryValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<ShapeEntryValue<'ctx>> for PointerValue<'ctx> {
fn from(value: ShapeEntryValue<'ctx>) -> Self { fn from(value: ShapeEntryValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()
@ -186,7 +167,7 @@ fn broadcast_shapes<'ctx, G, Shape>(
None, None,
) )
}; };
let shape_entry = llvm_shape_ty.map_pointer_value(pshape_entry, None); let shape_entry = llvm_shape_ty.map_value(pshape_entry, None);
let in_ndims = llvm_usize.const_int(*in_ndims, false); let in_ndims = llvm_usize.const_int(*in_ndims, false);
shape_entry.store_ndims(ctx, in_ndims); shape_entry.store_ndims(ctx, in_ndims);

View File

@ -1,17 +1,16 @@
use inkwell::{ use inkwell::{
types::{BasicType, BasicTypeEnum, IntType}, types::{BasicType, BasicTypeEnum, IntType},
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use super::NDArrayValue; use super::{ArrayLikeValue, NDArrayValue, ProxyValue};
use crate::codegen::{ use crate::codegen::{
stmt::gen_if_callback, stmt::gen_if_callback,
types::{ types::{
ndarray::{ContiguousNDArrayType, NDArrayType}, ndarray::{ContiguousNDArrayType, NDArrayType},
structure::{StructField, StructProxyType}, structure::StructField,
}, },
values::{structure::StructProxyValue, ArrayLikeValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -24,25 +23,13 @@ pub struct ContiguousNDArrayValue<'ctx> {
} }
impl<'ctx> ContiguousNDArrayValue<'ctx> { impl<'ctx> ContiguousNDArrayValue<'ctx> {
/// Creates an [`ContiguousNDArrayValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
#[must_use] /// not an instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
dtype: BasicTypeEnum<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { <Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
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, dtype, llvm_usize, name)
} }
/// Creates an [`ContiguousNDArrayValue`] from a [`PointerValue`]. /// Creates an [`ContiguousNDArrayValue`] from a [`PointerValue`].
@ -53,7 +40,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, item: dtype, llvm_usize, name } Self { value: ptr, item: dtype, llvm_usize, name }
} }
@ -63,7 +50,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
} }
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) { pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.ndims_field().store(ctx, self.as_abi_value(ctx), value, self.name); self.ndims_field().set(ctx, self.as_base_value(), value, self.name);
} }
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -71,11 +58,11 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
} }
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) { pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.as_abi_value(ctx), value, self.name); self.shape_field().set(ctx, self.as_base_value(), value, self.name);
} }
pub fn load_shape(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { pub fn load_shape(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.shape_field().load(ctx, self.value, self.name) self.shape_field().get(ctx, self.value, self.name)
} }
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -83,21 +70,20 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
} }
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) { pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.data_field().store(ctx, self.as_abi_value(ctx), value, self.name); self.data_field().set(ctx, self.as_base_value(), value, self.name);
} }
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.data_field().load(ctx, self.value, self.name) self.data_field().get(ctx, self.value, self.name)
} }
} }
impl<'ctx> ProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = ContiguousNDArrayType<'ctx>; type Type = ContiguousNDArrayType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
<Self as ProxyValue<'ctx>>::Type::from_pointer_type( <Self as ProxyValue<'ctx>>::Type::from_type(
self.as_base_value().get_type(), self.as_base_value().get_type(),
self.item, self.item,
self.llvm_usize, self.llvm_usize,
@ -107,14 +93,8 @@ impl<'ctx> ProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {}
impl<'ctx> From<ContiguousNDArrayValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<ContiguousNDArrayValue<'ctx>> for PointerValue<'ctx> {
fn from(value: ContiguousNDArrayValue<'ctx>) -> Self { fn from(value: ContiguousNDArrayValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()
@ -153,7 +133,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|_, ctx| Ok(self.is_c_contiguous(ctx)), |_, ctx| Ok(self.is_c_contiguous(ctx)),
|_, ctx| { |_, ctx| {
// This ndarray is contiguous. // This ndarray is contiguous.
let data = self.data_field().load(ctx, self.as_abi_value(ctx), self.name); let data = self.data_field(ctx).get(ctx, self.as_base_value(), self.name);
let data = ctx let data = ctx
.builder .builder
.build_pointer_cast(data, result.item.ptr_type(AddressSpace::default()), "") .build_pointer_cast(data, result.item.ptr_type(AddressSpace::default()), "")

View File

@ -1,6 +1,6 @@
use inkwell::{ use inkwell::{
types::IntType, types::IntType,
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -12,12 +12,10 @@ use crate::{
irrt, irrt,
types::{ types::{
ndarray::{NDArrayType, NDIndexType}, ndarray::{NDArrayType, NDIndexType},
structure::{StructField, StructProxyType}, structure::StructField,
utils::SliceType, utils::SliceType,
}, },
values::{ values::{ndarray::NDArrayValue, utils::RustSlice, ProxyValue},
ndarray::NDArrayValue, structure::StructProxyValue, utils::RustSlice, ProxyValue,
},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
typecheck::typedef::Type, typecheck::typedef::Type,
@ -32,24 +30,13 @@ pub struct NDIndexValue<'ctx> {
} }
impl<'ctx> NDIndexValue<'ctx> { impl<'ctx> NDIndexValue<'ctx> {
/// Creates an [`NDIndexValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `ndindex`, returning [Err] if `value` is not an
#[must_use] /// instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { <Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
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 [`NDIndexValue`] from a [`PointerValue`]. /// Creates an [`NDIndexValue`] from a [`PointerValue`].
@ -59,7 +46,7 @@ impl<'ctx> NDIndexValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name } Self { value: ptr, llvm_usize, name }
} }
@ -69,11 +56,11 @@ impl<'ctx> NDIndexValue<'ctx> {
} }
pub fn load_type(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_type(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.type_field().load(ctx, self.value, self.name) self.type_field().get(ctx, self.value, self.name)
} }
pub fn store_type(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) { pub fn store_type(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.type_field().store(ctx, self.value, value, self.name); self.type_field().set(ctx, self.value, value, self.name);
} }
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -81,34 +68,27 @@ impl<'ctx> NDIndexValue<'ctx> {
} }
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.data_field().load(ctx, self.value, self.name) self.data_field().get(ctx, self.value, self.name)
} }
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) { pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.data_field().store(ctx, self.value, value, self.name); self.data_field().set(ctx, self.value, value, self.name);
} }
} }
impl<'ctx> ProxyValue<'ctx> for NDIndexValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for NDIndexValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = NDIndexType<'ctx>; type Type = NDIndexType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize) Self::Type::from_type(self.value.get_type(), self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for NDIndexValue<'ctx> {}
impl<'ctx> From<NDIndexValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<NDIndexValue<'ctx>> for PointerValue<'ctx> {
fn from(value: NDIndexValue<'ctx>) -> Self { fn from(value: NDIndexValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()

View File

@ -213,7 +213,9 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
Binop::normal(Operator::Mult), Binop::normal(Operator::Mult),
(&Some(rhs_dtype), b_kj), (&Some(rhs_dtype), b_kj),
ctx.current_loc, ctx.current_loc,
)?; )?
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
// dst_[...]ij += x // dst_[...]ij += x
let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap(); 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), Binop::normal(Operator::Add),
(&Some(dst_dtype), x), (&Some(dst_dtype), x),
ctx.current_loc, ctx.current_loc,
)?; )?
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
ctx.builder.build_store(pdst_ij, dst_ij).unwrap(); ctx.builder.build_store(pdst_ij, dst_ij).unwrap();
Ok(()) Ok(())

View File

@ -2,14 +2,14 @@ use std::iter::repeat_n;
use inkwell::{ use inkwell::{
types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType}, types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue}, values::{BasicValue, BasicValueEnum, IntValue, PointerValue},
AddressSpace, IntPredicate, AddressSpace, IntPredicate,
}; };
use itertools::Itertools; use itertools::Itertools;
use super::{ use super::{
structure::StructProxyValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, TypedArrayLikeAccessor,
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator, UntypedArrayLikeMutator,
}; };
use crate::{ use crate::{
@ -18,11 +18,7 @@ use crate::{
llvm_intrinsics::{call_int_umin, call_memcpy_generic_array}, llvm_intrinsics::{call_int_umin, call_memcpy_generic_array},
stmt::gen_for_callback_incrementing, stmt::gen_for_callback_incrementing,
type_aligned_alloca, type_aligned_alloca,
types::{ types::{ndarray::NDArrayType, structure::StructField, TupleType},
ndarray::NDArrayType,
structure::{StructField, StructProxyType},
TupleType,
},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
typecheck::typedef::{Type, TypeEnum}, typecheck::typedef::{Type, TypeEnum},
@ -53,26 +49,13 @@ pub struct NDArrayValue<'ctx> {
} }
impl<'ctx> NDArrayValue<'ctx> { impl<'ctx> NDArrayValue<'ctx> {
/// Creates an [`NDArrayValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
#[must_use] /// instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
dtype: BasicTypeEnum<'ctx>,
ndims: u64,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { NDArrayType::is_representable(value.get_type(), llvm_usize)
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, dtype, ndims, llvm_usize, name)
} }
/// Creates an [`NDArrayValue`] from a [`PointerValue`]. /// Creates an [`NDArrayValue`] from a [`PointerValue`].
@ -84,50 +67,57 @@ impl<'ctx> NDArrayValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
NDArrayValue { value: ptr, dtype, ndims, llvm_usize, name } NDArrayValue { value: ptr, dtype, ndims, llvm_usize, name }
} }
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn ndims_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().ndims self.get_type().get_fields(ctx.ctx).ndims
}
/// Returns the pointer to the field storing the number of dimensions of this `NDArray`.
fn ptr_to_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.ndims_field(ctx).ptr_by_gep(ctx, self.value, self.name)
} }
/// Stores the number of dimensions `ndims` into this instance. /// Stores the number of dimensions `ndims` into this instance.
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, ndims: IntValue<'ctx>) { pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, ndims: IntValue<'ctx>) {
debug_assert_eq!(ndims.get_type(), ctx.get_size_type()); debug_assert_eq!(ndims.get_type(), ctx.get_size_type());
self.ndims_field().store(ctx, self.value, ndims, self.name); let pndims = self.ptr_to_ndims(ctx);
ctx.builder.build_store(pndims, ndims).unwrap();
} }
/// Returns the number of dimensions of this `NDArray` as a value. /// Returns the number of dimensions of this `NDArray` as a value.
pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.ndims_field().load(ctx, self.value, self.name) let pndims = self.ptr_to_ndims(ctx);
ctx.builder.build_load(pndims, "").map(BasicValueEnum::into_int_value).unwrap()
} }
fn itemsize_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn itemsize_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().itemsize self.get_type().get_fields(ctx.ctx).itemsize
} }
/// Stores the size of each element `itemsize` into this instance. /// Stores the size of each element `itemsize` into this instance.
pub fn store_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>, itemsize: IntValue<'ctx>) { pub fn store_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>, itemsize: IntValue<'ctx>) {
debug_assert_eq!(itemsize.get_type(), ctx.get_size_type()); debug_assert_eq!(itemsize.get_type(), ctx.get_size_type());
self.itemsize_field().store(ctx, self.value, itemsize, self.name); self.itemsize_field(ctx).set(ctx, self.value, itemsize, self.name);
} }
/// Returns the size of each element of this `NDArray` as a value. /// Returns the size of each element of this `NDArray` as a value.
pub fn load_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.itemsize_field().load(ctx, self.value, self.name) self.itemsize_field(ctx).get(ctx, self.value, self.name)
} }
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn shape_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().shape self.get_type().get_fields(ctx.ctx).shape
} }
/// Stores the array of dimension sizes `dims` into this instance. /// Stores the array of dimension sizes `dims` into this instance.
fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) { fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.value, dims, self.name); self.shape_field(ctx).set(ctx, self.as_base_value(), dims, self.name);
} }
/// Convenience method for creating a new array storing dimension sizes with the given `size`. /// Convenience method for creating a new array storing dimension sizes with the given `size`.
@ -146,13 +136,16 @@ impl<'ctx> NDArrayValue<'ctx> {
NDArrayShapeProxy(self) NDArrayShapeProxy(self)
} }
fn strides_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn strides_field(
self.get_type().get_fields().strides &self,
ctx: &CodeGenContext<'ctx, '_>,
) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).strides
} }
/// Stores the array of stride sizes `strides` into this instance. /// Stores the array of stride sizes `strides` into this instance.
fn store_strides(&self, ctx: &CodeGenContext<'ctx, '_>, strides: PointerValue<'ctx>) { fn store_strides(&self, ctx: &CodeGenContext<'ctx, '_>, strides: PointerValue<'ctx>) {
self.strides_field().store(ctx, self.value, strides, self.name); self.strides_field(ctx).set(ctx, self.as_base_value(), strides, self.name);
} }
/// Convenience method for creating a new array storing the stride with the given `size`. /// Convenience method for creating a new array storing the stride with the given `size`.
@ -171,14 +164,14 @@ impl<'ctx> NDArrayValue<'ctx> {
NDArrayStridesProxy(self) NDArrayStridesProxy(self)
} }
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn data_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().data self.get_type().get_fields(ctx.ctx).data
} }
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
/// on the field. /// on the field.
pub fn ptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { pub fn ptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.data_field().ptr_by_gep(ctx, self.value, self.name) self.data_field(ctx).ptr_by_gep(ctx, self.value, self.name)
} }
/// Stores the array of data elements `data` into this instance. /// Stores the array of data elements `data` into this instance.
@ -187,7 +180,7 @@ impl<'ctx> NDArrayValue<'ctx> {
.builder .builder
.build_bit_cast(data, ctx.ctx.i8_type().ptr_type(AddressSpace::default()), "") .build_bit_cast(data, ctx.ctx.i8_type().ptr_type(AddressSpace::default()), "")
.unwrap(); .unwrap();
self.data_field().store(ctx, self.value, data.into_pointer_value(), self.name); self.data_field(ctx).set(ctx, self.as_base_value(), data.into_pointer_value(), self.name);
} }
/// Convenience method for creating a new array storing data elements with the given element /// Convenience method for creating a new array storing data elements with the given element
@ -478,12 +471,11 @@ impl<'ctx> NDArrayValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = NDArrayType<'ctx>; type Type = NDArrayType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
NDArrayType::from_pointer_type( NDArrayType::from_type(
self.as_base_value().get_type(), self.as_base_value().get_type(),
self.dtype, self.dtype,
self.ndims, self.ndims,
@ -494,14 +486,8 @@ impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for NDArrayValue<'ctx> {}
impl<'ctx> From<NDArrayValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<NDArrayValue<'ctx>> for PointerValue<'ctx> {
fn from(value: NDArrayValue<'ctx>) -> Self { fn from(value: NDArrayValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()
@ -526,7 +512,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayShapeProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
_: &G, _: &G,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
self.0.shape_field().load(ctx, self.0.value, self.0.name) self.0.shape_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
} }
fn size<G: CodeGenerator + ?Sized>( fn size<G: CodeGenerator + ?Sized>(
@ -624,7 +610,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayStridesProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
_: &G, _: &G,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
self.0.strides_field().load(ctx, self.0.value, self.0.name) self.0.strides_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
} }
fn size<G: CodeGenerator + ?Sized>( fn size<G: CodeGenerator + ?Sized>(
@ -722,7 +708,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
_: &G, _: &G,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
self.0.data_field().load(ctx, self.0.value, self.0.name) self.0.data_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
} }
fn size<G: CodeGenerator + ?Sized>( fn size<G: CodeGenerator + ?Sized>(
@ -976,7 +962,7 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
{ {
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
.map_pointer_value(object.into_pointer_value(), None); .map_value(object.into_pointer_value(), None);
ScalarOrNDArray::NDArray(ndarray) ScalarOrNDArray::NDArray(ndarray)
} }
@ -989,7 +975,7 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> { pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> {
match self { match self {
ScalarOrNDArray::Scalar(scalar) => scalar, ScalarOrNDArray::Scalar(scalar) => scalar,
ScalarOrNDArray::NDArray(ndarray) => ndarray.value.into(), ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(),
} }
} }

View File

@ -1,18 +1,15 @@
use inkwell::{ use inkwell::{
types::{BasicType, IntType}, types::{BasicType, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue}, values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use super::NDArrayValue; use super::{NDArrayValue, ProxyValue};
use crate::codegen::{ use crate::codegen::{
irrt, irrt,
stmt::{gen_for_callback, BreakContinueHooks}, stmt::{gen_for_callback, BreakContinueHooks},
types::{ types::{ndarray::NDIterType, structure::StructField},
ndarray::NDIterType, values::{ArraySliceValue, TypedArrayLikeAdapter},
structure::{StructField, StructProxyType},
},
values::{structure::StructProxyValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -26,26 +23,13 @@ pub struct NDIterValue<'ctx> {
} }
impl<'ctx> NDIterValue<'ctx> { impl<'ctx> NDIterValue<'ctx> {
/// Creates an [`NDArrayValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
#[must_use] /// instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
parent: NDArrayValue<'ctx>,
indices: ArraySliceValue<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { <Self as ProxyValue>::Type::is_representable(value.get_type(), llvm_usize)
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, parent, indices, llvm_usize, name)
} }
/// Creates an [`NDArrayValue`] from a [`PointerValue`]. /// Creates an [`NDArrayValue`] from a [`PointerValue`].
@ -57,7 +41,7 @@ impl<'ctx> NDIterValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, parent, indices, llvm_usize, name } Self { value: ptr, parent, indices, llvm_usize, name }
} }
@ -81,8 +65,11 @@ impl<'ctx> NDIterValue<'ctx> {
irrt::ndarray::call_nac3_nditer_next(ctx, *self); irrt::ndarray::call_nac3_nditer_next(ctx, *self);
} }
fn element_field(&self) -> StructField<'ctx, PointerValue<'ctx>> { fn element_field(
self.get_type().get_fields().element &self,
ctx: &CodeGenContext<'ctx, '_>,
) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).element
} }
/// Get pointer to the current element. /// Get pointer to the current element.
@ -90,7 +77,7 @@ impl<'ctx> NDIterValue<'ctx> {
pub fn get_pointer(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { pub fn get_pointer(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let elem_ty = self.parent.dtype; let elem_ty = self.parent.dtype;
let p = self.element_field().load(ctx, self.as_abi_value(ctx), self.name); let p = self.element_field(ctx).get(ctx, self.as_base_value(), self.name);
ctx.builder ctx.builder
.build_pointer_cast(p, elem_ty.ptr_type(AddressSpace::default()), "element") .build_pointer_cast(p, elem_ty.ptr_type(AddressSpace::default()), "element")
.unwrap() .unwrap()
@ -103,14 +90,14 @@ impl<'ctx> NDIterValue<'ctx> {
ctx.builder.build_load(p, "value").unwrap() ctx.builder.build_load(p, "value").unwrap()
} }
fn nth_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn nth_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().nth self.get_type().get_fields(ctx.ctx).nth
} }
/// Get the index of the current element if this ndarray were a flat ndarray. /// Get the index of the current element if this ndarray were a flat ndarray.
#[must_use] #[must_use]
pub fn get_index(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn get_index(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.nth_field().load(ctx, self.as_abi_value(ctx), self.name) self.nth_field(ctx).get(ctx, self.as_base_value(), self.name)
} }
/// Get the indices of the current element. /// Get the indices of the current element.
@ -127,25 +114,18 @@ impl<'ctx> NDIterValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for NDIterValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for NDIterValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = NDIterType<'ctx>; type Type = NDIterType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
NDIterType::from_pointer_type(self.as_base_value().get_type(), self.llvm_usize) NDIterType::from_type(self.as_base_value().get_type(), self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for NDIterValue<'ctx> {}
impl<'ctx> From<NDIterValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<NDIterValue<'ctx>> for PointerValue<'ctx> {
fn from(value: NDIterValue<'ctx>) -> Self { fn from(value: NDIterValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()

View File

@ -42,7 +42,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])` // 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
let input_seq = ListType::from_unifier_type(generator, ctx, input_seq_ty) let input_seq = ListType::from_unifier_type(generator, ctx, input_seq_ty)
.map_pointer_value(input_seq.into_pointer_value(), None); .map_value(input_seq.into_pointer_value(), None);
let len = input_seq.load_size(ctx, None); let len = input_seq.load_size(ctx, None);
// TODO: Find a way to remove this mid-BB allocation // TODO: Find a way to remove this mid-BB allocation
@ -86,7 +86,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
// 2. A tuple of ints; e.g., `np.empty((600, 800, 3))` // 2. A tuple of ints; e.g., `np.empty((600, 800, 3))`
let input_seq = TupleType::from_unifier_type(generator, ctx, input_seq_ty) let input_seq = TupleType::from_unifier_type(generator, ctx, input_seq_ty)
.map_struct_value(input_seq.into_struct_value(), None); .map_value(input_seq.into_struct_value(), None);
let len = input_seq.get_type().num_elements(); let len = input_seq.get_type().num_elements();
@ -106,7 +106,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
for i in 0..input_seq.get_type().num_elements() { for i in 0..input_seq.get_type().num_elements() {
// Get the i-th element off of the tuple and load it into `result`. // 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(); let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
unsafe { unsafe {

View File

@ -1,75 +0,0 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue},
};
use super::ProxyValue;
use crate::codegen::{types::OptionType, CodeGenContext};
/// 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

@ -1,50 +1,27 @@
use inkwell::{ use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
types::IntType,
values::{ArrayValue, BasicValueEnum, IntValue, PointerValue},
};
use super::ProxyValue; use super::ProxyValue;
use crate::codegen::{types::RangeType, CodeGenContext, CodeGenerator}; use crate::codegen::{types::RangeType, CodeGenContext};
/// Proxy type for accessing a `range` value in LLVM. /// Proxy type for accessing a `range` value in LLVM.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct RangeValue<'ctx> { pub struct RangeValue<'ctx> {
value: PointerValue<'ctx>, value: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
} }
impl<'ctx> RangeValue<'ctx> { impl<'ctx> RangeValue<'ctx> {
/// Creates an [`RangeValue`] from a [`PointerValue`]. /// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance.
#[must_use] pub fn is_representable(value: PointerValue<'ctx>) -> Result<(), String> {
pub fn from_array_value<G: CodeGenerator + ?Sized>( RangeType::is_representable(value.get_type())
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
val: ArrayValue<'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 [`RangeValue`] from a [`PointerValue`]. /// Creates an [`RangeValue`] from a [`PointerValue`].
#[must_use] #[must_use]
pub fn from_pointer_value( pub fn from_pointer_value(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self {
ptr: PointerValue<'ctx>, debug_assert!(Self::is_representable(ptr).is_ok());
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
RangeValue { value: ptr, llvm_usize, name } RangeValue { value: ptr, name }
} }
fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
@ -54,7 +31,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe { unsafe {
ctx.builder ctx.builder
.build_in_bounds_gep( .build_in_bounds_gep(
self.as_abi_value(ctx), self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(0, false)], &[llvm_i32.const_zero(), llvm_i32.const_int(0, false)],
var_name.as_str(), var_name.as_str(),
) )
@ -69,7 +46,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe { unsafe {
ctx.builder ctx.builder
.build_in_bounds_gep( .build_in_bounds_gep(
self.as_abi_value(ctx), self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)], &[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
var_name.as_str(), var_name.as_str(),
) )
@ -84,7 +61,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe { unsafe {
ctx.builder ctx.builder
.build_in_bounds_gep( .build_in_bounds_gep(
self.as_abi_value(ctx), self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(2, false)], &[llvm_i32.const_zero(), llvm_i32.const_int(2, false)],
var_name.as_str(), var_name.as_str(),
) )
@ -157,21 +134,16 @@ impl<'ctx> RangeValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = RangeType<'ctx>; type Type = RangeType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
RangeType::from_pointer_type(self.value.get_type(), self.llvm_usize) RangeType::from_type(self.value.get_type())
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> From<RangeValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<RangeValue<'ctx>> for PointerValue<'ctx> {

View File

@ -1,87 +0,0 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
};
use crate::codegen::{
types::{structure::StructField, StringType},
values::ProxyValue,
CodeGenContext,
};
/// 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,24 +0,0 @@
use inkwell::values::{BasicValueEnum, PointerValue, StructValue};
use super::ProxyValue;
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>:
ProxyValue<'ctx, Base = PointerValue<'ctx>, Type: StructProxyType<'ctx, Value = Self>>
{
/// Returns this value as a [`StructValue`].
#[must_use]
fn get_struct_value(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructValue<'ctx> {
ctx.builder
.build_load(self.get_pointer_value(ctx), "")
.map(BasicValueEnum::into_struct_value)
.unwrap()
}
/// Returns this value as a [`PointerValue`].
#[must_use]
fn get_pointer_value(&self, _: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.as_base_value()
}
}

View File

@ -1,6 +1,6 @@
use inkwell::{ use inkwell::{
types::IntType, types::IntType,
values::{BasicValue, BasicValueEnum, PointerValue, StructValue}, values::{BasicValue, BasicValueEnum, StructValue},
}; };
use super::ProxyValue; use super::ProxyValue;
@ -14,6 +14,15 @@ pub struct TupleValue<'ctx> {
} }
impl<'ctx> TupleValue<'ctx> { impl<'ctx> TupleValue<'ctx> {
/// Checks whether `value` is an instance of `tuple`, returning [Err] if `value` is not an
/// instance.
pub fn is_representable(
value: StructValue<'ctx>,
_llvm_usize: IntType<'ctx>,
) -> Result<(), String> {
TupleType::is_representable(value.get_type())
}
/// Creates an [`TupleValue`] from a [`StructValue`]. /// Creates an [`TupleValue`] from a [`StructValue`].
#[must_use] #[must_use]
pub fn from_struct_value( pub fn from_struct_value(
@ -21,31 +30,13 @@ impl<'ctx> TupleValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(value, llvm_usize).is_ok()); debug_assert!(Self::is_representable(value, llvm_usize).is_ok());
Self { value, llvm_usize, name } Self { value, llvm_usize, name }
} }
/// Creates an [`TupleValue`] 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 {
Self::from_struct_value(
ctx.builder
.build_load(ptr, name.unwrap_or_default())
.map(BasicValueEnum::into_struct_value)
.unwrap(),
llvm_usize,
name,
)
}
/// Stores a value into the tuple element at the given `index`. /// Stores a value into the tuple element at the given `index`.
pub fn insert_element( pub fn store_element(
&mut self, &mut self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
index: u32, index: u32,
@ -63,11 +54,7 @@ impl<'ctx> TupleValue<'ctx> {
} }
/// Loads a value from the tuple element at the given `index`. /// Loads a value from the tuple element at the given `index`.
pub fn extract_element( pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
&self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
) -> BasicValueEnum<'ctx> {
ctx.builder ctx.builder
.build_extract_value( .build_extract_value(
self.value, self.value,
@ -79,21 +66,16 @@ impl<'ctx> TupleValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for TupleValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for TupleValue<'ctx> {
type ABI = StructValue<'ctx>;
type Base = StructValue<'ctx>; type Base = StructValue<'ctx>;
type Type = TupleType<'ctx>; type Type = TupleType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
TupleType::from_struct_type(self.as_base_value().get_type(), self.llvm_usize) TupleType::from_type(self.as_base_value().get_type(), self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> From<TupleValue<'ctx>> for StructValue<'ctx> { impl<'ctx> From<TupleValue<'ctx>> for StructValue<'ctx> {

View File

@ -1,17 +1,14 @@
use inkwell::{ use inkwell::{
types::IntType, types::IntType,
values::{IntValue, PointerValue, StructValue}, values::{IntValue, PointerValue},
}; };
use nac3parser::ast::Expr; use nac3parser::ast::Expr;
use crate::{ use crate::{
codegen::{ codegen::{
types::{ types::{structure::StructField, utils::SliceType},
structure::{StructField, StructProxyType}, values::ProxyValue,
utils::SliceType,
},
values::{structure::StructProxyValue, ProxyValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
typecheck::typedef::Type, typecheck::typedef::Type,
@ -27,25 +24,13 @@ pub struct SliceValue<'ctx> {
} }
impl<'ctx> SliceValue<'ctx> { impl<'ctx> SliceValue<'ctx> {
/// Creates an [`SliceValue`] from a [`StructValue`]. /// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
#[must_use] /// not an instance.
pub fn from_struct_value<G: CodeGenerator + ?Sized>( pub fn is_representable(
generator: &mut G, value: PointerValue<'ctx>,
ctx: &mut CodeGenContext<'ctx, '_>,
val: StructValue<'ctx>,
int_ty: IntType<'ctx>,
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, ) -> Result<(), String> {
) -> Self { <Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
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, int_ty, llvm_usize, name)
} }
/// Creates an [`SliceValue`] from a [`PointerValue`]. /// Creates an [`SliceValue`] from a [`PointerValue`].
@ -56,7 +41,7 @@ impl<'ctx> SliceValue<'ctx> {
llvm_usize: IntType<'ctx>, llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>, name: Option<&'ctx str>,
) -> Self { ) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, int_ty, llvm_usize, name } Self { value: ptr, int_ty, llvm_usize, name }
} }
@ -66,7 +51,7 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.start_defined_field().load(ctx, self.value, self.name) self.start_defined_field().get(ctx, self.value, self.name)
} }
fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -74,22 +59,22 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.start_field().load(ctx, self.value, self.name) self.start_field().get(ctx, self.value, self.name)
} }
pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) { pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
match value { match value {
Some(start) => { Some(start) => {
self.start_defined_field().store( self.start_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_all_ones(), ctx.ctx.bool_type().const_all_ones(),
self.name, self.name,
); );
self.start_field().store(ctx, self.value, start, self.name); self.start_field().set(ctx, self.value, start, self.name);
} }
None => self.start_defined_field().store( None => self.start_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_zero(), ctx.ctx.bool_type().const_zero(),
@ -103,7 +88,7 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.stop_defined_field().load(ctx, self.value, self.name) self.stop_defined_field().get(ctx, self.value, self.name)
} }
fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -111,22 +96,22 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.stop_field().load(ctx, self.value, self.name) self.stop_field().get(ctx, self.value, self.name)
} }
pub fn store_stop(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) { pub fn store_stop(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
match value { match value {
Some(stop) => { Some(stop) => {
self.stop_defined_field().store( self.stop_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_all_ones(), ctx.ctx.bool_type().const_all_ones(),
self.name, self.name,
); );
self.stop_field().store(ctx, self.value, stop, self.name); self.stop_field().set(ctx, self.value, stop, self.name);
} }
None => self.stop_defined_field().store( None => self.stop_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_zero(), ctx.ctx.bool_type().const_zero(),
@ -140,7 +125,7 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.step_defined_field().load(ctx, self.value, self.name) self.step_defined_field().get(ctx, self.value, self.name)
} }
fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> { fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -148,22 +133,22 @@ impl<'ctx> SliceValue<'ctx> {
} }
pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.step_field().load(ctx, self.value, self.name) self.step_field().get(ctx, self.value, self.name)
} }
pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) { pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
match value { match value {
Some(step) => { Some(step) => {
self.step_defined_field().store( self.step_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_all_ones(), ctx.ctx.bool_type().const_all_ones(),
self.name, self.name,
); );
self.step_field().store(ctx, self.value, step, self.name); self.step_field().set(ctx, self.value, step, self.name);
} }
None => self.step_defined_field().store( None => self.step_defined_field().set(
ctx, ctx,
self.value, self.value,
ctx.ctx.bool_type().const_zero(), ctx.ctx.bool_type().const_zero(),
@ -174,25 +159,18 @@ impl<'ctx> SliceValue<'ctx> {
} }
impl<'ctx> ProxyValue<'ctx> for SliceValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for SliceValue<'ctx> {
type ABI = PointerValue<'ctx>;
type Base = PointerValue<'ctx>; type Base = PointerValue<'ctx>;
type Type = SliceType<'ctx>; type Type = SliceType<'ctx>;
fn get_type(&self) -> Self::Type { fn get_type(&self) -> Self::Type {
Self::Type::from_pointer_type(self.value.get_type(), self.int_ty, self.llvm_usize) Self::Type::from_type(self.value.get_type(), self.int_ty, self.llvm_usize)
} }
fn as_base_value(&self) -> Self::Base { fn as_base_value(&self) -> Self::Base {
self.value self.value
} }
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
self.as_base_value()
}
} }
impl<'ctx> StructProxyValue<'ctx> for SliceValue<'ctx> {}
impl<'ctx> From<SliceValue<'ctx>> for PointerValue<'ctx> { impl<'ctx> From<SliceValue<'ctx>> for PointerValue<'ctx> {
fn from(value: SliceValue<'ctx>) -> Self { fn from(value: SliceValue<'ctx>) -> Self {
value.as_base_value() value.as_base_value()

View File

@ -17,10 +17,10 @@ use crate::{
builtin_fns, builtin_fns,
numpy::*, numpy::*,
stmt::{exn_constructor, gen_if_callback}, stmt::{exn_constructor, gen_if_callback},
types::{ndarray::NDArrayType, RangeType}, types::ndarray::NDArrayType,
values::{ values::{
ndarray::{shape::parse_numpy_int_sequence, ScalarOrNDArray}, ndarray::{shape::parse_numpy_int_sequence, ScalarOrNDArray},
ProxyValue, ProxyValue, RangeValue,
}, },
}, },
symbol_resolver::SymbolValue, symbol_resolver::SymbolValue,
@ -36,7 +36,9 @@ pub fn get_exn_constructor(
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
) -> (TopLevelDef, TopLevelDef, Type, Type) { ) -> (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 exception_fields = make_exception_fields(int32, int64, string);
let exn_cons_args = vec![ let exn_cons_args = vec![
FuncArg { FuncArg {
@ -575,7 +577,7 @@ impl<'a> BuiltinBuilder<'a> {
let (zelf_ty, zelf) = obj.unwrap(); let (zelf_ty, zelf) = obj.unwrap();
let zelf = let zelf =
zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value(); zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
let zelf = RangeType::new(ctx).map_pointer_value(zelf, Some("range")); let zelf = RangeValue::from_pointer_value(zelf, Some("range"));
let mut start = None; let mut start = None;
let mut stop = None; let mut stop = None;
@ -662,7 +664,7 @@ impl<'a> BuiltinBuilder<'a> {
zelf.store_end(ctx, stop); zelf.store_end(ctx, stop);
zelf.store_step(ctx, step); zelf.store_step(ctx, step);
Ok(Some(zelf.as_abi_value(ctx).into())) Ok(Some(zelf.as_base_value().into()))
}, },
)))), )))),
loc: None, loc: None,
@ -1278,7 +1280,7 @@ impl<'a> BuiltinBuilder<'a> {
let ndarray = let ndarray =
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?; args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
.map_pointer_value(ndarray.into_pointer_value(), None); .map_value(ndarray.into_pointer_value(), None);
let size = ctx let size = ctx
.builder .builder
@ -1310,7 +1312,7 @@ impl<'a> BuiltinBuilder<'a> {
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?; args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
.map_pointer_value(ndarray.into_pointer_value(), None); .map_value(ndarray.into_pointer_value(), None);
let result_tuple = match prim { let result_tuple = match prim {
PrimDef::FunNpShape => ndarray.make_shape_tuple(generator, ctx), PrimDef::FunNpShape => ndarray.make_shape_tuple(generator, ctx),
@ -1318,7 +1320,7 @@ impl<'a> BuiltinBuilder<'a> {
_ => unreachable!(), _ => unreachable!(),
}; };
Ok(Some(result_tuple.as_abi_value(ctx).into())) Ok(Some(result_tuple.as_base_value().into()))
}), }),
) )
} }
@ -1351,10 +1353,10 @@ impl<'a> BuiltinBuilder<'a> {
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty)
.map_pointer_value(arg_val.into_pointer_value(), None); .map_value(arg_val.into_pointer_value(), None);
let ndarray = ndarray.transpose(generator, ctx, None); // TODO: Add axes argument let ndarray = ndarray.transpose(generator, ctx, None); // TODO: Add axes argument
Ok(Some(ndarray.as_abi_value(ctx).into())) Ok(Some(ndarray.as_base_value().into()))
}), }),
), ),
@ -1389,7 +1391,7 @@ impl<'a> BuiltinBuilder<'a> {
args[1].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?; args[1].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?;
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty) let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
.map_pointer_value(ndarray_val.into_pointer_value(), None); .map_value(ndarray_val.into_pointer_value(), None);
let shape = parse_numpy_int_sequence(generator, ctx, (shape_ty, shape_val)); let shape = parse_numpy_int_sequence(generator, ctx, (shape_ty, shape_val));
@ -1408,7 +1410,7 @@ impl<'a> BuiltinBuilder<'a> {
_ => unreachable!(), _ => unreachable!(),
}; };
Ok(Some(new_ndarray.as_abi_value(ctx).as_basic_value_enum())) Ok(Some(new_ndarray.as_base_value().as_basic_value_enum()))
}), }),
) )
} }

View File

@ -1521,7 +1521,8 @@ impl TopLevelComposer {
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) .any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{ {
// create constructor for these classes // 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 { let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![ args: vec![
FuncArg { FuncArg {

View File

@ -58,7 +58,7 @@ rm -f ./*.o ./*.bc demo
if [ -z "$i686" ]; then if [ -z "$i686" ]; then
$nac3standalone "${nac3args[@]}" $nac3standalone "${nac3args[@]}"
clang -c -std=gnu11 -Wall -Wextra -O3 -o demo.o demo.c clang -c -std=gnu11 -Wall -Wextra -O3 -o demo.o demo.c
clang -o demo module.o demo.o $DEMO_LINALG_STUB -fuse-ld=lld -lm clang -o demo module.o demo.o $DEMO_LINALG_STUB -lm -Wl,--no-warn-search-mismatch
else else
$nac3standalone --triple i686-unknown-linux-gnu --target-features +sse2 "${nac3args[@]}" $nac3standalone --triple i686-unknown-linux-gnu --target-features +sse2 "${nac3args[@]}"
clang -m32 -c -std=gnu11 -Wall -Wextra -O3 -msse2 -o demo.o demo.c clang -m32 -c -std=gnu11 -Wall -Wextra -O3 -msse2 -o demo.o demo.c