Compare commits
4 Commits
master
...
feature/su
Author | SHA1 | Date | |
---|---|---|---|
9e12ce442f | |||
c01087cc3c | |||
ef53c7a2cb | |||
7ad72b7eea |
103
Cargo.lock
generated
103
Cargo.lock
generated
@ -126,9 +126,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.7"
|
version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@ -170,7 +170,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -187,14 +187,14 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console"
|
name = "console"
|
||||||
version = "0.15.10"
|
version = "0.15.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
|
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"windows-sys 0.52.0",
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -221,18 +221,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.14"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@ -249,18 +249,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.12"
|
version = "0.3.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
@ -305,9 +305,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "1.0.0"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
@ -378,9 +378,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.2"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
@ -417,11 +417,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.11"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -472,7 +472,7 @@ checksum = "9dd28cfd4cfba665d47d31c08a6ba637eed16770abca2eccbbc3ca831fef1e44"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -559,9 +559,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.168"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@ -678,7 +678,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
"trybuild",
|
"trybuild",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -799,7 +799,7 @@ dependencies = [
|
|||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -927,7 +927,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-macros-backend",
|
"pyo3-macros-backend",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -940,14 +940,14 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-build-config",
|
"pyo3-build-config",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.38"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -1062,9 +1062,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
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 = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
@ -1095,29 +1095,29 @@ checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.217"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.217"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.134"
|
version = "1.0.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1226,7 +1226,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1242,9 +1242,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.94"
|
version = "2.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1265,13 +1265,12 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.15.0"
|
version = "3.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@ -1279,9 +1278,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term"
|
name = "term"
|
||||||
version = "1.0.1"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3bb6001afcea98122260987f8b7b5da969ecad46dbf0b5453702f776b491a41"
|
checksum = "4df4175de05129f31b80458c6df371a15e7fc3fd367272e6bf938e5c351c7ea0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"home",
|
"home",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@ -1326,7 +1325,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1604,9 +1603,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.22"
|
version = "0.6.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -1638,5 +1637,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.94",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
6
flake.lock
generated
6
flake.lock
generated
@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1735834308,
|
"lastModified": 1733940404,
|
||||||
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=",
|
"narHash": "sha256-Pj39hSoUA86ZePPF/UXiYHHM7hMIkios8TYG29kQT4g=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6df24922a1400241dae323af55f30e4318a6ca65",
|
"rev": "5d67ea6b4b63378b9c13be21e2ec9d1afc921713",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
37
nac3artiq/demo/mangling_test.py
Normal file
37
nac3artiq/demo/mangling_test.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from min_artiq import *
|
||||||
|
from numpy import int32
|
||||||
|
|
||||||
|
@nac3
|
||||||
|
class NameManglingTest:
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
__var1: int32
|
||||||
|
__var2__: int32
|
||||||
|
__: int32
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.core = Core()
|
||||||
|
self.__var1 = 42
|
||||||
|
self.__var2__ = 10
|
||||||
|
self.__ = 99
|
||||||
|
|
||||||
|
@rpc
|
||||||
|
def get_var1(self) -> int32:
|
||||||
|
return self.__var1
|
||||||
|
|
||||||
|
@rpc
|
||||||
|
def get_var2(self) -> int32:
|
||||||
|
return self.__var2__
|
||||||
|
|
||||||
|
@rpc
|
||||||
|
def get_var3(self) -> int32:
|
||||||
|
return self.__
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
assert self.get_var1() == 42
|
||||||
|
assert self.get_var2() == 10
|
||||||
|
assert self.get_var3() == 99
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
NameManglingTest().run()
|
@ -1,29 +0,0 @@
|
|||||||
from min_artiq import *
|
|
||||||
import numpy
|
|
||||||
from numpy import int32
|
|
||||||
|
|
||||||
|
|
||||||
@nac3
|
|
||||||
class NumpyBoolDecay:
|
|
||||||
core: KernelInvariant[Core]
|
|
||||||
np_true: KernelInvariant[bool]
|
|
||||||
np_false: KernelInvariant[bool]
|
|
||||||
np_int: KernelInvariant[int32]
|
|
||||||
np_float: KernelInvariant[float]
|
|
||||||
np_str: KernelInvariant[str]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.core = Core()
|
|
||||||
self.np_true = numpy.True_
|
|
||||||
self.np_false = numpy.False_
|
|
||||||
self.np_int = numpy.int32(0)
|
|
||||||
self.np_float = numpy.float64(0.0)
|
|
||||||
self.np_str = numpy.str_("")
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def run(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
NumpyBoolDecay().run()
|
|
@ -162,7 +162,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||||
fn get_name(&self) -> &str {
|
fn get_name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
@ -464,7 +464,7 @@ 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(generator, ctx.ctx, dtype, ndims)
|
let ndarray = NDArrayType::new(generator, ctx.ctx, dtype, Some(ndims))
|
||||||
.map_value(arg.into_pointer_value(), None);
|
.map_value(arg.into_pointer_value(), None);
|
||||||
|
|
||||||
let ndims = llvm_usize.const_int(ndims, false);
|
let ndims = llvm_usize.const_int(ndims, false);
|
||||||
@ -597,7 +597,7 @@ fn format_rpc_ret<'ctx>(
|
|||||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, ret_ty);
|
let (dtype, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, ret_ty);
|
||||||
let dtype_llvm = ctx.get_llvm_type(generator, dtype);
|
let dtype_llvm = ctx.get_llvm_type(generator, dtype);
|
||||||
let ndims = extract_ndims(&ctx.unifier, ndims);
|
let ndims = extract_ndims(&ctx.unifier, ndims);
|
||||||
let ndarray = NDArrayType::new(generator, ctx.ctx, dtype_llvm, ndims)
|
let ndarray = NDArrayType::new(generator, ctx.ctx, dtype_llvm, Some(ndims))
|
||||||
.construct_uninitialized(generator, ctx, None);
|
.construct_uninitialized(generator, ctx, None);
|
||||||
|
|
||||||
// NOTE: Current content of `ndarray`:
|
// NOTE: Current content of `ndarray`:
|
||||||
@ -1505,7 +1505,7 @@ pub fn call_rtio_log_impl<'ctx>(
|
|||||||
/// Generates a call to `core_log`.
|
/// Generates a call to `core_log`.
|
||||||
pub fn gen_core_log<'ctx>(
|
pub fn gen_core_log<'ctx>(
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
obj: Option<&(Type, ValueEnum<'ctx>)>,
|
obj: &Option<(Type, ValueEnum<'ctx>)>,
|
||||||
fun: (&FunSignature, DefinitionId),
|
fun: (&FunSignature, DefinitionId),
|
||||||
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
||||||
generator: &mut dyn CodeGenerator,
|
generator: &mut dyn CodeGenerator,
|
||||||
@ -1522,7 +1522,7 @@ pub fn gen_core_log<'ctx>(
|
|||||||
/// Generates a call to `rtio_log`.
|
/// Generates a call to `rtio_log`.
|
||||||
pub fn gen_rtio_log<'ctx>(
|
pub fn gen_rtio_log<'ctx>(
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
obj: Option<&(Type, ValueEnum<'ctx>)>,
|
obj: &Option<(Type, ValueEnum<'ctx>)>,
|
||||||
fun: (&FunSignature, DefinitionId),
|
fun: (&FunSignature, DefinitionId),
|
||||||
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
||||||
generator: &mut dyn CodeGenerator,
|
generator: &mut dyn CodeGenerator,
|
||||||
|
@ -330,7 +330,7 @@ impl Nac3 {
|
|||||||
vars: into_var_map([arg_ty]),
|
vars: into_var_map([arg_ty]),
|
||||||
},
|
},
|
||||||
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| {
|
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| {
|
||||||
gen_core_log(ctx, obj.as_ref(), fun, &args, generator)?;
|
gen_core_log(ctx, &obj, fun, &args, generator)?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}))),
|
}))),
|
||||||
@ -360,7 +360,7 @@ impl Nac3 {
|
|||||||
vars: into_var_map([arg_ty]),
|
vars: into_var_map([arg_ty]),
|
||||||
},
|
},
|
||||||
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| {
|
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| {
|
||||||
gen_rtio_log(ctx, obj.as_ref(), fun, &args, generator)?;
|
gen_rtio_log(ctx, &obj, fun, &args, generator)?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}))),
|
}))),
|
||||||
|
@ -931,13 +931,10 @@ impl InnerResolver {
|
|||||||
|_| Ok(Ok(extracted_ty)),
|
|_| Ok(Ok(extracted_ty)),
|
||||||
)
|
)
|
||||||
} else if unifier.unioned(extracted_ty, primitives.bool) {
|
} else if unifier.unioned(extracted_ty, primitives.bool) {
|
||||||
if obj.extract::<bool>().is_ok()
|
obj.extract::<bool>().map_or_else(
|
||||||
|| obj.call_method("__bool__", (), None)?.extract::<bool>().is_ok()
|
|_| Ok(Err(format!("{obj} is not in the range of bool"))),
|
||||||
{
|
|_| Ok(Ok(extracted_ty)),
|
||||||
Ok(Ok(extracted_ty))
|
)
|
||||||
} else {
|
|
||||||
Ok(Err(format!("{obj} is not in the range of bool")))
|
|
||||||
}
|
|
||||||
} else if unifier.unioned(extracted_ty, primitives.float) {
|
} else if unifier.unioned(extracted_ty, primitives.float) {
|
||||||
obj.extract::<f64>().map_or_else(
|
obj.extract::<f64>().map_or_else(
|
||||||
|_| Ok(Err(format!("{obj} is not in the range of float64"))),
|
|_| Ok(Err(format!("{obj} is not in the range of float64"))),
|
||||||
@ -950,6 +947,28 @@ impl InnerResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_class_name(&self, field_id: StrRef, defs: &[Arc<RwLock<TopLevelDef>>]) -> Option<String> {
|
||||||
|
|
||||||
|
let field_to_val = self.field_to_val.read();
|
||||||
|
let class_id = field_to_val.iter().find_map(|(&(id, ref name), _)| {
|
||||||
|
if *name == field_id {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let id_to_def = self.id_to_def.read();
|
||||||
|
let def_id = id_to_def.get(&StrRef::from(class_id.to_string()))?;
|
||||||
|
|
||||||
|
let definition = defs[def_id.0].read();
|
||||||
|
if let TopLevelDef::Class { name, .. } = &*definition {
|
||||||
|
Some(name.to_string()) // return class name
|
||||||
|
} else {
|
||||||
|
None // then not a class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_obj_value<'ctx>(
|
pub fn get_obj_value<'ctx>(
|
||||||
&self,
|
&self,
|
||||||
py: Python,
|
py: Python,
|
||||||
@ -977,14 +996,10 @@ impl InnerResolver {
|
|||||||
let val: u64 = obj.extract().unwrap();
|
let val: u64 = obj.extract().unwrap();
|
||||||
self.id_to_primitive.write().insert(id, PrimitiveValue::U64(val));
|
self.id_to_primitive.write().insert(id, PrimitiveValue::U64(val));
|
||||||
Ok(Some(ctx.ctx.i64_type().const_int(val, false).into()))
|
Ok(Some(ctx.ctx.i64_type().const_int(val, false).into()))
|
||||||
} else if ty_id == self.primitive_ids.bool {
|
} else if ty_id == self.primitive_ids.bool || ty_id == self.primitive_ids.np_bool_ {
|
||||||
let val: bool = obj.extract().unwrap();
|
let val: bool = obj.extract().unwrap();
|
||||||
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
|
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
|
||||||
Ok(Some(ctx.ctx.i8_type().const_int(u64::from(val), false).into()))
|
Ok(Some(ctx.ctx.i8_type().const_int(u64::from(val), false).into()))
|
||||||
} else if ty_id == self.primitive_ids.np_bool_ {
|
|
||||||
let val: bool = obj.call_method("__bool__", (), None)?.extract().unwrap();
|
|
||||||
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
|
|
||||||
Ok(Some(ctx.ctx.i8_type().const_int(u64::from(val), false).into()))
|
|
||||||
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
|
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
|
||||||
let val: String = obj.extract().unwrap();
|
let val: String = obj.extract().unwrap();
|
||||||
self.id_to_primitive.write().insert(id, PrimitiveValue::Str(val.clone()));
|
self.id_to_primitive.write().insert(id, PrimitiveValue::Str(val.clone()));
|
||||||
@ -1114,7 +1129,7 @@ impl InnerResolver {
|
|||||||
self.global_value_ids.write().insert(id, obj.into());
|
self.global_value_ids.write().insert(id, obj.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ndims = llvm_ndarray.ndims();
|
let ndims = llvm_ndarray.ndims().unwrap();
|
||||||
|
|
||||||
// Obtain the shape of the ndarray
|
// Obtain the shape of the ndarray
|
||||||
let shape_tuple: &PyTuple = obj.getattr("shape")?.downcast()?;
|
let shape_tuple: &PyTuple = obj.getattr("shape")?.downcast()?;
|
||||||
@ -1131,10 +1146,7 @@ impl InnerResolver {
|
|||||||
super::CompileError::new_err(format!("Error getting element {i}: {e}"))
|
super::CompileError::new_err(format!("Error getting element {i}: {e}"))
|
||||||
})?
|
})?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let value = ctx
|
let value = value.into_int_value();
|
||||||
.builder
|
|
||||||
.build_int_z_extend(value.into_int_value(), llvm_usize, "")
|
|
||||||
.unwrap();
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, PyErr>>()?;
|
.collect::<Result<Vec<_>, PyErr>>()?;
|
||||||
@ -1213,16 +1225,8 @@ impl InnerResolver {
|
|||||||
data_global.set_initializer(&data);
|
data_global.set_initializer(&data);
|
||||||
|
|
||||||
// Get the constant itemsize.
|
// Get the constant itemsize.
|
||||||
//
|
let itemsize = dtype.size_of().unwrap();
|
||||||
// NOTE: dtype.size_of() may return a non-constant, where `TargetData::get_store_size`
|
let itemsize = itemsize.get_zero_extended_constant().unwrap();
|
||||||
// will always return a constant size.
|
|
||||||
let itemsize = ctx
|
|
||||||
.registry
|
|
||||||
.llvm_options
|
|
||||||
.create_target_machine()
|
|
||||||
.map(|tm| tm.get_target_data().get_store_size(&dtype))
|
|
||||||
.unwrap();
|
|
||||||
assert_ne!(itemsize, 0);
|
|
||||||
|
|
||||||
// Create the strides needed for ndarray.strides
|
// Create the strides needed for ndarray.strides
|
||||||
let strides = make_contiguous_strides(itemsize, ndims, &shape_u64s);
|
let strides = make_contiguous_strides(itemsize, ndims, &shape_u64s);
|
||||||
@ -1232,7 +1236,7 @@ impl InnerResolver {
|
|||||||
|
|
||||||
// create a global for ndarray.strides and initialize it
|
// create a global for ndarray.strides and initialize it
|
||||||
let strides_global = ctx.module.add_global(
|
let strides_global = ctx.module.add_global(
|
||||||
llvm_usize.array_type(ndims as u32),
|
llvm_i8.array_type(ndims as u32),
|
||||||
Some(AddressSpace::default()),
|
Some(AddressSpace::default()),
|
||||||
&format!("${id_str}.strides"),
|
&format!("${id_str}.strides"),
|
||||||
);
|
);
|
||||||
@ -1248,30 +1252,9 @@ impl InnerResolver {
|
|||||||
|
|
||||||
let ndarray_ndims = llvm_usize.const_int(ndims, false);
|
let ndarray_ndims = llvm_usize.const_int(ndims, false);
|
||||||
|
|
||||||
// calling as_pointer_value on shape and strides returns [i64 x ndims]*
|
|
||||||
// convert into i64* to conform with expected layout of ndarray
|
|
||||||
|
|
||||||
let ndarray_shape = shape_global.as_pointer_value();
|
let ndarray_shape = shape_global.as_pointer_value();
|
||||||
let ndarray_shape = unsafe {
|
|
||||||
ctx.builder
|
|
||||||
.build_in_bounds_gep(
|
|
||||||
ndarray_shape,
|
|
||||||
&[llvm_usize.const_zero(), llvm_usize.const_zero()],
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ndarray_strides = strides_global.as_pointer_value();
|
let ndarray_strides = strides_global.as_pointer_value();
|
||||||
let ndarray_strides = unsafe {
|
|
||||||
ctx.builder
|
|
||||||
.build_in_bounds_gep(
|
|
||||||
ndarray_strides,
|
|
||||||
&[llvm_usize.const_zero(), llvm_usize.const_zero()],
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ndarray = llvm_ndarray
|
let ndarray = llvm_ndarray
|
||||||
.as_base_type()
|
.as_base_type()
|
||||||
@ -1452,12 +1435,9 @@ impl InnerResolver {
|
|||||||
} else if ty_id == self.primitive_ids.uint64 {
|
} else if ty_id == self.primitive_ids.uint64 {
|
||||||
let val: u64 = obj.extract()?;
|
let val: u64 = obj.extract()?;
|
||||||
Ok(SymbolValue::U64(val))
|
Ok(SymbolValue::U64(val))
|
||||||
} else if ty_id == self.primitive_ids.bool {
|
} else if ty_id == self.primitive_ids.bool || ty_id == self.primitive_ids.np_bool_ {
|
||||||
let val: bool = obj.extract()?;
|
let val: bool = obj.extract()?;
|
||||||
Ok(SymbolValue::Bool(val))
|
Ok(SymbolValue::Bool(val))
|
||||||
} else if ty_id == self.primitive_ids.np_bool_ {
|
|
||||||
let val: bool = obj.call_method("__bool__", (), None)?.extract()?;
|
|
||||||
Ok(SymbolValue::Bool(val))
|
|
||||||
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
|
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
|
||||||
let val: String = obj.extract()?;
|
let val: String = obj.extract()?;
|
||||||
Ok(SymbolValue::Str(val))
|
Ok(SymbolValue::Str(val))
|
||||||
@ -1555,12 +1535,25 @@ impl SymbolResolver for Resolver {
|
|||||||
fn get_symbol_value<'ctx>(
|
fn get_symbol_value<'ctx>(
|
||||||
&self,
|
&self,
|
||||||
id: StrRef,
|
id: StrRef,
|
||||||
_: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
_: &mut dyn CodeGenerator,
|
_: &mut dyn CodeGenerator,
|
||||||
) -> Option<ValueEnum<'ctx>> {
|
) -> Option<ValueEnum<'ctx>> {
|
||||||
|
let (resolved_id, _is_dunder) = if id.to_string().starts_with("__") && !id.to_string().ends_with("__") && !id.to_string().contains('.') {
|
||||||
|
let inner_resolver = &self.0;
|
||||||
|
|
||||||
|
if let Some(class_name) = inner_resolver.get_class_name(id, &ctx.top_level.definitions.read()) {
|
||||||
|
let stripped_class_name = class_name.trim_start_matches('_');
|
||||||
|
let mangled_id: StrRef = format!("_{}{}", stripped_class_name, id).into();
|
||||||
|
(mangled_id, true)
|
||||||
|
} else {
|
||||||
|
(id, false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(id, false)
|
||||||
|
};
|
||||||
let sym_value = {
|
let sym_value = {
|
||||||
let id_to_val = self.0.id_to_pyval.read();
|
let id_to_val = self.0.id_to_pyval.read();
|
||||||
id_to_val.get(&id).cloned()
|
id_to_val.get(&resolved_id).cloned()
|
||||||
}
|
}
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
|
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
|
||||||
@ -1569,14 +1562,14 @@ impl SymbolResolver for Resolver {
|
|||||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||||
for (key, val) in members {
|
for (key, val) in members {
|
||||||
let key: &str = key.extract()?;
|
let key: &str = key.extract()?;
|
||||||
if key == id.to_string() {
|
if key == resolved_id.to_string() {
|
||||||
let id = self.0.helper.id_fn.call1(py, (val,))?.extract(py)?;
|
let pyid = self.0.helper.id_fn.call1(py, (val,))?.extract(py)?;
|
||||||
sym_value = Some((id, val.extract()?));
|
sym_value = Some((pyid, val.extract()?));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((pyid, val)) = &sym_value {
|
if let Some((pyid, val)) = &sym_value {
|
||||||
self.0.id_to_pyval.write().insert(id, (*pyid, val.clone()));
|
self.0.id_to_pyval.write().insert(resolved_id, (*pyid, val.clone()));
|
||||||
}
|
}
|
||||||
Ok(sym_value)
|
Ok(sym_value)
|
||||||
})
|
})
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
#include "irrt/exception.hpp"
|
#include "irrt/exception.hpp"
|
||||||
#include "irrt/list.hpp"
|
#include "irrt/list.hpp"
|
||||||
#include "irrt/math.hpp"
|
#include "irrt/math.hpp"
|
||||||
|
#include "irrt/ndarray.hpp"
|
||||||
#include "irrt/range.hpp"
|
#include "irrt/range.hpp"
|
||||||
#include "irrt/slice.hpp"
|
#include "irrt/slice.hpp"
|
||||||
#include "irrt/string.hpp"
|
|
||||||
#include "irrt/ndarray/basic.hpp"
|
#include "irrt/ndarray/basic.hpp"
|
||||||
#include "irrt/ndarray/def.hpp"
|
#include "irrt/ndarray/def.hpp"
|
||||||
#include "irrt/ndarray/iter.hpp"
|
#include "irrt/ndarray/iter.hpp"
|
||||||
#include "irrt/ndarray/indexing.hpp"
|
#include "irrt/ndarray/indexing.hpp"
|
||||||
#include "irrt/ndarray/array.hpp"
|
|
||||||
#include "irrt/ndarray/reshape.hpp"
|
|
||||||
#include "irrt/ndarray/broadcast.hpp"
|
|
||||||
#include "irrt/ndarray/transpose.hpp"
|
|
||||||
#include "irrt/ndarray/matmul.hpp"
|
|
@ -21,5 +21,7 @@ using uint64_t = unsigned _ExtInt(64);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// NDArray indices are always `uint32_t`.
|
||||||
|
using NDIndexInt = uint32_t;
|
||||||
// The type of an index or a value describing the length of a range/slice is always `int32_t`.
|
// The type of an index or a value describing the length of a range/slice is always `int32_t`.
|
||||||
using SliceIndex = int32_t;
|
using SliceIndex = int32_t;
|
||||||
|
@ -2,21 +2,6 @@
|
|||||||
|
|
||||||
#include "irrt/int_types.hpp"
|
#include "irrt/int_types.hpp"
|
||||||
#include "irrt/math_util.hpp"
|
#include "irrt/math_util.hpp"
|
||||||
#include "irrt/slice.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/**
|
|
||||||
* @brief A list in NAC3.
|
|
||||||
*
|
|
||||||
* The `items` field is opaque. You must rely on external contexts to
|
|
||||||
* know how to interpret it.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
struct List {
|
|
||||||
uint8_t* items;
|
|
||||||
SizeT len;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// Handle list assignment and dropping part of the list when
|
// Handle list assignment and dropping part of the list when
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// adapted from GNU Scientific Library: https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
// adapted from GNU Scientific Library: https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
||||||
// need to make sure `exp >= 0` before calling this function
|
// need to make sure `exp >= 0` before calling this function
|
||||||
|
151
nac3core/irrt/irrt/ndarray.hpp
Normal file
151
nac3core/irrt/irrt/ndarray.hpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "irrt/int_types.hpp"
|
||||||
|
|
||||||
|
// TODO: To be deleted since NDArray with strides is done.
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename SizeT>
|
||||||
|
SizeT __nac3_ndarray_calc_size_impl(const SizeT* list_data, SizeT list_len, SizeT begin_idx, SizeT end_idx) {
|
||||||
|
__builtin_assume(end_idx <= list_len);
|
||||||
|
|
||||||
|
SizeT num_elems = 1;
|
||||||
|
for (SizeT i = begin_idx; i < end_idx; ++i) {
|
||||||
|
SizeT val = list_data[i];
|
||||||
|
__builtin_assume(val > 0);
|
||||||
|
num_elems *= val;
|
||||||
|
}
|
||||||
|
return num_elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SizeT>
|
||||||
|
void __nac3_ndarray_calc_nd_indices_impl(SizeT index, const SizeT* dims, SizeT num_dims, NDIndexInt* idxs) {
|
||||||
|
SizeT stride = 1;
|
||||||
|
for (SizeT dim = 0; dim < num_dims; dim++) {
|
||||||
|
SizeT i = num_dims - dim - 1;
|
||||||
|
__builtin_assume(dims[i] > 0);
|
||||||
|
idxs[i] = (index / stride) % dims[i];
|
||||||
|
stride *= dims[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SizeT>
|
||||||
|
SizeT __nac3_ndarray_flatten_index_impl(const SizeT* dims,
|
||||||
|
SizeT num_dims,
|
||||||
|
const NDIndexInt* indices,
|
||||||
|
SizeT num_indices) {
|
||||||
|
SizeT idx = 0;
|
||||||
|
SizeT stride = 1;
|
||||||
|
for (SizeT i = 0; i < num_dims; ++i) {
|
||||||
|
SizeT ri = num_dims - i - 1;
|
||||||
|
if (ri < num_indices) {
|
||||||
|
idx += stride * indices[ri];
|
||||||
|
}
|
||||||
|
|
||||||
|
__builtin_assume(dims[i] > 0);
|
||||||
|
stride *= dims[ri];
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SizeT>
|
||||||
|
void __nac3_ndarray_calc_broadcast_impl(const SizeT* lhs_dims,
|
||||||
|
SizeT lhs_ndims,
|
||||||
|
const SizeT* rhs_dims,
|
||||||
|
SizeT rhs_ndims,
|
||||||
|
SizeT* out_dims) {
|
||||||
|
SizeT max_ndims = lhs_ndims > rhs_ndims ? lhs_ndims : rhs_ndims;
|
||||||
|
|
||||||
|
for (SizeT i = 0; i < max_ndims; ++i) {
|
||||||
|
const SizeT* lhs_dim_sz = i < lhs_ndims ? &lhs_dims[lhs_ndims - i - 1] : nullptr;
|
||||||
|
const SizeT* rhs_dim_sz = i < rhs_ndims ? &rhs_dims[rhs_ndims - i - 1] : nullptr;
|
||||||
|
SizeT* out_dim = &out_dims[max_ndims - i - 1];
|
||||||
|
|
||||||
|
if (lhs_dim_sz == nullptr) {
|
||||||
|
*out_dim = *rhs_dim_sz;
|
||||||
|
} else if (rhs_dim_sz == nullptr) {
|
||||||
|
*out_dim = *lhs_dim_sz;
|
||||||
|
} else if (*lhs_dim_sz == 1) {
|
||||||
|
*out_dim = *rhs_dim_sz;
|
||||||
|
} else if (*rhs_dim_sz == 1) {
|
||||||
|
*out_dim = *lhs_dim_sz;
|
||||||
|
} else if (*lhs_dim_sz == *rhs_dim_sz) {
|
||||||
|
*out_dim = *lhs_dim_sz;
|
||||||
|
} else {
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SizeT>
|
||||||
|
void __nac3_ndarray_calc_broadcast_idx_impl(const SizeT* src_dims,
|
||||||
|
SizeT src_ndims,
|
||||||
|
const NDIndexInt* in_idx,
|
||||||
|
NDIndexInt* out_idx) {
|
||||||
|
for (SizeT i = 0; i < src_ndims; ++i) {
|
||||||
|
SizeT src_i = src_ndims - i - 1;
|
||||||
|
out_idx[src_i] = src_dims[src_i] == 1 ? 0 : in_idx[src_i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
uint32_t __nac3_ndarray_calc_size(const uint32_t* list_data, uint32_t list_len, uint32_t begin_idx, uint32_t end_idx) {
|
||||||
|
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx, end_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
__nac3_ndarray_calc_size64(const uint64_t* list_data, uint64_t list_len, uint64_t begin_idx, uint64_t end_idx) {
|
||||||
|
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx, end_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_nd_indices(uint32_t index, const uint32_t* dims, uint32_t num_dims, NDIndexInt* idxs) {
|
||||||
|
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_nd_indices64(uint64_t index, const uint64_t* dims, uint64_t num_dims, NDIndexInt* idxs) {
|
||||||
|
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
__nac3_ndarray_flatten_index(const uint32_t* dims, uint32_t num_dims, const NDIndexInt* indices, uint32_t num_indices) {
|
||||||
|
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices, num_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t __nac3_ndarray_flatten_index64(const uint64_t* dims,
|
||||||
|
uint64_t num_dims,
|
||||||
|
const NDIndexInt* indices,
|
||||||
|
uint64_t num_indices) {
|
||||||
|
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices, num_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_broadcast(const uint32_t* lhs_dims,
|
||||||
|
uint32_t lhs_ndims,
|
||||||
|
const uint32_t* rhs_dims,
|
||||||
|
uint32_t rhs_ndims,
|
||||||
|
uint32_t* out_dims) {
|
||||||
|
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims, rhs_ndims, out_dims);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_broadcast64(const uint64_t* lhs_dims,
|
||||||
|
uint64_t lhs_ndims,
|
||||||
|
const uint64_t* rhs_dims,
|
||||||
|
uint64_t rhs_ndims,
|
||||||
|
uint64_t* out_dims) {
|
||||||
|
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims, rhs_ndims, out_dims);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_broadcast_idx(const uint32_t* src_dims,
|
||||||
|
uint32_t src_ndims,
|
||||||
|
const NDIndexInt* in_idx,
|
||||||
|
NDIndexInt* out_idx) {
|
||||||
|
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx, out_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __nac3_ndarray_calc_broadcast_idx64(const uint64_t* src_dims,
|
||||||
|
uint64_t src_ndims,
|
||||||
|
const NDIndexInt* in_idx,
|
||||||
|
NDIndexInt* out_idx) {
|
||||||
|
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx, out_idx);
|
||||||
|
}
|
||||||
|
} // namespace
|
@ -1,132 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/debug.hpp"
|
|
||||||
#include "irrt/exception.hpp"
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
#include "irrt/list.hpp"
|
|
||||||
#include "irrt/ndarray/basic.hpp"
|
|
||||||
#include "irrt/ndarray/def.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace ndarray::array {
|
|
||||||
/**
|
|
||||||
* @brief In the context of `np.array(<list>)`, deduce the ndarray's shape produced by `<list>` and raise
|
|
||||||
* an exception if there is anything wrong with `<shape>` (e.g., inconsistent dimensions `np.array([[1.0, 2.0],
|
|
||||||
* [3.0]])`)
|
|
||||||
*
|
|
||||||
* If this function finds no issues with `<list>`, the deduced shape is written to `shape`. The caller has the
|
|
||||||
* responsibility to allocate `[SizeT; ndims]` for `shape`. The caller must also initialize `shape` with `-1`s because
|
|
||||||
* of implementation details.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void set_and_validate_list_shape_helper(SizeT axis, List<SizeT>* list, SizeT ndims, SizeT* shape) {
|
|
||||||
if (shape[axis] == -1) {
|
|
||||||
// Dimension is unspecified. Set it.
|
|
||||||
shape[axis] = list->len;
|
|
||||||
} else {
|
|
||||||
// Dimension is specified. Check.
|
|
||||||
if (shape[axis] != list->len) {
|
|
||||||
// Mismatch, throw an error.
|
|
||||||
// NOTE: NumPy's error message is more complex and needs more PARAMS to display.
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR,
|
|
||||||
"The requested array has an inhomogenous shape "
|
|
||||||
"after {0} dimension(s).",
|
|
||||||
axis, shape[axis], list->len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (axis + 1 == ndims) {
|
|
||||||
// `list` has type `list[ItemType]`
|
|
||||||
// Do nothing
|
|
||||||
} else {
|
|
||||||
// `list` has type `list[list[...]]`
|
|
||||||
List<SizeT>** lists = (List<SizeT>**)(list->items);
|
|
||||||
for (SizeT i = 0; i < list->len; i++) {
|
|
||||||
set_and_validate_list_shape_helper<SizeT>(axis + 1, lists[i], ndims, shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief See `set_and_validate_list_shape_helper`.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void set_and_validate_list_shape(List<SizeT>* list, SizeT ndims, SizeT* shape) {
|
|
||||||
for (SizeT axis = 0; axis < ndims; axis++) {
|
|
||||||
shape[axis] = -1; // Sentinel to say this dimension is unspecified.
|
|
||||||
}
|
|
||||||
set_and_validate_list_shape_helper<SizeT>(0, list, ndims, shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief In the context of `np.array(<list>)`, copied the contents stored in `list` to `ndarray`.
|
|
||||||
*
|
|
||||||
* `list` is assumed to be "legal". (i.e., no inconsistent dimensions)
|
|
||||||
*
|
|
||||||
* # Notes on `ndarray`
|
|
||||||
* The caller is responsible for allocating space for `ndarray`.
|
|
||||||
* Here is what this function expects from `ndarray` when called:
|
|
||||||
* - `ndarray->data` has to be allocated, contiguous, and may contain uninitialized values.
|
|
||||||
* - `ndarray->itemsize` has to be initialized.
|
|
||||||
* - `ndarray->ndims` has to be initialized.
|
|
||||||
* - `ndarray->shape` has to be initialized.
|
|
||||||
* - `ndarray->strides` is ignored, but note that `ndarray->data` is contiguous.
|
|
||||||
* When this function call ends:
|
|
||||||
* - `ndarray->data` is written with contents from `<list>`.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void write_list_to_array_helper(SizeT axis, SizeT* index, List<SizeT>* list, NDArray<SizeT>* ndarray) {
|
|
||||||
debug_assert_eq(SizeT, list->len, ndarray->shape[axis]);
|
|
||||||
if (IRRT_DEBUG_ASSERT_BOOL) {
|
|
||||||
if (!ndarray::basic::is_c_contiguous(ndarray)) {
|
|
||||||
raise_debug_assert(SizeT, "ndarray is not C-contiguous", ndarray->strides[0], ndarray->strides[1],
|
|
||||||
NO_PARAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (axis + 1 == ndarray->ndims) {
|
|
||||||
// `list` has type `list[scalar]`
|
|
||||||
// `ndarray` is contiguous, so we can do this, and this is fast.
|
|
||||||
uint8_t* dst = static_cast<uint8_t*>(ndarray->data) + (ndarray->itemsize * (*index));
|
|
||||||
__builtin_memcpy(dst, list->items, ndarray->itemsize * list->len);
|
|
||||||
*index += list->len;
|
|
||||||
} else {
|
|
||||||
// `list` has type `list[list[...]]`
|
|
||||||
List<SizeT>** lists = (List<SizeT>**)(list->items);
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < list->len; i++) {
|
|
||||||
write_list_to_array_helper<SizeT>(axis + 1, index, lists[i], ndarray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief See `write_list_to_array_helper`.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void write_list_to_array(List<SizeT>* list, NDArray<SizeT>* ndarray) {
|
|
||||||
SizeT index = 0;
|
|
||||||
write_list_to_array_helper<SizeT>((SizeT)0, &index, list, ndarray);
|
|
||||||
}
|
|
||||||
} // namespace ndarray::array
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
using namespace ndarray::array;
|
|
||||||
|
|
||||||
void __nac3_ndarray_array_set_and_validate_list_shape(List<int32_t>* list, int32_t ndims, int32_t* shape) {
|
|
||||||
set_and_validate_list_shape(list, ndims, shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_array_set_and_validate_list_shape64(List<int64_t>* list, int64_t ndims, int64_t* shape) {
|
|
||||||
set_and_validate_list_shape(list, ndims, shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_array_write_list_to_array(List<int32_t>* list, NDArray<int32_t>* ndarray) {
|
|
||||||
write_list_to_array(list, ndarray);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_array_write_list_to_array64(List<int64_t>* list, NDArray<int64_t>* ndarray) {
|
|
||||||
write_list_to_array(list, ndarray);
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,8 @@
|
|||||||
#include "irrt/ndarray/def.hpp"
|
#include "irrt/ndarray/def.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
namespace ndarray::basic {
|
namespace ndarray {
|
||||||
|
namespace basic {
|
||||||
/**
|
/**
|
||||||
* @brief Assert that `shape` does not contain negative dimensions.
|
* @brief Assert that `shape` does not contain negative dimensions.
|
||||||
*
|
*
|
||||||
@ -246,7 +247,8 @@ void copy_data(const NDArray<SizeT>* src_ndarray, NDArray<SizeT>* dst_ndarray) {
|
|||||||
ndarray::basic::set_pelement_value(dst_ndarray, dst_element, src_element);
|
ndarray::basic::set_pelement_value(dst_ndarray, dst_element, src_element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace ndarray::basic
|
} // namespace basic
|
||||||
|
} // namespace ndarray
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -1,165 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
#include "irrt/ndarray/def.hpp"
|
|
||||||
#include "irrt/slice.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename SizeT>
|
|
||||||
struct ShapeEntry {
|
|
||||||
SizeT ndims;
|
|
||||||
SizeT* shape;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace ndarray::broadcast {
|
|
||||||
/**
|
|
||||||
* @brief Return true if `src_shape` can broadcast to `dst_shape`.
|
|
||||||
*
|
|
||||||
* See https://numpy.org/doc/stable/user/basics.broadcasting.html
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
bool can_broadcast_shape_to(SizeT target_ndims, const SizeT* target_shape, SizeT src_ndims, const SizeT* src_shape) {
|
|
||||||
if (src_ndims > target_ndims) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < src_ndims; i++) {
|
|
||||||
SizeT target_dim = target_shape[target_ndims - i - 1];
|
|
||||||
SizeT src_dim = src_shape[src_ndims - i - 1];
|
|
||||||
if (!(src_dim == 1 || target_dim == src_dim)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Performs `np.broadcast_shapes(<shapes>)`
|
|
||||||
*
|
|
||||||
* @param num_shapes Number of entries in `shapes`
|
|
||||||
* @param shapes The list of shape to do `np.broadcast_shapes` on.
|
|
||||||
* @param dst_ndims The length of `dst_shape`.
|
|
||||||
* `dst_ndims` must be `max([shape.ndims for shape in shapes])`, but the caller has to calculate it/provide it.
|
|
||||||
* for this function since they should already know in order to allocate `dst_shape` in the first place.
|
|
||||||
* @param dst_shape The resulting shape. Must be pre-allocated by the caller. This function calculate the result
|
|
||||||
* of `np.broadcast_shapes` and write it here.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void broadcast_shapes(SizeT num_shapes, const ShapeEntry<SizeT>* shapes, SizeT dst_ndims, SizeT* dst_shape) {
|
|
||||||
for (SizeT dst_axis = 0; dst_axis < dst_ndims; dst_axis++) {
|
|
||||||
dst_shape[dst_axis] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IRRT_DEBUG_ASSERT
|
|
||||||
SizeT max_ndims_found = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < num_shapes; i++) {
|
|
||||||
ShapeEntry<SizeT> entry = shapes[i];
|
|
||||||
|
|
||||||
// Check pre-condition: `dst_ndims` must be `max([shape.ndims for shape in shapes])`
|
|
||||||
debug_assert(SizeT, entry.ndims <= dst_ndims);
|
|
||||||
|
|
||||||
#ifdef IRRT_DEBUG_ASSERT
|
|
||||||
max_ndims_found = max(max_ndims_found, entry.ndims);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (SizeT j = 0; j < entry.ndims; j++) {
|
|
||||||
SizeT entry_axis = entry.ndims - j - 1;
|
|
||||||
SizeT dst_axis = dst_ndims - j - 1;
|
|
||||||
|
|
||||||
SizeT entry_dim = entry.shape[entry_axis];
|
|
||||||
SizeT dst_dim = dst_shape[dst_axis];
|
|
||||||
|
|
||||||
if (dst_dim == 1) {
|
|
||||||
dst_shape[dst_axis] = entry_dim;
|
|
||||||
} else if (entry_dim == 1 || entry_dim == dst_dim) {
|
|
||||||
// Do nothing
|
|
||||||
} else {
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR,
|
|
||||||
"shape mismatch: objects cannot be broadcast "
|
|
||||||
"to a single shape.",
|
|
||||||
NO_PARAM, NO_PARAM, NO_PARAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IRRT_DEBUG_ASSERT
|
|
||||||
// Check pre-condition: `dst_ndims` must be `max([shape.ndims for shape in shapes])`
|
|
||||||
debug_assert_eq(SizeT, max_ndims_found, dst_ndims);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Perform `np.broadcast_to(<ndarray>, <target_shape>)` and appropriate assertions.
|
|
||||||
*
|
|
||||||
* This function attempts to broadcast `src_ndarray` to a new shape defined by `dst_ndarray.shape`,
|
|
||||||
* and return the result by modifying `dst_ndarray`.
|
|
||||||
*
|
|
||||||
* # Notes on `dst_ndarray`
|
|
||||||
* The caller is responsible for allocating space for the resulting ndarray.
|
|
||||||
* Here is what this function expects from `dst_ndarray` when called:
|
|
||||||
* - `dst_ndarray->data` does not have to be initialized.
|
|
||||||
* - `dst_ndarray->itemsize` does not have to be initialized.
|
|
||||||
* - `dst_ndarray->ndims` must be initialized, determining the length of `dst_ndarray->shape`
|
|
||||||
* - `dst_ndarray->shape` must be allocated, and must contain the desired target broadcast shape.
|
|
||||||
* - `dst_ndarray->strides` must be allocated, through it can contain uninitialized values.
|
|
||||||
* When this function call ends:
|
|
||||||
* - `dst_ndarray->data` is set to `src_ndarray->data` (`dst_ndarray` is just a view to `src_ndarray`)
|
|
||||||
* - `dst_ndarray->itemsize` is set to `src_ndarray->itemsize`
|
|
||||||
* - `dst_ndarray->ndims` is unchanged.
|
|
||||||
* - `dst_ndarray->shape` is unchanged.
|
|
||||||
* - `dst_ndarray->strides` is updated accordingly by how ndarray broadcast_to works.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void broadcast_to(const NDArray<SizeT>* src_ndarray, NDArray<SizeT>* dst_ndarray) {
|
|
||||||
if (!ndarray::broadcast::can_broadcast_shape_to(dst_ndarray->ndims, dst_ndarray->shape, src_ndarray->ndims,
|
|
||||||
src_ndarray->shape)) {
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "operands could not be broadcast together", NO_PARAM, NO_PARAM,
|
|
||||||
NO_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
dst_ndarray->data = src_ndarray->data;
|
|
||||||
dst_ndarray->itemsize = src_ndarray->itemsize;
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < dst_ndarray->ndims; i++) {
|
|
||||||
SizeT src_axis = src_ndarray->ndims - i - 1;
|
|
||||||
SizeT dst_axis = dst_ndarray->ndims - i - 1;
|
|
||||||
if (src_axis < 0 || (src_ndarray->shape[src_axis] == 1 && dst_ndarray->shape[dst_axis] != 1)) {
|
|
||||||
// Freeze the steps in-place
|
|
||||||
dst_ndarray->strides[dst_axis] = 0;
|
|
||||||
} else {
|
|
||||||
dst_ndarray->strides[dst_axis] = src_ndarray->strides[src_axis];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace ndarray::broadcast
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
using namespace ndarray::broadcast;
|
|
||||||
|
|
||||||
void __nac3_ndarray_broadcast_to(NDArray<int32_t>* src_ndarray, NDArray<int32_t>* dst_ndarray) {
|
|
||||||
broadcast_to(src_ndarray, dst_ndarray);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_broadcast_to64(NDArray<int64_t>* src_ndarray, NDArray<int64_t>* dst_ndarray) {
|
|
||||||
broadcast_to(src_ndarray, dst_ndarray);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_broadcast_shapes(int32_t num_shapes,
|
|
||||||
const ShapeEntry<int32_t>* shapes,
|
|
||||||
int32_t dst_ndims,
|
|
||||||
int32_t* dst_shape) {
|
|
||||||
broadcast_shapes(num_shapes, shapes, dst_ndims, dst_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_broadcast_shapes64(int64_t num_shapes,
|
|
||||||
const ShapeEntry<int64_t>* shapes,
|
|
||||||
int64_t dst_ndims,
|
|
||||||
int64_t* dst_shape) {
|
|
||||||
broadcast_shapes(num_shapes, shapes, dst_ndims, dst_shape);
|
|
||||||
}
|
|
||||||
}
|
|
@ -65,7 +65,8 @@ struct NDIndex {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
namespace ndarray::indexing {
|
namespace ndarray {
|
||||||
|
namespace indexing {
|
||||||
/**
|
/**
|
||||||
* @brief Perform ndarray "basic indexing" (https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing)
|
* @brief Perform ndarray "basic indexing" (https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing)
|
||||||
*
|
*
|
||||||
@ -161,8 +162,7 @@ void index(SizeT num_indices, const NDIndex* indices, const NDArray<SizeT>* src_
|
|||||||
|
|
||||||
Range<int32_t> range = slice->indices_checked<SizeT>(src_ndarray->shape[src_axis]);
|
Range<int32_t> range = slice->indices_checked<SizeT>(src_ndarray->shape[src_axis]);
|
||||||
|
|
||||||
dst_ndarray->data =
|
dst_ndarray->data = static_cast<uint8_t*>(dst_ndarray->data) + (SizeT)range.start * src_ndarray->strides[src_axis];
|
||||||
static_cast<uint8_t*>(dst_ndarray->data) + (SizeT)range.start * src_ndarray->strides[src_axis];
|
|
||||||
dst_ndarray->strides[dst_axis] = ((SizeT)range.step) * src_ndarray->strides[src_axis];
|
dst_ndarray->strides[dst_axis] = ((SizeT)range.step) * src_ndarray->strides[src_axis];
|
||||||
dst_ndarray->shape[dst_axis] = (SizeT)range.len<SizeT>();
|
dst_ndarray->shape[dst_axis] = (SizeT)range.len<SizeT>();
|
||||||
|
|
||||||
@ -197,7 +197,8 @@ void index(SizeT num_indices, const NDIndex* indices, const NDArray<SizeT>* src_
|
|||||||
debug_assert_eq(SizeT, src_ndarray->ndims, src_axis);
|
debug_assert_eq(SizeT, src_ndarray->ndims, src_axis);
|
||||||
debug_assert_eq(SizeT, dst_ndarray->ndims, dst_axis);
|
debug_assert_eq(SizeT, dst_ndarray->ndims, dst_axis);
|
||||||
}
|
}
|
||||||
} // namespace ndarray::indexing
|
} // namespace indexing
|
||||||
|
} // namespace ndarray
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/debug.hpp"
|
|
||||||
#include "irrt/exception.hpp"
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
#include "irrt/ndarray/basic.hpp"
|
|
||||||
#include "irrt/ndarray/broadcast.hpp"
|
|
||||||
#include "irrt/ndarray/iter.hpp"
|
|
||||||
|
|
||||||
// NOTE: Everything would be much easier and elegant if einsum is implemented.
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace ndarray::matmul {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Perform the broadcast in `np.einsum("...ij,...jk->...ik", a, b)`.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* Suppose `a_shape == [1, 97, 4, 2]`
|
|
||||||
* and `b_shape == [99, 98, 1, 2, 5]`,
|
|
||||||
*
|
|
||||||
* ...then `new_a_shape == [99, 98, 97, 4, 2]`,
|
|
||||||
* `new_b_shape == [99, 98, 97, 2, 5]`,
|
|
||||||
* and `dst_shape == [99, 98, 97, 4, 5]`.
|
|
||||||
* ^^^^^^^^^^ ^^^^
|
|
||||||
* (broadcasted) (4x2 @ 2x5 => 4x5)
|
|
||||||
*
|
|
||||||
* @param a_ndims Length of `a_shape`.
|
|
||||||
* @param a_shape Shape of `a`.
|
|
||||||
* @param b_ndims Length of `b_shape`.
|
|
||||||
* @param b_shape Shape of `b`.
|
|
||||||
* @param final_ndims Should be equal to `max(a_ndims, b_ndims)`. This is the length of `new_a_shape`,
|
|
||||||
* `new_b_shape`, and `dst_shape` - the number of dimensions after broadcasting.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void calculate_shapes(SizeT a_ndims,
|
|
||||||
SizeT* a_shape,
|
|
||||||
SizeT b_ndims,
|
|
||||||
SizeT* b_shape,
|
|
||||||
SizeT final_ndims,
|
|
||||||
SizeT* new_a_shape,
|
|
||||||
SizeT* new_b_shape,
|
|
||||||
SizeT* dst_shape) {
|
|
||||||
debug_assert(SizeT, a_ndims >= 2);
|
|
||||||
debug_assert(SizeT, b_ndims >= 2);
|
|
||||||
debug_assert_eq(SizeT, max(a_ndims, b_ndims), final_ndims);
|
|
||||||
|
|
||||||
// Check that a and b are compatible for matmul
|
|
||||||
if (a_shape[a_ndims - 1] != b_shape[b_ndims - 2]) {
|
|
||||||
// This is a custom error message. Different from NumPy.
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "Cannot multiply LHS (shape ?x{0}) with RHS (shape {1}x?})",
|
|
||||||
a_shape[a_ndims - 1], b_shape[b_ndims - 2], NO_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SizeT num_entries = 2;
|
|
||||||
ShapeEntry<SizeT> entries[num_entries] = {{.ndims = a_ndims - 2, .shape = a_shape},
|
|
||||||
{.ndims = b_ndims - 2, .shape = b_shape}};
|
|
||||||
|
|
||||||
// TODO: Optimize this
|
|
||||||
ndarray::broadcast::broadcast_shapes<SizeT>(num_entries, entries, final_ndims - 2, new_a_shape);
|
|
||||||
ndarray::broadcast::broadcast_shapes<SizeT>(num_entries, entries, final_ndims - 2, new_b_shape);
|
|
||||||
ndarray::broadcast::broadcast_shapes<SizeT>(num_entries, entries, final_ndims - 2, dst_shape);
|
|
||||||
|
|
||||||
new_a_shape[final_ndims - 2] = a_shape[a_ndims - 2];
|
|
||||||
new_a_shape[final_ndims - 1] = a_shape[a_ndims - 1];
|
|
||||||
new_b_shape[final_ndims - 2] = b_shape[b_ndims - 2];
|
|
||||||
new_b_shape[final_ndims - 1] = b_shape[b_ndims - 1];
|
|
||||||
dst_shape[final_ndims - 2] = a_shape[a_ndims - 2];
|
|
||||||
dst_shape[final_ndims - 1] = b_shape[b_ndims - 1];
|
|
||||||
}
|
|
||||||
} // namespace ndarray::matmul
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
using namespace ndarray::matmul;
|
|
||||||
|
|
||||||
void __nac3_ndarray_matmul_calculate_shapes(int32_t a_ndims,
|
|
||||||
int32_t* a_shape,
|
|
||||||
int32_t b_ndims,
|
|
||||||
int32_t* b_shape,
|
|
||||||
int32_t final_ndims,
|
|
||||||
int32_t* new_a_shape,
|
|
||||||
int32_t* new_b_shape,
|
|
||||||
int32_t* dst_shape) {
|
|
||||||
calculate_shapes(a_ndims, a_shape, b_ndims, b_shape, final_ndims, new_a_shape, new_b_shape, dst_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_matmul_calculate_shapes64(int64_t a_ndims,
|
|
||||||
int64_t* a_shape,
|
|
||||||
int64_t b_ndims,
|
|
||||||
int64_t* b_shape,
|
|
||||||
int64_t final_ndims,
|
|
||||||
int64_t* new_a_shape,
|
|
||||||
int64_t* new_b_shape,
|
|
||||||
int64_t* dst_shape) {
|
|
||||||
calculate_shapes(a_ndims, a_shape, b_ndims, b_shape, final_ndims, new_a_shape, new_b_shape, dst_shape);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/exception.hpp"
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
#include "irrt/ndarray/def.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace ndarray::reshape {
|
|
||||||
/**
|
|
||||||
* @brief Perform assertions on and resolve unknown dimensions in `new_shape` in `np.reshape(<ndarray>, new_shape)`
|
|
||||||
*
|
|
||||||
* If `new_shape` indeed contains unknown dimensions (specified with `-1`, just like numpy), `new_shape` will be
|
|
||||||
* modified to contain the resolved dimension.
|
|
||||||
*
|
|
||||||
* To perform assertions on and resolve unknown dimensions in `new_shape`, we don't need the actual
|
|
||||||
* `<ndarray>` object itself, but only the `.size` of the `<ndarray>`.
|
|
||||||
*
|
|
||||||
* @param size The `.size` of `<ndarray>`
|
|
||||||
* @param new_ndims Number of elements in `new_shape`
|
|
||||||
* @param new_shape Target shape to reshape to
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void resolve_and_check_new_shape(SizeT size, SizeT new_ndims, SizeT* new_shape) {
|
|
||||||
// Is there a -1 in `new_shape`?
|
|
||||||
bool neg1_exists = false;
|
|
||||||
// Location of -1, only initialized if `neg1_exists` is true
|
|
||||||
SizeT neg1_axis_i;
|
|
||||||
// The computed ndarray size of `new_shape`
|
|
||||||
SizeT new_size = 1;
|
|
||||||
|
|
||||||
for (SizeT axis_i = 0; axis_i < new_ndims; axis_i++) {
|
|
||||||
SizeT dim = new_shape[axis_i];
|
|
||||||
if (dim < 0) {
|
|
||||||
if (dim == -1) {
|
|
||||||
if (neg1_exists) {
|
|
||||||
// Multiple `-1` found. Throw an error.
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "can only specify one unknown dimension", NO_PARAM,
|
|
||||||
NO_PARAM, NO_PARAM);
|
|
||||||
} else {
|
|
||||||
neg1_exists = true;
|
|
||||||
neg1_axis_i = axis_i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: What? In `np.reshape` any negative dimensions is
|
|
||||||
// treated like its `-1`.
|
|
||||||
//
|
|
||||||
// Try running `np.zeros((3, 4)).reshape((-999, 2))`
|
|
||||||
//
|
|
||||||
// It is not documented by numpy.
|
|
||||||
// Throw an error for now...
|
|
||||||
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "Found non -1 negative dimension {0} on axis {1}", dim, axis_i,
|
|
||||||
NO_PARAM);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
new_size *= dim;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool can_reshape;
|
|
||||||
if (neg1_exists) {
|
|
||||||
// Let `x` be the unknown dimension
|
|
||||||
// Solve `x * <new_size> = <size>`
|
|
||||||
if (new_size == 0 && size == 0) {
|
|
||||||
// `x` has infinitely many solutions
|
|
||||||
can_reshape = false;
|
|
||||||
} else if (new_size == 0 && size != 0) {
|
|
||||||
// `x` has no solutions
|
|
||||||
can_reshape = false;
|
|
||||||
} else if (size % new_size != 0) {
|
|
||||||
// `x` has no integer solutions
|
|
||||||
can_reshape = false;
|
|
||||||
} else {
|
|
||||||
can_reshape = true;
|
|
||||||
new_shape[neg1_axis_i] = size / new_size; // Resolve dimension
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
can_reshape = (new_size == size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!can_reshape) {
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "cannot reshape array of size {0} into given shape", size, NO_PARAM,
|
|
||||||
NO_PARAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace ndarray::reshape
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void __nac3_ndarray_reshape_resolve_and_check_new_shape(int32_t size, int32_t new_ndims, int32_t* new_shape) {
|
|
||||||
ndarray::reshape::resolve_and_check_new_shape(size, new_ndims, new_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_reshape_resolve_and_check_new_shape64(int64_t size, int64_t new_ndims, int64_t* new_shape) {
|
|
||||||
ndarray::reshape::resolve_and_check_new_shape(size, new_ndims, new_shape);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/debug.hpp"
|
|
||||||
#include "irrt/exception.hpp"
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
#include "irrt/ndarray/def.hpp"
|
|
||||||
#include "irrt/slice.hpp"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Notes on `np.transpose(<array>, <axes>)`
|
|
||||||
*
|
|
||||||
* TODO: `axes`, if specified, can actually contain negative indices,
|
|
||||||
* but it is not documented in numpy.
|
|
||||||
*
|
|
||||||
* Supporting it for now.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace ndarray::transpose {
|
|
||||||
/**
|
|
||||||
* @brief Do assertions on `<axes>` in `np.transpose(<array>, <axes>)`.
|
|
||||||
*
|
|
||||||
* Note that `np.transpose`'s `<axe>` argument is optional. If the argument
|
|
||||||
* is specified but the user, use this function to do assertions on it.
|
|
||||||
*
|
|
||||||
* @param ndims The number of dimensions of `<array>`
|
|
||||||
* @param num_axes Number of elements in `<axes>` as specified by the user.
|
|
||||||
* This should be equal to `ndims`. If not, a "ValueError: axes don't match array" is thrown.
|
|
||||||
* @param axes The user specified `<axes>`.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void assert_transpose_axes(SizeT ndims, SizeT num_axes, const SizeT* axes) {
|
|
||||||
if (ndims != num_axes) {
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "axes don't match array", NO_PARAM, NO_PARAM, NO_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Optimize this
|
|
||||||
bool* axe_specified = (bool*)__builtin_alloca(sizeof(bool) * ndims);
|
|
||||||
for (SizeT i = 0; i < ndims; i++)
|
|
||||||
axe_specified[i] = false;
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < ndims; i++) {
|
|
||||||
SizeT axis = slice::resolve_index_in_length(ndims, axes[i]);
|
|
||||||
if (axis == -1) {
|
|
||||||
// TODO: numpy actually throws a `numpy.exceptions.AxisError`
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "axis {0} is out of bounds for array of dimension {1}", axis, ndims,
|
|
||||||
NO_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (axe_specified[axis]) {
|
|
||||||
raise_exception(SizeT, EXN_VALUE_ERROR, "repeated axis in transpose", NO_PARAM, NO_PARAM, NO_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
axe_specified[axis] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a transpose view of `src_ndarray` and perform proper assertions.
|
|
||||||
*
|
|
||||||
* This function is very similar to doing `dst_ndarray = np.transpose(src_ndarray, <axes>)`.
|
|
||||||
* If `<axes>` is supposed to be `None`, caller can pass in a `nullptr` to `<axes>`.
|
|
||||||
*
|
|
||||||
* The transpose view created is returned by modifying `dst_ndarray`.
|
|
||||||
*
|
|
||||||
* The caller is responsible for setting up `dst_ndarray` before calling this function.
|
|
||||||
* Here is what this function expects from `dst_ndarray` when called:
|
|
||||||
* - `dst_ndarray->data` does not have to be initialized.
|
|
||||||
* - `dst_ndarray->itemsize` does not have to be initialized.
|
|
||||||
* - `dst_ndarray->ndims` must be initialized, must be equal to `src_ndarray->ndims`.
|
|
||||||
* - `dst_ndarray->shape` must be allocated, through it can contain uninitialized values.
|
|
||||||
* - `dst_ndarray->strides` must be allocated, through it can contain uninitialized values.
|
|
||||||
* When this function call ends:
|
|
||||||
* - `dst_ndarray->data` is set to `src_ndarray->data` (`dst_ndarray` is just a view to `src_ndarray`)
|
|
||||||
* - `dst_ndarray->itemsize` is set to `src_ndarray->itemsize`
|
|
||||||
* - `dst_ndarray->ndims` is unchanged
|
|
||||||
* - `dst_ndarray->shape` is updated according to how `np.transpose` works
|
|
||||||
* - `dst_ndarray->strides` is updated according to how `np.transpose` works
|
|
||||||
*
|
|
||||||
* @param src_ndarray The NDArray to build a transpose view on
|
|
||||||
* @param dst_ndarray The resulting NDArray after transpose. Further details in the comments above,
|
|
||||||
* @param num_axes Number of elements in axes. Unused if `axes` is nullptr.
|
|
||||||
* @param axes Axes permutation. Set it to `nullptr` if `<axes>` is `None`.
|
|
||||||
*/
|
|
||||||
template<typename SizeT>
|
|
||||||
void transpose(const NDArray<SizeT>* src_ndarray, NDArray<SizeT>* dst_ndarray, SizeT num_axes, const SizeT* axes) {
|
|
||||||
debug_assert_eq(SizeT, src_ndarray->ndims, dst_ndarray->ndims);
|
|
||||||
const auto ndims = src_ndarray->ndims;
|
|
||||||
|
|
||||||
if (axes != nullptr)
|
|
||||||
assert_transpose_axes(ndims, num_axes, axes);
|
|
||||||
|
|
||||||
dst_ndarray->data = src_ndarray->data;
|
|
||||||
dst_ndarray->itemsize = src_ndarray->itemsize;
|
|
||||||
|
|
||||||
// Check out https://ajcr.net/stride-guide-part-2/ to see how `np.transpose` works behind the scenes.
|
|
||||||
if (axes == nullptr) {
|
|
||||||
// `np.transpose(<array>, axes=None)`
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Minor note: `np.transpose(<array>, axes=None)` is equivalent to
|
|
||||||
* `np.transpose(<array>, axes=[N-1, N-2, ..., 0])` - basically it
|
|
||||||
* is reversing the order of strides and shape.
|
|
||||||
*
|
|
||||||
* This is a fast implementation to handle this special (but very common) case.
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (SizeT axis = 0; axis < ndims; axis++) {
|
|
||||||
dst_ndarray->shape[axis] = src_ndarray->shape[ndims - axis - 1];
|
|
||||||
dst_ndarray->strides[axis] = src_ndarray->strides[ndims - axis - 1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// `np.transpose(<array>, <axes>)`
|
|
||||||
|
|
||||||
// Permute strides and shape according to `axes`, while resolving negative indices in `axes`
|
|
||||||
for (SizeT axis = 0; axis < ndims; axis++) {
|
|
||||||
// `i` cannot be OUT_OF_BOUNDS because of assertions
|
|
||||||
SizeT i = slice::resolve_index_in_length(ndims, axes[axis]);
|
|
||||||
|
|
||||||
dst_ndarray->shape[axis] = src_ndarray->shape[i];
|
|
||||||
dst_ndarray->strides[axis] = src_ndarray->strides[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace ndarray::transpose
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
using namespace ndarray::transpose;
|
|
||||||
void __nac3_ndarray_transpose(const NDArray<int32_t>* src_ndarray,
|
|
||||||
NDArray<int32_t>* dst_ndarray,
|
|
||||||
int32_t num_axes,
|
|
||||||
const int32_t* axes) {
|
|
||||||
transpose(src_ndarray, dst_ndarray, num_axes, axes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __nac3_ndarray_transpose64(const NDArray<int64_t>* src_ndarray,
|
|
||||||
NDArray<int64_t>* dst_ndarray,
|
|
||||||
int64_t num_axes,
|
|
||||||
const int64_t* axes) {
|
|
||||||
transpose(src_ndarray, dst_ndarray, num_axes, axes);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "irrt/int_types.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename SizeT>
|
|
||||||
bool __nac3_str_eq_impl(const char* str1, SizeT len1, const char* str2, SizeT len2) {
|
|
||||||
if (len1 != len2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return __builtin_memcmp(str1, str2, static_cast<SizeT>(len1)) == 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
bool nac3_str_eq(const char* str1, uint32_t len1, const char* str2, uint32_t len2) {
|
|
||||||
return __nac3_str_eq_impl<uint32_t>(str1, len1, str2, len2);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nac3_str_eq64(const char* str1, uint64_t len1, const char* str2, uint64_t len2) {
|
|
||||||
return __nac3_str_eq_impl<uint64_t>(str1, len1, str2, len2);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ use inkwell::{
|
|||||||
values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue, StructValue},
|
values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue, StructValue},
|
||||||
AddressSpace, IntPredicate, OptimizationLevel,
|
AddressSpace, IntPredicate, OptimizationLevel,
|
||||||
};
|
};
|
||||||
use itertools::{izip, Either, Itertools};
|
use itertools::{chain, izip, Either, Itertools};
|
||||||
|
|
||||||
use nac3parser::ast::{
|
use nac3parser::ast::{
|
||||||
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
||||||
@ -24,26 +24,26 @@ use super::{
|
|||||||
irrt::*,
|
irrt::*,
|
||||||
llvm_intrinsics::{
|
llvm_intrinsics::{
|
||||||
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
|
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
|
||||||
call_memcpy_generic,
|
call_int_umin, call_memcpy_generic,
|
||||||
},
|
},
|
||||||
macros::codegen_unreachable,
|
macros::codegen_unreachable,
|
||||||
need_sret,
|
need_sret, numpy,
|
||||||
stmt::{
|
stmt::{
|
||||||
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::{ndarray::NDArrayType, ListType},
|
types::{ndarray::NDArrayType, ListType},
|
||||||
values::{
|
values::{
|
||||||
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
ndarray::{NDArrayValue, RustNDIndex},
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
||||||
UntypedArrayLikeAccessor,
|
TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
|
||||||
},
|
},
|
||||||
CodeGenContext, CodeGenTask, CodeGenerator,
|
CodeGenContext, CodeGenTask, CodeGenerator,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
symbol_resolver::{SymbolValue, ValueEnum},
|
symbol_resolver::{SymbolValue, ValueEnum},
|
||||||
toplevel::{
|
toplevel::{
|
||||||
helper::{arraylike_flatten_element_type, PrimDef},
|
helper::{extract_ndims, PrimDef},
|
||||||
numpy::unpack_ndarray_var_tys,
|
numpy::unpack_ndarray_var_tys,
|
||||||
DefinitionId, TopLevelDef,
|
DefinitionId, TopLevelDef,
|
||||||
},
|
},
|
||||||
@ -79,7 +79,7 @@ pub fn get_subst_key(
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> CodeGenContext<'ctx, '_> {
|
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
/// Builds a sequence of `getelementptr` and `load` instructions which stores the value of a
|
/// Builds a sequence of `getelementptr` and `load` instructions which stores the value of a
|
||||||
/// struct field into an LLVM value.
|
/// struct field into an LLVM value.
|
||||||
pub fn build_gep_and_load(
|
pub fn build_gep_and_load(
|
||||||
@ -1095,6 +1095,33 @@ pub fn destructure_range<'ctx>(
|
|||||||
(start, end, step)
|
(start, end, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
|
||||||
|
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
|
||||||
|
///
|
||||||
|
/// Setting `ty` to [`None`] implies that the list is empty **and** does not have a known element
|
||||||
|
/// type, and will therefore set the `list.data` type as `size_t*`. It is undefined behavior to
|
||||||
|
/// generate a sized list with an unknown element type.
|
||||||
|
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
ty: Option<BasicTypeEnum<'ctx>>,
|
||||||
|
length: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ListValue<'ctx> {
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
let llvm_elem_ty = ty.unwrap_or(llvm_usize.into());
|
||||||
|
|
||||||
|
// List structure; type { ty*, size_t }
|
||||||
|
let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty);
|
||||||
|
let list = arr_ty.alloca(generator, ctx, name);
|
||||||
|
|
||||||
|
let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap();
|
||||||
|
list.store_size(ctx, generator, length);
|
||||||
|
list.create_data(ctx, llvm_elem_ty, None);
|
||||||
|
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates LLVM IR for a [list comprehension expression][expr].
|
/// Generates LLVM IR for a [list comprehension expression][expr].
|
||||||
pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
@ -1167,11 +1194,12 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
|||||||
"listcomp.alloc_size",
|
"listcomp.alloc_size",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
list = ListType::new(generator, ctx.ctx, elem_ty).construct(
|
list = allocate_list(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
|
Some(elem_ty),
|
||||||
list_alloc_size.into_int_value(),
|
list_alloc_size.into_int_value(),
|
||||||
Some("listcomp"),
|
Some("listcomp.addr"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
||||||
@ -1218,12 +1246,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
|||||||
Some("length"),
|
Some("length"),
|
||||||
)
|
)
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
list = ListType::new(generator, ctx.ctx, elem_ty).construct(
|
list = allocate_list(generator, ctx, Some(elem_ty), length, Some("listcomp"));
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
length,
|
|
||||||
Some("listcomp"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
||||||
// counter = -1
|
// counter = -1
|
||||||
@ -1388,8 +1411,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
.build_int_add(lhs.load_size(ctx, None), rhs.load_size(ctx, None), "")
|
.build_int_add(lhs.load_size(ctx, None), rhs.load_size(ctx, None), "")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_list = ListType::new(generator, ctx.ctx, llvm_elem_ty)
|
let new_list = allocate_list(generator, ctx, Some(llvm_elem_ty), size, None);
|
||||||
.construct(generator, ctx, size, None);
|
|
||||||
|
|
||||||
let lhs_size = ctx
|
let lhs_size = ctx
|
||||||
.builder
|
.builder
|
||||||
@ -1476,9 +1498,10 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
|
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
|
||||||
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
|
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
|
||||||
|
|
||||||
let new_list = ListType::new(generator, ctx.ctx, elem_llvm_ty).construct(
|
let new_list = allocate_list(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
|
Some(elem_llvm_ty),
|
||||||
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
|
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
@ -1531,77 +1554,98 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||||
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||||
{
|
{
|
||||||
let left = ScalarOrNDArray::from_value(generator, ctx, (ty1, left_val));
|
let is_ndarray1 = ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||||
let right = ScalarOrNDArray::from_value(generator, ctx, (ty2, right_val));
|
let is_ndarray2 = ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||||
|
|
||||||
let ty1_dtype = arraylike_flatten_element_type(&mut ctx.unifier, ty1);
|
if is_ndarray1 && is_ndarray2 {
|
||||||
let ty2_dtype = arraylike_flatten_element_type(&mut ctx.unifier, ty2);
|
let (ndarray_dtype1, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty1);
|
||||||
|
let (ndarray_dtype2, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty2);
|
||||||
|
|
||||||
// Inhomogeneous binary operations are not supported.
|
assert!(ctx.unifier.unioned(ndarray_dtype1, ndarray_dtype2));
|
||||||
assert!(ctx.unifier.unioned(ty1_dtype, ty2_dtype));
|
|
||||||
|
|
||||||
let common_dtype = ty1_dtype;
|
let left_val = NDArrayType::from_unifier_type(generator, ctx, ty1)
|
||||||
let llvm_common_dtype = left.get_dtype();
|
.map_value(left_val.into_pointer_value(), None);
|
||||||
|
let right_val = NDArrayType::from_unifier_type(generator, ctx, ty2)
|
||||||
|
.map_value(right_val.into_pointer_value(), None);
|
||||||
|
|
||||||
let out = match op.variant {
|
let res = if op.base == Operator::MatMult {
|
||||||
BinopVariant::Normal => NDArrayOut::NewNDArray { dtype: llvm_common_dtype },
|
// MatMult is the only binop which is not an elementwise op
|
||||||
BinopVariant::AugAssign => {
|
numpy::ndarray_matmul_2d(
|
||||||
// Augmented assignment - `left` has to be an ndarray. If it were a scalar then NAC3
|
|
||||||
// simply doesn't support it.
|
|
||||||
if let ScalarOrNDArray::NDArray(out_ndarray) = left {
|
|
||||||
NDArrayOut::WriteToNDArray { ndarray: out_ndarray }
|
|
||||||
} else {
|
|
||||||
panic!("left must be an ndarray")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if op.base == Operator::MatMult {
|
|
||||||
let left = left.to_ndarray(generator, ctx);
|
|
||||||
let right = right.to_ndarray(generator, ctx);
|
|
||||||
let result = left
|
|
||||||
.matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out))
|
|
||||||
.split_unsized(generator, ctx);
|
|
||||||
Ok(Some(result.to_basic_value_enum().into()))
|
|
||||||
} else {
|
|
||||||
// For other operations, they are all elementwise operations.
|
|
||||||
|
|
||||||
// There are only three cases:
|
|
||||||
// - LHS is a scalar, RHS is an ndarray.
|
|
||||||
// - LHS is an ndarray, RHS is a scalar.
|
|
||||||
// - LHS is an ndarray, RHS is an ndarray.
|
|
||||||
//
|
|
||||||
// For all cases, the scalar operand is promoted to an ndarray,
|
|
||||||
// the two are then broadcasted, and starmapped through.
|
|
||||||
|
|
||||||
let left = left.to_ndarray(generator, ctx);
|
|
||||||
let right = right.to_ndarray(generator, ctx);
|
|
||||||
|
|
||||||
let result = NDArrayType::new_broadcast(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
llvm_common_dtype,
|
|
||||||
&[left.get_type(), right.get_type()],
|
|
||||||
)
|
|
||||||
.broadcast_starmap(generator, ctx, &[left, right], out, |generator, ctx, scalars| {
|
|
||||||
let left_value = scalars[0];
|
|
||||||
let right_value = scalars[1];
|
|
||||||
|
|
||||||
let result = gen_binop_expr_with_values(
|
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
(&Some(ty1_dtype), left_value),
|
ndarray_dtype1,
|
||||||
op,
|
match op.variant {
|
||||||
(&Some(ty2_dtype), right_value),
|
BinopVariant::Normal => None,
|
||||||
ctx.current_loc,
|
BinopVariant::AugAssign => Some(left_val),
|
||||||
|
},
|
||||||
|
left_val,
|
||||||
|
right_val,
|
||||||
)?
|
)?
|
||||||
.unwrap()
|
} else {
|
||||||
.to_basic_value_enum(ctx, generator, common_dtype)?;
|
numpy::ndarray_elementwise_binop_impl(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
ndarray_dtype1,
|
||||||
|
match op.variant {
|
||||||
|
BinopVariant::Normal => None,
|
||||||
|
BinopVariant::AugAssign => Some(left_val),
|
||||||
|
},
|
||||||
|
(ty1, left_val.as_base_value().into(), false),
|
||||||
|
(ty2, right_val.as_base_value().into(), false),
|
||||||
|
|generator, ctx, (lhs, rhs)| {
|
||||||
|
gen_binop_expr_with_values(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
(&Some(ndarray_dtype1), lhs),
|
||||||
|
op,
|
||||||
|
(&Some(ndarray_dtype2), rhs),
|
||||||
|
ctx.current_loc,
|
||||||
|
)?
|
||||||
|
.unwrap()
|
||||||
|
.to_basic_value_enum(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
ndarray_dtype1,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(Some(res.as_base_value().into()))
|
||||||
})
|
} else {
|
||||||
.unwrap();
|
let (ndarray_dtype, _) =
|
||||||
Ok(Some(result.as_base_value().into()))
|
unpack_ndarray_var_tys(&mut ctx.unifier, if is_ndarray1 { ty1 } else { ty2 });
|
||||||
|
let ndarray_val =
|
||||||
|
NDArrayType::from_unifier_type(generator, ctx, if is_ndarray1 { ty1 } else { ty2 })
|
||||||
|
.map_value(
|
||||||
|
if is_ndarray1 { left_val } else { right_val }.into_pointer_value(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let res = numpy::ndarray_elementwise_binop_impl(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
ndarray_dtype,
|
||||||
|
match op.variant {
|
||||||
|
BinopVariant::Normal => None,
|
||||||
|
BinopVariant::AugAssign => Some(ndarray_val),
|
||||||
|
},
|
||||||
|
(ty1, left_val, !is_ndarray1),
|
||||||
|
(ty2, right_val, !is_ndarray2),
|
||||||
|
|generator, ctx, (lhs, rhs)| {
|
||||||
|
gen_binop_expr_with_values(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
(&Some(ndarray_dtype), lhs),
|
||||||
|
op,
|
||||||
|
(&Some(ndarray_dtype), rhs),
|
||||||
|
ctx.current_loc,
|
||||||
|
)?
|
||||||
|
.unwrap()
|
||||||
|
.to_basic_value_enum(ctx, generator, ndarray_dtype)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Some(res.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());
|
||||||
@ -1764,10 +1808,10 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
_ => val.into(),
|
_ => val.into(),
|
||||||
}
|
}
|
||||||
} else if ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) {
|
} else if ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) {
|
||||||
|
let llvm_ndarray_ty = NDArrayType::from_unifier_type(generator, ctx, ty);
|
||||||
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 val = llvm_ndarray_ty.map_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
|
||||||
@ -1785,18 +1829,20 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
op
|
op
|
||||||
};
|
};
|
||||||
|
|
||||||
let mapped_ndarray = ndarray.map(
|
let res = numpy::ndarray_elementwise_unaryop_impl(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() },
|
ndarray_dtype,
|
||||||
|generator, ctx, scalar| {
|
None,
|
||||||
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))?
|
val,
|
||||||
.map(|val| val.to_basic_value_enum(ctx, generator, ndarray_dtype))
|
|generator, ctx, val| {
|
||||||
|
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), val))?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.to_basic_value_enum(ctx, generator, ndarray_dtype)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
mapped_ndarray.as_base_value().into()
|
res.as_base_value().into()
|
||||||
} else {
|
} else {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}))
|
}))
|
||||||
@ -1839,56 +1885,87 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
|||||||
if left_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
if left_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||||
|| right_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
|| right_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||||
{
|
{
|
||||||
let (Some(left_ty), left) = left else { codegen_unreachable!(ctx) };
|
let (Some(left_ty), lhs) = left else { codegen_unreachable!(ctx) };
|
||||||
let (Some(right_ty), right) = comparators[0] else { codegen_unreachable!(ctx) };
|
let (Some(right_ty), rhs) = comparators[0] else { codegen_unreachable!(ctx) };
|
||||||
let op = ops[0];
|
let op = ops[0];
|
||||||
|
|
||||||
let left_ty_dtype = arraylike_flatten_element_type(&mut ctx.unifier, left_ty);
|
let is_ndarray1 =
|
||||||
let right_ty_dtype = arraylike_flatten_element_type(&mut ctx.unifier, right_ty);
|
left_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||||
|
let is_ndarray2 =
|
||||||
|
right_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||||
|
|
||||||
let left = ScalarOrNDArray::from_value(generator, ctx, (left_ty, left))
|
return if is_ndarray1 && is_ndarray2 {
|
||||||
.to_ndarray(generator, ctx);
|
let (ndarray_dtype1, _) = unpack_ndarray_var_tys(&mut ctx.unifier, left_ty);
|
||||||
let right = ScalarOrNDArray::from_value(generator, ctx, (right_ty, right))
|
let (ndarray_dtype2, _) = unpack_ndarray_var_tys(&mut ctx.unifier, right_ty);
|
||||||
.to_ndarray(generator, ctx);
|
|
||||||
|
|
||||||
let result_ndarray = NDArrayType::new_broadcast(
|
assert!(ctx.unifier.unioned(ndarray_dtype1, ndarray_dtype2));
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
ctx.ctx.i8_type().into(),
|
|
||||||
&[left.get_type(), right.get_type()],
|
|
||||||
)
|
|
||||||
.broadcast_starmap(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&[left, right],
|
|
||||||
NDArrayOut::NewNDArray { dtype: ctx.ctx.i8_type().into() },
|
|
||||||
|generator, ctx, scalars| {
|
|
||||||
let left_scalar = scalars[0];
|
|
||||||
let right_scalar = scalars[1];
|
|
||||||
|
|
||||||
let val = gen_cmpop_expr_with_values(
|
let left_val = NDArrayType::from_unifier_type(generator, ctx, left_ty)
|
||||||
generator,
|
.map_value(lhs.into_pointer_value(), None);
|
||||||
ctx,
|
let res = numpy::ndarray_elementwise_binop_impl(
|
||||||
(Some(left_ty_dtype), left_scalar),
|
generator,
|
||||||
&[op],
|
ctx,
|
||||||
&[(Some(right_ty_dtype), right_scalar)],
|
ctx.primitives.bool,
|
||||||
)?
|
None,
|
||||||
.unwrap()
|
(left_ty, left_val.as_base_value().into(), false),
|
||||||
.to_basic_value_enum(
|
(right_ty, rhs, false),
|
||||||
ctx,
|
|generator, ctx, (lhs, rhs)| {
|
||||||
generator,
|
let val = gen_cmpop_expr_with_values(
|
||||||
ctx.primitives.bool,
|
generator,
|
||||||
)?;
|
ctx,
|
||||||
|
(Some(ndarray_dtype1), lhs),
|
||||||
|
&[op],
|
||||||
|
&[(Some(ndarray_dtype2), rhs)],
|
||||||
|
)?
|
||||||
|
.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(Some(result_ndarray.as_base_value().into()));
|
Ok(Some(res.as_base_value().into()))
|
||||||
|
} else {
|
||||||
|
let (ndarray_dtype, _) = unpack_ndarray_var_tys(
|
||||||
|
&mut ctx.unifier,
|
||||||
|
if is_ndarray1 { left_ty } else { right_ty },
|
||||||
|
);
|
||||||
|
let res = numpy::ndarray_elementwise_binop_impl(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
ctx.primitives.bool,
|
||||||
|
None,
|
||||||
|
(left_ty, lhs, !is_ndarray1),
|
||||||
|
(right_ty, rhs, !is_ndarray2),
|
||||||
|
|generator, ctx, (lhs, rhs)| {
|
||||||
|
let val = gen_cmpop_expr_with_values(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
(Some(ndarray_dtype), lhs),
|
||||||
|
&[op],
|
||||||
|
&[(Some(ndarray_dtype), rhs)],
|
||||||
|
)?
|
||||||
|
.unwrap()
|
||||||
|
.to_basic_value_enum(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
ctx.primitives.bool,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(generator.bool_to_i8(ctx, val.into_int_value()).into())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Some(res.as_base_value().into()))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cmp_val = izip!(once(&left).chain(comparators.iter()), comparators.iter(), ops.iter(),)
|
let cmp_val = izip!(chain(once(&left), comparators.iter()), comparators.iter(), ops.iter(),)
|
||||||
.fold(Ok(None), |prev: Result<Option<_>, String>, (lhs, rhs, op)| {
|
.fold(Ok(None), |prev: Result<Option<_>, String>, (lhs, rhs, op)| {
|
||||||
let (left_ty, lhs) = lhs;
|
let (left_ty, lhs) = lhs;
|
||||||
let (right_ty, rhs) = rhs;
|
let (right_ty, rhs) = rhs;
|
||||||
@ -1968,43 +2045,111 @@ 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 lhs = lhs.into_struct_value();
|
let llvm_i1 = ctx.ctx.bool_type();
|
||||||
let rhs = rhs.into_struct_value();
|
|
||||||
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
|
||||||
|
let lhs = lhs.into_struct_value();
|
||||||
|
let rhs = rhs.into_struct_value();
|
||||||
|
|
||||||
let plhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
let plhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
||||||
ctx.builder.build_store(plhs, lhs).unwrap();
|
ctx.builder.build_store(plhs, lhs).unwrap();
|
||||||
let prhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
let prhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
||||||
ctx.builder.build_store(prhs, rhs).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(
|
let lhs_len = ctx.build_in_bounds_gep_and_load(
|
||||||
plhs,
|
plhs,
|
||||||
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
|
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
|
||||||
|
None,
|
||||||
|
).into_int_value();
|
||||||
|
let rhs_len = ctx.build_in_bounds_gep_and_load(
|
||||||
|
prhs,
|
||||||
|
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
|
||||||
None,
|
None,
|
||||||
).into_int_value();
|
).into_int_value();
|
||||||
|
|
||||||
let rhs_ptr = ctx.build_in_bounds_gep_and_load(
|
let len = call_int_umin(ctx, lhs_len, rhs_len, None);
|
||||||
prhs,
|
|
||||||
&[llvm_usize.const_zero(), llvm_i32.const_zero()],
|
let current_bb = ctx.builder.get_insert_block().unwrap();
|
||||||
|
let post_foreach_cmp = ctx.ctx.insert_basic_block_after(current_bb, "foreach.cmp.end");
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(post_foreach_cmp);
|
||||||
|
let cmp_phi = ctx.builder.build_phi(llvm_i1, "").unwrap();
|
||||||
|
ctx.builder.position_at_end(current_bb);
|
||||||
|
|
||||||
|
gen_for_callback_incrementing(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
None,
|
None,
|
||||||
).into_pointer_value();
|
llvm_usize.const_zero(),
|
||||||
let rhs_len = ctx.build_in_bounds_gep_and_load(
|
(len, false),
|
||||||
prhs,
|
|generator, ctx, _, i| {
|
||||||
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
|
let lhs_char = {
|
||||||
None,
|
let plhs_data = ctx.build_in_bounds_gep_and_load(
|
||||||
).into_int_value();
|
plhs,
|
||||||
let result = call_string_eq(generator, ctx, lhs_ptr, lhs_len, rhs_ptr, rhs_len);
|
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
|
||||||
|
None,
|
||||||
|
).into_pointer_value();
|
||||||
|
|
||||||
|
ctx.build_in_bounds_gep_and_load(
|
||||||
|
plhs_data,
|
||||||
|
&[i],
|
||||||
|
None
|
||||||
|
).into_int_value()
|
||||||
|
};
|
||||||
|
let rhs_char = {
|
||||||
|
let prhs_data = ctx.build_in_bounds_gep_and_load(
|
||||||
|
prhs,
|
||||||
|
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
|
||||||
|
None,
|
||||||
|
).into_pointer_value();
|
||||||
|
|
||||||
|
ctx.build_in_bounds_gep_and_load(
|
||||||
|
prhs_data,
|
||||||
|
&[i],
|
||||||
|
None
|
||||||
|
).into_int_value()
|
||||||
|
};
|
||||||
|
|
||||||
|
gen_if_callback(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
|_, ctx| {
|
||||||
|
Ok(ctx.builder.build_int_compare(IntPredicate::NE, lhs_char, rhs_char, "").unwrap())
|
||||||
|
},
|
||||||
|
|_, ctx| {
|
||||||
|
let bb = ctx.builder.get_insert_block().unwrap();
|
||||||
|
cmp_phi.add_incoming(&[(&llvm_i1.const_zero(), bb)]);
|
||||||
|
ctx.builder.build_unconditional_branch(post_foreach_cmp).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|_, _| Ok(()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
llvm_usize.const_int(1, false),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let bb = ctx.builder.get_insert_block().unwrap();
|
||||||
|
let is_len_eq = ctx.builder.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
lhs_len,
|
||||||
|
rhs_len,
|
||||||
|
"",
|
||||||
|
).unwrap();
|
||||||
|
cmp_phi.add_incoming(&[(&is_len_eq, bb)]);
|
||||||
|
ctx.builder.build_unconditional_branch(post_foreach_cmp).unwrap();
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(post_foreach_cmp);
|
||||||
|
let cmp_phi = cmp_phi.as_basic_value().into_int_value();
|
||||||
|
|
||||||
|
// Invert the final value if __ne__
|
||||||
if *op == Cmpop::NotEq {
|
if *op == Cmpop::NotEq {
|
||||||
ctx.builder.build_not(result, "").unwrap()
|
ctx.builder.build_not(cmp_phi, "").unwrap()
|
||||||
} else {
|
} else {
|
||||||
result
|
cmp_phi
|
||||||
}
|
}
|
||||||
} else if [left_ty, right_ty]
|
} else if [left_ty, right_ty]
|
||||||
.iter()
|
.iter()
|
||||||
@ -2367,6 +2512,319 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates code for a subscript expression on an `ndarray`.
|
||||||
|
///
|
||||||
|
/// * `ty` - The `Type` of the `NDArray` elements.
|
||||||
|
/// * `ndims` - The `Type` of the `NDArray` number-of-dimensions `Literal`.
|
||||||
|
/// * `v` - The `NDArray` value.
|
||||||
|
/// * `slice` - The slice expression used to subscript into the `ndarray`.
|
||||||
|
fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
ty: Type,
|
||||||
|
ndims_ty: Type,
|
||||||
|
v: NDArrayValue<'ctx>,
|
||||||
|
slice: &Expr<Option<Type>>,
|
||||||
|
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||||
|
let llvm_i1 = ctx.ctx.bool_type();
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
|
||||||
|
let TypeEnum::TLiteral { values, .. } = &*ctx.unifier.get_ty_immutable(ndims_ty) else {
|
||||||
|
codegen_unreachable!(ctx)
|
||||||
|
};
|
||||||
|
|
||||||
|
let ndims = values
|
||||||
|
.iter()
|
||||||
|
.map(|ndim| u64::try_from(ndim.clone()).map_err(|()| ndim.clone()))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.map_err(|val| {
|
||||||
|
format!(
|
||||||
|
"Expected non-negative literal for ndarray.ndims, got {}",
|
||||||
|
i128::try_from(val).unwrap()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
assert!(!ndims.is_empty());
|
||||||
|
|
||||||
|
// The number of dimensions subscripted by the index expression.
|
||||||
|
// Slicing a ndarray will yield the same number of dimensions, whereas indexing into a
|
||||||
|
// dimension will remove a dimension.
|
||||||
|
let subscripted_dims = match &slice.node {
|
||||||
|
ExprKind::Tuple { elts, .. } => elts.iter().fold(0, |acc, value_subexpr| {
|
||||||
|
if let ExprKind::Slice { .. } = &value_subexpr.node {
|
||||||
|
acc
|
||||||
|
} else {
|
||||||
|
acc + 1
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExprKind::Slice { .. } => 0,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let llvm_ndarray_data_t = ctx.get_llvm_type(generator, ty).as_basic_type_enum();
|
||||||
|
let sizeof_elem = llvm_ndarray_data_t.size_of().unwrap();
|
||||||
|
|
||||||
|
// Check that len is non-zero
|
||||||
|
let len = v.load_ndims(ctx);
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
ctx.builder.build_int_compare(IntPredicate::SGT, len, llvm_usize.const_zero(), "").unwrap(),
|
||||||
|
"0:IndexError",
|
||||||
|
"too many indices for array: array is {0}-dimensional but 1 were indexed",
|
||||||
|
[Some(len), None, None],
|
||||||
|
slice.location,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Normalizes a possibly-negative index to its corresponding positive index
|
||||||
|
let normalize_index = |generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
index: IntValue<'ctx>,
|
||||||
|
dim: u64| {
|
||||||
|
gen_if_else_expr_callback(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
|_, ctx| {
|
||||||
|
Ok(ctx
|
||||||
|
.builder
|
||||||
|
.build_int_compare(IntPredicate::SGE, index, index.get_type().const_zero(), "")
|
||||||
|
.unwrap())
|
||||||
|
},
|
||||||
|
|_, _| Ok(Some(index)),
|
||||||
|
|generator, ctx| {
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
|
||||||
|
let len = unsafe {
|
||||||
|
v.shape().get_typed_unchecked(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
&llvm_usize.const_int(dim, true),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_add(
|
||||||
|
len,
|
||||||
|
ctx.builder.build_int_s_extend(index, llvm_usize, "").unwrap(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(Some(ctx.builder.build_int_truncate(index, llvm_i32, "").unwrap()))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map(|v| v.map(BasicValueEnum::into_int_value))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts a slice expression into a slice-range tuple
|
||||||
|
let expr_to_slice = |generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
node: &ExprKind<Option<Type>>,
|
||||||
|
dim: u64| {
|
||||||
|
match node {
|
||||||
|
ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||||
|
let Some(index) =
|
||||||
|
normalize_index(generator, ctx, llvm_i32.const_int(*v as u64, true), dim)?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some((index, index, llvm_i32.const_int(1, true))))
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Slice { lower, upper, step } => {
|
||||||
|
let dim_sz = unsafe {
|
||||||
|
v.shape().get_typed_unchecked(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
&llvm_usize.const_int(dim, false),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_slice_indices(lower, upper, step, ctx, generator, dim_sz)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let Some(index) = generator.gen_expr(ctx, slice)? else { return Ok(None) };
|
||||||
|
let index = index
|
||||||
|
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
||||||
|
.into_int_value();
|
||||||
|
let Some(index) = normalize_index(generator, ctx, index, dim)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some((index, index, llvm_i32.const_int(1, true))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let make_indices_arr = |generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>|
|
||||||
|
-> Result<_, String> {
|
||||||
|
Ok(if let ExprKind::Tuple { elts, .. } = &slice.node {
|
||||||
|
let llvm_int_ty = ctx.get_llvm_type(generator, elts[0].custom.unwrap());
|
||||||
|
let index_addr = generator.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
llvm_int_ty,
|
||||||
|
llvm_usize.const_int(elts.len() as u64, false),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for (i, elt) in elts.iter().enumerate() {
|
||||||
|
let Some(index) = generator.gen_expr(ctx, elt)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = index
|
||||||
|
.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?
|
||||||
|
.into_int_value();
|
||||||
|
let Some(index) = normalize_index(generator, ctx, index, 0)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let store_ptr = unsafe {
|
||||||
|
index_addr.ptr_offset_unchecked(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
&llvm_usize.const_int(i as u64, false),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
ctx.builder.build_store(store_ptr, index).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(index_addr)
|
||||||
|
} else if let Some(index) = generator.gen_expr(ctx, slice)? {
|
||||||
|
let llvm_int_ty = ctx.get_llvm_type(generator, slice.custom.unwrap());
|
||||||
|
let index_addr = generator.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
llvm_int_ty,
|
||||||
|
llvm_usize.const_int(1u64, false),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let index =
|
||||||
|
index.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?.into_int_value();
|
||||||
|
let Some(index) = normalize_index(generator, ctx, index, 0)? else { return Ok(None) };
|
||||||
|
|
||||||
|
let store_ptr = unsafe {
|
||||||
|
index_addr.ptr_offset_unchecked(ctx, generator, &llvm_usize.const_zero(), None)
|
||||||
|
};
|
||||||
|
ctx.builder.build_store(store_ptr, index).unwrap();
|
||||||
|
|
||||||
|
Some(index_addr)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(if ndims.len() == 1 && ndims[0] - subscripted_dims == 0 {
|
||||||
|
let Some(index_addr) = make_indices_arr(generator, ctx)? else { return Ok(None) };
|
||||||
|
|
||||||
|
v.data().get(ctx, generator, &index_addr, None).into()
|
||||||
|
} else {
|
||||||
|
match &slice.node {
|
||||||
|
ExprKind::Tuple { elts, .. } => {
|
||||||
|
let slices = elts
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(dim, elt)| expr_to_slice(generator, ctx, &elt.node, dim as u64))
|
||||||
|
.take_while_inclusive(|slice| slice.as_ref().is_ok_and(Option::is_some))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
if slices.len() < elts.len() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slices = slices.into_iter().map(Option::unwrap).collect_vec();
|
||||||
|
|
||||||
|
numpy::ndarray_sliced_copy(generator, ctx, ty, v, &slices)?.as_base_value().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Slice { .. } => {
|
||||||
|
let Some(slice) = expr_to_slice(generator, ctx, &slice.node, 0)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
numpy::ndarray_sliced_copy(generator, ctx, ty, v, &[slice])?.as_base_value().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
// Accessing an element from a multi-dimensional `ndarray`
|
||||||
|
let Some(index_addr) = make_indices_arr(generator, ctx)? else { return Ok(None) };
|
||||||
|
|
||||||
|
let num_dims = extract_ndims(&ctx.unifier, ndims_ty) - 1;
|
||||||
|
|
||||||
|
// Create a new array, remove the top dimension from the dimension-size-list, and copy the
|
||||||
|
// elements over
|
||||||
|
let ndarray =
|
||||||
|
NDArrayType::new(generator, ctx.ctx, llvm_ndarray_data_t, Some(num_dims))
|
||||||
|
.construct_uninitialized(generator, ctx, None);
|
||||||
|
|
||||||
|
let ndarray_num_dims = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_z_extend_or_bit_cast(
|
||||||
|
ndarray.load_ndims(ctx),
|
||||||
|
llvm_usize.size_of().get_type(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let v_dims_src_ptr = unsafe {
|
||||||
|
v.shape().ptr_offset_unchecked(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
&llvm_usize.const_int(1, false),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
call_memcpy_generic(
|
||||||
|
ctx,
|
||||||
|
ndarray.shape().base_ptr(ctx, generator),
|
||||||
|
v_dims_src_ptr,
|
||||||
|
ctx.builder
|
||||||
|
.build_int_mul(ndarray_num_dims, llvm_usize.size_of(), "")
|
||||||
|
.map(Into::into)
|
||||||
|
.unwrap(),
|
||||||
|
llvm_i1.const_zero(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ndarray_num_elems = ndarray::call_ndarray_calc_size(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
&ndarray.shape().as_slice_value(ctx, generator),
|
||||||
|
(None, None),
|
||||||
|
);
|
||||||
|
let ndarray_num_elems = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_z_extend_or_bit_cast(ndarray_num_elems, sizeof_elem.get_type(), "")
|
||||||
|
.unwrap();
|
||||||
|
unsafe { ndarray.create_data(generator, ctx) };
|
||||||
|
|
||||||
|
let v_data_src_ptr = v.data().ptr_offset(ctx, generator, &index_addr, None);
|
||||||
|
call_memcpy_generic(
|
||||||
|
ctx,
|
||||||
|
ndarray.data().base_ptr(ctx, generator),
|
||||||
|
v_data_src_ptr,
|
||||||
|
ctx.builder
|
||||||
|
.build_int_mul(
|
||||||
|
ndarray_num_elems,
|
||||||
|
llvm_ndarray_data_t.size_of().unwrap(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.map(Into::into)
|
||||||
|
.unwrap(),
|
||||||
|
llvm_i1.const_zero(),
|
||||||
|
);
|
||||||
|
|
||||||
|
ndarray.as_base_value().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// See [`CodeGenerator::gen_expr`].
|
/// See [`CodeGenerator::gen_expr`].
|
||||||
pub fn gen_expr<'ctx, G: CodeGenerator>(
|
pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
@ -2481,20 +2939,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
|||||||
Some(elements[0].get_type())
|
Some(elements[0].get_type())
|
||||||
};
|
};
|
||||||
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
|
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
|
||||||
let arr_str_ptr = if let Some(ty) = ty {
|
let arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list"));
|
||||||
ListType::new(generator, ctx.ctx, ty).construct(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
length,
|
|
||||||
Some("list"),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
ListType::new_untyped(generator, ctx.ctx).construct_empty(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
Some("list"),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let arr_ptr = arr_str_ptr.data();
|
let arr_ptr = arr_str_ptr.data();
|
||||||
for (i, v) in elements.iter().enumerate() {
|
for (i, v) in elements.iter().enumerate() {
|
||||||
let elem_ptr = arr_ptr.ptr_offset(
|
let elem_ptr = arr_ptr.ptr_offset(
|
||||||
@ -2972,12 +3417,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
step,
|
step,
|
||||||
);
|
);
|
||||||
let res_array_ret = ListType::new(generator, ctx.ctx, ty).construct(
|
let res_array_ret =
|
||||||
generator,
|
allocate_list(generator, ctx, Some(ty), length, Some("ret"));
|
||||||
ctx,
|
|
||||||
length,
|
|
||||||
Some("ret"),
|
|
||||||
);
|
|
||||||
let Some(res_ind) = handle_slice_indices(
|
let Some(res_ind) = handle_slice_indices(
|
||||||
&None,
|
&None,
|
||||||
&None,
|
&None,
|
||||||
|
@ -17,7 +17,6 @@ pub trait CodeGenerator {
|
|||||||
/// Return the module name for the code generator.
|
/// Return the module name for the code generator.
|
||||||
fn get_name(&self) -> &str;
|
fn get_name(&self) -> &str;
|
||||||
|
|
||||||
/// Return an instance of [`IntType`] corresponding to the type of `size_t` for this instance.
|
|
||||||
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx>;
|
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx>;
|
||||||
|
|
||||||
/// Generate function call and returns the function return value.
|
/// Generate function call and returns the function return value.
|
||||||
|
@ -24,52 +24,42 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
src_arr: ListValue<'ctx>,
|
src_arr: ListValue<'ctx>,
|
||||||
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||||
) {
|
) {
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let size_ty = generator.get_size_type(ctx.ctx);
|
||||||
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
let int32 = ctx.ctx.i32_type();
|
||||||
|
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
|
||||||
assert_eq!(dest_idx.0.get_type(), llvm_i32);
|
|
||||||
assert_eq!(dest_idx.1.get_type(), llvm_i32);
|
|
||||||
assert_eq!(dest_idx.2.get_type(), llvm_i32);
|
|
||||||
assert_eq!(src_idx.0.get_type(), llvm_i32);
|
|
||||||
assert_eq!(src_idx.1.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 slice_assign_fun = {
|
let slice_assign_fun = {
|
||||||
let ty_vec = vec![
|
let ty_vec = vec![
|
||||||
llvm_i32.into(), // dest start idx
|
int32.into(), // dest start idx
|
||||||
llvm_i32.into(), // dest end idx
|
int32.into(), // dest end idx
|
||||||
llvm_i32.into(), // dest step
|
int32.into(), // dest step
|
||||||
elem_ptr_type.into(), // dest arr ptr
|
elem_ptr_type.into(), // dest arr ptr
|
||||||
llvm_i32.into(), // dest arr len
|
int32.into(), // dest arr len
|
||||||
llvm_i32.into(), // src start idx
|
int32.into(), // src start idx
|
||||||
llvm_i32.into(), // src end idx
|
int32.into(), // src end idx
|
||||||
llvm_i32.into(), // src step
|
int32.into(), // src step
|
||||||
elem_ptr_type.into(), // src arr ptr
|
elem_ptr_type.into(), // src arr ptr
|
||||||
llvm_i32.into(), // src arr len
|
int32.into(), // src arr len
|
||||||
llvm_i32.into(), // size
|
int32.into(), // size
|
||||||
];
|
];
|
||||||
ctx.module.get_function(fun_symbol).unwrap_or_else(|| {
|
ctx.module.get_function(fun_symbol).unwrap_or_else(|| {
|
||||||
let fn_t = llvm_i32.fn_type(ty_vec.as_slice(), false);
|
let fn_t = int32.fn_type(ty_vec.as_slice(), false);
|
||||||
ctx.module.add_function(fun_symbol, fn_t, None)
|
ctx.module.add_function(fun_symbol, fn_t, None)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let zero = llvm_i32.const_zero();
|
let zero = int32.const_zero();
|
||||||
let one = llvm_i32.const_int(1, false);
|
let one = int32.const_int(1, false);
|
||||||
let dest_arr_ptr = dest_arr.data().base_ptr(ctx, generator);
|
let dest_arr_ptr = dest_arr.data().base_ptr(ctx, generator);
|
||||||
let dest_arr_ptr =
|
let dest_arr_ptr =
|
||||||
ctx.builder.build_pointer_cast(dest_arr_ptr, elem_ptr_type, "dest_arr_ptr_cast").unwrap();
|
ctx.builder.build_pointer_cast(dest_arr_ptr, elem_ptr_type, "dest_arr_ptr_cast").unwrap();
|
||||||
let dest_len = dest_arr.load_size(ctx, Some("dest.len"));
|
let dest_len = dest_arr.load_size(ctx, Some("dest.len"));
|
||||||
let dest_len =
|
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32").unwrap();
|
||||||
ctx.builder.build_int_truncate_or_bit_cast(dest_len, llvm_i32, "srclen32").unwrap();
|
|
||||||
let src_arr_ptr = src_arr.data().base_ptr(ctx, generator);
|
let src_arr_ptr = src_arr.data().base_ptr(ctx, generator);
|
||||||
let src_arr_ptr =
|
let src_arr_ptr =
|
||||||
ctx.builder.build_pointer_cast(src_arr_ptr, elem_ptr_type, "src_arr_ptr_cast").unwrap();
|
ctx.builder.build_pointer_cast(src_arr_ptr, elem_ptr_type, "src_arr_ptr_cast").unwrap();
|
||||||
let src_len = src_arr.load_size(ctx, Some("src.len"));
|
let src_len = src_arr.load_size(ctx, Some("src.len"));
|
||||||
let src_len =
|
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32").unwrap();
|
||||||
ctx.builder.build_int_truncate_or_bit_cast(src_len, llvm_i32, "srclen32").unwrap();
|
|
||||||
|
|
||||||
// index in bound and positive should be done
|
// index in bound and positive should be done
|
||||||
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
||||||
@ -146,7 +136,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
BasicTypeEnum::StructType(t) => t.size_of().unwrap(),
|
BasicTypeEnum::StructType(t) => t.size_of().unwrap(),
|
||||||
_ => codegen_unreachable!(ctx),
|
_ => codegen_unreachable!(ctx),
|
||||||
};
|
};
|
||||||
ctx.builder.build_int_truncate_or_bit_cast(s, llvm_i32, "size").unwrap()
|
ctx.builder.build_int_truncate_or_bit_cast(s, int32, "size").unwrap()
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
@ -157,7 +147,6 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.map(Either::unwrap_left)
|
.map(Either::unwrap_left)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// update length
|
// update length
|
||||||
let need_update =
|
let need_update =
|
||||||
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update").unwrap();
|
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update").unwrap();
|
||||||
@ -166,8 +155,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||||
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
|
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
|
||||||
ctx.builder.position_at_end(update_bb);
|
ctx.builder.position_at_end(update_bb);
|
||||||
let new_len =
|
let new_len = ctx.builder.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len").unwrap();
|
||||||
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap();
|
|
||||||
dest_arr.store_size(ctx, generator, new_len);
|
dest_arr.store_size(ctx, generator, new_len);
|
||||||
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
|
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
|
@ -62,13 +62,8 @@ pub fn call_isinf<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
v: FloatValue<'ctx>,
|
v: FloatValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let llvm_f64 = ctx.ctx.f64_type();
|
|
||||||
|
|
||||||
assert_eq!(v.get_type(), llvm_f64);
|
|
||||||
|
|
||||||
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
|
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
|
||||||
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false);
|
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
|
||||||
ctx.module.add_function("__nac3_isinf", fn_type, None)
|
ctx.module.add_function("__nac3_isinf", fn_type, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,13 +84,8 @@ pub fn call_isnan<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
v: FloatValue<'ctx>,
|
v: FloatValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let llvm_f64 = ctx.ctx.f64_type();
|
|
||||||
|
|
||||||
assert_eq!(v.get_type(), llvm_f64);
|
|
||||||
|
|
||||||
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
|
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
|
||||||
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false);
|
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
|
||||||
ctx.module.add_function("__nac3_isnan", fn_type, None)
|
ctx.module.add_function("__nac3_isnan", fn_type, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -114,8 +104,6 @@ pub fn call_isnan<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
pub fn call_gamma<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
pub fn call_gamma<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
||||||
let llvm_f64 = ctx.ctx.f64_type();
|
let llvm_f64 = ctx.ctx.f64_type();
|
||||||
|
|
||||||
assert_eq!(v.get_type(), llvm_f64);
|
|
||||||
|
|
||||||
let intrinsic_fn = ctx.module.get_function("__nac3_gamma").unwrap_or_else(|| {
|
let intrinsic_fn = ctx.module.get_function("__nac3_gamma").unwrap_or_else(|| {
|
||||||
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
||||||
ctx.module.add_function("__nac3_gamma", fn_type, None)
|
ctx.module.add_function("__nac3_gamma", fn_type, None)
|
||||||
@ -133,8 +121,6 @@ pub fn call_gamma<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) ->
|
|||||||
pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
||||||
let llvm_f64 = ctx.ctx.f64_type();
|
let llvm_f64 = ctx.ctx.f64_type();
|
||||||
|
|
||||||
assert_eq!(v.get_type(), llvm_f64);
|
|
||||||
|
|
||||||
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| {
|
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| {
|
||||||
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
||||||
ctx.module.add_function("__nac3_gammaln", fn_type, None)
|
ctx.module.add_function("__nac3_gammaln", fn_type, None)
|
||||||
@ -152,8 +138,6 @@ pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -
|
|||||||
pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> FloatValue<'ctx> {
|
||||||
let llvm_f64 = ctx.ctx.f64_type();
|
let llvm_f64 = ctx.ctx.f64_type();
|
||||||
|
|
||||||
assert_eq!(v.get_type(), llvm_f64);
|
|
||||||
|
|
||||||
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| {
|
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| {
|
||||||
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
|
||||||
ctx.module.add_function("__nac3_j0", fn_type, None)
|
ctx.module.add_function("__nac3_j0", fn_type, None)
|
||||||
|
@ -15,14 +15,12 @@ pub use list::*;
|
|||||||
pub use math::*;
|
pub use math::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use slice::*;
|
pub use slice::*;
|
||||||
pub use string::*;
|
|
||||||
|
|
||||||
mod list;
|
mod list;
|
||||||
mod math;
|
mod math;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
mod range;
|
mod range;
|
||||||
mod slice;
|
mod slice;
|
||||||
mod string;
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn load_irrt<'ctx>(ctx: &'ctx Context, symbol_resolver: &dyn SymbolResolver) -> Module<'ctx> {
|
pub fn load_irrt<'ctx>(ctx: &'ctx Context, symbol_resolver: &dyn SymbolResolver) -> Module<'ctx> {
|
||||||
@ -132,11 +130,10 @@ pub fn handle_slice_indices<'ctx, G: CodeGenerator>(
|
|||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
length: IntValue<'ctx>,
|
length: IntValue<'ctx>,
|
||||||
) -> Result<Option<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>)>, String> {
|
) -> Result<Option<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>)>, String> {
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
let int32 = ctx.ctx.i32_type();
|
||||||
|
let zero = int32.const_zero();
|
||||||
let zero = llvm_i32.const_zero();
|
let one = int32.const_int(1, false);
|
||||||
let one = llvm_i32.const_int(1, false);
|
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32").unwrap();
|
||||||
let length = ctx.builder.build_int_truncate_or_bit_cast(length, llvm_i32, "leni32").unwrap();
|
|
||||||
Ok(Some(match (start, end, step) {
|
Ok(Some(match (start, end, step) {
|
||||||
(s, e, None) => (
|
(s, e, None) => (
|
||||||
if let Some(s) = s.as_ref() {
|
if let Some(s) = s.as_ref() {
|
||||||
@ -145,7 +142,7 @@ pub fn handle_slice_indices<'ctx, G: CodeGenerator>(
|
|||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
llvm_i32.const_zero()
|
int32.const_zero()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let e = if let Some(s) = e.as_ref() {
|
let e = if let Some(s) = e.as_ref() {
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
use inkwell::{types::BasicTypeEnum, values::IntValue};
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
expr::infer_and_call_function,
|
|
||||||
irrt::get_usize_dependent_function_name,
|
|
||||||
values::{ndarray::NDArrayValue, ListValue, ProxyValue, TypedArrayLikeAccessor},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_array_set_and_validate_list_shape`.
|
|
||||||
///
|
|
||||||
/// Deduces the target shape of the `ndarray` from the provided `list`, raising an exception if
|
|
||||||
/// there is any issue with the resultant `shape`.
|
|
||||||
///
|
|
||||||
/// `shape` must be pre-allocated by the caller of this function to `[usize; ndims]`, and must be
|
|
||||||
/// initialized to all `-1`s.
|
|
||||||
pub fn call_nac3_ndarray_array_set_and_validate_list_shape<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
list: ListValue<'ctx>,
|
|
||||||
ndims: IntValue<'ctx>,
|
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
) {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
assert_eq!(list.get_type().element_type().unwrap(), ctx.ctx.i8_type().into());
|
|
||||||
assert_eq!(ndims.get_type(), llvm_usize);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
"__nac3_ndarray_array_set_and_validate_list_shape",
|
|
||||||
);
|
|
||||||
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[list.as_base_value().into(), ndims.into(), shape.base_ptr(ctx, generator).into()],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_array_write_list_to_array`.
|
|
||||||
///
|
|
||||||
/// Copies the contents stored in `list` into `ndarray`.
|
|
||||||
///
|
|
||||||
/// The `ndarray` must fulfill the following preconditions:
|
|
||||||
///
|
|
||||||
/// - `ndarray.itemsize`: Must be initialized.
|
|
||||||
/// - `ndarray.ndims`: Must be initialized.
|
|
||||||
/// - `ndarray.shape`: Must be initialized.
|
|
||||||
/// - `ndarray.data`: Must be allocated and contiguous.
|
|
||||||
pub fn call_nac3_ndarray_array_write_list_to_array<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
list: ListValue<'ctx>,
|
|
||||||
ndarray: NDArrayValue<'ctx>,
|
|
||||||
) {
|
|
||||||
assert_eq!(list.get_type().element_type().unwrap(), ctx.ctx.i8_type().into());
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
"__nac3_ndarray_array_write_list_to_array",
|
|
||||||
);
|
|
||||||
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[list.as_base_value().into(), ndarray.as_base_value().into()],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::BasicTypeEnum,
|
|
||||||
values::{BasicValueEnum, IntValue, PointerValue},
|
values::{BasicValueEnum, IntValue, PointerValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
@ -8,26 +7,19 @@ use crate::codegen::{
|
|||||||
expr::{create_and_call_function, 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,
|
types::ProxyType,
|
||||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
|
values::{ndarray::NDArrayValue, ProxyValue},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_util_assert_shape_no_negative`.
|
|
||||||
///
|
|
||||||
/// Assets that `shape` does not contain negative dimensions.
|
|
||||||
pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
ndims: IntValue<'ctx>,
|
||||||
|
shape: PointerValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(
|
let name = get_usize_dependent_function_name(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
@ -38,37 +30,23 @@ pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator +
|
|||||||
ctx,
|
ctx,
|
||||||
&name,
|
&name,
|
||||||
Some(llvm_usize.into()),
|
Some(llvm_usize.into()),
|
||||||
&[
|
&[(llvm_usize.into(), ndims.into()), (llvm_pusize.into(), shape.into())],
|
||||||
(llvm_usize.into(), shape.size(ctx, generator).into()),
|
|
||||||
(llvm_pusize.into(), shape.base_ptr(ctx, generator).into()),
|
|
||||||
],
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_util_assert_shape_output_shape_same`.
|
|
||||||
///
|
|
||||||
/// Asserts that `ndarray_shape` and `output_shape` are the same in the context of writing output to
|
|
||||||
/// an `ndarray`.
|
|
||||||
pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
ndarray_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
ndarray_ndims: IntValue<'ctx>,
|
||||||
output_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
ndarray_shape: PointerValue<'ctx>,
|
||||||
|
output_ndims: IntValue<'ctx>,
|
||||||
|
output_shape: IntValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(ndarray_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(output_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(
|
let name = get_usize_dependent_function_name(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
@ -80,20 +58,16 @@ pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator +
|
|||||||
&name,
|
&name,
|
||||||
Some(llvm_usize.into()),
|
Some(llvm_usize.into()),
|
||||||
&[
|
&[
|
||||||
(llvm_usize.into(), ndarray_shape.size(ctx, generator).into()),
|
(llvm_usize.into(), ndarray_ndims.into()),
|
||||||
(llvm_pusize.into(), ndarray_shape.base_ptr(ctx, generator).into()),
|
(llvm_pusize.into(), ndarray_shape.into()),
|
||||||
(llvm_usize.into(), output_shape.size(ctx, generator).into()),
|
(llvm_usize.into(), output_ndims.into()),
|
||||||
(llvm_pusize.into(), output_shape.base_ptr(ctx, generator).into()),
|
(llvm_pusize.into(), output_shape.into()),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_size`.
|
|
||||||
///
|
|
||||||
/// Returns a [`usize`][CodeGenerator::get_size_type] value of the number of elements of an
|
|
||||||
/// `ndarray`, corresponding to the value of `ndarray.size`.
|
|
||||||
pub fn call_nac3_ndarray_size<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_size<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -116,10 +90,6 @@ pub fn call_nac3_ndarray_size<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_nbytes`.
|
|
||||||
///
|
|
||||||
/// Returns a [`usize`][CodeGenerator::get_size_type] value of the number of bytes consumed by the
|
|
||||||
/// data of the `ndarray`, corresponding to the value of `ndarray.nbytes`.
|
|
||||||
pub fn call_nac3_ndarray_nbytes<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_nbytes<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -142,10 +112,6 @@ pub fn call_nac3_ndarray_nbytes<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_len`.
|
|
||||||
///
|
|
||||||
/// Returns a [`usize`][CodeGenerator::get_size_type] value of the size of the topmost dimension of
|
|
||||||
/// the `ndarray`, corresponding to the value of `ndarray.__len__`.
|
|
||||||
pub fn call_nac3_ndarray_len<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -168,9 +134,6 @@ pub fn call_nac3_ndarray_len<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_is_c_contiguous`.
|
|
||||||
///
|
|
||||||
/// Returns an `i1` value indicating whether the `ndarray` is C-contiguous.
|
|
||||||
pub fn call_nac3_ndarray_is_c_contiguous<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_is_c_contiguous<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -193,9 +156,6 @@ pub fn call_nac3_ndarray_is_c_contiguous<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_get_nth_pelement`.
|
|
||||||
///
|
|
||||||
/// Returns a [`PointerValue`] to the `index`-th flattened element of the `ndarray`.
|
|
||||||
pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -207,8 +167,6 @@ pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||||
|
|
||||||
assert_eq!(index.get_type(), llvm_usize);
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_nth_pelement");
|
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_nth_pelement");
|
||||||
|
|
||||||
create_and_call_function(
|
create_and_call_function(
|
||||||
@ -223,16 +181,11 @@ pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_get_pelement_by_indices`.
|
|
||||||
///
|
|
||||||
/// `indices` must have the same number of elements as the number of dimensions in `ndarray`.
|
|
||||||
///
|
|
||||||
/// Returns a [`PointerValue`] to the element indexed by `indices`.
|
|
||||||
pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
ndarray: NDArrayValue<'ctx>,
|
ndarray: NDArrayValue<'ctx>,
|
||||||
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
indices: PointerValue<'ctx>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'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());
|
||||||
@ -240,11 +193,6 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
|
|||||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name =
|
let name =
|
||||||
get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_pelement_by_indices");
|
get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_pelement_by_indices");
|
||||||
|
|
||||||
@ -254,7 +202,7 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
|
|||||||
Some(llvm_pi8.into()),
|
Some(llvm_pi8.into()),
|
||||||
&[
|
&[
|
||||||
(llvm_ndarray.into(), ndarray.as_base_value().into()),
|
(llvm_ndarray.into(), ndarray.as_base_value().into()),
|
||||||
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
|
(llvm_pusize.into(), indices.into()),
|
||||||
],
|
],
|
||||||
Some("pelement"),
|
Some("pelement"),
|
||||||
None,
|
None,
|
||||||
@ -263,9 +211,6 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_set_strides_by_shape`.
|
|
||||||
///
|
|
||||||
/// Sets `ndarray.strides` assuming that `ndarray.shape` is C-contiguous.
|
|
||||||
pub fn call_nac3_ndarray_set_strides_by_shape<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_set_strides_by_shape<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -286,11 +231,6 @@ pub fn call_nac3_ndarray_set_strides_by_shape<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_copy_data`.
|
|
||||||
///
|
|
||||||
/// Copies all elements from `src_ndarray` to `dst_ndarray` using their flattened views. The number
|
|
||||||
/// of elements in `src_ndarray` must be greater than or equal to the number of elements in
|
|
||||||
/// `dst_ndarray`.
|
|
||||||
pub fn call_nac3_ndarray_copy_data<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_copy_data<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
use inkwell::values::IntValue;
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
expr::infer_and_call_function,
|
|
||||||
irrt::get_usize_dependent_function_name,
|
|
||||||
types::{ndarray::ShapeEntryType, ProxyType},
|
|
||||||
values::{
|
|
||||||
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
|
|
||||||
TypedArrayLikeMutator,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_broadcast_to`.
|
|
||||||
///
|
|
||||||
/// Attempts to broadcast `src_ndarray` to the new shape defined by `dst_ndarray`.
|
|
||||||
///
|
|
||||||
/// `dst_ndarray` must meet the following preconditions:
|
|
||||||
///
|
|
||||||
/// - `dst_ndarray.ndims` must be initialized and matching the length of `dst_ndarray.shape`.
|
|
||||||
/// - `dst_ndarray.shape` must be initialized and contains the target broadcast shape.
|
|
||||||
/// - `dst_ndarray.strides` must be allocated and may contain uninitialized values.
|
|
||||||
pub fn call_nac3_ndarray_broadcast_to<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
src_ndarray: NDArrayValue<'ctx>,
|
|
||||||
dst_ndarray: NDArrayValue<'ctx>,
|
|
||||||
) {
|
|
||||||
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_broadcast_to");
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_broadcast_shapes`.
|
|
||||||
///
|
|
||||||
/// Attempts to calculate the resultant shape from broadcasting all shapes in `shape_entries`,
|
|
||||||
/// writing the result to `dst_shape`.
|
|
||||||
pub fn call_nac3_ndarray_broadcast_shapes<'ctx, G, Shape>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
num_shape_entries: IntValue<'ctx>,
|
|
||||||
shape_entries: ArraySliceValue<'ctx>,
|
|
||||||
dst_ndims: IntValue<'ctx>,
|
|
||||||
dst_shape: &Shape,
|
|
||||||
) where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
Shape: TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>
|
|
||||||
+ TypedArrayLikeMutator<'ctx, G, IntValue<'ctx>>,
|
|
||||||
{
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
assert_eq!(num_shape_entries.get_type(), llvm_usize);
|
|
||||||
assert!(ShapeEntryType::is_type(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
shape_entries.base_ptr(ctx, generator).get_type()
|
|
||||||
)
|
|
||||||
.is_ok());
|
|
||||||
assert_eq!(dst_ndims.get_type(), llvm_usize);
|
|
||||||
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_broadcast_shapes");
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[
|
|
||||||
num_shape_entries.into(),
|
|
||||||
shape_entries.base_ptr(ctx, generator).into(),
|
|
||||||
dst_ndims.into(),
|
|
||||||
dst_shape.base_ptr(ctx, generator).into(),
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,11 +5,6 @@ use crate::codegen::{
|
|||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_index`.
|
|
||||||
///
|
|
||||||
/// Performs [basic indexing](https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing)
|
|
||||||
/// on `src_ndarray` using `indices`, writing the result to `dst_ndarray`, corresponding to the
|
|
||||||
/// operation `dst_ndarray = src_ndarray[indices]`.
|
|
||||||
pub fn call_nac3_ndarray_index<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_index<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::BasicTypeEnum,
|
|
||||||
values::{BasicValueEnum, IntValue},
|
values::{BasicValueEnum, IntValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
@ -10,29 +9,21 @@ use crate::codegen::{
|
|||||||
types::ProxyType,
|
types::ProxyType,
|
||||||
values::{
|
values::{
|
||||||
ndarray::{NDArrayValue, NDIterValue},
|
ndarray::{NDArrayValue, NDIterValue},
|
||||||
ProxyValue, TypedArrayLikeAccessor,
|
ArrayLikeValue, ArraySliceValue, ProxyValue,
|
||||||
},
|
},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generates a call to `__nac3_nditer_initialize`.
|
|
||||||
///
|
|
||||||
/// Initializes the `iter` object.
|
|
||||||
pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
iter: NDIterValue<'ctx>,
|
iter: NDIterValue<'ctx>,
|
||||||
ndarray: NDArrayValue<'ctx>,
|
ndarray: NDArrayValue<'ctx>,
|
||||||
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
indices: ArraySliceValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_nditer_initialize");
|
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_nditer_initialize");
|
||||||
|
|
||||||
create_and_call_function(
|
create_and_call_function(
|
||||||
@ -49,10 +40,6 @@ pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_nditer_initialize_has_element`.
|
|
||||||
///
|
|
||||||
/// Returns an `i1` value indicating whether there are elements left to traverse for the `iter`
|
|
||||||
/// object.
|
|
||||||
pub fn call_nac3_nditer_has_element<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_nditer_has_element<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
@ -72,9 +59,6 @@ pub fn call_nac3_nditer_has_element<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a call to `__nac3_nditer_next`.
|
|
||||||
///
|
|
||||||
/// Moves `iter` to point to the next element.
|
|
||||||
pub fn call_nac3_nditer_next<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_nditer_next<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
use inkwell::{types::BasicTypeEnum, values::IntValue};
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
|
|
||||||
values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_matmul_calculate_shapes`.
|
|
||||||
///
|
|
||||||
/// Calculates the broadcasted shapes for `a`, `b`, and the `ndarray` holding the final values of
|
|
||||||
/// `a @ b`.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
a_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
b_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
final_ndims: IntValue<'ctx>,
|
|
||||||
new_a_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
new_b_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
dst_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
) {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(a_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(b_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(new_a_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(new_b_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
BasicTypeEnum::try_from(dst_shape.element_type(ctx, generator)).unwrap(),
|
|
||||||
llvm_usize.into()
|
|
||||||
);
|
|
||||||
|
|
||||||
let name =
|
|
||||||
get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_matmul_calculate_shapes");
|
|
||||||
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[
|
|
||||||
a_shape.size(ctx, generator).into(),
|
|
||||||
a_shape.base_ptr(ctx, generator).into(),
|
|
||||||
b_shape.size(ctx, generator).into(),
|
|
||||||
b_shape.base_ptr(ctx, generator).into(),
|
|
||||||
final_ndims.into(),
|
|
||||||
new_a_shape.base_ptr(ctx, generator).into(),
|
|
||||||
new_b_shape.base_ptr(ctx, generator).into(),
|
|
||||||
dst_shape.base_ptr(ctx, generator).into(),
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,17 +1,391 @@
|
|||||||
pub use array::*;
|
use inkwell::{
|
||||||
|
types::IntType,
|
||||||
|
values::{BasicValueEnum, CallSiteValue, IntValue},
|
||||||
|
AddressSpace, IntPredicate,
|
||||||
|
};
|
||||||
|
use itertools::Either;
|
||||||
|
|
||||||
|
use crate::codegen::{
|
||||||
|
llvm_intrinsics,
|
||||||
|
macros::codegen_unreachable,
|
||||||
|
stmt::gen_for_callback_incrementing,
|
||||||
|
values::{
|
||||||
|
ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue,
|
||||||
|
TypedArrayLikeAccessor, TypedArrayLikeAdapter, UntypedArrayLikeAccessor,
|
||||||
|
},
|
||||||
|
CodeGenContext, CodeGenerator,
|
||||||
|
};
|
||||||
pub use basic::*;
|
pub use basic::*;
|
||||||
pub use broadcast::*;
|
|
||||||
pub use indexing::*;
|
pub use indexing::*;
|
||||||
pub use iter::*;
|
pub use iter::*;
|
||||||
pub use matmul::*;
|
|
||||||
pub use reshape::*;
|
|
||||||
pub use transpose::*;
|
|
||||||
|
|
||||||
mod array;
|
|
||||||
mod basic;
|
mod basic;
|
||||||
mod broadcast;
|
|
||||||
mod indexing;
|
mod indexing;
|
||||||
mod iter;
|
mod iter;
|
||||||
mod matmul;
|
|
||||||
mod reshape;
|
/// Generates a call to `__nac3_ndarray_calc_size`. Returns an [`IntValue`] representing the
|
||||||
mod transpose;
|
/// calculated total size.
|
||||||
|
///
|
||||||
|
/// * `dims` - An [`ArrayLikeIndexer`] containing the size of each dimension.
|
||||||
|
/// * `range` - The dimension index to begin and end (exclusively) calculating the dimensions for,
|
||||||
|
/// or [`None`] if starting from the first dimension and ending at the last dimension
|
||||||
|
/// respectively.
|
||||||
|
pub fn call_ndarray_calc_size<'ctx, G, Dims>(
|
||||||
|
generator: &G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
dims: &Dims,
|
||||||
|
(begin, end): (Option<IntValue<'ctx>>, Option<IntValue<'ctx>>),
|
||||||
|
) -> IntValue<'ctx>
|
||||||
|
where
|
||||||
|
G: CodeGenerator + ?Sized,
|
||||||
|
Dims: ArrayLikeIndexer<'ctx>,
|
||||||
|
{
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
|
let ndarray_calc_size_fn_name = match llvm_usize.get_bit_width() {
|
||||||
|
32 => "__nac3_ndarray_calc_size",
|
||||||
|
64 => "__nac3_ndarray_calc_size64",
|
||||||
|
bw => codegen_unreachable!(ctx, "Unsupported size type bit width: {}", bw),
|
||||||
|
};
|
||||||
|
let ndarray_calc_size_fn_t = llvm_usize.fn_type(
|
||||||
|
&[llvm_pusize.into(), llvm_usize.into(), llvm_usize.into(), llvm_usize.into()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let ndarray_calc_size_fn =
|
||||||
|
ctx.module.get_function(ndarray_calc_size_fn_name).unwrap_or_else(|| {
|
||||||
|
ctx.module.add_function(ndarray_calc_size_fn_name, ndarray_calc_size_fn_t, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let begin = begin.unwrap_or_else(|| llvm_usize.const_zero());
|
||||||
|
let end = end.unwrap_or_else(|| dims.size(ctx, generator));
|
||||||
|
ctx.builder
|
||||||
|
.build_call(
|
||||||
|
ndarray_calc_size_fn,
|
||||||
|
&[
|
||||||
|
dims.base_ptr(ctx, generator).into(),
|
||||||
|
dims.size(ctx, generator).into(),
|
||||||
|
begin.into(),
|
||||||
|
end.into(),
|
||||||
|
],
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.map(CallSiteValue::try_as_basic_value)
|
||||||
|
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||||
|
.map(Either::unwrap_left)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a call to `__nac3_ndarray_calc_nd_indices`. Returns a [`TypeArrayLikeAdpater`]
|
||||||
|
/// containing `i32` indices of the flattened index.
|
||||||
|
///
|
||||||
|
/// * `index` - The index to compute the multidimensional index for.
|
||||||
|
/// * `ndarray` - LLVM pointer to the `NDArray`. This value must be the LLVM representation of an
|
||||||
|
/// `NDArray`.
|
||||||
|
pub fn call_ndarray_calc_nd_indices<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
index: IntValue<'ctx>,
|
||||||
|
ndarray: NDArrayValue<'ctx>,
|
||||||
|
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
|
||||||
|
let llvm_void = ctx.ctx.void_type();
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
|
||||||
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
|
let ndarray_calc_nd_indices_fn_name = match llvm_usize.get_bit_width() {
|
||||||
|
32 => "__nac3_ndarray_calc_nd_indices",
|
||||||
|
64 => "__nac3_ndarray_calc_nd_indices64",
|
||||||
|
bw => codegen_unreachable!(ctx, "Unsupported size type bit width: {}", bw),
|
||||||
|
};
|
||||||
|
let ndarray_calc_nd_indices_fn =
|
||||||
|
ctx.module.get_function(ndarray_calc_nd_indices_fn_name).unwrap_or_else(|| {
|
||||||
|
let fn_type = llvm_void.fn_type(
|
||||||
|
&[llvm_usize.into(), llvm_pusize.into(), llvm_usize.into(), llvm_pi32.into()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.module.add_function(ndarray_calc_nd_indices_fn_name, fn_type, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let ndarray_num_dims = ndarray.load_ndims(ctx);
|
||||||
|
let ndarray_dims = ndarray.shape();
|
||||||
|
|
||||||
|
let indices = ctx.builder.build_array_alloca(llvm_i32, ndarray_num_dims, "").unwrap();
|
||||||
|
|
||||||
|
ctx.builder
|
||||||
|
.build_call(
|
||||||
|
ndarray_calc_nd_indices_fn,
|
||||||
|
&[
|
||||||
|
index.into(),
|
||||||
|
ndarray_dims.base_ptr(ctx, generator).into(),
|
||||||
|
ndarray_num_dims.into(),
|
||||||
|
indices.into(),
|
||||||
|
],
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
TypedArrayLikeAdapter::from(
|
||||||
|
ArraySliceValue::from_ptr_val(indices, ndarray_num_dims, None),
|
||||||
|
Box::new(|_, v| v.into_int_value()),
|
||||||
|
Box::new(|_, v| v.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_ndarray_flatten_index_impl<'ctx, G, Indices>(
|
||||||
|
generator: &G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
ndarray: NDArrayValue<'ctx>,
|
||||||
|
indices: &Indices,
|
||||||
|
) -> IntValue<'ctx>
|
||||||
|
where
|
||||||
|
G: CodeGenerator + ?Sized,
|
||||||
|
Indices: ArrayLikeIndexer<'ctx>,
|
||||||
|
{
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
|
||||||
|
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
|
||||||
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
IntType::try_from(indices.element_type(ctx, generator))
|
||||||
|
.map(IntType::get_bit_width)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
llvm_i32.get_bit_width(),
|
||||||
|
"Expected i32 value for argument `indices` to `call_ndarray_flatten_index_impl`"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
indices.size(ctx, generator).get_type().get_bit_width(),
|
||||||
|
llvm_usize.get_bit_width(),
|
||||||
|
"Expected usize integer value for argument `indices_size` to `call_ndarray_flatten_index_impl`"
|
||||||
|
);
|
||||||
|
|
||||||
|
let ndarray_flatten_index_fn_name = match llvm_usize.get_bit_width() {
|
||||||
|
32 => "__nac3_ndarray_flatten_index",
|
||||||
|
64 => "__nac3_ndarray_flatten_index64",
|
||||||
|
bw => codegen_unreachable!(ctx, "Unsupported size type bit width: {}", bw),
|
||||||
|
};
|
||||||
|
let ndarray_flatten_index_fn =
|
||||||
|
ctx.module.get_function(ndarray_flatten_index_fn_name).unwrap_or_else(|| {
|
||||||
|
let fn_type = llvm_usize.fn_type(
|
||||||
|
&[llvm_pusize.into(), llvm_usize.into(), llvm_pi32.into(), llvm_usize.into()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.module.add_function(ndarray_flatten_index_fn_name, fn_type, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let ndarray_num_dims = ndarray.load_ndims(ctx);
|
||||||
|
let ndarray_dims = ndarray.shape();
|
||||||
|
|
||||||
|
let index = ctx
|
||||||
|
.builder
|
||||||
|
.build_call(
|
||||||
|
ndarray_flatten_index_fn,
|
||||||
|
&[
|
||||||
|
ndarray_dims.base_ptr(ctx, generator).into(),
|
||||||
|
ndarray_num_dims.into(),
|
||||||
|
indices.base_ptr(ctx, generator).into(),
|
||||||
|
indices.size(ctx, generator).into(),
|
||||||
|
],
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.map(CallSiteValue::try_as_basic_value)
|
||||||
|
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||||
|
.map(Either::unwrap_left)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a call to `__nac3_ndarray_flatten_index`. Returns the flattened index for the
|
||||||
|
/// multidimensional index.
|
||||||
|
///
|
||||||
|
/// * `ndarray` - LLVM pointer to the `NDArray`. This value must be the LLVM representation of an
|
||||||
|
/// `NDArray`.
|
||||||
|
/// * `indices` - The multidimensional index to compute the flattened index for.
|
||||||
|
pub fn call_ndarray_flatten_index<'ctx, G, Index>(
|
||||||
|
generator: &G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
ndarray: NDArrayValue<'ctx>,
|
||||||
|
indices: &Index,
|
||||||
|
) -> IntValue<'ctx>
|
||||||
|
where
|
||||||
|
G: CodeGenerator + ?Sized,
|
||||||
|
Index: ArrayLikeIndexer<'ctx>,
|
||||||
|
{
|
||||||
|
call_ndarray_flatten_index_impl(generator, ctx, ndarray, indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a call to `__nac3_ndarray_calc_broadcast`. Returns a tuple containing the number of
|
||||||
|
/// dimension and size of each dimension of the resultant `ndarray`.
|
||||||
|
pub fn call_ndarray_calc_broadcast<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
lhs: NDArrayValue<'ctx>,
|
||||||
|
rhs: NDArrayValue<'ctx>,
|
||||||
|
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
|
let ndarray_calc_broadcast_fn_name = match llvm_usize.get_bit_width() {
|
||||||
|
32 => "__nac3_ndarray_calc_broadcast",
|
||||||
|
64 => "__nac3_ndarray_calc_broadcast64",
|
||||||
|
bw => codegen_unreachable!(ctx, "Unsupported size type bit width: {}", bw),
|
||||||
|
};
|
||||||
|
let ndarray_calc_broadcast_fn =
|
||||||
|
ctx.module.get_function(ndarray_calc_broadcast_fn_name).unwrap_or_else(|| {
|
||||||
|
let fn_type = llvm_usize.fn_type(
|
||||||
|
&[
|
||||||
|
llvm_pusize.into(),
|
||||||
|
llvm_usize.into(),
|
||||||
|
llvm_pusize.into(),
|
||||||
|
llvm_usize.into(),
|
||||||
|
llvm_pusize.into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.module.add_function(ndarray_calc_broadcast_fn_name, fn_type, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let lhs_ndims = lhs.load_ndims(ctx);
|
||||||
|
let rhs_ndims = rhs.load_ndims(ctx);
|
||||||
|
let min_ndims = llvm_intrinsics::call_int_umin(ctx, lhs_ndims, rhs_ndims, None);
|
||||||
|
|
||||||
|
gen_for_callback_incrementing(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
None,
|
||||||
|
llvm_usize.const_zero(),
|
||||||
|
(min_ndims, false),
|
||||||
|
|generator, ctx, _, idx| {
|
||||||
|
let idx = ctx.builder.build_int_sub(min_ndims, idx, "").unwrap();
|
||||||
|
let (lhs_dim_sz, rhs_dim_sz) = unsafe {
|
||||||
|
(
|
||||||
|
lhs.shape().get_typed_unchecked(ctx, generator, &idx, None),
|
||||||
|
rhs.shape().get_typed_unchecked(ctx, generator, &idx, None),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let llvm_usize_const_one = llvm_usize.const_int(1, false);
|
||||||
|
let lhs_eqz = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_compare(IntPredicate::EQ, lhs_dim_sz, llvm_usize_const_one, "")
|
||||||
|
.unwrap();
|
||||||
|
let rhs_eqz = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_compare(IntPredicate::EQ, rhs_dim_sz, llvm_usize_const_one, "")
|
||||||
|
.unwrap();
|
||||||
|
let lhs_or_rhs_eqz = ctx.builder.build_or(lhs_eqz, rhs_eqz, "").unwrap();
|
||||||
|
|
||||||
|
let lhs_eq_rhs = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_compare(IntPredicate::EQ, lhs_dim_sz, rhs_dim_sz, "")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let is_compatible = ctx.builder.build_or(lhs_or_rhs_eqz, lhs_eq_rhs, "").unwrap();
|
||||||
|
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
is_compatible,
|
||||||
|
"0:ValueError",
|
||||||
|
"operands could not be broadcast together",
|
||||||
|
[None, None, None],
|
||||||
|
ctx.current_loc,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
llvm_usize.const_int(1, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let max_ndims = llvm_intrinsics::call_int_umax(ctx, lhs_ndims, rhs_ndims, None);
|
||||||
|
let lhs_dims = lhs.shape().base_ptr(ctx, generator);
|
||||||
|
let lhs_ndims = lhs.load_ndims(ctx);
|
||||||
|
let rhs_dims = rhs.shape().base_ptr(ctx, generator);
|
||||||
|
let rhs_ndims = rhs.load_ndims(ctx);
|
||||||
|
let out_dims = ctx.builder.build_array_alloca(llvm_usize, max_ndims, "").unwrap();
|
||||||
|
let out_dims = ArraySliceValue::from_ptr_val(out_dims, max_ndims, None);
|
||||||
|
|
||||||
|
ctx.builder
|
||||||
|
.build_call(
|
||||||
|
ndarray_calc_broadcast_fn,
|
||||||
|
&[
|
||||||
|
lhs_dims.into(),
|
||||||
|
lhs_ndims.into(),
|
||||||
|
rhs_dims.into(),
|
||||||
|
rhs_ndims.into(),
|
||||||
|
out_dims.base_ptr(ctx, generator).into(),
|
||||||
|
],
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
TypedArrayLikeAdapter::from(
|
||||||
|
out_dims,
|
||||||
|
Box::new(|_, v| v.into_int_value()),
|
||||||
|
Box::new(|_, v| v.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a call to `__nac3_ndarray_calc_broadcast_idx`. Returns an [`ArrayAllocaValue`]
|
||||||
|
/// containing the indices used for accessing `array` corresponding to the index of the broadcasted
|
||||||
|
/// array `broadcast_idx`.
|
||||||
|
pub fn call_ndarray_calc_broadcast_index<
|
||||||
|
'ctx,
|
||||||
|
G: CodeGenerator + ?Sized,
|
||||||
|
BroadcastIdx: UntypedArrayLikeAccessor<'ctx>,
|
||||||
|
>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
array: NDArrayValue<'ctx>,
|
||||||
|
broadcast_idx: &BroadcastIdx,
|
||||||
|
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
|
||||||
|
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
|
let ndarray_calc_broadcast_fn_name = match llvm_usize.get_bit_width() {
|
||||||
|
32 => "__nac3_ndarray_calc_broadcast_idx",
|
||||||
|
64 => "__nac3_ndarray_calc_broadcast_idx64",
|
||||||
|
bw => codegen_unreachable!(ctx, "Unsupported size type bit width: {}", bw),
|
||||||
|
};
|
||||||
|
let ndarray_calc_broadcast_fn =
|
||||||
|
ctx.module.get_function(ndarray_calc_broadcast_fn_name).unwrap_or_else(|| {
|
||||||
|
let fn_type = llvm_usize.fn_type(
|
||||||
|
&[llvm_pusize.into(), llvm_usize.into(), llvm_pi32.into(), llvm_pi32.into()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.module.add_function(ndarray_calc_broadcast_fn_name, fn_type, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let broadcast_size = broadcast_idx.size(ctx, generator);
|
||||||
|
let out_idx = ctx.builder.build_array_alloca(llvm_i32, broadcast_size, "").unwrap();
|
||||||
|
|
||||||
|
let array_dims = array.shape().base_ptr(ctx, generator);
|
||||||
|
let array_ndims = array.load_ndims(ctx);
|
||||||
|
let broadcast_idx_ptr = unsafe {
|
||||||
|
broadcast_idx.ptr_offset_unchecked(ctx, generator, &llvm_usize.const_zero(), None)
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.builder
|
||||||
|
.build_call(
|
||||||
|
ndarray_calc_broadcast_fn,
|
||||||
|
&[array_dims.into(), array_ndims.into(), broadcast_idx_ptr.into(), out_idx.into()],
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
TypedArrayLikeAdapter::from(
|
||||||
|
ArraySliceValue::from_ptr_val(out_idx, broadcast_size, None),
|
||||||
|
Box::new(|_, v| v.into_int_value()),
|
||||||
|
Box::new(|_, v| v.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
use inkwell::values::IntValue;
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
expr::infer_and_call_function,
|
|
||||||
irrt::get_usize_dependent_function_name,
|
|
||||||
values::{ArrayLikeValue, ArraySliceValue},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_reshape_resolve_and_check_new_shape`.
|
|
||||||
///
|
|
||||||
/// Resolves unknown dimensions in `new_shape` for `numpy.reshape(<ndarray>, new_shape)`, raising an
|
|
||||||
/// assertion if multiple dimensions are unknown (`-1`).
|
|
||||||
pub fn call_nac3_ndarray_reshape_resolve_and_check_new_shape<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
size: IntValue<'ctx>,
|
|
||||||
new_ndims: IntValue<'ctx>,
|
|
||||||
new_shape: ArraySliceValue<'ctx>,
|
|
||||||
) {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
assert_eq!(size.get_type(), llvm_usize);
|
|
||||||
assert_eq!(new_ndims.get_type(), llvm_usize);
|
|
||||||
assert_eq!(new_shape.element_type(ctx, generator), llvm_usize.into());
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
"__nac3_ndarray_reshape_resolve_and_check_new_shape",
|
|
||||||
);
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[size.into(), new_ndims.into(), new_shape.base_ptr(ctx, generator).into()],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use inkwell::{values::IntValue, AddressSpace};
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
expr::infer_and_call_function,
|
|
||||||
irrt::get_usize_dependent_function_name,
|
|
||||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a call to `__nac3_ndarray_transpose`.
|
|
||||||
///
|
|
||||||
/// Creates a transpose view of `src_ndarray` and writes the result to `dst_ndarray`.
|
|
||||||
///
|
|
||||||
/// `dst_ndarray` must fulfill the following preconditions:
|
|
||||||
///
|
|
||||||
/// - `dst_ndarray.ndims` must be initialized and must be equal to `src_ndarray.ndims`.
|
|
||||||
/// - `dst_ndarray.shape` must be allocated and may contain uninitialized values.
|
|
||||||
/// - `dst_ndarray.strides` must be allocated and may contain uninitialized values.
|
|
||||||
pub fn call_nac3_ndarray_transpose<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
src_ndarray: NDArrayValue<'ctx>,
|
|
||||||
dst_ndarray: NDArrayValue<'ctx>,
|
|
||||||
axes: Option<&impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>>,
|
|
||||||
) {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
assert!(axes.is_none_or(|axes| axes.size(ctx, generator).get_type() == llvm_usize));
|
|
||||||
assert!(axes.is_none_or(|axes| axes.element_type(ctx, generator) == llvm_usize.into()));
|
|
||||||
|
|
||||||
let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_transpose");
|
|
||||||
infer_and_call_function(
|
|
||||||
ctx,
|
|
||||||
&name,
|
|
||||||
None,
|
|
||||||
&[
|
|
||||||
src_ndarray.as_base_value().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.ptr_type(AddressSpace::default()).const_null(), |axes| {
|
|
||||||
axes.base_ptr(ctx, generator)
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
@ -6,13 +6,6 @@ use itertools::Either;
|
|||||||
|
|
||||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
/// Invokes the `__nac3_range_slice_len` in IRRT.
|
|
||||||
///
|
|
||||||
/// - `start`: The `i32` start value for the slice.
|
|
||||||
/// - `end`: The `i32` end value for the slice.
|
|
||||||
/// - `step`: The `i32` step value for the slice.
|
|
||||||
///
|
|
||||||
/// Returns an `i32` value of the length of the slice.
|
|
||||||
pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
@ -21,15 +14,9 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
step: IntValue<'ctx>,
|
step: IntValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
const SYMBOL: &str = "__nac3_range_slice_len";
|
const SYMBOL: &str = "__nac3_range_slice_len";
|
||||||
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
|
|
||||||
assert_eq!(start.get_type(), llvm_i32);
|
|
||||||
assert_eq!(end.get_type(), llvm_i32);
|
|
||||||
assert_eq!(step.get_type(), llvm_i32);
|
|
||||||
|
|
||||||
let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
|
let 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);
|
let i32_t = ctx.ctx.i32_type();
|
||||||
|
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false);
|
||||||
ctx.module.add_function(SYMBOL, fn_t, None)
|
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -46,7 +33,6 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
[None, None, None],
|
[None, None, None],
|
||||||
ctx.current_loc,
|
ctx.current_loc,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
|
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
|
||||||
.map(CallSiteValue::try_as_basic_value)
|
.map(CallSiteValue::try_as_basic_value)
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue};
|
|
||||||
use itertools::Either;
|
|
||||||
|
|
||||||
use super::get_usize_dependent_function_name;
|
|
||||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
|
||||||
|
|
||||||
/// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal.
|
|
||||||
pub fn call_string_eq<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
str1_ptr: PointerValue<'ctx>,
|
|
||||||
str1_len: IntValue<'ctx>,
|
|
||||||
str2_ptr: PointerValue<'ctx>,
|
|
||||||
str2_len: IntValue<'ctx>,
|
|
||||||
) -> IntValue<'ctx> {
|
|
||||||
let llvm_i1 = ctx.ctx.bool_type();
|
|
||||||
|
|
||||||
let func_name = get_usize_dependent_function_name(generator, ctx, "nac3_str_eq");
|
|
||||||
|
|
||||||
let func = ctx.module.get_function(&func_name).unwrap_or_else(|| {
|
|
||||||
ctx.module.add_function(
|
|
||||||
&func_name,
|
|
||||||
llvm_i1.fn_type(
|
|
||||||
&[
|
|
||||||
str1_ptr.get_type().into(),
|
|
||||||
str1_len.get_type().into(),
|
|
||||||
str2_ptr.get_type().into(),
|
|
||||||
str2_len.get_type().into(),
|
|
||||||
],
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.builder
|
|
||||||
.build_call(
|
|
||||||
func,
|
|
||||||
&[str1_ptr.into(), str1_len.into(), str2_ptr.into(), str2_len.into()],
|
|
||||||
"str_eq_call",
|
|
||||||
)
|
|
||||||
.map(CallSiteValue::try_as_basic_value)
|
|
||||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
|
||||||
.map(Either::unwrap_left)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
use inkwell::{
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
intrinsics::Intrinsic,
|
intrinsics::Intrinsic,
|
||||||
types::AnyTypeEnum::IntType,
|
types::{AnyTypeEnum::IntType, FloatType},
|
||||||
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue},
|
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
@ -8,6 +9,34 @@ use itertools::Either;
|
|||||||
|
|
||||||
use super::CodeGenContext;
|
use super::CodeGenContext;
|
||||||
|
|
||||||
|
/// Returns the string representation for the floating-point type `ft` when used in intrinsic
|
||||||
|
/// functions.
|
||||||
|
fn get_float_intrinsic_repr(ctx: &Context, ft: FloatType) -> &'static str {
|
||||||
|
// Standard LLVM floating-point types
|
||||||
|
if ft == ctx.f16_type() {
|
||||||
|
return "f16";
|
||||||
|
}
|
||||||
|
if ft == ctx.f32_type() {
|
||||||
|
return "f32";
|
||||||
|
}
|
||||||
|
if ft == ctx.f64_type() {
|
||||||
|
return "f64";
|
||||||
|
}
|
||||||
|
if ft == ctx.f128_type() {
|
||||||
|
return "f128";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-standard floating-point types
|
||||||
|
if ft == ctx.x86_f80_type() {
|
||||||
|
return "f80";
|
||||||
|
}
|
||||||
|
if ft == ctx.ppc_f128_type() {
|
||||||
|
return "ppcf128";
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
/// Invokes the [`llvm.va_start`](https://llvm.org/docs/LangRef.html#llvm-va-start-intrinsic)
|
/// Invokes the [`llvm.va_start`](https://llvm.org/docs/LangRef.html#llvm-va-start-intrinsic)
|
||||||
/// intrinsic.
|
/// intrinsic.
|
||||||
pub fn call_va_start<'ctx>(ctx: &CodeGenContext<'ctx, '_>, arglist: PointerValue<'ctx>) {
|
pub fn call_va_start<'ctx>(ctx: &CodeGenContext<'ctx, '_>, arglist: PointerValue<'ctx>) {
|
||||||
@ -25,7 +54,7 @@ pub fn call_va_start<'ctx>(ctx: &CodeGenContext<'ctx, '_>, arglist: PointerValue
|
|||||||
ctx.builder.build_call(intrinsic_fn, &[arglist.into()], "").unwrap();
|
ctx.builder.build_call(intrinsic_fn, &[arglist.into()], "").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invokes the [`llvm.va_end`](https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic)
|
/// Invokes the [`llvm.va_start`](https://llvm.org/docs/LangRef.html#llvm-va-start-intrinsic)
|
||||||
/// intrinsic.
|
/// intrinsic.
|
||||||
pub fn call_va_end<'ctx>(ctx: &CodeGenContext<'ctx, '_>, arglist: PointerValue<'ctx>) {
|
pub fn call_va_end<'ctx>(ctx: &CodeGenContext<'ctx, '_>, arglist: PointerValue<'ctx>) {
|
||||||
const FN_NAME: &str = "llvm.va_end";
|
const FN_NAME: &str = "llvm.va_end";
|
||||||
|
@ -42,7 +42,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::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
|
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType};
|
||||||
|
|
||||||
pub mod builtin_fns;
|
pub mod builtin_fns;
|
||||||
pub mod concrete_type;
|
pub mod concrete_type;
|
||||||
@ -228,7 +228,7 @@ pub struct CodeGenContext<'ctx, 'a> {
|
|||||||
pub current_loc: Location,
|
pub current_loc: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenContext<'_, '_> {
|
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
/// Whether the [current basic block][Builder::get_insert_block] referenced by `builder`
|
/// Whether the [current basic block][Builder::get_insert_block] referenced by `builder`
|
||||||
/// contains a [terminator statement][BasicBlock::get_terminator].
|
/// contains a [terminator statement][BasicBlock::get_terminator].
|
||||||
pub fn is_terminated(&self) -> bool {
|
pub fn is_terminated(&self) -> bool {
|
||||||
@ -520,7 +520,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(generator, ctx, element_type, ndims).as_base_type().into()
|
NDArrayType::new(generator, ctx, element_type, Some(ndims)).as_base_type().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!(
|
_ => unreachable!(
|
||||||
@ -574,7 +574,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(generator, ctx, &fields).as_base_type().into()
|
ctx.struct_type(&fields, false).into()
|
||||||
}
|
}
|
||||||
TVirtual { .. } => unimplemented!(),
|
TVirtual { .. } => unimplemented!(),
|
||||||
_ => unreachable!("{}", ty_enum.get_type_name()),
|
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,11 +16,7 @@ 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,
|
values::{ArrayLikeIndexer, ArraySliceValue, ListValue, RangeValue},
|
||||||
values::{
|
|
||||||
ndarray::{RustNDIndex, ScalarOrNDArray},
|
|
||||||
ArrayLikeIndexer, ArraySliceValue, ListValue, ProxyValue, RangeValue,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -415,52 +411,7 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
|
|||||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
// Handle NDArray item assignment
|
// Handle NDArray item assignment
|
||||||
// Process target
|
todo!("ndarray subscript assignment is not yet implemented");
|
||||||
let target = generator
|
|
||||||
.gen_expr(ctx, target)?
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator, target_ty)?;
|
|
||||||
|
|
||||||
// Process key
|
|
||||||
let key = RustNDIndex::from_subscript_expr(generator, ctx, key)?;
|
|
||||||
|
|
||||||
// Process value
|
|
||||||
let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
|
|
||||||
|
|
||||||
// Reference code:
|
|
||||||
// ```python
|
|
||||||
// target = target[key]
|
|
||||||
// value = np.asarray(value)
|
|
||||||
//
|
|
||||||
// shape = np.broadcast_shape((target, value))
|
|
||||||
//
|
|
||||||
// target = np.broadcast_to(target, shape)
|
|
||||||
// value = np.broadcast_to(value, shape)
|
|
||||||
//
|
|
||||||
// # ...and finally copy 1-1 from value to target.
|
|
||||||
// ```
|
|
||||||
|
|
||||||
let target = NDArrayType::from_unifier_type(generator, ctx, target_ty)
|
|
||||||
.map_value(target.into_pointer_value(), None);
|
|
||||||
let target = target.index(generator, ctx, &key);
|
|
||||||
|
|
||||||
let value = ScalarOrNDArray::from_value(generator, ctx, (value_ty, value))
|
|
||||||
.to_ndarray(generator, ctx);
|
|
||||||
|
|
||||||
let broadcast_ndims =
|
|
||||||
[target.get_type().ndims(), value.get_type().ndims()].into_iter().max().unwrap();
|
|
||||||
let broadcast_result = NDArrayType::new(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
value.get_type().element_type(),
|
|
||||||
broadcast_ndims,
|
|
||||||
)
|
|
||||||
.broadcast(generator, ctx, &[target, value]);
|
|
||||||
|
|
||||||
let target = broadcast_result.ndarrays[0];
|
|
||||||
let value = broadcast_result.ndarrays[1];
|
|
||||||
|
|
||||||
target.copy_data_from(generator, ctx, value);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("encountered unknown target type: {}", ctx.unifier.stringify(target_ty));
|
panic!("encountered unknown target type: {}", ctx.unifier.stringify(target_ty));
|
||||||
|
@ -36,6 +36,7 @@ use crate::{
|
|||||||
struct Resolver {
|
struct Resolver {
|
||||||
id_to_type: HashMap<StrRef, Type>,
|
id_to_type: HashMap<StrRef, Type>,
|
||||||
id_to_def: RwLock<HashMap<StrRef, DefinitionId>>,
|
id_to_def: RwLock<HashMap<StrRef, DefinitionId>>,
|
||||||
|
class_names: HashMap<StrRef, Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver {
|
impl Resolver {
|
||||||
@ -103,9 +104,11 @@ fn test_primitives() {
|
|||||||
let top_level = Arc::new(composer.make_top_level_context());
|
let top_level = Arc::new(composer.make_top_level_context());
|
||||||
unifier.top_level = Some(top_level.clone());
|
unifier.top_level = Some(top_level.clone());
|
||||||
|
|
||||||
let resolver =
|
let resolver = Arc::new(Resolver {
|
||||||
Arc::new(Resolver { id_to_type: HashMap::new(), id_to_def: RwLock::new(HashMap::new()) })
|
id_to_type: HashMap::new(),
|
||||||
as Arc<dyn SymbolResolver + Send + Sync>;
|
id_to_def: RwLock::new(HashMap::new()),
|
||||||
|
class_names: HashMap::default(),
|
||||||
|
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
||||||
let signature = FunSignature {
|
let signature = FunSignature {
|
||||||
@ -295,7 +298,11 @@ fn test_simple_call() {
|
|||||||
loc: None,
|
loc: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let resolver = Resolver { id_to_type: HashMap::new(), id_to_def: RwLock::new(HashMap::new()) };
|
let resolver = Resolver {
|
||||||
|
id_to_type: HashMap::new(),
|
||||||
|
id_to_def: RwLock::new(HashMap::new()),
|
||||||
|
class_names: HashMap::default(),
|
||||||
|
};
|
||||||
resolver.add_id_def("foo".into(), DefinitionId(foo_id));
|
resolver.add_id_def("foo".into(), DefinitionId(foo_id));
|
||||||
let resolver = Arc::new(resolver) as Arc<dyn SymbolResolver + Send + Sync>;
|
let resolver = Arc::new(resolver) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
@ -464,6 +471,6 @@ fn test_classes_ndarray_type_new() {
|
|||||||
let llvm_i32 = ctx.i32_type();
|
let llvm_i32 = ctx.i32_type();
|
||||||
let llvm_usize = generator.get_size_type(&ctx);
|
let llvm_usize = generator.get_size_type(&ctx);
|
||||||
|
|
||||||
let llvm_ndarray = NDArrayType::new(&generator, &ctx, llvm_i32.into(), 2);
|
let llvm_ndarray = NDArrayType::new(&generator, &ctx, llvm_i32.into(), None);
|
||||||
assert!(NDArrayType::is_representable(llvm_ndarray.as_base_type(), llvm_usize).is_ok());
|
assert!(NDArrayType::is_representable(llvm_ndarray.as_base_type(), llvm_usize).is_ok());
|
||||||
}
|
}
|
||||||
|
@ -1,113 +1,69 @@
|
|||||||
use inkwell::{
|
use inkwell::{
|
||||||
context::{AsContextRef, Context},
|
context::Context,
|
||||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||||
values::{IntValue, PointerValue},
|
values::IntValue,
|
||||||
AddressSpace, IntPredicate, OptimizationLevel,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
use super::ProxyType;
|
use super::ProxyType;
|
||||||
use crate::{
|
use crate::codegen::{
|
||||||
codegen::{
|
values::{ArraySliceValue, ListValue, ProxyValue},
|
||||||
types::structure::{
|
CodeGenContext, CodeGenerator,
|
||||||
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
|
|
||||||
},
|
|
||||||
values::{ListValue, ProxyValue},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Proxy type for a `list` type in LLVM.
|
/// Proxy type for a `list` type in LLVM.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct ListType<'ctx> {
|
pub struct ListType<'ctx> {
|
||||||
ty: PointerType<'ctx>,
|
ty: PointerType<'ctx>,
|
||||||
item: Option<BasicTypeEnum<'ctx>>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct ListStructFields<'ctx> {
|
|
||||||
/// Array pointer to content.
|
|
||||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
pub items: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
|
|
||||||
/// Number of items in the array.
|
|
||||||
#[value_type(usize)]
|
|
||||||
pub len: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ListStructFields<'ctx> {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_typed(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
|
||||||
let mut counter = FieldIndexCounter::default();
|
|
||||||
|
|
||||||
ListStructFields {
|
|
||||||
items: StructField::create(
|
|
||||||
&mut counter,
|
|
||||||
"items",
|
|
||||||
item.ptr_type(AddressSpace::default()),
|
|
||||||
),
|
|
||||||
len: StructField::create(&mut counter, "len", llvm_usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ListType<'ctx> {
|
impl<'ctx> ListType<'ctx> {
|
||||||
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
|
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
|
||||||
pub fn is_representable(
|
pub fn is_representable(
|
||||||
llvm_ty: PointerType<'ctx>,
|
llvm_ty: PointerType<'ctx>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let ctx = llvm_ty.get_context();
|
let llvm_list_ty = llvm_ty.get_element_type();
|
||||||
|
let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else {
|
||||||
|
return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}"));
|
||||||
|
};
|
||||||
|
if llvm_list_ty.count_fields() != 2 {
|
||||||
|
return Err(format!(
|
||||||
|
"Expected 2 fields in `list`, got {}",
|
||||||
|
llvm_list_ty.count_fields()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let llvm_ty = llvm_ty.get_element_type();
|
let list_size_ty = llvm_list_ty.get_field_type_at_index(0).unwrap();
|
||||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
let Ok(_) = PointerType::try_from(list_size_ty) else {
|
||||||
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
|
return Err(format!("Expected pointer type for `list.0`, got {list_size_ty}"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let fields = ListStructFields::new(ctx, llvm_usize);
|
let list_data_ty = llvm_list_ty.get_field_type_at_index(1).unwrap();
|
||||||
|
let Ok(list_data_ty) = IntType::try_from(list_data_ty) else {
|
||||||
|
return Err(format!("Expected int type for `list.1`, got {list_data_ty}"));
|
||||||
|
};
|
||||||
|
if list_data_ty.get_bit_width() != llvm_usize.get_bit_width() {
|
||||||
|
return Err(format!(
|
||||||
|
"Expected {}-bit int type for `list.1`, got {}-bit int",
|
||||||
|
llvm_usize.get_bit_width(),
|
||||||
|
list_data_ty.get_bit_width()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
check_struct_type_matches_fields(
|
Ok(())
|
||||||
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.
|
|
||||||
#[must_use]
|
|
||||||
fn fields(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> ListStructFields<'ctx> {
|
|
||||||
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(
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
element_type: Option<BasicTypeEnum<'ctx>>,
|
element_type: BasicTypeEnum<'ctx>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
) -> PointerType<'ctx> {
|
) -> PointerType<'ctx> {
|
||||||
let element_type = element_type.unwrap_or(llvm_usize.into());
|
// struct List { data: T*, size: size_t }
|
||||||
|
let field_tys = [element_type.ptr_type(AddressSpace::default()).into(), llvm_usize.into()];
|
||||||
let field_tys =
|
|
||||||
Self::fields(element_type, llvm_usize).into_iter().map(|field| field.1).collect_vec();
|
|
||||||
|
|
||||||
ctx.struct_type(&field_tys, false).ptr_type(AddressSpace::default())
|
ctx.struct_type(&field_tys, false).ptr_type(AddressSpace::default())
|
||||||
}
|
}
|
||||||
@ -120,50 +76,9 @@ impl<'ctx> ListType<'ctx> {
|
|||||||
element_type: BasicTypeEnum<'ctx>,
|
element_type: BasicTypeEnum<'ctx>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let llvm_usize = generator.get_size_type(ctx);
|
let llvm_usize = generator.get_size_type(ctx);
|
||||||
let llvm_list = Self::llvm_type(ctx, Some(element_type), llvm_usize);
|
let llvm_list = Self::llvm_type(ctx, element_type, llvm_usize);
|
||||||
|
|
||||||
Self { ty: llvm_list, item: Some(element_type), llvm_usize }
|
ListType::from_type(llvm_list, llvm_usize)
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance of [`ListType`] with an unknown element type.
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_untyped<G: CodeGenerator + ?Sized>(generator: &G, ctx: &'ctx Context) -> Self {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx);
|
|
||||||
let llvm_list = Self::llvm_type(ctx, None, llvm_usize);
|
|
||||||
|
|
||||||
Self { ty: llvm_list, item: None, llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`ListType`] 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 `item_type`
|
|
||||||
let elem_type = match &*ctx.unifier.get_ty_immutable(ty) {
|
|
||||||
TypeEnum::TObj { obj_id, params, .. }
|
|
||||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
iter_type_vars(params).next().unwrap().ty
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => panic!("Expected `list` type, but got {}", ctx.unifier.stringify(ty)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let llvm_elem_type = if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(ty) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ctx.get_llvm_type(generator, elem_type))
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
ty: Self::llvm_type(ctx.ctx, llvm_elem_type, llvm_usize),
|
|
||||||
item: llvm_elem_type,
|
|
||||||
llvm_usize,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an [`ListType`] from a [`PointerType`].
|
/// Creates an [`ListType`] from a [`PointerType`].
|
||||||
@ -171,141 +86,47 @@ impl<'ctx> ListType<'ctx> {
|
|||||||
pub fn from_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::is_representable(ptr_ty, llvm_usize).is_ok());
|
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||||
|
|
||||||
let ctx = ptr_ty.get_context();
|
ListType { ty: ptr_ty, llvm_usize }
|
||||||
|
|
||||||
// We are just searching for the index off a field - Slot an arbitrary element type in.
|
|
||||||
let item_field_idx =
|
|
||||||
Self::fields(ctx.i8_type().into(), llvm_usize).index_of_field(|f| f.items);
|
|
||||||
let item = unsafe {
|
|
||||||
ptr_ty
|
|
||||||
.get_element_type()
|
|
||||||
.into_struct_type()
|
|
||||||
.get_field_type_at_index_unchecked(item_field_idx)
|
|
||||||
.into_pointer_type()
|
|
||||||
.get_element_type()
|
|
||||||
};
|
|
||||||
let item = BasicTypeEnum::try_from(item).unwrap_or_else(|()| {
|
|
||||||
panic!(
|
|
||||||
"Expected BasicTypeEnum for list element type, got {}",
|
|
||||||
ptr_ty.get_element_type().print_to_string()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
ListType { ty: ptr_ty, item: Some(item), llvm_usize }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type of the `size` field of this `list` type.
|
/// Returns the type of the `size` field of this `list` type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn size_type(&self) -> IntType<'ctx> {
|
pub fn size_type(&self) -> IntType<'ctx> {
|
||||||
self.llvm_usize
|
self.as_base_type()
|
||||||
|
.get_element_type()
|
||||||
|
.into_struct_type()
|
||||||
|
.get_field_type_at_index(1)
|
||||||
|
.map(BasicTypeEnum::into_int_type)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the element type of this `list` type.
|
/// Returns the element type of this `list` type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn element_type(&self) -> Option<BasicTypeEnum<'ctx>> {
|
pub fn element_type(&self) -> AnyTypeEnum<'ctx> {
|
||||||
self.item
|
self.as_base_type()
|
||||||
|
.get_element_type()
|
||||||
|
.into_struct_type()
|
||||||
|
.get_field_type_at_index(0)
|
||||||
|
.map(BasicTypeEnum::into_pointer_type)
|
||||||
|
.map(PointerType::get_element_type)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`ListValue`] as if by calling `alloca` on the base type.
|
/// Allocates an instance of [`ListValue`] as if by calling `alloca` on the base type.
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
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 [`ListValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a [`ListValue`] on the stack using `item` of this [`ListType`] instance.
|
|
||||||
///
|
|
||||||
/// The returned list will contain:
|
|
||||||
///
|
|
||||||
/// - `data`: Allocated with `len` number of elements.
|
|
||||||
/// - `len`: Initialized to the value of `len` passed to this function.
|
|
||||||
#[must_use]
|
|
||||||
pub fn construct<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
len: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let len = ctx.builder.build_int_z_extend(len, self.llvm_usize, "").unwrap();
|
|
||||||
|
|
||||||
// Generate a runtime assertion if allocating a non-empty list with unknown element type
|
|
||||||
if ctx.registry.llvm_options.opt_level == OptimizationLevel::None && self.item.is_none() {
|
|
||||||
let len_eqz = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_compare(IntPredicate::EQ, len, self.llvm_usize.const_zero(), "")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
ctx.make_assert(
|
|
||||||
generator,
|
|
||||||
len_eqz,
|
|
||||||
"0:AssertionError",
|
|
||||||
"Cannot allocate a non-empty list with unknown element type",
|
|
||||||
[None, None, None],
|
|
||||||
ctx.current_loc,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let plist = self.alloca_var(generator, ctx, name);
|
|
||||||
plist.store_size(ctx, generator, len);
|
|
||||||
|
|
||||||
let item = self.item.unwrap_or(self.llvm_usize.into());
|
|
||||||
plist.create_data(ctx, item, None);
|
|
||||||
|
|
||||||
plist
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience function for creating a list with zero elements.
|
|
||||||
///
|
|
||||||
/// This function is preferred over [`ListType::construct`] if the length is known to always be
|
|
||||||
/// 0, as this function avoids injecting an IR assertion for checking if a non-empty untyped
|
|
||||||
/// list is being allocated.
|
|
||||||
///
|
|
||||||
/// The returned list will contain:
|
|
||||||
///
|
|
||||||
/// - `data`: Initialized to `(T*) 0`.
|
|
||||||
/// - `len`: Initialized to `0`.
|
|
||||||
#[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 {
|
|
||||||
let plist = self.alloca_var(generator, ctx, name);
|
|
||||||
|
|
||||||
plist.store_size(ctx, generator, self.llvm_usize.const_zero());
|
|
||||||
plist.create_data(ctx, self.item.unwrap_or(self.llvm_usize.into()), None);
|
|
||||||
|
|
||||||
plist
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an existing value into a [`ListValue`].
|
/// Converts an existing value into a [`ListValue`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn map_value(
|
pub fn map_value(
|
||||||
@ -341,8 +162,36 @@ impl<'ctx> ProxyType<'ctx> for ListType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -16,11 +16,7 @@
|
|||||||
//! the returned object. This is similar to a `new` expression in C++ but the object is allocated
|
//! the returned object. This is similar to a `new` expression in C++ but the object is allocated
|
||||||
//! on the stack.
|
//! on the stack.
|
||||||
|
|
||||||
use inkwell::{
|
use inkwell::{context::Context, types::BasicType, values::IntValue};
|
||||||
context::Context,
|
|
||||||
types::BasicType,
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
values::{ArraySliceValue, ProxyValue},
|
values::{ArraySliceValue, ProxyValue},
|
||||||
@ -28,13 +24,11 @@ use super::{
|
|||||||
};
|
};
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use tuple::*;
|
|
||||||
|
|
||||||
mod list;
|
mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
mod range;
|
mod range;
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
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.
|
||||||
@ -59,66 +53,23 @@ pub trait ProxyType<'ctx>: Into<Self::Base> {
|
|||||||
llvm_ty: Self::Base,
|
llvm_ty: Self::Base,
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
|
|
||||||
/// Returns the type that should be used in `alloca` IR statements.
|
/// Creates a new value of this type, returning the LLVM instance of this value.
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx>;
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
|
||||||
/// Creates a new value of this type by invoking `alloca` at the current builder location,
|
|
||||||
/// returning a [`PointerValue`] instance representing the allocated value.
|
|
||||||
fn raw_alloca(
|
|
||||||
&self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> PointerValue<'ctx> {
|
|
||||||
ctx.builder
|
|
||||||
.build_alloca(self.alloca_type().as_basic_type_enum(), name.unwrap_or_default())
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new value of this type by invoking `alloca` at the beginning of the function,
|
|
||||||
/// returning a [`PointerValue`] instance representing the allocated value.
|
|
||||||
fn raw_alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> <Self::Value as ProxyValue<'ctx>>::Base;
|
||||||
generator.gen_var_alloc(ctx, self.alloca_type().as_basic_type_enum(), name).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new array value of this type by invoking `alloca` at the current builder location,
|
/// Creates a new array value of this type, returning an [`ArraySliceValue`] encapsulating the
|
||||||
/// returning an [`ArraySliceValue`] encapsulating the resulting array.
|
/// resulting array.
|
||||||
fn array_alloca(
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
size: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> ArraySliceValue<'ctx> {
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
ctx.builder
|
|
||||||
.build_array_alloca(
|
|
||||||
self.alloca_type().as_basic_type_enum(),
|
|
||||||
size,
|
|
||||||
name.unwrap_or_default(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
size,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new array value of this type by invoking `alloca` at the beginning of the
|
|
||||||
/// function, returning an [`ArraySliceValue`] encapsulating the resulting array.
|
|
||||||
fn array_alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
size: IntValue<'ctx>,
|
size: IntValue<'ctx>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> ArraySliceValue<'ctx> {
|
) -> ArraySliceValue<'ctx>;
|
||||||
generator
|
|
||||||
.gen_array_var_alloc(ctx, self.alloca_type().as_basic_type_enum(), size, name)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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;
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
types::BasicTypeEnum,
|
|
||||||
values::{BasicValueEnum, IntValue},
|
|
||||||
AddressSpace,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
codegen::{
|
|
||||||
irrt,
|
|
||||||
stmt::gen_if_else_expr_callback,
|
|
||||||
types::{ndarray::NDArrayType, ListType, ProxyType},
|
|
||||||
values::{
|
|
||||||
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
|
|
||||||
TypedArrayLikeAdapter, TypedArrayLikeMutator,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
toplevel::helper::{arraylike_flatten_element_type, arraylike_get_ndims},
|
|
||||||
typecheck::typedef::{Type, TypeEnum},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get the expected `dtype` and `ndims` of the ndarray returned by `np_array(<list>)`.
|
|
||||||
fn get_list_object_dtype_and_ndims<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
list_ty: Type,
|
|
||||||
) -> (BasicTypeEnum<'ctx>, u64) {
|
|
||||||
let dtype = arraylike_flatten_element_type(&mut ctx.unifier, list_ty);
|
|
||||||
let ndims = arraylike_get_ndims(&mut ctx.unifier, list_ty);
|
|
||||||
|
|
||||||
(ctx.get_llvm_type(generator, dtype), ndims)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayType<'ctx> {
|
|
||||||
/// Implementation of `np_array(<list>, copy=True)`
|
|
||||||
fn construct_numpy_array_from_list_copy_true_impl<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(list_ty, list): (Type, ListValue<'ctx>),
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let (dtype, ndims_int) = get_list_object_dtype_and_ndims(generator, ctx, list_ty);
|
|
||||||
assert!(self.ndims >= ndims_int);
|
|
||||||
assert_eq!(dtype, self.dtype);
|
|
||||||
|
|
||||||
let list_value = list.as_i8_list(generator, ctx);
|
|
||||||
|
|
||||||
// Validate `list` has a consistent shape.
|
|
||||||
// Raise an exception if `list` is something abnormal like `[[1, 2], [3]]`.
|
|
||||||
// If `list` has a consistent shape, deduce the shape and write it to `shape`.
|
|
||||||
let ndims = self.llvm_usize.const_int(ndims_int, false);
|
|
||||||
let shape = ctx.builder.build_array_alloca(self.llvm_usize, ndims, "").unwrap();
|
|
||||||
let shape = ArraySliceValue::from_ptr_val(shape, ndims, None);
|
|
||||||
let shape = TypedArrayLikeAdapter::from(
|
|
||||||
shape,
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
irrt::ndarray::call_nac3_ndarray_array_set_and_validate_list_shape(
|
|
||||||
generator, ctx, list_value, ndims, &shape,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ndarray = Self::new(generator, ctx.ctx, dtype, ndims_int)
|
|
||||||
.construct_uninitialized(generator, ctx, name);
|
|
||||||
ndarray.copy_shape_from_array(generator, ctx, shape.base_ptr(ctx, generator));
|
|
||||||
unsafe { ndarray.create_data(generator, ctx) };
|
|
||||||
|
|
||||||
// Copy all contents from the list.
|
|
||||||
irrt::ndarray::call_nac3_ndarray_array_write_list_to_array(
|
|
||||||
generator, ctx, list_value, ndarray,
|
|
||||||
);
|
|
||||||
|
|
||||||
ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of `np_array(<list>, copy=None)`
|
|
||||||
fn construct_numpy_array_from_list_copy_none_impl<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(list_ty, list): (Type, ListValue<'ctx>),
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
// np_array without copying is only possible `list` is not nested.
|
|
||||||
//
|
|
||||||
// If `list` is `list[T]`, we can create an ndarray with `data` set
|
|
||||||
// to the array pointer of `list`.
|
|
||||||
//
|
|
||||||
// If `list` is `list[list[T]]` or worse, copy.
|
|
||||||
|
|
||||||
let (dtype, ndims) = get_list_object_dtype_and_ndims(generator, ctx, list_ty);
|
|
||||||
if ndims == 1 {
|
|
||||||
// `list` is not nested
|
|
||||||
assert_eq!(ndims, 1);
|
|
||||||
assert!(self.ndims >= ndims);
|
|
||||||
assert_eq!(dtype, self.dtype);
|
|
||||||
|
|
||||||
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
|
||||||
|
|
||||||
let ndarray = Self::new(generator, ctx.ctx, dtype, 1)
|
|
||||||
.construct_uninitialized(generator, ctx, name);
|
|
||||||
|
|
||||||
// Set data
|
|
||||||
let data = ctx
|
|
||||||
.builder
|
|
||||||
.build_pointer_cast(list.data().base_ptr(ctx, generator), llvm_pi8, "")
|
|
||||||
.unwrap();
|
|
||||||
ndarray.store_data(ctx, data);
|
|
||||||
|
|
||||||
// ndarray->shape[0] = list->len;
|
|
||||||
let shape = ndarray.shape();
|
|
||||||
let list_len = list.load_size(ctx, None);
|
|
||||||
unsafe {
|
|
||||||
shape.set_typed_unchecked(ctx, generator, &self.llvm_usize.const_zero(), list_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set strides, the `data` is contiguous
|
|
||||||
ndarray.set_strides_contiguous(generator, ctx);
|
|
||||||
|
|
||||||
ndarray
|
|
||||||
} else {
|
|
||||||
// `list` is nested, copy
|
|
||||||
self.construct_numpy_array_from_list_copy_true_impl(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(list_ty, list),
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of `np_array(<list>, copy=copy)`
|
|
||||||
fn construct_numpy_array_list_impl<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(list_ty, list): (Type, ListValue<'ctx>),
|
|
||||||
copy: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
assert_eq!(copy.get_type(), ctx.ctx.bool_type());
|
|
||||||
|
|
||||||
let (dtype, ndims) = get_list_object_dtype_and_ndims(generator, ctx, list_ty);
|
|
||||||
|
|
||||||
let ndarray = gen_if_else_expr_callback(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
|_generator, _ctx| Ok(copy),
|
|
||||||
|generator, ctx| {
|
|
||||||
let ndarray = self.construct_numpy_array_from_list_copy_true_impl(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(list_ty, list),
|
|
||||||
name,
|
|
||||||
);
|
|
||||||
Ok(Some(ndarray.as_base_value()))
|
|
||||||
},
|
|
||||||
|generator, ctx| {
|
|
||||||
let ndarray = self.construct_numpy_array_from_list_copy_none_impl(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(list_ty, list),
|
|
||||||
name,
|
|
||||||
);
|
|
||||||
Ok(Some(ndarray.as_base_value()))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.map(BasicValueEnum::into_pointer_value)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
NDArrayType::new(generator, ctx.ctx, dtype, ndims).map_value(ndarray, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of `np_array(<ndarray>, copy=copy)`.
|
|
||||||
pub fn construct_numpy_array_ndarray_impl<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
ndarray: NDArrayValue<'ctx>,
|
|
||||||
copy: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
assert_eq!(ndarray.get_type().dtype, self.dtype);
|
|
||||||
assert!(self.ndims >= ndarray.get_type().ndims);
|
|
||||||
assert_eq!(copy.get_type(), ctx.ctx.bool_type());
|
|
||||||
|
|
||||||
let ndarray_val = gen_if_else_expr_callback(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
|_generator, _ctx| Ok(copy),
|
|
||||||
|generator, ctx| {
|
|
||||||
let ndarray = ndarray.make_copy(generator, ctx); // Force copy
|
|
||||||
Ok(Some(ndarray.as_base_value()))
|
|
||||||
},
|
|
||||||
|_generator, _ctx| {
|
|
||||||
// No need to copy. Return `ndarray` itself.
|
|
||||||
Ok(Some(ndarray.as_base_value()))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.map(BasicValueEnum::into_pointer_value)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
ndarray.get_type().map_value(ndarray_val, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new ndarray like
|
|
||||||
/// [`np.array()`](https://numpy.org/doc/stable/reference/generated/numpy.array.html).
|
|
||||||
///
|
|
||||||
/// Note that the returned [`NDArrayValue`] may have fewer dimensions than is specified by this
|
|
||||||
/// instance. Use [`NDArrayValue::atleast_nd`] on the returned value if an `ndarray` instance
|
|
||||||
/// with the exact number of dimensions is needed.
|
|
||||||
pub fn construct_numpy_array<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(object_ty, object): (Type, BasicValueEnum<'ctx>),
|
|
||||||
copy: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
match &*ctx.unifier.get_ty_immutable(object_ty) {
|
|
||||||
TypeEnum::TObj { obj_id, .. }
|
|
||||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
let list = ListType::from_unifier_type(generator, ctx, object_ty)
|
|
||||||
.map_value(object.into_pointer_value(), None);
|
|
||||||
self.construct_numpy_array_list_impl(generator, ctx, (object_ty, list), copy, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeEnum::TObj { obj_id, .. }
|
|
||||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
|
|
||||||
.map_value(object.into_pointer_value(), None);
|
|
||||||
self.construct_numpy_array_ndarray_impl(generator, ctx, ndarray, copy, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => panic!("Unrecognized object type: {}", ctx.unifier.stringify(object_ty)), // Typechecker ensures this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
context::{AsContextRef, Context},
|
|
||||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
|
||||||
};
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
types::{
|
|
||||||
structure::{check_struct_type_matches_fields, StructField, StructFields},
|
|
||||||
ProxyType,
|
|
||||||
},
|
|
||||||
values::{ndarray::ShapeEntryValue, ProxyValue},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct ShapeEntryType<'ctx> {
|
|
||||||
ty: PointerType<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct ShapeEntryStructFields<'ctx> {
|
|
||||||
#[value_type(usize)]
|
|
||||||
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
|
||||||
pub shape: StructField<'ctx, PointerValue<'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.
|
|
||||||
#[must_use]
|
|
||||||
fn fields(
|
|
||||||
ctx: impl AsContextRef<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
) -> ShapeEntryStructFields<'ctx> {
|
|
||||||
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`.
|
|
||||||
#[must_use]
|
|
||||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance of [`ShapeEntryType`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn new<G: CodeGenerator + ?Sized>(generator: &G, ctx: &'ctx Context) -> Self {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx);
|
|
||||||
let llvm_ty = Self::llvm_type(ctx, llvm_usize);
|
|
||||||
|
|
||||||
Self { ty: llvm_ty, llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [`ShapeEntryType`] from a [`PointerType`] representing an `ShapeEntry`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
|
||||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
|
||||||
|
|
||||||
Self { ty: ptr_ty, llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates an instance of [`ShapeEntryValue`] as if by calling `alloca` on the base type.
|
|
||||||
#[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 [`ShapeEntryValue`] as if by calling `alloca` on the base type.
|
|
||||||
#[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 [`ShapeEntryValue`].
|
|
||||||
#[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, self.llvm_usize, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ProxyType<'ctx> for ShapeEntryType<'ctx> {
|
|
||||||
type Base = PointerType<'ctx>;
|
|
||||||
type Value = ShapeEntryValue<'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>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &'ctx Context,
|
|
||||||
llvm_ty: Self::Base,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
|
||||||
self.ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<ShapeEntryType<'ctx>> for PointerType<'ctx> {
|
|
||||||
fn from(value: ShapeEntryType<'ctx>) -> Self {
|
|
||||||
value.as_base_type()
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
ProxyType,
|
ProxyType,
|
||||||
},
|
},
|
||||||
values::{ndarray::ContiguousNDArrayValue, ProxyValue},
|
values::{ndarray::ContiguousNDArrayValue, ArraySliceValue, ProxyValue},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
},
|
},
|
||||||
toplevel::numpy::unpack_ndarray_var_tys,
|
toplevel::numpy::unpack_ndarray_var_tys,
|
||||||
@ -31,7 +31,7 @@ pub struct ContiguousNDArrayType<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||||
pub struct ContiguousNDArrayStructFields<'ctx> {
|
pub struct ContiguousNDArrayFields<'ctx> {
|
||||||
#[value_type(usize)]
|
#[value_type(usize)]
|
||||||
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
||||||
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
||||||
@ -40,12 +40,12 @@ pub struct ContiguousNDArrayStructFields<'ctx> {
|
|||||||
pub data: StructField<'ctx, PointerValue<'ctx>>,
|
pub data: StructField<'ctx, PointerValue<'ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> ContiguousNDArrayStructFields<'ctx> {
|
impl<'ctx> ContiguousNDArrayFields<'ctx> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_typed(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
pub fn new_typed(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||||
let mut counter = FieldIndexCounter::default();
|
let mut counter = FieldIndexCounter::default();
|
||||||
|
|
||||||
ContiguousNDArrayStructFields {
|
ContiguousNDArrayFields {
|
||||||
ndims: StructField::create(&mut counter, "ndims", llvm_usize),
|
ndims: StructField::create(&mut counter, "ndims", llvm_usize),
|
||||||
shape: StructField::create(
|
shape: StructField::create(
|
||||||
&mut counter,
|
&mut counter,
|
||||||
@ -72,7 +72,7 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
|
let fields = ContiguousNDArrayFields::new(ctx, llvm_usize);
|
||||||
|
|
||||||
check_struct_type_matches_fields(
|
check_struct_type_matches_fields(
|
||||||
fields,
|
fields,
|
||||||
@ -93,14 +93,14 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
|||||||
fn fields(
|
fn fields(
|
||||||
item: BasicTypeEnum<'ctx>,
|
item: BasicTypeEnum<'ctx>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
) -> ContiguousNDArrayStructFields<'ctx> {
|
) -> ContiguousNDArrayFields<'ctx> {
|
||||||
ContiguousNDArrayStructFields::new_typed(item, llvm_usize)
|
ContiguousNDArrayFields::new_typed(item, llvm_usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`NDArrayType::fields`].
|
/// See [`NDArrayType::fields`].
|
||||||
// TODO: Move this into e.g. StructProxyType
|
// TODO: Move this into e.g. StructProxyType
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_fields(&self) -> ContiguousNDArrayStructFields<'ctx> {
|
pub fn get_fields(&self) -> ContiguousNDArrayFields<'ctx> {
|
||||||
Self::fields(self.item, self.llvm_usize)
|
Self::fields(self.item, self.llvm_usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,37 +157,16 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
|||||||
Self { ty: ptr_ty, item, llvm_usize }
|
Self { ty: ptr_ty, item, llvm_usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base
|
/// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base type.
|
||||||
/// type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
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.item,
|
|
||||||
self.llvm_usize,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base
|
|
||||||
/// type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
self.item,
|
self.item,
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
name,
|
name,
|
||||||
@ -234,8 +213,36 @@ impl<'ctx> ProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -1,236 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
values::{BasicValueEnum, IntValue},
|
|
||||||
IntPredicate,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::NDArrayType;
|
|
||||||
use crate::{
|
|
||||||
codegen::{
|
|
||||||
irrt, types::ProxyType, values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
typecheck::typedef::Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get the zero value in `np.zeros()` of a `dtype`.
|
|
||||||
fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
|
||||||
.iter()
|
|
||||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
|
||||||
{
|
|
||||||
ctx.ctx.i32_type().const_zero().into()
|
|
||||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
|
||||||
.iter()
|
|
||||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
|
||||||
{
|
|
||||||
ctx.ctx.i64_type().const_zero().into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.float) {
|
|
||||||
ctx.ctx.f64_type().const_zero().into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
|
|
||||||
ctx.ctx.bool_type().const_zero().into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
|
||||||
ctx.gen_string(generator, "").into()
|
|
||||||
} else {
|
|
||||||
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the one value in `np.ones()` of a `dtype`.
|
|
||||||
fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
|
||||||
.iter()
|
|
||||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
|
||||||
{
|
|
||||||
let is_signed = ctx.unifier.unioned(dtype, ctx.primitives.int32);
|
|
||||||
ctx.ctx.i32_type().const_int(1, is_signed).into()
|
|
||||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
|
||||||
.iter()
|
|
||||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
|
||||||
{
|
|
||||||
let is_signed = ctx.unifier.unioned(dtype, ctx.primitives.int64);
|
|
||||||
ctx.ctx.i64_type().const_int(1, is_signed).into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.float) {
|
|
||||||
ctx.ctx.f64_type().const_float(1.0).into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
|
|
||||||
ctx.ctx.bool_type().const_int(1, false).into()
|
|
||||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
|
||||||
ctx.gen_string(generator, "1").into()
|
|
||||||
} else {
|
|
||||||
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayType<'ctx> {
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.empty`](https://numpy.org/doc/stable/reference/generated/numpy.empty.html).
|
|
||||||
pub fn construct_numpy_empty<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let ndarray = self.construct_uninitialized(generator, ctx, name);
|
|
||||||
|
|
||||||
// Validate `shape`
|
|
||||||
irrt::ndarray::call_nac3_ndarray_util_assert_shape_no_negative(generator, ctx, shape);
|
|
||||||
|
|
||||||
ndarray.copy_shape_from_array(generator, ctx, shape.base_ptr(ctx, generator));
|
|
||||||
unsafe { ndarray.create_data(generator, ctx) };
|
|
||||||
|
|
||||||
ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.full`](https://numpy.org/doc/stable/reference/generated/numpy.full.html).
|
|
||||||
pub fn construct_numpy_full<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
fill_value: BasicValueEnum<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let ndarray = self.construct_numpy_empty(generator, ctx, shape, name);
|
|
||||||
ndarray.fill(generator, ctx, fill_value);
|
|
||||||
ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.zero`](https://numpy.org/doc/stable/reference/generated/numpy.zeros.html).
|
|
||||||
pub fn construct_numpy_zeros<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
assert_eq!(
|
|
||||||
ctx.get_llvm_type(generator, dtype),
|
|
||||||
self.dtype,
|
|
||||||
"Expected LLVM dtype={} but got {}",
|
|
||||||
self.dtype.print_to_string(),
|
|
||||||
ctx.get_llvm_type(generator, dtype).print_to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let fill_value = ndarray_zero_value(generator, ctx, dtype);
|
|
||||||
self.construct_numpy_full(generator, ctx, shape, fill_value, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.ones`](https://numpy.org/doc/stable/reference/generated/numpy.ones.html).
|
|
||||||
pub fn construct_numpy_ones<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
assert_eq!(
|
|
||||||
ctx.get_llvm_type(generator, dtype),
|
|
||||||
self.dtype,
|
|
||||||
"Expected LLVM dtype={} but got {}",
|
|
||||||
self.dtype.print_to_string(),
|
|
||||||
ctx.get_llvm_type(generator, dtype).print_to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let fill_value = ndarray_one_value(generator, ctx, dtype);
|
|
||||||
self.construct_numpy_full(generator, ctx, shape, fill_value, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.eye`](https://numpy.org/doc/stable/reference/generated/numpy.eye.html).
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn construct_numpy_eye<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
nrows: IntValue<'ctx>,
|
|
||||||
ncols: IntValue<'ctx>,
|
|
||||||
offset: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
assert_eq!(
|
|
||||||
ctx.get_llvm_type(generator, dtype),
|
|
||||||
self.dtype,
|
|
||||||
"Expected LLVM dtype={} but got {}",
|
|
||||||
self.dtype.print_to_string(),
|
|
||||||
ctx.get_llvm_type(generator, dtype).print_to_string(),
|
|
||||||
);
|
|
||||||
assert_eq!(nrows.get_type(), self.llvm_usize);
|
|
||||||
assert_eq!(ncols.get_type(), self.llvm_usize);
|
|
||||||
assert_eq!(offset.get_type(), self.llvm_usize);
|
|
||||||
|
|
||||||
let ndzero = ndarray_zero_value(generator, ctx, dtype);
|
|
||||||
let ndone = ndarray_one_value(generator, ctx, dtype);
|
|
||||||
|
|
||||||
let ndarray = self.construct_dyn_shape(generator, ctx, &[nrows, ncols], name);
|
|
||||||
|
|
||||||
// Create data and make the matrix like look np.eye()
|
|
||||||
unsafe {
|
|
||||||
ndarray.create_data(generator, ctx);
|
|
||||||
}
|
|
||||||
ndarray
|
|
||||||
.foreach(generator, ctx, |generator, ctx, _, nditer| {
|
|
||||||
// NOTE: rows and cols can never be zero here, since this ndarray's `np.size` would be zero
|
|
||||||
// and this loop would not execute.
|
|
||||||
|
|
||||||
let indices = nditer.get_indices();
|
|
||||||
|
|
||||||
let row_i = unsafe {
|
|
||||||
indices.get_typed_unchecked(ctx, generator, &self.llvm_usize.const_zero(), None)
|
|
||||||
};
|
|
||||||
let col_i = unsafe {
|
|
||||||
indices.get_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&self.llvm_usize.const_int(1, false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let be_one = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
|
||||||
ctx.builder.build_int_add(row_i, offset, "").unwrap(),
|
|
||||||
col_i,
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let value = ctx.builder.build_select(be_one, ndone, ndzero, "value").unwrap();
|
|
||||||
|
|
||||||
let p = nditer.get_pointer(ctx);
|
|
||||||
ctx.builder.build_store(p, value).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an ndarray like
|
|
||||||
/// [`np.identity`](https://numpy.org/doc/stable/reference/generated/numpy.identity.html).
|
|
||||||
pub fn construct_numpy_identity<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dtype: Type,
|
|
||||||
size: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let offset = self.llvm_usize.const_zero();
|
|
||||||
self.construct_numpy_eye(generator, ctx, dtype, size, size, offset, name)
|
|
||||||
}
|
|
||||||
}
|
|
@ -90,33 +90,15 @@ impl<'ctx> NDIndexType<'ctx> {
|
|||||||
Self { ty: ptr_ty, llvm_usize }
|
Self { ty: ptr_ty, llvm_usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`NDIndexValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
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 [`NDIndexValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
@ -132,7 +114,7 @@ impl<'ctx> NDIndexType<'ctx> {
|
|||||||
) -> ArraySliceValue<'ctx> {
|
) -> ArraySliceValue<'ctx> {
|
||||||
// Allocate the LLVM ndindices.
|
// Allocate the LLVM ndindices.
|
||||||
let num_ndindices = self.llvm_usize.const_int(in_ndindices.len() as u64, false);
|
let num_ndindices = self.llvm_usize.const_int(in_ndindices.len() as u64, false);
|
||||||
let ndindices = self.array_alloca_var(generator, ctx, num_ndindices, None);
|
let ndindices = self.array_alloca(generator, ctx, num_ndindices, None);
|
||||||
|
|
||||||
// Initialize all of them.
|
// Initialize all of them.
|
||||||
for (i, in_ndindex) in in_ndindices.iter().enumerate() {
|
for (i, in_ndindex) in in_ndindices.iter().enumerate() {
|
||||||
@ -189,8 +171,36 @@ impl<'ctx> ProxyType<'ctx> for NDIndexType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -1,187 +0,0 @@
|
|||||||
use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
stmt::gen_for_callback,
|
|
||||||
types::{
|
|
||||||
ndarray::{NDArrayType, NDIterType},
|
|
||||||
ProxyType,
|
|
||||||
},
|
|
||||||
values::{
|
|
||||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
|
||||||
ArrayLikeValue, ProxyValue,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayType<'ctx> {
|
|
||||||
/// Generate LLVM IR to broadcast `ndarray`s together, and starmap through them with `mapping`
|
|
||||||
/// elementwise.
|
|
||||||
///
|
|
||||||
/// `mapping` is an LLVM IR generator. The input of `mapping` is the list of elements when
|
|
||||||
/// iterating through the input `ndarrays` after broadcasting. The output of `mapping` is the
|
|
||||||
/// result of the elementwise operation.
|
|
||||||
///
|
|
||||||
/// `out` specifies whether the result should be a new ndarray or to be written an existing
|
|
||||||
/// ndarray.
|
|
||||||
pub fn broadcast_starmap<'a, G, MappingFn>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
ndarrays: &[NDArrayValue<'ctx>],
|
|
||||||
out: NDArrayOut<'ctx>,
|
|
||||||
mapping: MappingFn,
|
|
||||||
) -> Result<<Self as ProxyType<'ctx>>::Value, String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
MappingFn: FnOnce(
|
|
||||||
&mut G,
|
|
||||||
&mut CodeGenContext<'ctx, 'a>,
|
|
||||||
&[BasicValueEnum<'ctx>],
|
|
||||||
) -> Result<BasicValueEnum<'ctx>, String>,
|
|
||||||
{
|
|
||||||
// Broadcast inputs
|
|
||||||
let broadcast_result = self.broadcast(generator, ctx, ndarrays);
|
|
||||||
|
|
||||||
let out_ndarray = match out {
|
|
||||||
NDArrayOut::NewNDArray { dtype } => {
|
|
||||||
// Create a new ndarray based on the broadcast shape.
|
|
||||||
let result_ndarray =
|
|
||||||
NDArrayType::new(generator, ctx.ctx, dtype, broadcast_result.ndims)
|
|
||||||
.construct_uninitialized(generator, ctx, None);
|
|
||||||
result_ndarray.copy_shape_from_array(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
broadcast_result.shape.base_ptr(ctx, generator),
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
result_ndarray.create_data(generator, ctx);
|
|
||||||
}
|
|
||||||
result_ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
NDArrayOut::WriteToNDArray { ndarray: result_ndarray } => {
|
|
||||||
// Use an existing ndarray.
|
|
||||||
|
|
||||||
// Check that its shape is compatible with the broadcast shape.
|
|
||||||
result_ndarray.assert_can_be_written_by_out(generator, ctx, broadcast_result.shape);
|
|
||||||
result_ndarray
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Map element-wise and store results into `mapped_ndarray`.
|
|
||||||
let nditer = NDIterType::new(generator, ctx.ctx).construct(generator, ctx, out_ndarray);
|
|
||||||
gen_for_callback(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
Some("broadcast_starmap"),
|
|
||||||
|generator, ctx| {
|
|
||||||
// Create NDIters for all broadcasted input ndarrays.
|
|
||||||
let other_nditers = broadcast_result
|
|
||||||
.ndarrays
|
|
||||||
.iter()
|
|
||||||
.map(|ndarray| {
|
|
||||||
NDIterType::new(generator, ctx.ctx).construct(generator, ctx, *ndarray)
|
|
||||||
})
|
|
||||||
.collect_vec();
|
|
||||||
Ok((nditer, other_nditers))
|
|
||||||
},
|
|
||||||
|generator, ctx, (out_nditer, _in_nditers)| {
|
|
||||||
// We can simply use `out_nditer`'s `has_element()`.
|
|
||||||
// `in_nditers`' `has_element()`s should return the same value.
|
|
||||||
Ok(out_nditer.has_element(generator, ctx))
|
|
||||||
},
|
|
||||||
|generator, ctx, _hooks, (out_nditer, in_nditers)| {
|
|
||||||
// Get all the scalars from the broadcasted input ndarrays, pass them to `mapping`,
|
|
||||||
// and write to `out_ndarray`.
|
|
||||||
let in_scalars =
|
|
||||||
in_nditers.iter().map(|nditer| nditer.get_scalar(ctx)).collect_vec();
|
|
||||||
|
|
||||||
let result = mapping(generator, ctx, &in_scalars)?;
|
|
||||||
|
|
||||||
let p = out_nditer.get_pointer(ctx);
|
|
||||||
ctx.builder.build_store(p, result).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
|generator, ctx, (out_nditer, in_nditers)| {
|
|
||||||
// Advance all iterators
|
|
||||||
out_nditer.next(generator, ctx);
|
|
||||||
in_nditers.iter().for_each(|nditer| nditer.next(generator, ctx));
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(out_ndarray)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ScalarOrNDArray<'ctx> {
|
|
||||||
/// Starmap through a list of inputs using `mapping`, where an input could be an ndarray, a
|
|
||||||
/// scalar.
|
|
||||||
///
|
|
||||||
/// This function is very helpful when implementing NumPy functions that takes on either scalars
|
|
||||||
/// or ndarrays or a mix of them as their inputs and produces either an ndarray with broadcast,
|
|
||||||
/// or a scalar if all its inputs are all scalars.
|
|
||||||
///
|
|
||||||
/// For example ,this function can be used to implement `np.add`, which has the following
|
|
||||||
/// behaviors:
|
|
||||||
///
|
|
||||||
/// - `np.add(3, 4) = 7` # (scalar, scalar) -> scalar
|
|
||||||
/// - `np.add(3, np.array([4, 5, 6]))` # (scalar, ndarray) -> ndarray; the first `scalar` is
|
|
||||||
/// converted into an ndarray and broadcasted.
|
|
||||||
/// - `np.add(np.array([[1], [2], [3]]), np.array([[4, 5, 6]]))` # (ndarray, ndarray) ->
|
|
||||||
/// ndarray; there is broadcasting.
|
|
||||||
///
|
|
||||||
/// ## Details:
|
|
||||||
///
|
|
||||||
/// If `inputs` are all [`ScalarOrNDArray::Scalar`], the output will be a
|
|
||||||
/// [`ScalarOrNDArray::Scalar`] with type `ret_dtype`.
|
|
||||||
///
|
|
||||||
/// Otherwise (if there are any [`ScalarOrNDArray::NDArray`] in `inputs`), all inputs will be
|
|
||||||
/// 'as-ndarray'-ed into ndarrays, then all inputs (now all ndarrays) will be passed to
|
|
||||||
/// [`NDArrayValue::broadcasting_starmap`] and **create** a new ndarray with dtype `ret_dtype`.
|
|
||||||
pub fn broadcasting_starmap<'a, G, MappingFn>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
inputs: &[ScalarOrNDArray<'ctx>],
|
|
||||||
ret_dtype: BasicTypeEnum<'ctx>,
|
|
||||||
mapping: MappingFn,
|
|
||||||
) -> Result<ScalarOrNDArray<'ctx>, String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
MappingFn: FnOnce(
|
|
||||||
&mut G,
|
|
||||||
&mut CodeGenContext<'ctx, 'a>,
|
|
||||||
&[BasicValueEnum<'ctx>],
|
|
||||||
) -> Result<BasicValueEnum<'ctx>, String>,
|
|
||||||
{
|
|
||||||
// Check if all inputs are Scalars
|
|
||||||
let all_scalars: Option<Vec<_>> =
|
|
||||||
inputs.iter().map(BasicValueEnum::<'ctx>::try_from).try_collect().ok();
|
|
||||||
|
|
||||||
if let Some(scalars) = all_scalars {
|
|
||||||
let scalars = scalars.iter().copied().collect_vec();
|
|
||||||
let value = mapping(generator, ctx, &scalars)?;
|
|
||||||
|
|
||||||
Ok(ScalarOrNDArray::Scalar(value))
|
|
||||||
} else {
|
|
||||||
// Promote all input to ndarrays and map through them.
|
|
||||||
let inputs = inputs.iter().map(|input| input.to_ndarray(generator, ctx)).collect_vec();
|
|
||||||
let ndarray = NDArrayType::new_broadcast(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
ret_dtype,
|
|
||||||
&inputs.iter().map(NDArrayValue::get_type).collect_vec(),
|
|
||||||
)
|
|
||||||
.broadcast_starmap(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&inputs,
|
|
||||||
NDArrayOut::NewNDArray { dtype: ret_dtype },
|
|
||||||
mapping,
|
|
||||||
)?;
|
|
||||||
Ok(ScalarOrNDArray::NDArray(ndarray))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,23 +14,18 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeMutator},
|
values::{ndarray::NDArrayValue, ArraySliceValue, ProxyValue, TypedArrayLikeMutator},
|
||||||
{CodeGenContext, CodeGenerator},
|
{CodeGenContext, CodeGenerator},
|
||||||
},
|
},
|
||||||
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},
|
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},
|
||||||
typecheck::typedef::Type,
|
typecheck::typedef::Type,
|
||||||
};
|
};
|
||||||
pub use broadcast::*;
|
|
||||||
pub use contiguous::*;
|
pub use contiguous::*;
|
||||||
pub use indexing::*;
|
pub use indexing::*;
|
||||||
pub use nditer::*;
|
pub use nditer::*;
|
||||||
|
|
||||||
mod array;
|
|
||||||
mod broadcast;
|
|
||||||
mod contiguous;
|
mod contiguous;
|
||||||
pub mod factory;
|
|
||||||
mod indexing;
|
mod indexing;
|
||||||
mod map;
|
|
||||||
mod nditer;
|
mod nditer;
|
||||||
|
|
||||||
/// Proxy type for a `ndarray` type in LLVM.
|
/// Proxy type for a `ndarray` type in LLVM.
|
||||||
@ -38,7 +33,7 @@ mod nditer;
|
|||||||
pub struct NDArrayType<'ctx> {
|
pub struct NDArrayType<'ctx> {
|
||||||
ty: PointerType<'ctx>,
|
ty: PointerType<'ctx>,
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
dtype: BasicTypeEnum<'ctx>,
|
||||||
ndims: u64,
|
ndims: Option<u64>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +108,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
generator: &G,
|
generator: &G,
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
dtype: BasicTypeEnum<'ctx>,
|
||||||
ndims: u64,
|
ndims: Option<u64>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let llvm_usize = generator.get_size_type(ctx);
|
let llvm_usize = generator.get_size_type(ctx);
|
||||||
let llvm_ndarray = Self::llvm_type(ctx, llvm_usize);
|
let llvm_ndarray = Self::llvm_type(ctx, llvm_usize);
|
||||||
@ -121,20 +116,6 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
NDArrayType { ty: llvm_ndarray, dtype, ndims, llvm_usize }
|
NDArrayType { ty: llvm_ndarray, dtype, ndims, llvm_usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an instance of [`NDArrayType`] as a result of a broadcast operation over one or more
|
|
||||||
/// `ndarray` operands.
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_broadcast<G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &'ctx Context,
|
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
|
||||||
inputs: &[NDArrayType<'ctx>],
|
|
||||||
) -> Self {
|
|
||||||
assert!(!inputs.is_empty());
|
|
||||||
|
|
||||||
Self::new(generator, ctx, dtype, inputs.iter().map(NDArrayType::ndims).max().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance of [`NDArrayType`] with `ndims` of 0.
|
/// Creates an instance of [`NDArrayType`] with `ndims` of 0.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_unsized<G: CodeGenerator + ?Sized>(
|
pub fn new_unsized<G: CodeGenerator + ?Sized>(
|
||||||
@ -145,7 +126,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
let llvm_usize = generator.get_size_type(ctx);
|
let llvm_usize = generator.get_size_type(ctx);
|
||||||
let llvm_ndarray = Self::llvm_type(ctx, llvm_usize);
|
let llvm_ndarray = Self::llvm_type(ctx, llvm_usize);
|
||||||
|
|
||||||
NDArrayType { ty: llvm_ndarray, dtype, ndims: 0, llvm_usize }
|
NDArrayType { ty: llvm_ndarray, dtype, ndims: Some(0), llvm_usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an [`NDArrayType`] from a [unifier type][Type].
|
/// Creates an [`NDArrayType`] from a [unifier type][Type].
|
||||||
@ -164,7 +145,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
NDArrayType {
|
NDArrayType {
|
||||||
ty: Self::llvm_type(ctx.ctx, llvm_usize),
|
ty: Self::llvm_type(ctx.ctx, llvm_usize),
|
||||||
dtype: llvm_dtype,
|
dtype: llvm_dtype,
|
||||||
ndims,
|
ndims: Some(ndims),
|
||||||
llvm_usize,
|
llvm_usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +155,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
pub fn from_type(
|
pub fn from_type(
|
||||||
ptr_ty: PointerType<'ctx>,
|
ptr_ty: PointerType<'ctx>,
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
dtype: BasicTypeEnum<'ctx>,
|
||||||
ndims: u64,
|
ndims: Option<u64>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||||
@ -196,40 +177,20 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
|
|
||||||
/// Returns the number of dimensions of this `ndarray` type.
|
/// Returns the number of dimensions of this `ndarray` type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn ndims(&self) -> u64 {
|
pub fn ndims(&self) -> Option<u64> {
|
||||||
self.ndims
|
self.ndims
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`NDArrayValue`] as if by calling `alloca` on the base type.
|
/// Allocates an instance of [`NDArrayValue`] as if by calling `alloca` on the base type.
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
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.dtype,
|
|
||||||
self.ndims,
|
|
||||||
self.llvm_usize,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates an instance of [`NDArrayValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
self.dtype,
|
self.dtype,
|
||||||
self.ndims,
|
self.ndims,
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
@ -253,7 +214,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
ndims: IntValue<'ctx>,
|
ndims: IntValue<'ctx>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
let ndarray = self.alloca_var(generator, ctx, name);
|
let ndarray = self.alloca(generator, ctx, name);
|
||||||
|
|
||||||
let itemsize = ctx
|
let itemsize = ctx
|
||||||
.builder
|
.builder
|
||||||
@ -286,7 +247,35 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
let ndims = self.llvm_usize.const_int(self.ndims, false);
|
assert!(self.ndims.is_some(), "NDArrayType::construct can only be called on an instance with compile-time known ndims (self.ndims = Some(ndims))");
|
||||||
|
|
||||||
|
let Some(ndims) = self.ndims.map(|ndims| self.llvm_usize.const_int(ndims, false)) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.construct_impl(generator, ctx, ndims, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate an [`NDArrayValue`] on the stack given its `ndims` and `dtype`.
|
||||||
|
///
|
||||||
|
/// `shape` and `strides` will be automatically allocated onto the stack.
|
||||||
|
///
|
||||||
|
/// The returned ndarray's content will be:
|
||||||
|
/// - `data`: uninitialized.
|
||||||
|
/// - `itemsize`: set to the size of `dtype`.
|
||||||
|
/// - `ndims`: set to the value of `ndims`.
|
||||||
|
/// - `shape`: allocated with an array of length `ndims` with uninitialized values.
|
||||||
|
/// - `strides`: allocated with an array of length `ndims` with uninitialized values.
|
||||||
|
#[deprecated = "Prefer construct_uninitialized or construct_*_shape."]
|
||||||
|
#[must_use]
|
||||||
|
pub fn construct_dyn_ndims<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
ndims: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
|
assert!(self.ndims.is_none(), "NDArrayType::construct_dyn_ndims can only be called on an instance with compile-time unknown ndims (self.ndims = None)");
|
||||||
|
|
||||||
self.construct_impl(generator, ctx, ndims, name)
|
self.construct_impl(generator, ctx, ndims, name)
|
||||||
}
|
}
|
||||||
@ -302,9 +291,9 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
shape: &[u64],
|
shape: &[u64],
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
assert_eq!(shape.len() as u64, self.ndims);
|
assert!(self.ndims.is_none_or(|ndims| shape.len() as u64 == ndims));
|
||||||
|
|
||||||
let ndarray = Self::new(generator, ctx.ctx, self.dtype, shape.len() as u64)
|
let ndarray = Self::new(generator, ctx.ctx, self.dtype, Some(shape.len() as u64))
|
||||||
.construct_uninitialized(generator, ctx, name);
|
.construct_uninitialized(generator, ctx, name);
|
||||||
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
@ -337,9 +326,9 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
shape: &[IntValue<'ctx>],
|
shape: &[IntValue<'ctx>],
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
assert_eq!(shape.len() as u64, self.ndims);
|
assert!(self.ndims.is_none_or(|ndims| shape.len() as u64 == ndims));
|
||||||
|
|
||||||
let ndarray = Self::new(generator, ctx.ctx, self.dtype, shape.len() as u64)
|
let ndarray = Self::new(generator, ctx.ctx, self.dtype, Some(shape.len() as u64))
|
||||||
.construct_uninitialized(generator, ctx, name);
|
.construct_uninitialized(generator, ctx, name);
|
||||||
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
@ -379,7 +368,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
|||||||
let value = value.as_basic_value_enum();
|
let value = value.as_basic_value_enum();
|
||||||
|
|
||||||
assert_eq!(value.get_type(), self.dtype);
|
assert_eq!(value.get_type(), self.dtype);
|
||||||
assert_eq!(self.ndims, 0);
|
assert!(self.ndims.is_none_or(|ndims| ndims == 0));
|
||||||
|
|
||||||
// We have to put the value on the stack to get a data pointer.
|
// We have to put the value on the stack to get a data pointer.
|
||||||
let data = ctx.builder.build_alloca(value.get_type(), "construct_unsized").unwrap();
|
let data = ctx.builder.build_alloca(value.get_type(), "construct_unsized").unwrap();
|
||||||
@ -436,8 +425,36 @@ impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -14,7 +14,7 @@ use crate::codegen::{
|
|||||||
types::structure::{check_struct_type_matches_fields, StructField, StructFields},
|
types::structure::{check_struct_type_matches_fields, StructField, StructFields},
|
||||||
values::{
|
values::{
|
||||||
ndarray::{NDArrayValue, NDIterValue},
|
ndarray::{NDArrayValue, NDIterValue},
|
||||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
|
ArraySliceValue, ProxyValue,
|
||||||
},
|
},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
@ -109,31 +109,8 @@ impl<'ctx> NDIterType<'ctx> {
|
|||||||
self.llvm_usize
|
self.llvm_usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`NDIterValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
pub fn alloca<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
parent: NDArrayValue<'ctx>,
|
|
||||||
indices: ArraySliceValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
|
||||||
self.raw_alloca(ctx, name),
|
|
||||||
parent,
|
|
||||||
indices,
|
|
||||||
self.llvm_usize,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates an instance of [`NDIterValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
@ -142,7 +119,7 @@ impl<'ctx> NDIterType<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
parent,
|
parent,
|
||||||
indices,
|
indices,
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
@ -151,11 +128,6 @@ impl<'ctx> NDIterType<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate an [`NDIter`] that iterates through the given `ndarray`.
|
/// Allocate an [`NDIter`] that iterates through the given `ndarray`.
|
||||||
///
|
|
||||||
/// Note: This function allocates an array on the stack at the current builder location, which
|
|
||||||
/// may lead to stack explosion if called in a hot loop. Therefore, callers are recommended to
|
|
||||||
/// call `llvm.stacksave` before calling this function and call `llvm.stackrestore` after the
|
|
||||||
/// [`NDIter`] is no longer needed.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn construct<G: CodeGenerator + ?Sized>(
|
pub fn construct<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
@ -163,18 +135,22 @@ impl<'ctx> NDIterType<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
ndarray: NDArrayValue<'ctx>,
|
ndarray: NDArrayValue<'ctx>,
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
let nditer = self.raw_alloca_var(generator, ctx, None);
|
let nditer = self.raw_alloca(generator, ctx, None);
|
||||||
let ndims = self.llvm_usize.const_int(ndarray.get_type().ndims(), false);
|
let ndims = ndarray.load_ndims(ctx);
|
||||||
|
|
||||||
// The caller has the responsibility to allocate 'indices' for `NDIter`.
|
// The caller has the responsibility to allocate 'indices' for `NDIter`.
|
||||||
let indices =
|
let indices =
|
||||||
generator.gen_array_var_alloc(ctx, self.llvm_usize.into(), ndims, None).unwrap();
|
generator.gen_array_var_alloc(ctx, self.llvm_usize.into(), ndims, None).unwrap();
|
||||||
let indices =
|
|
||||||
TypedArrayLikeAdapter::from(indices, |_, _, v| v.into_int_value(), |_, _, v| v.into());
|
|
||||||
|
|
||||||
let nditer = self.map_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
|
let nditer = <Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||||
|
nditer,
|
||||||
|
ndarray,
|
||||||
|
indices,
|
||||||
|
self.llvm_usize,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, &indices);
|
irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, indices);
|
||||||
|
|
||||||
nditer
|
nditer
|
||||||
}
|
}
|
||||||
@ -221,8 +197,36 @@ impl<'ctx> ProxyType<'ctx> for NDIterType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use inkwell::{
|
use inkwell::{
|
||||||
context::Context,
|
context::Context,
|
||||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||||
|
values::IntValue,
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ProxyType;
|
use super::ProxyType;
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
values::{ProxyValue, RangeValue},
|
values::{ArraySliceValue, ProxyValue, RangeValue},
|
||||||
{CodeGenContext, CodeGenerator},
|
{CodeGenContext, CodeGenerator},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,29 +78,15 @@ impl<'ctx> RangeType<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca<G: CodeGenerator + ?Sized>(
|
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,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -139,8 +126,36 @@ impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty)
|
Self::is_representable(llvm_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -5,7 +5,6 @@ use inkwell::{
|
|||||||
types::{BasicTypeEnum, IntType, StructType},
|
types::{BasicTypeEnum, IntType, StructType},
|
||||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::codegen::CodeGenContext;
|
use crate::codegen::CodeGenContext;
|
||||||
|
|
||||||
@ -56,20 +55,6 @@ pub trait StructFields<'ctx>: Eq + Copy {
|
|||||||
{
|
{
|
||||||
self.into_vec().into_iter()
|
self.into_vec().into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the field index of a field in this structure.
|
|
||||||
fn index_of_field<V>(&self, name: impl FnOnce(&Self) -> StructField<'ctx, V>) -> u32
|
|
||||||
where
|
|
||||||
V: BasicValue<'ctx> + TryFrom<BasicValueEnum<'ctx>, Error = ()>,
|
|
||||||
{
|
|
||||||
let field_name = name(self).name;
|
|
||||||
self.index_of_field_name(field_name).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the field index of a field with the given name in this structure.
|
|
||||||
fn index_of_field_name(&self, field_name: &str) -> Option<u32> {
|
|
||||||
self.iter().find_position(|(name, _)| *name == field_name).map(|(idx, _)| idx as u32)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single field of an LLVM structure.
|
/// A single field of an LLVM structure.
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
context::Context,
|
|
||||||
types::{BasicType, BasicTypeEnum, IntType, StructType},
|
|
||||||
values::BasicValueEnum,
|
|
||||||
};
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::ProxyType;
|
|
||||||
use crate::{
|
|
||||||
codegen::{
|
|
||||||
values::{ProxyValue, TupleValue},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
typecheck::typedef::{Type, TypeEnum},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct TupleType<'ctx> {
|
|
||||||
ty: StructType<'ctx>,
|
|
||||||
llvm_usize: IntType<'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.
|
|
||||||
#[must_use]
|
|
||||||
fn llvm_type(ctx: &'ctx Context, tys: &[BasicTypeEnum<'ctx>]) -> StructType<'ctx> {
|
|
||||||
ctx.struct_type(tys, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance of [`TupleType`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn new<G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &'ctx Context,
|
|
||||||
tys: &[BasicTypeEnum<'ctx>],
|
|
||||||
) -> Self {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx);
|
|
||||||
let llvm_tuple = Self::llvm_type(ctx, tys);
|
|
||||||
|
|
||||||
Self { ty: llvm_tuple, llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`TupleType`] from a [unifier type][Type].
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
ty: Type,
|
|
||||||
) -> Self {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
// Sanity check on object type.
|
|
||||||
let TypeEnum::TTuple { ty: tys, .. } = &*ctx.unifier.get_ty_immutable(ty) else {
|
|
||||||
panic!("Expected type to be a TypeEnum::TTuple, got {}", ctx.unifier.stringify(ty));
|
|
||||||
};
|
|
||||||
|
|
||||||
let llvm_tys = tys.iter().map(|ty| ctx.get_llvm_type(generator, *ty)).collect_vec();
|
|
||||||
Self { ty: Self::llvm_type(ctx.ctx, &llvm_tys), llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`TupleType`] from a [`StructType`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
|
||||||
debug_assert!(Self::is_representable(struct_ty).is_ok());
|
|
||||||
|
|
||||||
TupleType { ty: struct_ty, llvm_usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of elements present in this [`TupleType`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn num_elements(&self) -> u32 {
|
|
||||||
self.ty.count_fields()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the type of the tuple element at the given `index`, or [`None`] if `index` is out of
|
|
||||||
/// range.
|
|
||||||
#[must_use]
|
|
||||||
pub fn type_at_index(&self, index: u32) -> Option<BasicTypeEnum<'ctx>> {
|
|
||||||
if index < self.num_elements() {
|
|
||||||
Some(unsafe { self.type_at_index_unchecked(index) })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the type of the tuple element at the given `index`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that the index is valid.
|
|
||||||
#[must_use]
|
|
||||||
pub unsafe fn type_at_index_unchecked(&self, index: u32) -> BasicTypeEnum<'ctx> {
|
|
||||||
self.ty.get_field_type_at_index_unchecked(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value.
|
|
||||||
#[must_use]
|
|
||||||
pub fn construct(
|
|
||||||
&self,
|
|
||||||
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
|
|
||||||
/// objects.
|
|
||||||
#[must_use]
|
|
||||||
pub fn construct_from_objects<I: IntoIterator<Item = BasicValueEnum<'ctx>>>(
|
|
||||||
&self,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
objects: I,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> <Self as ProxyType<'ctx>>::Value {
|
|
||||||
let values = objects.into_iter().collect_vec();
|
|
||||||
|
|
||||||
assert_eq!(values.len(), self.num_elements() as usize);
|
|
||||||
assert!(values
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } }));
|
|
||||||
|
|
||||||
let mut value = self.construct(ctx, name);
|
|
||||||
for (i, val) in values.into_iter().enumerate() {
|
|
||||||
value.store_element(ctx, i as u32, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an existing value into a [`ListValue`].
|
|
||||||
#[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_struct_value(value, self.llvm_usize, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
|
|
||||||
type Base = StructType<'ctx>;
|
|
||||||
type Value = TupleValue<'ctx>;
|
|
||||||
|
|
||||||
fn is_type<G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &G,
|
|
||||||
ctx: &'ctx Context,
|
|
||||||
llvm_ty: impl BasicType<'ctx>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
|
|
||||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
|
||||||
} else {
|
|
||||||
Err(format!("Expected struct type, got {llvm_ty:?}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
|
||||||
_generator: &G,
|
|
||||||
_ctx: &'ctx Context,
|
|
||||||
llvm_ty: Self::Base,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
Self::is_representable(llvm_ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
|
||||||
self.as_base_type()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
|
||||||
self.ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<TupleType<'ctx>> for StructType<'ctx> {
|
|
||||||
fn from(value: TupleType<'ctx>) -> Self {
|
|
||||||
value.as_base_type()
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,7 @@ use crate::codegen::{
|
|||||||
},
|
},
|
||||||
ProxyType,
|
ProxyType,
|
||||||
},
|
},
|
||||||
values::{utils::SliceValue, ProxyValue},
|
values::{utils::SliceValue, ArraySliceValue, ProxyValue},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -154,35 +154,16 @@ impl<'ctx> SliceType<'ctx> {
|
|||||||
self.int_ty
|
self.int_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an instance of [`SliceValue`] as if by calling `alloca` on the base type.
|
/// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base type.
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca`].
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloca(
|
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.int_ty,
|
|
||||||
self.llvm_usize,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates an instance of [`SliceValue`] as if by calling `alloca` on the base type.
|
|
||||||
///
|
|
||||||
/// See [`ProxyType::raw_alloca_var`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'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(
|
||||||
self.raw_alloca_var(generator, ctx, name),
|
self.raw_alloca(generator, ctx, name),
|
||||||
self.int_ty,
|
self.int_ty,
|
||||||
self.llvm_usize,
|
self.llvm_usize,
|
||||||
name,
|
name,
|
||||||
@ -229,8 +210,36 @@ impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> {
|
|||||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
self.as_base_type().get_element_type().into_struct_type()
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
size: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> ArraySliceValue<'ctx> {
|
||||||
|
generator
|
||||||
|
.gen_array_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_base_type(&self) -> Self::Base {
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
@ -51,8 +51,8 @@ pub trait ArrayLikeIndexer<'ctx, Index = IntValue<'ctx>>: ArrayLikeValue<'ctx> {
|
|||||||
/// This function should be called with a valid index.
|
/// This function should be called with a valid index.
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx>;
|
) -> PointerValue<'ctx>;
|
||||||
@ -76,8 +76,8 @@ pub trait UntypedArrayLikeAccessor<'ctx, Index = IntValue<'ctx>>:
|
|||||||
/// This function should be called with a valid index.
|
/// This function should be called with a valid index.
|
||||||
unsafe fn get_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn get_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
@ -107,8 +107,8 @@ pub trait UntypedArrayLikeMutator<'ctx, Index = IntValue<'ctx>>:
|
|||||||
/// This function should be called with a valid index.
|
/// This function should be called with a valid index.
|
||||||
unsafe fn set_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn set_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) {
|
) {
|
||||||
@ -130,33 +130,32 @@ pub trait UntypedArrayLikeMutator<'ctx, Index = IntValue<'ctx>>:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An array-like value that can have its array elements accessed as an arbitrary type `T`.
|
/// An array-like value that can have its array elements accessed as an arbitrary type `T`.
|
||||||
pub trait TypedArrayLikeAccessor<'ctx, G: CodeGenerator + ?Sized, T, Index = IntValue<'ctx>>:
|
pub trait TypedArrayLikeAccessor<'ctx, T, Index = IntValue<'ctx>>:
|
||||||
UntypedArrayLikeAccessor<'ctx, Index>
|
UntypedArrayLikeAccessor<'ctx, Index>
|
||||||
{
|
{
|
||||||
/// Casts an element from [`BasicValueEnum`] into `T`.
|
/// Casts an element from [`BasicValueEnum`] into `T`.
|
||||||
fn downcast_to_type(
|
fn downcast_to_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) -> T;
|
) -> T;
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This function should be called with a valid index.
|
/// This function should be called with a valid index.
|
||||||
unsafe fn get_typed_unchecked(
|
unsafe fn get_typed_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> T {
|
) -> T {
|
||||||
let value = unsafe { self.get_unchecked(ctx, generator, idx, name) };
|
let value = unsafe { self.get_unchecked(ctx, generator, idx, name) };
|
||||||
self.downcast_to_type(ctx, generator, value)
|
self.downcast_to_type(ctx, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the data at the `idx`-th index.
|
/// Returns the data at the `idx`-th index.
|
||||||
fn get_typed(
|
fn get_typed<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
@ -164,63 +163,62 @@ pub trait TypedArrayLikeAccessor<'ctx, G: CodeGenerator + ?Sized, T, Index = Int
|
|||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> T {
|
) -> T {
|
||||||
let value = self.get(ctx, generator, idx, name);
|
let value = self.get(ctx, generator, idx, name);
|
||||||
self.downcast_to_type(ctx, generator, value)
|
self.downcast_to_type(ctx, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An array-like value that can have its array elements mutated as an arbitrary type `T`.
|
/// An array-like value that can have its array elements mutated as an arbitrary type `T`.
|
||||||
pub trait TypedArrayLikeMutator<'ctx, G: CodeGenerator + ?Sized, T, Index = IntValue<'ctx>>:
|
pub trait TypedArrayLikeMutator<'ctx, T, Index = IntValue<'ctx>>:
|
||||||
UntypedArrayLikeMutator<'ctx, Index>
|
UntypedArrayLikeMutator<'ctx, Index>
|
||||||
{
|
{
|
||||||
/// Casts an element from T into [`BasicValueEnum`].
|
/// Casts an element from T into [`BasicValueEnum`].
|
||||||
fn upcast_from_type(
|
fn upcast_from_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
|
||||||
value: T,
|
value: T,
|
||||||
) -> BasicValueEnum<'ctx>;
|
) -> BasicValueEnum<'ctx>;
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This function should be called with a valid index.
|
/// This function should be called with a valid index.
|
||||||
unsafe fn set_typed_unchecked(
|
unsafe fn set_typed_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
generator: &G,
|
|
||||||
idx: &Index,
|
|
||||||
value: T,
|
|
||||||
) {
|
|
||||||
let value = self.upcast_from_type(ctx, generator, value);
|
|
||||||
unsafe { self.set_unchecked(ctx, generator, idx, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the data at the `idx`-th index.
|
|
||||||
fn set_typed(
|
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
value: T,
|
value: T,
|
||||||
) {
|
) {
|
||||||
let value = self.upcast_from_type(ctx, generator, value);
|
let value = self.upcast_from_type(ctx, value);
|
||||||
|
unsafe { self.set_unchecked(ctx, generator, idx, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the data at the `idx`-th index.
|
||||||
|
fn set_typed<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
generator: &mut G,
|
||||||
|
idx: &Index,
|
||||||
|
value: T,
|
||||||
|
) {
|
||||||
|
let value = self.upcast_from_type(ctx, value);
|
||||||
self.set(ctx, generator, idx, value);
|
self.set(ctx, generator, idx, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type alias for a function that casts a [`BasicValueEnum`] into a `T`.
|
||||||
|
type ValueDowncastFn<'ctx, T> =
|
||||||
|
Box<dyn Fn(&mut CodeGenContext<'ctx, '_>, BasicValueEnum<'ctx>) -> T + 'ctx>;
|
||||||
|
/// Type alias for a function that casts a `T` into a [`BasicValueEnum`].
|
||||||
|
type ValueUpcastFn<'ctx, T> = Box<dyn Fn(&mut CodeGenContext<'ctx, '_>, T) -> BasicValueEnum<'ctx>>;
|
||||||
|
|
||||||
/// An adapter for constraining untyped array values as typed values.
|
/// An adapter for constraining untyped array values as typed values.
|
||||||
#[derive(Copy, Clone)]
|
pub struct TypedArrayLikeAdapter<'ctx, T, Adapted: ArrayLikeValue<'ctx> = ArraySliceValue<'ctx>> {
|
||||||
pub struct TypedArrayLikeAdapter<
|
|
||||||
'ctx,
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
T,
|
|
||||||
Adapted: ArrayLikeValue<'ctx> = ArraySliceValue<'ctx>,
|
|
||||||
> {
|
|
||||||
adapted: Adapted,
|
adapted: Adapted,
|
||||||
downcast_fn: fn(&CodeGenContext<'ctx, '_>, &G, BasicValueEnum<'ctx>) -> T,
|
downcast_fn: ValueDowncastFn<'ctx, T>,
|
||||||
upcast_fn: fn(&CodeGenContext<'ctx, '_>, &G, T) -> BasicValueEnum<'ctx>,
|
upcast_fn: ValueUpcastFn<'ctx, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Adapted> TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
impl<'ctx, T, Adapted> TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: ArrayLikeValue<'ctx>,
|
Adapted: ArrayLikeValue<'ctx>,
|
||||||
{
|
{
|
||||||
@ -231,70 +229,61 @@ where
|
|||||||
/// * `upcast_fn` - The function converting a T into a [`BasicValueEnum`].
|
/// * `upcast_fn` - The function converting a T into a [`BasicValueEnum`].
|
||||||
pub fn from(
|
pub fn from(
|
||||||
adapted: Adapted,
|
adapted: Adapted,
|
||||||
downcast_fn: fn(&CodeGenContext<'ctx, '_>, &G, BasicValueEnum<'ctx>) -> T,
|
downcast_fn: ValueDowncastFn<'ctx, T>,
|
||||||
upcast_fn: fn(&CodeGenContext<'ctx, '_>, &G, T) -> BasicValueEnum<'ctx>,
|
upcast_fn: ValueUpcastFn<'ctx, T>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
TypedArrayLikeAdapter { adapted, downcast_fn, upcast_fn }
|
TypedArrayLikeAdapter { adapted, downcast_fn, upcast_fn }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Adapted> ArrayLikeValue<'ctx>
|
impl<'ctx, T, Adapted> ArrayLikeValue<'ctx> for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
|
||||||
where
|
where
|
||||||
Adapted: ArrayLikeValue<'ctx>,
|
Adapted: ArrayLikeValue<'ctx>,
|
||||||
{
|
{
|
||||||
fn element_type<CG: CodeGenerator + ?Sized>(
|
fn element_type<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
generator: &CG,
|
generator: &G,
|
||||||
) -> AnyTypeEnum<'ctx> {
|
) -> AnyTypeEnum<'ctx> {
|
||||||
self.adapted.element_type(ctx, generator)
|
self.adapted.element_type(ctx, generator)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_ptr<CG: CodeGenerator + ?Sized>(
|
fn base_ptr<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
generator: &CG,
|
generator: &G,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
self.adapted.base_ptr(ctx, generator)
|
self.adapted.base_ptr(ctx, generator)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size<CG: CodeGenerator + ?Sized>(
|
fn size<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
generator: &CG,
|
generator: &G,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
self.adapted.size(ctx, generator)
|
self.adapted.size(ctx, generator)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_slice_value<CG: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
generator: &CG,
|
|
||||||
) -> ArraySliceValue<'ctx> {
|
|
||||||
self.adapted.as_slice_value(ctx, generator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Index, Adapted> ArrayLikeIndexer<'ctx, Index>
|
impl<'ctx, T, Index, Adapted> ArrayLikeIndexer<'ctx, Index>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: ArrayLikeIndexer<'ctx, Index>,
|
Adapted: ArrayLikeIndexer<'ctx, Index>,
|
||||||
{
|
{
|
||||||
unsafe fn ptr_offset_unchecked<CG: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &CG,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
unsafe { self.adapted.ptr_offset_unchecked(ctx, generator, idx, name) }
|
unsafe { self.adapted.ptr_offset_unchecked(ctx, generator, idx, name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ptr_offset<CG: CodeGenerator + ?Sized>(
|
fn ptr_offset<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &mut CG,
|
generator: &mut G,
|
||||||
idx: &Index,
|
idx: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
@ -302,46 +291,44 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Index, Adapted> UntypedArrayLikeAccessor<'ctx, Index>
|
impl<'ctx, T, Index, Adapted> UntypedArrayLikeAccessor<'ctx, Index>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: UntypedArrayLikeAccessor<'ctx, Index>,
|
Adapted: UntypedArrayLikeAccessor<'ctx, Index>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Index, Adapted> UntypedArrayLikeMutator<'ctx, Index>
|
impl<'ctx, T, Index, Adapted> UntypedArrayLikeMutator<'ctx, Index>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: UntypedArrayLikeMutator<'ctx, Index>,
|
Adapted: UntypedArrayLikeMutator<'ctx, Index>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Index, Adapted> TypedArrayLikeAccessor<'ctx, G, T, Index>
|
impl<'ctx, T, Index, Adapted> TypedArrayLikeAccessor<'ctx, T, Index>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: UntypedArrayLikeAccessor<'ctx, Index>,
|
Adapted: UntypedArrayLikeAccessor<'ctx, Index>,
|
||||||
{
|
{
|
||||||
fn downcast_to_type(
|
fn downcast_to_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) -> T {
|
) -> T {
|
||||||
(self.downcast_fn)(ctx, generator, value)
|
(self.downcast_fn)(ctx, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized, T, Index, Adapted> TypedArrayLikeMutator<'ctx, G, T, Index>
|
impl<'ctx, T, Index, Adapted> TypedArrayLikeMutator<'ctx, T, Index>
|
||||||
for TypedArrayLikeAdapter<'ctx, G, T, Adapted>
|
for TypedArrayLikeAdapter<'ctx, T, Adapted>
|
||||||
where
|
where
|
||||||
Adapted: UntypedArrayLikeMutator<'ctx, Index>,
|
Adapted: UntypedArrayLikeMutator<'ctx, Index>,
|
||||||
{
|
{
|
||||||
fn upcast_from_type(
|
fn upcast_from_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
|
||||||
value: T,
|
value: T,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
(self.upcast_fn)(ctx, generator, value)
|
(self.upcast_fn)(ctx, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,12 +384,12 @@ impl<'ctx> ArrayLikeValue<'ctx> for ArraySliceValue<'ctx> {
|
|||||||
impl<'ctx> ArrayLikeIndexer<'ctx> for ArraySliceValue<'ctx> {
|
impl<'ctx> ArrayLikeIndexer<'ctx> for ArraySliceValue<'ctx> {
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &IntValue<'ctx>,
|
idx: &IntValue<'ctx>,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let var_name = name.or(self.2).map(|v| format!("{v}.addr")).unwrap_or_default();
|
let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ctx.builder
|
ctx.builder
|
||||||
|
@ -8,7 +8,7 @@ use super::{
|
|||||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||||
};
|
};
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
types::{structure::StructField, ListType, ProxyType},
|
types::ListType,
|
||||||
{CodeGenContext, CodeGenerator},
|
{CodeGenContext, CodeGenerator},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,26 +42,48 @@ impl<'ctx> ListValue<'ctx> {
|
|||||||
ListValue { value: ptr, llvm_usize, name }
|
ListValue { value: ptr, llvm_usize, name }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn items_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
|
||||||
self.get_type().get_fields(&ctx.ctx).items
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||||
self.items_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ctx.builder
|
||||||
|
.build_in_bounds_gep(
|
||||||
|
self.as_base_value(),
|
||||||
|
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
|
||||||
|
var_name.as_str(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the pointer to the field storing the size of this `list`.
|
||||||
|
fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let var_name = self.name.map(|v| format!("{v}.size.addr")).unwrap_or_default();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ctx.builder
|
||||||
|
.build_in_bounds_gep(
|
||||||
|
self.as_base_value(),
|
||||||
|
&[llvm_i32.const_zero(), llvm_i32.const_int(1, true)],
|
||||||
|
var_name.as_str(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(ctx).set(ctx, self.value, data, self.name);
|
ctx.builder.build_store(self.pptr_to_data(ctx), data).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
/// type `elem_ty` and `size`.
|
/// type `elem_ty` and `size`.
|
||||||
///
|
///
|
||||||
/// If `size` is [None], the size stored in the field of this instance is used instead. If
|
/// If `size` is [None], the size stored in the field of this instance is used instead.
|
||||||
/// `size` is resolved to `0` at runtime, `(T*) 0` will be assigned to `data`.
|
|
||||||
pub fn create_data(
|
pub fn create_data(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
@ -92,10 +114,6 @@ impl<'ctx> ListValue<'ctx> {
|
|||||||
ListDataProxy(self)
|
ListDataProxy(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
|
||||||
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<G: CodeGenerator + ?Sized>(
|
pub fn store_size<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
@ -105,33 +123,22 @@ impl<'ctx> ListValue<'ctx> {
|
|||||||
) {
|
) {
|
||||||
debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx));
|
debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx));
|
||||||
|
|
||||||
self.len_field(ctx).set(ctx, self.value, size, self.name);
|
let psize = self.ptr_to_size(ctx);
|
||||||
|
ctx.builder.build_store(psize, size).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size of this `list` as a value.
|
/// Returns the size of this `list` as a value.
|
||||||
pub fn load_size(
|
pub fn load_size(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
|
||||||
&self,
|
let psize = self.ptr_to_size(ctx);
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
let var_name = name
|
||||||
name: Option<&'ctx str>,
|
.map(ToString::to_string)
|
||||||
) -> IntValue<'ctx> {
|
.or_else(|| self.name.map(|v| format!("{v}.size")))
|
||||||
self.len_field(ctx).get(ctx, self.value, name)
|
.unwrap_or_default();
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an instance of [`ListValue`] with the `items` pointer cast to `i8*`.
|
ctx.builder
|
||||||
#[must_use]
|
.build_load(psize, var_name.as_str())
|
||||||
pub fn as_i8_list<G: CodeGenerator + ?Sized>(
|
.map(BasicValueEnum::into_int_value)
|
||||||
&self,
|
.unwrap()
|
||||||
generator: &G,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
) -> ListValue<'ctx> {
|
|
||||||
let llvm_i8 = ctx.ctx.i8_type();
|
|
||||||
let llvm_list_i8 = <Self as ProxyValue>::Type::new(generator, ctx.ctx, llvm_i8.into());
|
|
||||||
|
|
||||||
Self::from_pointer_value(
|
|
||||||
ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_base_type(), "").unwrap(),
|
|
||||||
self.llvm_usize,
|
|
||||||
self.name,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,8 +199,8 @@ impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> {
|
|||||||
impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> {
|
impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> {
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &IntValue<'ctx>,
|
idx: &IntValue<'ctx>,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
|
@ -5,13 +5,11 @@ use crate::codegen::CodeGenerator;
|
|||||||
pub use array::*;
|
pub use array::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use tuple::*;
|
|
||||||
|
|
||||||
mod array;
|
mod array;
|
||||||
mod list;
|
mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
mod range;
|
mod range;
|
||||||
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.
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
types::IntType,
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
};
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
irrt,
|
|
||||||
types::{
|
|
||||||
ndarray::{NDArrayType, ShapeEntryType},
|
|
||||||
structure::StructField,
|
|
||||||
ProxyType,
|
|
||||||
},
|
|
||||||
values::{
|
|
||||||
ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue,
|
|
||||||
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ShapeEntryValue<'ctx> {
|
|
||||||
value: PointerValue<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ShapeEntryValue<'ctx> {
|
|
||||||
/// Checks whether `value` is an instance of `ShapeEntry`, returning [Err] if `value` is
|
|
||||||
/// not an instance.
|
|
||||||
pub fn is_representable(
|
|
||||||
value: PointerValue<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`ShapeEntryValue`] 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_representable(ptr, llvm_usize).is_ok());
|
|
||||||
|
|
||||||
Self { value: ptr, llvm_usize, name }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
|
||||||
self.get_type().get_fields(self.value.get_type().get_context()).ndims
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the number of dimensions into this value.
|
|
||||||
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
|
|
||||||
self.ndims_field().set(ctx, self.value, value, self.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
|
||||||
self.get_type().get_fields(self.value.get_type().get_context()).shape
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the shape into this value.
|
|
||||||
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
|
|
||||||
self.shape_field().set(ctx, self.value, value, self.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ProxyValue<'ctx> for ShapeEntryValue<'ctx> {
|
|
||||||
type Base = PointerValue<'ctx>;
|
|
||||||
type Type = ShapeEntryType<'ctx>;
|
|
||||||
|
|
||||||
fn get_type(&self) -> Self::Type {
|
|
||||||
Self::Type::from_type(self.value.get_type(), self.llvm_usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_base_value(&self) -> Self::Base {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<ShapeEntryValue<'ctx>> for PointerValue<'ctx> {
|
|
||||||
fn from(value: ShapeEntryValue<'ctx>) -> Self {
|
|
||||||
value.as_base_value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayValue<'ctx> {
|
|
||||||
/// Create a broadcast view on this ndarray with a target shape.
|
|
||||||
///
|
|
||||||
/// The input shape will be checked to make sure that it contains no negative values.
|
|
||||||
///
|
|
||||||
/// * `target_ndims` - The ndims type after broadcasting to the given shape.
|
|
||||||
/// The caller has to figure this out for this function.
|
|
||||||
/// * `target_shape` - An array pointer pointing to the target shape.
|
|
||||||
#[must_use]
|
|
||||||
pub fn broadcast_to<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
target_ndims: u64,
|
|
||||||
target_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
) -> Self {
|
|
||||||
assert!(self.ndims <= target_ndims);
|
|
||||||
assert_eq!(target_shape.element_type(ctx, generator), self.llvm_usize.into());
|
|
||||||
|
|
||||||
let broadcast_ndarray = NDArrayType::new(generator, ctx.ctx, self.dtype, target_ndims)
|
|
||||||
.construct_uninitialized(generator, ctx, None);
|
|
||||||
broadcast_ndarray.copy_shape_from_array(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
target_shape.base_ptr(ctx, generator),
|
|
||||||
);
|
|
||||||
|
|
||||||
irrt::ndarray::call_nac3_ndarray_broadcast_to(generator, ctx, *self, broadcast_ndarray);
|
|
||||||
broadcast_ndarray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A result produced by [`broadcast_all_ndarrays`]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BroadcastAllResult<'ctx, G: CodeGenerator + ?Sized> {
|
|
||||||
/// The statically known `ndims` of the broadcast result.
|
|
||||||
pub ndims: u64,
|
|
||||||
|
|
||||||
/// The broadcasting shape.
|
|
||||||
pub shape: TypedArrayLikeAdapter<'ctx, G, IntValue<'ctx>>,
|
|
||||||
|
|
||||||
/// Broadcasted views on the inputs.
|
|
||||||
///
|
|
||||||
/// All of them will have `shape` [`BroadcastAllResult::shape`] and
|
|
||||||
/// `ndims` [`BroadcastAllResult::ndims`]. The length of the vector
|
|
||||||
/// is the same as the input.
|
|
||||||
pub ndarrays: Vec<NDArrayValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper function to call [`irrt::ndarray::call_nac3_ndarray_broadcast_shapes`].
|
|
||||||
fn broadcast_shapes<'ctx, G, Shape>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
in_shape_entries: &[(ArraySliceValue<'ctx>, u64)], // (shape, shape's length/ndims)
|
|
||||||
broadcast_ndims: u64,
|
|
||||||
broadcast_shape: &Shape,
|
|
||||||
) where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
Shape: TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>
|
|
||||||
+ TypedArrayLikeMutator<'ctx, G, IntValue<'ctx>>,
|
|
||||||
{
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let llvm_shape_ty = ShapeEntryType::new(generator, ctx.ctx);
|
|
||||||
|
|
||||||
assert!(in_shape_entries
|
|
||||||
.iter()
|
|
||||||
.all(|entry| entry.0.element_type(ctx, generator) == llvm_usize.into()));
|
|
||||||
assert_eq!(broadcast_shape.element_type(ctx, generator), llvm_usize.into());
|
|
||||||
|
|
||||||
// Prepare input shape entries to be passed to `call_nac3_ndarray_broadcast_shapes`.
|
|
||||||
let num_shape_entries =
|
|
||||||
llvm_usize.const_int(u64::try_from(in_shape_entries.len()).unwrap(), false);
|
|
||||||
let shape_entries = llvm_shape_ty.array_alloca(ctx, num_shape_entries, None);
|
|
||||||
for (i, (in_shape, in_ndims)) in in_shape_entries.iter().enumerate() {
|
|
||||||
let pshape_entry = unsafe {
|
|
||||||
shape_entries.ptr_offset_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(i as u64, false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let shape_entry = llvm_shape_ty.map_value(pshape_entry, None);
|
|
||||||
|
|
||||||
let in_ndims = llvm_usize.const_int(*in_ndims, false);
|
|
||||||
shape_entry.store_ndims(ctx, in_ndims);
|
|
||||||
|
|
||||||
shape_entry.store_shape(ctx, in_shape.base_ptr(ctx, generator));
|
|
||||||
}
|
|
||||||
|
|
||||||
let broadcast_ndims = llvm_usize.const_int(broadcast_ndims, false);
|
|
||||||
irrt::ndarray::call_nac3_ndarray_broadcast_shapes(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
num_shape_entries,
|
|
||||||
shape_entries,
|
|
||||||
broadcast_ndims,
|
|
||||||
broadcast_shape,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayType<'ctx> {
|
|
||||||
/// Broadcast all ndarrays according to
|
|
||||||
/// [`np.broadcast()`](https://numpy.org/doc/stable/reference/generated/numpy.broadcast.html)
|
|
||||||
/// and return a [`BroadcastAllResult`] containing all the information of the result of the
|
|
||||||
/// broadcast operation.
|
|
||||||
pub fn broadcast<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
ndarrays: &[NDArrayValue<'ctx>],
|
|
||||||
) -> BroadcastAllResult<'ctx, G> {
|
|
||||||
assert!(!ndarrays.is_empty());
|
|
||||||
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
// Infer the broadcast output ndims.
|
|
||||||
let broadcast_ndims_int =
|
|
||||||
ndarrays.iter().map(|ndarray| ndarray.get_type().ndims()).max().unwrap();
|
|
||||||
assert!(self.ndims() >= broadcast_ndims_int);
|
|
||||||
|
|
||||||
let broadcast_ndims = llvm_usize.const_int(broadcast_ndims_int, false);
|
|
||||||
let broadcast_shape = ArraySliceValue::from_ptr_val(
|
|
||||||
ctx.builder.build_array_alloca(llvm_usize, broadcast_ndims, "").unwrap(),
|
|
||||||
broadcast_ndims,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let broadcast_shape = TypedArrayLikeAdapter::from(
|
|
||||||
broadcast_shape,
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let shape_entries = ndarrays
|
|
||||||
.iter()
|
|
||||||
.map(|ndarray| {
|
|
||||||
(ndarray.shape().as_slice_value(ctx, generator), ndarray.get_type().ndims())
|
|
||||||
})
|
|
||||||
.collect_vec();
|
|
||||||
broadcast_shapes(generator, ctx, &shape_entries, broadcast_ndims_int, &broadcast_shape);
|
|
||||||
|
|
||||||
// Broadcast all the inputs to shape `dst_shape`.
|
|
||||||
let broadcast_ndarrays = ndarrays
|
|
||||||
.iter()
|
|
||||||
.map(|ndarray| {
|
|
||||||
ndarray.broadcast_to(generator, ctx, broadcast_ndims_int, &broadcast_shape)
|
|
||||||
})
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
BroadcastAllResult {
|
|
||||||
ndims: broadcast_ndims_int,
|
|
||||||
shape: broadcast_shape,
|
|
||||||
ndarrays: broadcast_ndarrays,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -118,10 +118,12 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
) -> ContiguousNDArrayValue<'ctx> {
|
) -> ContiguousNDArrayValue<'ctx> {
|
||||||
let result = ContiguousNDArrayType::new(generator, ctx.ctx, self.dtype)
|
let result = ContiguousNDArrayType::new(generator, ctx.ctx, self.dtype)
|
||||||
.alloca_var(generator, ctx, self.name);
|
.alloca(generator, ctx, self.name);
|
||||||
|
|
||||||
// Set ndims and shape.
|
// Set ndims and shape.
|
||||||
let ndims = self.llvm_usize.const_int(self.ndims, false);
|
let ndims = self
|
||||||
|
.ndims
|
||||||
|
.map_or_else(|| self.load_ndims(ctx), |ndims| self.llvm_usize.const_int(ndims, false));
|
||||||
result.store_ndims(ctx, ndims);
|
result.store_ndims(ctx, ndims);
|
||||||
|
|
||||||
let shape = self.shape();
|
let shape = self.shape();
|
||||||
@ -178,7 +180,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
// TODO: Debug assert `ndims == carray.ndims` to catch bugs.
|
// TODO: Debug assert `ndims == carray.ndims` to catch bugs.
|
||||||
|
|
||||||
// Allocate the resulting ndarray.
|
// Allocate the resulting ndarray.
|
||||||
let ndarray = NDArrayType::new(generator, ctx.ctx, carray.item, ndims)
|
let ndarray = NDArrayType::new(generator, ctx.ctx, carray.item, Some(ndims))
|
||||||
.construct_uninitialized(generator, ctx, carray.name);
|
.construct_uninitialized(generator, ctx, carray.name);
|
||||||
|
|
||||||
// Copy shape and update strides
|
// Copy shape and update strides
|
||||||
|
@ -98,8 +98,8 @@ impl<'ctx> From<NDIndexValue<'ctx>> for PointerValue<'ctx> {
|
|||||||
impl<'ctx> NDArrayValue<'ctx> {
|
impl<'ctx> NDArrayValue<'ctx> {
|
||||||
/// Get the expected `ndims` after indexing with `indices`.
|
/// Get the expected `ndims` after indexing with `indices`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn deduce_ndims_after_indexing_with(&self, indices: &[RustNDIndex<'ctx>]) -> u64 {
|
fn deduce_ndims_after_indexing_with(&self, indices: &[RustNDIndex<'ctx>]) -> Option<u64> {
|
||||||
let mut ndims = self.ndims;
|
let mut ndims = self.ndims?;
|
||||||
|
|
||||||
for index in indices {
|
for index in indices {
|
||||||
match index {
|
match index {
|
||||||
@ -113,7 +113,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ndims
|
Some(ndims)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index into the ndarray, and return a newly-allocated view on this ndarray.
|
/// Index into the ndarray, and return a newly-allocated view on this ndarray.
|
||||||
@ -127,6 +127,8 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
indices: &[RustNDIndex<'ctx>],
|
indices: &[RustNDIndex<'ctx>],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
assert!(self.ndims.is_some(), "NDArrayValue::index is only supported for instances with compile-time known ndims (self.ndims = Some(...))");
|
||||||
|
|
||||||
let dst_ndims = self.deduce_ndims_after_indexing_with(indices);
|
let dst_ndims = self.deduce_ndims_after_indexing_with(indices);
|
||||||
let dst_ndarray = NDArrayType::new(generator, ctx.ctx, self.dtype, dst_ndims)
|
let dst_ndarray = NDArrayType::new(generator, ctx.ctx, self.dtype, dst_ndims)
|
||||||
.construct_uninitialized(generator, ctx, None);
|
.construct_uninitialized(generator, ctx, None);
|
||||||
@ -246,7 +248,7 @@ impl<'ctx> RustNDIndex<'ctx> {
|
|||||||
RustNDIndex::Slice(in_rust_slice) => {
|
RustNDIndex::Slice(in_rust_slice) => {
|
||||||
let user_slice_ptr =
|
let user_slice_ptr =
|
||||||
SliceType::new(ctx.ctx, ctx.ctx.i32_type(), generator.get_size_type(ctx.ctx))
|
SliceType::new(ctx.ctx, ctx.ctx.i32_type(), generator.get_size_type(ctx.ctx))
|
||||||
.alloca_var(generator, ctx, None);
|
.alloca(generator, ctx, None);
|
||||||
in_rust_slice.write_to_slice(ctx, user_slice_ptr);
|
in_rust_slice.write_to_slice(ctx, user_slice_ptr);
|
||||||
|
|
||||||
dst_ndindex.store_data(
|
dst_ndindex.store_data(
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
|
|
||||||
|
|
||||||
use crate::codegen::{
|
|
||||||
values::{
|
|
||||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
|
||||||
ProxyValue,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayValue<'ctx> {
|
|
||||||
/// Map through this ndarray with an elementwise function.
|
|
||||||
pub fn map<'a, G, Mapping>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
out: NDArrayOut<'ctx>,
|
|
||||||
mapping: Mapping,
|
|
||||||
) -> Result<Self, String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
Mapping: FnOnce(
|
|
||||||
&mut G,
|
|
||||||
&mut CodeGenContext<'ctx, 'a>,
|
|
||||||
BasicValueEnum<'ctx>,
|
|
||||||
) -> Result<BasicValueEnum<'ctx>, String>,
|
|
||||||
{
|
|
||||||
self.get_type().broadcast_starmap(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&[*self],
|
|
||||||
out,
|
|
||||||
|generator, ctx, scalars| mapping(generator, ctx, scalars[0]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ScalarOrNDArray<'ctx> {
|
|
||||||
/// Map through this [`ScalarOrNDArray`] with an elementwise function.
|
|
||||||
///
|
|
||||||
/// If this is a scalar, `mapping` will directly act on the scalar. This function will return a
|
|
||||||
/// [`ScalarOrNDArray::Scalar`] of that result.
|
|
||||||
///
|
|
||||||
/// If this is an ndarray, `mapping` will be applied to the elements of the ndarray. A new
|
|
||||||
/// ndarray of the results will be created and returned as a [`ScalarOrNDArray::NDArray`].
|
|
||||||
pub fn map<'a, G, Mapping>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
ret_dtype: BasicTypeEnum<'ctx>,
|
|
||||||
mapping: Mapping,
|
|
||||||
) -> Result<ScalarOrNDArray<'ctx>, String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
Mapping: FnOnce(
|
|
||||||
&mut G,
|
|
||||||
&mut CodeGenContext<'ctx, 'a>,
|
|
||||||
BasicValueEnum<'ctx>,
|
|
||||||
) -> Result<BasicValueEnum<'ctx>, String>,
|
|
||||||
{
|
|
||||||
ScalarOrNDArray::broadcasting_starmap(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&[*self],
|
|
||||||
ret_dtype,
|
|
||||||
|generator, ctx, scalars| mapping(generator, ctx, scalars[0]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,323 +0,0 @@
|
|||||||
use std::cmp::max;
|
|
||||||
|
|
||||||
use nac3parser::ast::Operator;
|
|
||||||
|
|
||||||
use super::{NDArrayOut, NDArrayValue, RustNDIndex};
|
|
||||||
use crate::{
|
|
||||||
codegen::{
|
|
||||||
expr::gen_binop_expr_with_values,
|
|
||||||
irrt,
|
|
||||||
stmt::gen_for_callback_incrementing,
|
|
||||||
types::ndarray::NDArrayType,
|
|
||||||
values::{
|
|
||||||
ArrayLikeValue, ArraySliceValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
|
||||||
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
toplevel::helper::arraylike_flatten_element_type,
|
|
||||||
typecheck::{magic_methods::Binop, typedef::Type},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Perform `np.einsum("...ij,...jk->...ik", in_a, in_b)`.
|
|
||||||
///
|
|
||||||
/// `dst_dtype` defines the dtype of the returned ndarray.
|
|
||||||
fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
dst_dtype: Type,
|
|
||||||
(in_a_ty, in_a): (Type, NDArrayValue<'ctx>),
|
|
||||||
(in_b_ty, in_b): (Type, NDArrayValue<'ctx>),
|
|
||||||
) -> NDArrayValue<'ctx> {
|
|
||||||
assert!(in_a.ndims >= 2, "in_a (which is {}) must be >= 2", in_a.ndims);
|
|
||||||
assert!(in_b.ndims >= 2, "in_b (which is {}) must be >= 2", in_b.ndims);
|
|
||||||
|
|
||||||
let lhs_dtype = arraylike_flatten_element_type(&mut ctx.unifier, in_a_ty);
|
|
||||||
let rhs_dtype = arraylike_flatten_element_type(&mut ctx.unifier, in_b_ty);
|
|
||||||
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let llvm_dst_dtype = ctx.get_llvm_type(generator, dst_dtype);
|
|
||||||
|
|
||||||
// Deduce ndims of the result of matmul.
|
|
||||||
let ndims_int = max(in_a.ndims, in_b.ndims);
|
|
||||||
let ndims = llvm_usize.const_int(ndims_int, false);
|
|
||||||
|
|
||||||
// Broadcasts `in_a.shape[:-2]` and `in_b.shape[:-2]` together and allocate the
|
|
||||||
// destination ndarray to store the result of matmul.
|
|
||||||
let (lhs, rhs, dst) = {
|
|
||||||
let in_lhs_ndims = llvm_usize.const_int(in_a.ndims, false);
|
|
||||||
let in_lhs_shape = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
in_a.shape().base_ptr(ctx, generator),
|
|
||||||
in_lhs_ndims,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
let in_rhs_ndims = llvm_usize.const_int(in_b.ndims, false);
|
|
||||||
let in_rhs_shape = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
in_b.shape().base_ptr(ctx, generator),
|
|
||||||
in_rhs_ndims,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
let lhs_shape = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
ctx.builder.build_array_alloca(llvm_usize, ndims, "").unwrap(),
|
|
||||||
ndims,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
let rhs_shape = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
ctx.builder.build_array_alloca(llvm_usize, ndims, "").unwrap(),
|
|
||||||
ndims,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
let dst_shape = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(
|
|
||||||
ctx.builder.build_array_alloca(llvm_usize, ndims, "").unwrap(),
|
|
||||||
ndims,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Matmul dimension compatibility is checked here.
|
|
||||||
irrt::ndarray::call_nac3_ndarray_matmul_calculate_shapes(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&in_lhs_shape,
|
|
||||||
&in_rhs_shape,
|
|
||||||
ndims,
|
|
||||||
&lhs_shape,
|
|
||||||
&rhs_shape,
|
|
||||||
&dst_shape,
|
|
||||||
);
|
|
||||||
|
|
||||||
let lhs = in_a.broadcast_to(generator, ctx, ndims_int, &lhs_shape);
|
|
||||||
let rhs = in_b.broadcast_to(generator, ctx, ndims_int, &rhs_shape);
|
|
||||||
|
|
||||||
let dst = NDArrayType::new(generator, ctx.ctx, llvm_dst_dtype, ndims_int)
|
|
||||||
.construct_uninitialized(generator, ctx, None);
|
|
||||||
dst.copy_shape_from_array(generator, ctx, dst_shape.base_ptr(ctx, generator));
|
|
||||||
unsafe {
|
|
||||||
dst.create_data(generator, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
(lhs, rhs, dst)
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = unsafe {
|
|
||||||
lhs.shape().get_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(ndims_int - 1, false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let at_row = i64::try_from(ndims_int - 2).unwrap();
|
|
||||||
let at_col = i64::try_from(ndims_int - 1).unwrap();
|
|
||||||
|
|
||||||
let dst_dtype_llvm = ctx.get_llvm_type(generator, dst_dtype);
|
|
||||||
let dst_zero = dst_dtype_llvm.const_zero();
|
|
||||||
|
|
||||||
dst.foreach(generator, ctx, |generator, ctx, _, hdl| {
|
|
||||||
let pdst_ij = hdl.get_pointer(ctx);
|
|
||||||
|
|
||||||
ctx.builder.build_store(pdst_ij, dst_zero).unwrap();
|
|
||||||
|
|
||||||
let indices = hdl.get_indices::<G>();
|
|
||||||
let i = unsafe {
|
|
||||||
indices.get_unchecked(ctx, generator, &llvm_usize.const_int(at_row as u64, true), None)
|
|
||||||
};
|
|
||||||
let j = unsafe {
|
|
||||||
indices.get_unchecked(ctx, generator, &llvm_usize.const_int(at_col as u64, true), None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let num_0 = llvm_usize.const_int(0, false);
|
|
||||||
let num_1 = llvm_usize.const_int(1, false);
|
|
||||||
|
|
||||||
gen_for_callback_incrementing(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
None,
|
|
||||||
num_0,
|
|
||||||
(len, false),
|
|
||||||
|generator, ctx, _, k| {
|
|
||||||
// `indices` is modified to index into `a` and `b`, and restored.
|
|
||||||
unsafe {
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_row as u64, true),
|
|
||||||
i,
|
|
||||||
);
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_col as u64, true),
|
|
||||||
k.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let a_ik = unsafe { lhs.data().get_unchecked(ctx, generator, &indices, None) };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_row as u64, true),
|
|
||||||
k.into(),
|
|
||||||
);
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_col as u64, true),
|
|
||||||
j,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let b_kj = unsafe { rhs.data().get_unchecked(ctx, generator, &indices, None) };
|
|
||||||
|
|
||||||
// Restore `indices`.
|
|
||||||
unsafe {
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_row as u64, true),
|
|
||||||
i,
|
|
||||||
);
|
|
||||||
indices.set_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(at_col as u64, true),
|
|
||||||
j,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// x = a_[...]ik * b_[...]kj
|
|
||||||
let x = gen_binop_expr_with_values(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(&Some(lhs_dtype), a_ik),
|
|
||||||
Binop::normal(Operator::Mult),
|
|
||||||
(&Some(rhs_dtype), b_kj),
|
|
||||||
ctx.current_loc,
|
|
||||||
)?
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator, dst_dtype)?;
|
|
||||||
|
|
||||||
// dst_[...]ij += x
|
|
||||||
let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap();
|
|
||||||
let dst_ij = gen_binop_expr_with_values(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(&Some(dst_dtype), dst_ij),
|
|
||||||
Binop::normal(Operator::Add),
|
|
||||||
(&Some(dst_dtype), x),
|
|
||||||
ctx.current_loc,
|
|
||||||
)?
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator, dst_dtype)?;
|
|
||||||
ctx.builder.build_store(pdst_ij, dst_ij).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
num_1,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
dst
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayValue<'ctx> {
|
|
||||||
/// Perform [`np.matmul`](https://numpy.org/doc/stable/reference/generated/numpy.matmul.html).
|
|
||||||
///
|
|
||||||
/// This function always return an [`NDArrayValue`]. You may want to use
|
|
||||||
/// [`NDArrayValue::split_unsized`] to handle when the output could be a scalar.
|
|
||||||
///
|
|
||||||
/// `dst_dtype` defines the dtype of the returned ndarray.
|
|
||||||
#[must_use]
|
|
||||||
pub fn matmul<G: CodeGenerator>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
self_ty: Type,
|
|
||||||
(other_ty, other): (Type, Self),
|
|
||||||
(out_dtype, out): (Type, NDArrayOut<'ctx>),
|
|
||||||
) -> Self {
|
|
||||||
// Sanity check, but type inference should prevent this.
|
|
||||||
assert!(self.ndims > 0 && other.ndims > 0, "np.matmul disallows scalar input");
|
|
||||||
|
|
||||||
// If both arguments are 2-D they are multiplied like conventional matrices.
|
|
||||||
//
|
|
||||||
// If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the
|
|
||||||
// last two indices and broadcast accordingly.
|
|
||||||
//
|
|
||||||
// If the first argument is 1-D, it is promoted to a matrix by prepending a 1 to its
|
|
||||||
// dimensions. After matrix multiplication the prepended 1 is removed.
|
|
||||||
//
|
|
||||||
// If the second argument is 1-D, it is promoted to a matrix by appending a 1 to its
|
|
||||||
// dimensions. After matrix multiplication the appended 1 is removed.
|
|
||||||
|
|
||||||
let new_a = if self.ndims == 1 {
|
|
||||||
// Prepend 1 to its dimensions
|
|
||||||
self.index(generator, ctx, &[RustNDIndex::NewAxis, RustNDIndex::Ellipsis])
|
|
||||||
} else {
|
|
||||||
*self
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_b = if other.ndims == 1 {
|
|
||||||
// Append 1 to its dimensions
|
|
||||||
other.index(generator, ctx, &[RustNDIndex::Ellipsis, RustNDIndex::NewAxis])
|
|
||||||
} else {
|
|
||||||
other
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE: `result` will always be a newly allocated ndarray.
|
|
||||||
// Current implementation cannot do in-place matrix muliplication.
|
|
||||||
let mut result =
|
|
||||||
matmul_at_least_2d(generator, ctx, out_dtype, (self_ty, new_a), (other_ty, new_b));
|
|
||||||
|
|
||||||
// Postprocessing on the result to remove prepended/appended axes.
|
|
||||||
let mut postindices = vec![];
|
|
||||||
let zero = ctx.ctx.i32_type().const_zero();
|
|
||||||
|
|
||||||
if self.ndims == 1 {
|
|
||||||
// Remove the prepended 1
|
|
||||||
postindices.push(RustNDIndex::SingleElement(zero));
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.ndims == 1 {
|
|
||||||
// Remove the appended 1
|
|
||||||
postindices.push(RustNDIndex::Ellipsis);
|
|
||||||
postindices.push(RustNDIndex::SingleElement(zero));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !postindices.is_empty() {
|
|
||||||
result = result.index(generator, ctx, &postindices);
|
|
||||||
}
|
|
||||||
|
|
||||||
match out {
|
|
||||||
NDArrayOut::NewNDArray { .. } => result,
|
|
||||||
NDArrayOut::WriteToNDArray { ndarray: out_ndarray } => {
|
|
||||||
let result_shape = result.shape();
|
|
||||||
out_ndarray.assert_can_be_written_by_out(generator, ctx, result_shape);
|
|
||||||
|
|
||||||
out_ndarray.copy_data_from(generator, ctx, result);
|
|
||||||
out_ndarray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +1,29 @@
|
|||||||
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},
|
values::{BasicValueEnum, IntValue, PointerValue},
|
||||||
AddressSpace, IntPredicate,
|
AddressSpace, IntPredicate,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, TypedArrayLikeAccessor,
|
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator,
|
||||||
TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
|
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||||
UntypedArrayLikeMutator,
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::codegen::{
|
||||||
codegen::{
|
irrt,
|
||||||
irrt,
|
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::{ndarray::NDArrayType, structure::StructField},
|
||||||
types::{ndarray::NDArrayType, structure::StructField, TupleType},
|
CodeGenContext, CodeGenerator,
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
typecheck::typedef::{Type, TypeEnum},
|
|
||||||
};
|
};
|
||||||
pub use broadcast::*;
|
|
||||||
pub use contiguous::*;
|
pub use contiguous::*;
|
||||||
pub use indexing::*;
|
pub use indexing::*;
|
||||||
pub use nditer::*;
|
pub use nditer::*;
|
||||||
|
pub use view::*;
|
||||||
|
|
||||||
mod broadcast;
|
|
||||||
mod contiguous;
|
mod contiguous;
|
||||||
mod indexing;
|
mod indexing;
|
||||||
mod map;
|
|
||||||
mod matmul;
|
|
||||||
mod nditer;
|
mod nditer;
|
||||||
pub mod shape;
|
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
/// Proxy type for accessing an `NDArray` value in LLVM.
|
/// Proxy type for accessing an `NDArray` value in LLVM.
|
||||||
@ -42,7 +31,7 @@ mod view;
|
|||||||
pub struct NDArrayValue<'ctx> {
|
pub struct NDArrayValue<'ctx> {
|
||||||
value: PointerValue<'ctx>,
|
value: PointerValue<'ctx>,
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
dtype: BasicTypeEnum<'ctx>,
|
||||||
ndims: u64,
|
ndims: Option<u64>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
}
|
}
|
||||||
@ -62,7 +51,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
pub fn from_pointer_value(
|
pub fn from_pointer_value(
|
||||||
ptr: PointerValue<'ctx>,
|
ptr: PointerValue<'ctx>,
|
||||||
dtype: BasicTypeEnum<'ctx>,
|
dtype: BasicTypeEnum<'ctx>,
|
||||||
ndims: u64,
|
ndims: Option<u64>,
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
name: Option<&'ctx str>,
|
name: Option<&'ctx str>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -124,6 +113,12 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
self.get_type().get_fields(ctx.ctx).shape
|
self.get_type().get_fields(ctx.ctx).shape
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the double-indirection pointer to the `shape` array, as if by calling
|
||||||
|
/// `getelementptr` on the field.
|
||||||
|
fn ptr_to_shape(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||||
|
self.shape_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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(ctx).set(ctx, self.as_base_value(), dims, self.name);
|
self.shape_field(ctx).set(ctx, self.as_base_value(), dims, self.name);
|
||||||
@ -152,6 +147,12 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
self.get_type().get_fields(ctx.ctx).strides
|
self.get_type().get_fields(ctx.ctx).strides
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the double-indirection pointer to the `strides` array, as if by calling
|
||||||
|
/// `getelementptr` on the field.
|
||||||
|
fn ptr_to_strides(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||||
|
self.strides_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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(ctx).set(ctx, self.as_base_value(), strides, self.name);
|
self.strides_field(ctx).set(ctx, self.as_base_value(), strides, self.name);
|
||||||
@ -184,7 +185,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stores the array of data elements `data` into this instance.
|
/// Stores the array of data elements `data` into this instance.
|
||||||
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
|
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
|
||||||
let data = ctx
|
let data = 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()), "")
|
||||||
@ -245,7 +246,26 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
src_ndarray: NDArrayValue<'ctx>,
|
src_ndarray: NDArrayValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.ndims, src_ndarray.ndims);
|
if self.ndims.is_some() && src_ndarray.ndims.is_some() {
|
||||||
|
assert_eq!(self.ndims, src_ndarray.ndims);
|
||||||
|
} else {
|
||||||
|
let self_ndims = self.load_ndims(ctx);
|
||||||
|
let src_ndims = src_ndarray.load_ndims(ctx);
|
||||||
|
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
ctx.builder.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
self_ndims,
|
||||||
|
src_ndims,
|
||||||
|
""
|
||||||
|
).unwrap(),
|
||||||
|
"0:AssertionError",
|
||||||
|
"NDArrayValue::copy_shape_from_ndarray: Expected self.ndims ({0}) == src_ndarray.ndims ({1})",
|
||||||
|
[Some(self_ndims), Some(src_ndims), None],
|
||||||
|
ctx.current_loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let src_shape = src_ndarray.shape().base_ptr(ctx, generator);
|
let src_shape = src_ndarray.shape().base_ptr(ctx, generator);
|
||||||
self.copy_shape_from_array(generator, ctx, src_shape);
|
self.copy_shape_from_array(generator, ctx, src_shape);
|
||||||
@ -277,7 +297,26 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
src_ndarray: NDArrayValue<'ctx>,
|
src_ndarray: NDArrayValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.ndims, src_ndarray.ndims);
|
if self.ndims.is_some() && src_ndarray.ndims.is_some() {
|
||||||
|
assert_eq!(self.ndims, src_ndarray.ndims);
|
||||||
|
} else {
|
||||||
|
let self_ndims = self.load_ndims(ctx);
|
||||||
|
let src_ndims = src_ndarray.load_ndims(ctx);
|
||||||
|
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
ctx.builder.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
self_ndims,
|
||||||
|
src_ndims,
|
||||||
|
""
|
||||||
|
).unwrap(),
|
||||||
|
"0:AssertionError",
|
||||||
|
"NDArrayValue::copy_shape_from_ndarray: Expected self.ndims ({0}) == src_ndarray.ndims ({1})",
|
||||||
|
[Some(self_ndims), Some(src_ndims), None],
|
||||||
|
ctx.current_loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let src_strides = src_ndarray.strides().base_ptr(ctx, generator);
|
let src_strides = src_ndarray.strides().base_ptr(ctx, generator);
|
||||||
self.copy_strides_from_array(generator, ctx, src_strides);
|
self.copy_strides_from_array(generator, ctx, src_strides);
|
||||||
@ -332,17 +371,17 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
irrt::ndarray::call_nac3_ndarray_set_strides_by_shape(generator, ctx, *self);
|
irrt::ndarray::call_nac3_ndarray_set_strides_by_shape(generator, ctx, *self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone/Copy this ndarray - Allocate a new ndarray with the same shape as this ndarray and
|
|
||||||
/// copy the contents over.
|
|
||||||
///
|
|
||||||
/// The new ndarray will own its data and will be C-contiguous.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_copy<G: CodeGenerator + ?Sized>(
|
pub fn make_copy<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let clone = self.get_type().construct_uninitialized(generator, ctx, None);
|
let clone = if self.ndims.is_some() {
|
||||||
|
self.get_type().construct_uninitialized(generator, ctx, None)
|
||||||
|
} else {
|
||||||
|
self.get_type().construct_dyn_ndims(generator, ctx, self.load_ndims(ctx), None)
|
||||||
|
};
|
||||||
|
|
||||||
let shape = self.shape();
|
let shape = self.shape();
|
||||||
clone.copy_shape_from_array(generator, ctx, shape.base_ptr(ctx, generator));
|
clone.copy_shape_from_array(generator, ctx, shape.base_ptr(ctx, generator));
|
||||||
@ -367,149 +406,32 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
irrt::ndarray::call_nac3_ndarray_copy_data(generator, ctx, src, *self);
|
irrt::ndarray::call_nac3_ndarray_copy_data(generator, ctx, src, *self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the ndarray with a scalar.
|
|
||||||
///
|
|
||||||
/// `fill_value` must have the same LLVM type as the `dtype` of this ndarray.
|
|
||||||
pub fn fill<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
|
||||||
) {
|
|
||||||
// TODO: It is possible to optimize this by exploiting contiguous strides with memset.
|
|
||||||
// Probably best to implement in IRRT.
|
|
||||||
self.foreach(generator, ctx, |_, ctx, _, nditer| {
|
|
||||||
let p = nditer.get_pointer(ctx);
|
|
||||||
ctx.builder.build_store(p, value).unwrap();
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create the shape tuple of this ndarray like
|
|
||||||
/// [`np.shape(<ndarray>)`](https://numpy.org/doc/stable/reference/generated/numpy.shape.html).
|
|
||||||
///
|
|
||||||
/// All elements in the tuple are `i32`.
|
|
||||||
pub fn make_shape_tuple<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
) -> TupleValue<'ctx> {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
|
|
||||||
let objects = (0..self.ndims)
|
|
||||||
.map(|i| {
|
|
||||||
let dim = unsafe {
|
|
||||||
self.shape().get_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&self.llvm_usize.const_int(i, false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
ctx.builder.build_int_truncate_or_bit_cast(dim, llvm_i32, "").unwrap()
|
|
||||||
})
|
|
||||||
.map(|obj| obj.as_basic_value_enum())
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
TupleType::new(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
&repeat_n(llvm_i32.into(), self.ndims as usize).collect_vec(),
|
|
||||||
)
|
|
||||||
.construct_from_objects(ctx, objects, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create the strides tuple of this ndarray like
|
|
||||||
/// [`<ndarray>.strides`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html).
|
|
||||||
///
|
|
||||||
/// All elements in the tuple are `i32`.
|
|
||||||
pub fn make_strides_tuple<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
) -> TupleValue<'ctx> {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
|
|
||||||
let objects = (0..self.ndims)
|
|
||||||
.map(|i| {
|
|
||||||
let dim = unsafe {
|
|
||||||
self.strides().get_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&self.llvm_usize.const_int(i, false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
ctx.builder.build_int_truncate_or_bit_cast(dim, llvm_i32, "").unwrap()
|
|
||||||
})
|
|
||||||
.map(|obj| obj.as_basic_value_enum())
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
TupleType::new(
|
|
||||||
generator,
|
|
||||||
ctx.ctx,
|
|
||||||
&repeat_n(llvm_i32.into(), self.ndims as usize).collect_vec(),
|
|
||||||
)
|
|
||||||
.construct_from_objects(ctx, objects, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this ndarray is unsized - `ndims == 0` and only contains a scalar.
|
/// Returns true if this ndarray is unsized - `ndims == 0` and only contains a scalar.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_unsized(&self) -> bool {
|
pub fn is_unsized(&self) -> Option<bool> {
|
||||||
self.ndims == 0
|
self.ndims.map(|ndims| ndims == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the element present in this `ndarray` if this is unsized.
|
/// If this ndarray is unsized, return its sole value as an [`AnyObject`].
|
||||||
pub fn get_unsized_element<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
) -> Option<BasicValueEnum<'ctx>> {
|
|
||||||
if self.is_unsized() {
|
|
||||||
// NOTE: `np.size(self) == 0` here is never possible.
|
|
||||||
let zero = generator.get_size_type(ctx.ctx).const_zero();
|
|
||||||
let value = unsafe { self.data().get_unchecked(ctx, generator, &zero, None) };
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this ndarray is unsized, return its sole value as an [`BasicValueEnum`].
|
|
||||||
/// Otherwise, do nothing and return the ndarray itself.
|
/// Otherwise, do nothing and return the ndarray itself.
|
||||||
|
// TODO: Rename to get_unsized_element
|
||||||
pub fn split_unsized<G: CodeGenerator + ?Sized>(
|
pub fn split_unsized<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
) -> ScalarOrNDArray<'ctx> {
|
) -> ScalarOrNDArray<'ctx> {
|
||||||
if let Some(unsized_elem) = self.get_unsized_element(generator, ctx) {
|
let Some(is_unsized) = self.is_unsized() else { todo!() };
|
||||||
ScalarOrNDArray::Scalar(unsized_elem)
|
|
||||||
|
if is_unsized {
|
||||||
|
// NOTE: `np.size(self) == 0` here is never possible.
|
||||||
|
let zero = generator.get_size_type(ctx.ctx).const_zero();
|
||||||
|
let value = unsafe { self.data().get_unchecked(ctx, generator, &zero, None) };
|
||||||
|
|
||||||
|
ScalarOrNDArray::Scalar(value)
|
||||||
} else {
|
} else {
|
||||||
ScalarOrNDArray::NDArray(*self)
|
ScalarOrNDArray::NDArray(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this `NDArray` can be used as an `out` ndarray for an operation.
|
|
||||||
///
|
|
||||||
/// Raise an exception if the shapes do not match.
|
|
||||||
pub fn assert_can_be_written_by_out<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
out_shape: impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
) {
|
|
||||||
let ndarray_shape = self.shape();
|
|
||||||
let output_shape = out_shape;
|
|
||||||
|
|
||||||
irrt::ndarray::call_nac3_ndarray_util_assert_output_shape_same(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
&ndarray_shape,
|
|
||||||
&output_shape,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
|
impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
|
||||||
@ -569,8 +491,8 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayShapeProxy<'ctx, '_> {
|
|||||||
impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {
|
impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &IntValue<'ctx>,
|
idx: &IntValue<'ctx>,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
@ -608,26 +530,20 @@ impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_
|
|||||||
impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {}
|
||||||
impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized> TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>
|
impl<'ctx> TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {
|
||||||
for NDArrayShapeProxy<'ctx, '_>
|
|
||||||
{
|
|
||||||
fn downcast_to_type(
|
fn downcast_to_type(
|
||||||
&self,
|
&self,
|
||||||
_: &CodeGenContext<'ctx, '_>,
|
_: &mut CodeGenContext<'ctx, '_>,
|
||||||
_: &G,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
value.into_int_value()
|
value.into_int_value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized> TypedArrayLikeMutator<'ctx, G, IntValue<'ctx>>
|
impl<'ctx> TypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayShapeProxy<'ctx, '_> {
|
||||||
for NDArrayShapeProxy<'ctx, '_>
|
|
||||||
{
|
|
||||||
fn upcast_from_type(
|
fn upcast_from_type(
|
||||||
&self,
|
&self,
|
||||||
_: &CodeGenContext<'ctx, '_>,
|
_: &mut CodeGenContext<'ctx, '_>,
|
||||||
_: &G,
|
|
||||||
value: IntValue<'ctx>,
|
value: IntValue<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
value.into()
|
value.into()
|
||||||
@ -667,8 +583,8 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayStridesProxy<'ctx, '_> {
|
|||||||
impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {
|
impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &IntValue<'ctx>,
|
idx: &IntValue<'ctx>,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
@ -706,26 +622,20 @@ impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx,
|
|||||||
impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {}
|
||||||
impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized> TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>
|
impl<'ctx> TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {
|
||||||
for NDArrayStridesProxy<'ctx, '_>
|
|
||||||
{
|
|
||||||
fn downcast_to_type(
|
fn downcast_to_type(
|
||||||
&self,
|
&self,
|
||||||
_: &CodeGenContext<'ctx, '_>,
|
_: &mut CodeGenContext<'ctx, '_>,
|
||||||
_: &G,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
value.into_int_value()
|
value.into_int_value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, G: CodeGenerator + ?Sized> TypedArrayLikeMutator<'ctx, G, IntValue<'ctx>>
|
impl<'ctx> TypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayStridesProxy<'ctx, '_> {
|
||||||
for NDArrayStridesProxy<'ctx, '_>
|
|
||||||
{
|
|
||||||
fn upcast_from_type(
|
fn upcast_from_type(
|
||||||
&self,
|
&self,
|
||||||
_: &CodeGenContext<'ctx, '_>,
|
_: &mut CodeGenContext<'ctx, '_>,
|
||||||
_: &G,
|
|
||||||
value: IntValue<'ctx>,
|
value: IntValue<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
value.into()
|
value.into()
|
||||||
@ -758,19 +668,41 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> {
|
|||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &G,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
irrt::ndarray::call_nac3_ndarray_len(generator, ctx, *self.0)
|
irrt::ndarray::call_ndarray_calc_size(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
&self.as_slice_value(ctx, generator),
|
||||||
|
(None, None),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> ArrayLikeIndexer<'ctx> for NDArrayDataProxy<'ctx, '_> {
|
impl<'ctx> ArrayLikeIndexer<'ctx> for NDArrayDataProxy<'ctx, '_> {
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
idx: &IntValue<'ctx>,
|
idx: &IntValue<'ctx>,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let ptr = irrt::ndarray::call_nac3_ndarray_get_nth_pelement(generator, ctx, *self.0, *idx);
|
let sizeof_elem = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_truncate_or_bit_cast(
|
||||||
|
self.element_type(ctx, generator).size_of().unwrap(),
|
||||||
|
idx.get_type(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let idx = ctx.builder.build_int_mul(*idx, sizeof_elem, "").unwrap();
|
||||||
|
let ptr = unsafe {
|
||||||
|
ctx.builder
|
||||||
|
.build_in_bounds_gep(
|
||||||
|
self.base_ptr(ctx, generator),
|
||||||
|
&[idx],
|
||||||
|
name.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
// Current implementation is transparent - The returned pointer type is
|
// Current implementation is transparent - The returned pointer type is
|
||||||
// already cast into the expected type, allowing for immediately
|
// already cast into the expected type, allowing for immediately
|
||||||
@ -781,7 +713,7 @@ impl<'ctx> ArrayLikeIndexer<'ctx> for NDArrayDataProxy<'ctx, '_> {
|
|||||||
BasicTypeEnum::try_from(self.element_type(ctx, generator))
|
BasicTypeEnum::try_from(self.element_type(ctx, generator))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.ptr_type(AddressSpace::default()),
|
.ptr_type(AddressSpace::default()),
|
||||||
name.unwrap_or_default(),
|
"",
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -829,33 +761,55 @@ impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> ArrayLikeIndexer<'ctx, Index>
|
|||||||
{
|
{
|
||||||
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
generator: &G,
|
generator: &mut G,
|
||||||
indices: &Index,
|
indices: &Index,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
assert_eq!(indices.element_type(ctx, generator), generator.get_size_type(ctx.ctx).into());
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
|
||||||
let indices = TypedArrayLikeAdapter::from(
|
let indices_elem_ty = indices
|
||||||
indices.as_slice_value(ctx, generator),
|
.ptr_offset(ctx, generator, &llvm_usize.const_zero(), None)
|
||||||
|_, _, v| v.into_int_value(),
|
.get_type()
|
||||||
|_, _, v| v.into(),
|
.get_element_type();
|
||||||
|
let Ok(indices_elem_ty) = IntType::try_from(indices_elem_ty) else {
|
||||||
|
panic!("Expected list[int32] but got {indices_elem_ty}")
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
indices_elem_ty.get_bit_width(),
|
||||||
|
32,
|
||||||
|
"Expected list[int32] but got list[int{}]",
|
||||||
|
indices_elem_ty.get_bit_width()
|
||||||
);
|
);
|
||||||
|
|
||||||
let ptr = irrt::ndarray::call_nac3_ndarray_get_pelement_by_indices(
|
let index = irrt::ndarray::call_ndarray_flatten_index(generator, ctx, *self.0, indices);
|
||||||
generator, ctx, *self.0, &indices,
|
let sizeof_elem = ctx
|
||||||
);
|
.builder
|
||||||
|
.build_int_truncate_or_bit_cast(
|
||||||
|
self.element_type(ctx, generator).size_of().unwrap(),
|
||||||
|
index.get_type(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let index = ctx.builder.build_int_mul(index, sizeof_elem, "").unwrap();
|
||||||
|
|
||||||
// Current implementation is transparent - The returned pointer type is
|
let ptr = unsafe {
|
||||||
// already cast into the expected type, allowing for immediately
|
ctx.builder
|
||||||
// load/store.
|
.build_in_bounds_gep(
|
||||||
|
self.base_ptr(ctx, generator),
|
||||||
|
&[index],
|
||||||
|
name.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
// TODO: Current implementation is transparent
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.build_pointer_cast(
|
.build_pointer_cast(
|
||||||
ptr,
|
ptr,
|
||||||
BasicTypeEnum::try_from(self.element_type(ctx, generator))
|
BasicTypeEnum::try_from(self.element_type(ctx, generator))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.ptr_type(AddressSpace::default()),
|
.ptr_type(AddressSpace::default()),
|
||||||
name.unwrap_or_default(),
|
"",
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -950,9 +904,10 @@ impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> UntypedArrayLikeMutator<'ctx,
|
|||||||
/// This function is used generating strides for globally defined contiguous ndarrays.
|
/// This function is used generating strides for globally defined contiguous ndarrays.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_contiguous_strides(itemsize: u64, ndims: u64, shape: &[u64]) -> Vec<u64> {
|
pub fn make_contiguous_strides(itemsize: u64, ndims: u64, shape: &[u64]) -> Vec<u64> {
|
||||||
let mut strides = vec![0u64; ndims as usize];
|
let mut strides = Vec::with_capacity(ndims as usize);
|
||||||
let mut stride_product = 1u64;
|
let mut stride_product = 1u64;
|
||||||
for axis in (0..ndims).rev() {
|
for i in 0..ndims {
|
||||||
|
let axis = ndims - i - 1;
|
||||||
strides[axis as usize] = stride_product * itemsize;
|
strides[axis as usize] = stride_product * itemsize;
|
||||||
stride_product *= shape[axis as usize];
|
stride_product *= shape[axis as usize];
|
||||||
}
|
}
|
||||||
@ -966,52 +921,7 @@ pub enum ScalarOrNDArray<'ctx> {
|
|||||||
NDArray(NDArrayValue<'ctx>),
|
NDArray(NDArrayValue<'ctx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> TryFrom<&ScalarOrNDArray<'ctx>> for BasicValueEnum<'ctx> {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: &ScalarOrNDArray<'ctx>) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
ScalarOrNDArray::Scalar(scalar) => Ok(*scalar),
|
|
||||||
ScalarOrNDArray::NDArray(_) => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> TryFrom<&ScalarOrNDArray<'ctx>> for NDArrayValue<'ctx> {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: &ScalarOrNDArray<'ctx>) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
ScalarOrNDArray::Scalar(_) => Err(()),
|
|
||||||
ScalarOrNDArray::NDArray(ndarray) => Ok(*ndarray),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ScalarOrNDArray<'ctx> {
|
impl<'ctx> ScalarOrNDArray<'ctx> {
|
||||||
/// Split on `object` either into a scalar or an ndarray.
|
|
||||||
///
|
|
||||||
/// If `object` is an ndarray, [`ScalarOrNDArray::NDArray`].
|
|
||||||
///
|
|
||||||
/// For everything else, it is wrapped with [`ScalarOrNDArray::Scalar`].
|
|
||||||
pub fn from_value<G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(object_ty, object): (Type, BasicValueEnum<'ctx>),
|
|
||||||
) -> ScalarOrNDArray<'ctx> {
|
|
||||||
match &*ctx.unifier.get_ty(object_ty) {
|
|
||||||
TypeEnum::TObj { obj_id, .. }
|
|
||||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
|
|
||||||
.map_value(object.into_pointer_value(), None);
|
|
||||||
ScalarOrNDArray::NDArray(ndarray)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => ScalarOrNDArray::Scalar(object),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the underlying [`BasicValueEnum<'ctx>`] of this [`ScalarOrNDArray`].
|
/// Get the underlying [`BasicValueEnum<'ctx>`] of this [`ScalarOrNDArray`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> {
|
pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> {
|
||||||
@ -1020,57 +930,4 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
|
|||||||
ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(),
|
ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this [`ScalarOrNDArray`] to an ndarray - behaves like `np.asarray`.
|
|
||||||
///
|
|
||||||
/// - If this is an ndarray, the ndarray is returned.
|
|
||||||
/// - If this is a scalar, this function returns new ndarray created with
|
|
||||||
/// [`NDArrayType::construct_unsized`].
|
|
||||||
pub fn to_ndarray<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
) -> NDArrayValue<'ctx> {
|
|
||||||
match self {
|
|
||||||
ScalarOrNDArray::NDArray(ndarray) => *ndarray,
|
|
||||||
ScalarOrNDArray::Scalar(scalar) => {
|
|
||||||
NDArrayType::new_unsized(generator, ctx.ctx, scalar.get_type())
|
|
||||||
.construct_unsized(generator, ctx, scalar, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the dtype of the ndarray created if this were called with
|
|
||||||
/// [`ScalarOrNDArray::to_ndarray`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_dtype(&self) -> BasicTypeEnum<'ctx> {
|
|
||||||
match self {
|
|
||||||
ScalarOrNDArray::NDArray(ndarray) => ndarray.dtype,
|
|
||||||
ScalarOrNDArray::Scalar(scalar) => scalar.get_type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An helper enum specifying how a function should produce its output.
|
|
||||||
///
|
|
||||||
/// Many functions in NumPy has an optional `out` parameter (e.g., `matmul`). If `out` is specified
|
|
||||||
/// with an ndarray, the result of a function will be written to `out`. If `out` is not specified, a
|
|
||||||
/// function will create a new ndarray and store the result in it.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum NDArrayOut<'ctx> {
|
|
||||||
/// Tell a function should create a new ndarray with the expected element type `dtype`.
|
|
||||||
NewNDArray { dtype: BasicTypeEnum<'ctx> },
|
|
||||||
/// Tell a function to write the result to `ndarray`.
|
|
||||||
WriteToNDArray { ndarray: NDArrayValue<'ctx> },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> NDArrayOut<'ctx> {
|
|
||||||
/// Get the dtype of this output.
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_dtype(&self) -> BasicTypeEnum<'ctx> {
|
|
||||||
match self {
|
|
||||||
NDArrayOut::NewNDArray { dtype } => *dtype,
|
|
||||||
NDArrayOut::WriteToNDArray { ndarray } => ndarray.dtype,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use inkwell::{
|
|||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{NDArrayValue, ProxyValue};
|
use super::{NDArrayValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator};
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
irrt,
|
irrt,
|
||||||
stmt::{gen_for_callback, BreakContinueHooks},
|
stmt::{gen_for_callback, BreakContinueHooks},
|
||||||
@ -69,10 +69,7 @@ impl<'ctx> NDIterValue<'ctx> {
|
|||||||
irrt::ndarray::call_nac3_nditer_next(generator, ctx, *self);
|
irrt::ndarray::call_nac3_nditer_next(generator, ctx, *self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element_field(
|
fn element(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||||
&self,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
) -> StructField<'ctx, PointerValue<'ctx>> {
|
|
||||||
self.get_type().get_fields(ctx.ctx).element
|
self.get_type().get_fields(ctx.ctx).element
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +78,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(ctx).get(ctx, self.as_base_value(), self.name);
|
let p = self.element(ctx).get(ctx, self.as_base_value(), None);
|
||||||
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()
|
||||||
@ -94,25 +91,30 @@ impl<'ctx> NDIterValue<'ctx> {
|
|||||||
ctx.builder.build_load(p, "value").unwrap()
|
ctx.builder.build_load(p, "value").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nth_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
fn nth(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||||
self.get_type().get_fields(ctx.ctx).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(ctx).get(ctx, self.as_base_value(), self.name)
|
self.nth(ctx).get(ctx, self.as_base_value(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the indices of the current element.
|
/// Get the indices of the current element.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_indices<G: CodeGenerator + ?Sized>(
|
pub fn get_indices(
|
||||||
&self,
|
&'ctx self,
|
||||||
) -> TypedArrayLikeAdapter<'ctx, G, IntValue<'ctx>> {
|
) -> impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> + TypedArrayLikeMutator<'ctx, IntValue<'ctx>>
|
||||||
|
{
|
||||||
TypedArrayLikeAdapter::from(
|
TypedArrayLikeAdapter::from(
|
||||||
self.indices,
|
self.indices,
|
||||||
|_, _, val| val.into_int_value(),
|
Box::new(|ctx, val| {
|
||||||
|_, _, val| val.into(),
|
ctx.builder
|
||||||
|
.build_int_z_extend_or_bit_cast(val.into_int_value(), self.llvm_usize, "")
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
Box::new(|_, val| val.into()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,10 +143,6 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
///
|
///
|
||||||
/// `body` has access to [`BreakContinueHooks`] to short-circuit and [`NDIterValue`] to
|
/// `body` has access to [`BreakContinueHooks`] to short-circuit and [`NDIterValue`] to
|
||||||
/// get properties of the current iteration (e.g., the current element, indices, etc.)
|
/// get properties of the current iteration (e.g., the current element, indices, etc.)
|
||||||
///
|
|
||||||
/// Note: The caller is recommended to call `llvm.stacksave` and `llvm.stackrestore` before and
|
|
||||||
/// after invoking this function respectively. See [`NDIterType::construct`] for an explanation
|
|
||||||
/// on why this is suggested.
|
|
||||||
pub fn foreach<'a, G, F>(
|
pub fn foreach<'a, G, F>(
|
||||||
&self,
|
&self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
|
@ -1,152 +0,0 @@
|
|||||||
use inkwell::values::{BasicValueEnum, IntValue};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
codegen::{
|
|
||||||
stmt::gen_for_callback_incrementing,
|
|
||||||
types::{ListType, TupleType},
|
|
||||||
values::{
|
|
||||||
ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
|
||||||
TypedArrayLikeMutator, UntypedArrayLikeAccessor,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
|
||||||
},
|
|
||||||
typecheck::typedef::{Type, TypeEnum},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Parse a NumPy-like "int sequence" input and return the int sequence as an array and its length.
|
|
||||||
///
|
|
||||||
/// * `sequence` - The `sequence` parameter.
|
|
||||||
/// * `sequence_ty` - The typechecker type of `sequence`
|
|
||||||
///
|
|
||||||
/// The `sequence` argument type may only be one of the following:
|
|
||||||
/// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
|
|
||||||
/// 2. A tuple of `int32`; e.g., `np.empty((600, 800, 3))`
|
|
||||||
/// 3. A scalar `int32`; e.g., `np.empty(3)`, this is functionally equivalent to
|
|
||||||
/// `np.empty([3])`
|
|
||||||
///
|
|
||||||
/// All `int32` values will be sign-extended to `SizeT`.
|
|
||||||
pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
(input_seq_ty, input_seq): (Type, BasicValueEnum<'ctx>),
|
|
||||||
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let zero = llvm_usize.const_zero();
|
|
||||||
let one = llvm_usize.const_int(1, false);
|
|
||||||
|
|
||||||
// The result `list` to return.
|
|
||||||
match &*ctx.unifier.get_ty_immutable(input_seq_ty) {
|
|
||||||
TypeEnum::TObj { obj_id, .. }
|
|
||||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
|
|
||||||
|
|
||||||
let input_seq = ListType::from_unifier_type(generator, ctx, input_seq_ty)
|
|
||||||
.map_value(input_seq.into_pointer_value(), None);
|
|
||||||
|
|
||||||
let len = input_seq.load_size(ctx, None);
|
|
||||||
// TODO: Find a way to remove this mid-BB allocation
|
|
||||||
let result = ctx.builder.build_array_alloca(llvm_usize, len, "").unwrap();
|
|
||||||
let result = TypedArrayLikeAdapter::from(
|
|
||||||
ArraySliceValue::from_ptr_val(result, len, None),
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load all the `int32`s from the input_sequence, cast them to `SizeT`, and store them into `result`
|
|
||||||
gen_for_callback_incrementing(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
None,
|
|
||||||
zero,
|
|
||||||
(len, false),
|
|
||||||
|generator, ctx, _, i| {
|
|
||||||
// Load the i-th int32 in the input sequence
|
|
||||||
let int = unsafe {
|
|
||||||
input_seq.data().get_unchecked(ctx, generator, &i, None).into_int_value()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cast to SizeT
|
|
||||||
let int =
|
|
||||||
ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
|
|
||||||
|
|
||||||
// Store
|
|
||||||
unsafe { result.set_typed_unchecked(ctx, generator, &i, int) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
one,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeEnum::TTuple { .. } => {
|
|
||||||
// 2. A tuple of ints; e.g., `np.empty((600, 800, 3))`
|
|
||||||
|
|
||||||
let input_seq = TupleType::from_unifier_type(generator, ctx, input_seq_ty)
|
|
||||||
.map_value(input_seq.into_struct_value(), None);
|
|
||||||
|
|
||||||
let len = input_seq.get_type().num_elements();
|
|
||||||
|
|
||||||
let result = generator
|
|
||||||
.gen_array_var_alloc(
|
|
||||||
ctx,
|
|
||||||
llvm_usize.into(),
|
|
||||||
llvm_usize.const_int(u64::from(len), false),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let result = TypedArrayLikeAdapter::from(
|
|
||||||
result,
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for i in 0..input_seq.get_type().num_elements() {
|
|
||||||
// Get the i-th element off of the tuple and load it into `result`.
|
|
||||||
let int = input_seq.load_element(ctx, i).into_int_value();
|
|
||||||
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
result.set_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_int(u64::from(i), false),
|
|
||||||
int,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeEnum::TObj { obj_id, .. }
|
|
||||||
if *obj_id == ctx.primitives.int32.obj_id(&ctx.unifier).unwrap() =>
|
|
||||||
{
|
|
||||||
// 3. A scalar int; e.g., `np.empty(3)`, this is functionally equivalent to `np.empty([3])`
|
|
||||||
|
|
||||||
let input_int = input_seq.into_int_value();
|
|
||||||
|
|
||||||
let len = one;
|
|
||||||
let result = generator.gen_array_var_alloc(ctx, llvm_usize.into(), len, None).unwrap();
|
|
||||||
let result = TypedArrayLikeAdapter::from(
|
|
||||||
result,
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
);
|
|
||||||
let int =
|
|
||||||
ctx.builder.build_int_s_extend_or_bit_cast(input_int, llvm_usize, "").unwrap();
|
|
||||||
|
|
||||||
// Storing into result[0]
|
|
||||||
unsafe {
|
|
||||||
result.set_typed_unchecked(ctx, generator, &zero, int);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => panic!("encountered unknown sequence type: {}", ctx.unifier.stringify(input_seq_ty)),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,9 @@
|
|||||||
use std::iter::{once, repeat_n};
|
use std::iter::{once, repeat_n};
|
||||||
|
|
||||||
use inkwell::values::{IntValue, PointerValue};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
irrt,
|
values::ndarray::{NDArrayValue, RustNDIndex},
|
||||||
stmt::gen_if_callback,
|
|
||||||
types::ndarray::NDArrayType,
|
|
||||||
values::{
|
|
||||||
ndarray::{NDArrayValue, RustNDIndex},
|
|
||||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
|
||||||
},
|
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,7 +19,9 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
ndmin: u64,
|
ndmin: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ndims = self.ndims;
|
assert!(self.ndims.is_some(), "NDArrayValue::atleast_nd is only supported for instances with compile-time known ndims (self.ndims = Some(...))");
|
||||||
|
|
||||||
|
let ndims = self.ndims.unwrap();
|
||||||
|
|
||||||
if ndims < ndmin {
|
if ndims < ndmin {
|
||||||
// Extend the dimensions with np.newaxis.
|
// Extend the dimensions with np.newaxis.
|
||||||
@ -38,117 +33,4 @@ impl<'ctx> NDArrayValue<'ctx> {
|
|||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a reshaped view on this ndarray like
|
|
||||||
/// [`np.reshape()`](https://numpy.org/doc/stable/reference/generated/numpy.reshape.html).
|
|
||||||
///
|
|
||||||
/// If there is a `-1` in `new_shape`, it will be resolved; `new_shape` would **NOT** be
|
|
||||||
/// modified as a result.
|
|
||||||
///
|
|
||||||
/// If reshape without copying is impossible, this function will allocate a new ndarray and copy
|
|
||||||
/// contents.
|
|
||||||
///
|
|
||||||
/// * `new_ndims` - The number of dimensions of `new_shape` as a [`Type`].
|
|
||||||
/// * `new_shape` - The target shape to do `np.reshape()`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn reshape_or_copy<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
new_ndims: u64,
|
|
||||||
new_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
|
||||||
) -> Self {
|
|
||||||
assert_eq!(new_shape.element_type(ctx, generator), self.llvm_usize.into());
|
|
||||||
|
|
||||||
// TODO: The current criterion for whether to do a full copy or not is by checking
|
|
||||||
// `is_c_contiguous`, but this is not optimal - there are cases when the ndarray is
|
|
||||||
// not contiguous but could be reshaped without copying data. Look into how numpy does
|
|
||||||
// it.
|
|
||||||
|
|
||||||
let dst_ndarray = NDArrayType::new(generator, ctx.ctx, self.dtype, new_ndims)
|
|
||||||
.construct_uninitialized(generator, ctx, None);
|
|
||||||
dst_ndarray.copy_shape_from_array(generator, ctx, new_shape.base_ptr(ctx, generator));
|
|
||||||
|
|
||||||
// Resolve negative indices
|
|
||||||
let size = self.size(generator, ctx);
|
|
||||||
let dst_ndims = self.llvm_usize.const_int(dst_ndarray.get_type().ndims(), false);
|
|
||||||
let dst_shape = dst_ndarray.shape();
|
|
||||||
irrt::ndarray::call_nac3_ndarray_reshape_resolve_and_check_new_shape(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
size,
|
|
||||||
dst_ndims,
|
|
||||||
dst_shape.as_slice_value(ctx, generator),
|
|
||||||
);
|
|
||||||
|
|
||||||
gen_if_callback(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
|generator, ctx| Ok(self.is_c_contiguous(generator, ctx)),
|
|
||||||
|generator, ctx| {
|
|
||||||
// Reshape is possible without copying
|
|
||||||
dst_ndarray.set_strides_contiguous(generator, ctx);
|
|
||||||
dst_ndarray.store_data(ctx, self.data().base_ptr(ctx, generator));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
|generator, ctx| {
|
|
||||||
// Reshape is impossible without copying
|
|
||||||
unsafe {
|
|
||||||
dst_ndarray.create_data(generator, ctx);
|
|
||||||
}
|
|
||||||
dst_ndarray.copy_data_from(generator, ctx, *self);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
dst_ndarray
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a transposed view on this ndarray like
|
|
||||||
/// [`np.transpose(<ndarray>, <axes> = None)`](https://numpy.org/doc/stable/reference/generated/numpy.transpose.html).
|
|
||||||
///
|
|
||||||
/// * `axes` - If specified, should be an array of the permutation (negative indices are
|
|
||||||
/// **allowed**).
|
|
||||||
#[must_use]
|
|
||||||
pub fn transpose<G: CodeGenerator + ?Sized>(
|
|
||||||
&self,
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
axes: Option<PointerValue<'ctx>>,
|
|
||||||
) -> Self {
|
|
||||||
assert!(
|
|
||||||
axes.is_none_or(|axes| axes.get_type().get_element_type() == self.llvm_usize.into())
|
|
||||||
);
|
|
||||||
|
|
||||||
// Define models
|
|
||||||
let transposed_ndarray = self.get_type().construct_uninitialized(generator, ctx, None);
|
|
||||||
|
|
||||||
let axes = if let Some(axes) = axes {
|
|
||||||
let num_axes = self.llvm_usize.const_int(self.ndims, false);
|
|
||||||
|
|
||||||
// `axes = nullptr` if `axes` is unspecified.
|
|
||||||
let axes = ArraySliceValue::from_ptr_val(axes, num_axes, None);
|
|
||||||
|
|
||||||
Some(TypedArrayLikeAdapter::from(
|
|
||||||
axes,
|
|
||||||
|_, _, val| val.into_int_value(),
|
|
||||||
|_, _, val| val.into(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
irrt::ndarray::call_nac3_ndarray_transpose(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
*self,
|
|
||||||
transposed_ndarray,
|
|
||||||
axes.as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
transposed_ndarray
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
use inkwell::{
|
|
||||||
types::IntType,
|
|
||||||
values::{BasicValue, BasicValueEnum, StructValue},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ProxyValue;
|
|
||||||
use crate::codegen::{types::TupleType, CodeGenContext};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct TupleValue<'ctx> {
|
|
||||||
value: StructValue<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_struct_value(
|
|
||||||
value: StructValue<'ctx>,
|
|
||||||
llvm_usize: IntType<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> Self {
|
|
||||||
debug_assert!(Self::is_representable(value, llvm_usize).is_ok());
|
|
||||||
|
|
||||||
Self { value, llvm_usize, name }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores a value into the tuple element at the given `index`.
|
|
||||||
pub fn store_element(
|
|
||||||
&mut self,
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
index: u32,
|
|
||||||
element: impl BasicValue<'ctx>,
|
|
||||||
) {
|
|
||||||
assert_eq!(element.as_basic_value_enum().get_type(), unsafe {
|
|
||||||
self.get_type().type_at_index_unchecked(index)
|
|
||||||
});
|
|
||||||
|
|
||||||
let new_value = ctx
|
|
||||||
.builder
|
|
||||||
.build_insert_value(self.value, element, index, self.name.unwrap_or_default())
|
|
||||||
.unwrap();
|
|
||||||
self.value = new_value.into_struct_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads a value from the tuple element at the given `index`.
|
|
||||||
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
|
|
||||||
ctx.builder
|
|
||||||
.build_extract_value(
|
|
||||||
self.value,
|
|
||||||
index,
|
|
||||||
&format!("{}[{{i}}]", self.name.unwrap_or("tuple")),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> ProxyValue<'ctx> for TupleValue<'ctx> {
|
|
||||||
type Base = StructValue<'ctx>;
|
|
||||||
type Type = TupleType<'ctx>;
|
|
||||||
|
|
||||||
fn get_type(&self) -> Self::Type {
|
|
||||||
TupleType::from_type(self.as_base_value().get_type(), self.llvm_usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_base_value(&self) -> Self::Base {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<TupleValue<'ctx>> for StructValue<'ctx> {
|
|
||||||
fn from(value: TupleValue<'ctx>) -> Self {
|
|
||||||
value.as_base_value()
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
|
||||||
use itertools::{izip, Itertools};
|
use itertools::{chain, izip, Itertools};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use nac3parser::ast::{Constant, Expr, Location, StrRef};
|
use nac3parser::ast::{Constant, Expr, Location, StrRef};
|
||||||
@ -452,11 +452,11 @@ pub fn parse_type_annotation<T>(
|
|||||||
type_vars.len()
|
type_vars.len()
|
||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
let fields = fields
|
let fields = chain(
|
||||||
.iter()
|
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||||
.map(|(k, v, m)| (*k, (*v, *m)))
|
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
|
||||||
.chain(methods.iter().map(|(k, v, _)| (*k, (*v, false))))
|
)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: VarMap::default() }))
|
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: VarMap::default() }))
|
||||||
} else {
|
} else {
|
||||||
Err(HashSet::from([format!("Cannot use function name as type at {loc}")]))
|
Err(HashSet::from([format!("Cannot use function name as type at {loc}")]))
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use inkwell::{values::BasicValue, IntPredicate};
|
use inkwell::{
|
||||||
|
attributes::{Attribute, AttributeLoc},
|
||||||
|
types::{BasicMetadataTypeEnum, BasicType},
|
||||||
|
values::{BasicMetadataValueEnum, BasicValue, CallSiteValue},
|
||||||
|
IntPredicate,
|
||||||
|
};
|
||||||
|
use itertools::Either;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
helper::{
|
helper::{debug_assert_prim_is_allowed, make_exception_fields, PrimDef, PrimDefDetails},
|
||||||
debug_assert_prim_is_allowed, extract_ndims, make_exception_fields, PrimDef, PrimDefDetails,
|
numpy::make_ndarray_ty,
|
||||||
},
|
|
||||||
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
|
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,8 +20,7 @@ use crate::{
|
|||||||
builtin_fns,
|
builtin_fns,
|
||||||
numpy::*,
|
numpy::*,
|
||||||
stmt::exn_constructor,
|
stmt::exn_constructor,
|
||||||
types::ndarray::NDArrayType,
|
values::{ProxyValue, RangeValue},
|
||||||
values::{ndarray::shape::parse_numpy_int_sequence, ProxyValue, RangeValue},
|
|
||||||
},
|
},
|
||||||
symbol_resolver::SymbolValue,
|
symbol_resolver::SymbolValue,
|
||||||
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},
|
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},
|
||||||
@ -145,6 +148,144 @@ fn create_fn_by_codegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a NumPy [`TopLevelDef`] function using an LLVM intrinsic.
|
||||||
|
///
|
||||||
|
/// * `name`: The name of the implemented NumPy function.
|
||||||
|
/// * `ret_ty`: The return type of this function.
|
||||||
|
/// * `param_ty`: The parameters accepted by this function, represented by a tuple of the
|
||||||
|
/// [parameter type][Type] and the parameter symbol name.
|
||||||
|
/// * `intrinsic_fn`: The fully-qualified name of the LLVM intrinsic function.
|
||||||
|
fn create_fn_by_intrinsic(
|
||||||
|
unifier: &mut Unifier,
|
||||||
|
var_map: &VarMap,
|
||||||
|
name: &'static str,
|
||||||
|
ret_ty: Type,
|
||||||
|
params: &[(Type, &'static str)],
|
||||||
|
intrinsic_fn: &'static str,
|
||||||
|
) -> TopLevelDef {
|
||||||
|
let param_tys = params.iter().map(|p| p.0).collect_vec();
|
||||||
|
|
||||||
|
create_fn_by_codegen(
|
||||||
|
unifier,
|
||||||
|
var_map,
|
||||||
|
name,
|
||||||
|
ret_ty,
|
||||||
|
params,
|
||||||
|
Box::new(move |ctx, _, fun, args, generator| {
|
||||||
|
let args_ty = fun.0.args.iter().map(|a| a.ty).collect_vec();
|
||||||
|
|
||||||
|
assert!(param_tys
|
||||||
|
.iter()
|
||||||
|
.zip(&args_ty)
|
||||||
|
.all(|(expected, actual)| ctx.unifier.unioned(*expected, *actual)));
|
||||||
|
|
||||||
|
let args_val = args_ty
|
||||||
|
.iter()
|
||||||
|
.zip_eq(args.iter())
|
||||||
|
.map(|(ty, arg)| arg.1.clone().to_basic_value_enum(ctx, generator, *ty).unwrap())
|
||||||
|
.map_into::<BasicMetadataValueEnum>()
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let intrinsic_fn = ctx.module.get_function(intrinsic_fn).unwrap_or_else(|| {
|
||||||
|
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
|
||||||
|
let param_llvm_ty = param_tys
|
||||||
|
.iter()
|
||||||
|
.map(|p| ctx.get_llvm_abi_type(generator, *p))
|
||||||
|
.map_into::<BasicMetadataTypeEnum>()
|
||||||
|
.collect_vec();
|
||||||
|
let fn_type = ret_llvm_ty.fn_type(param_llvm_ty.as_slice(), false);
|
||||||
|
|
||||||
|
ctx.module.add_function(intrinsic_fn, fn_type, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
let val = ctx
|
||||||
|
.builder
|
||||||
|
.build_call(intrinsic_fn, args_val.as_slice(), name)
|
||||||
|
.map(CallSiteValue::try_as_basic_value)
|
||||||
|
.map(Either::unwrap_left)
|
||||||
|
.unwrap();
|
||||||
|
Ok(val.into())
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a unary NumPy [`TopLevelDef`] function using an extern function (e.g. from `libc` or
|
||||||
|
/// `libm`).
|
||||||
|
///
|
||||||
|
/// * `name`: The name of the implemented NumPy function.
|
||||||
|
/// * `ret_ty`: The return type of this function.
|
||||||
|
/// * `param_ty`: The parameters accepted by this function, represented by a tuple of the
|
||||||
|
/// [parameter type][Type] and the parameter symbol name.
|
||||||
|
/// * `extern_fn`: The fully-qualified name of the extern function used as the implementation.
|
||||||
|
/// * `attrs`: The list of attributes to apply to this function declaration. Note that `nounwind` is
|
||||||
|
/// already implied by the C ABI.
|
||||||
|
fn create_fn_by_extern(
|
||||||
|
unifier: &mut Unifier,
|
||||||
|
var_map: &VarMap,
|
||||||
|
name: &'static str,
|
||||||
|
ret_ty: Type,
|
||||||
|
params: &[(Type, &'static str)],
|
||||||
|
extern_fn: &'static str,
|
||||||
|
attrs: &'static [&str],
|
||||||
|
) -> TopLevelDef {
|
||||||
|
let param_tys = params.iter().map(|p| p.0).collect_vec();
|
||||||
|
|
||||||
|
create_fn_by_codegen(
|
||||||
|
unifier,
|
||||||
|
var_map,
|
||||||
|
name,
|
||||||
|
ret_ty,
|
||||||
|
params,
|
||||||
|
Box::new(move |ctx, _, fun, args, generator| {
|
||||||
|
let args_ty = fun.0.args.iter().map(|a| a.ty).collect_vec();
|
||||||
|
|
||||||
|
assert!(param_tys
|
||||||
|
.iter()
|
||||||
|
.zip(&args_ty)
|
||||||
|
.all(|(expected, actual)| ctx.unifier.unioned(*expected, *actual)));
|
||||||
|
|
||||||
|
let args_val = args_ty
|
||||||
|
.iter()
|
||||||
|
.zip_eq(args.iter())
|
||||||
|
.map(|(ty, arg)| arg.1.clone().to_basic_value_enum(ctx, generator, *ty).unwrap())
|
||||||
|
.map_into::<BasicMetadataValueEnum>()
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let intrinsic_fn = ctx.module.get_function(extern_fn).unwrap_or_else(|| {
|
||||||
|
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
|
||||||
|
let param_llvm_ty = param_tys
|
||||||
|
.iter()
|
||||||
|
.map(|p| ctx.get_llvm_abi_type(generator, *p))
|
||||||
|
.map_into::<BasicMetadataTypeEnum>()
|
||||||
|
.collect_vec();
|
||||||
|
let fn_type = ret_llvm_ty.fn_type(param_llvm_ty.as_slice(), false);
|
||||||
|
let func = ctx.module.add_function(extern_fn, fn_type, None);
|
||||||
|
func.add_attribute(
|
||||||
|
AttributeLoc::Function,
|
||||||
|
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
for attr in attrs {
|
||||||
|
func.add_attribute(
|
||||||
|
AttributeLoc::Function,
|
||||||
|
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
func
|
||||||
|
});
|
||||||
|
|
||||||
|
let val = ctx
|
||||||
|
.builder
|
||||||
|
.build_call(intrinsic_fn, &args_val, name)
|
||||||
|
.map(CallSiteValue::try_as_basic_value)
|
||||||
|
.map(Either::unwrap_left)
|
||||||
|
.unwrap();
|
||||||
|
Ok(val.into())
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> BuiltinInfo {
|
pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> BuiltinInfo {
|
||||||
BuiltinBuilder::new(unifier, primitives)
|
BuiltinBuilder::new(unifier, primitives)
|
||||||
.build_all_builtins()
|
.build_all_builtins()
|
||||||
@ -195,6 +336,7 @@ struct BuiltinBuilder<'a> {
|
|||||||
|
|
||||||
ndarray_float: Type,
|
ndarray_float: Type,
|
||||||
ndarray_float_2d: Type,
|
ndarray_float_2d: Type,
|
||||||
|
ndarray_num_ty: Type,
|
||||||
|
|
||||||
float_or_ndarray_ty: TypeVar,
|
float_or_ndarray_ty: TypeVar,
|
||||||
float_or_ndarray_var_map: VarMap,
|
float_or_ndarray_var_map: VarMap,
|
||||||
@ -308,6 +450,7 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
|
|
||||||
ndarray_float,
|
ndarray_float,
|
||||||
ndarray_float_2d,
|
ndarray_float_2d,
|
||||||
|
ndarray_num_ty,
|
||||||
|
|
||||||
float_or_ndarray_ty,
|
float_or_ndarray_ty,
|
||||||
float_or_ndarray_var_map,
|
float_or_ndarray_var_map,
|
||||||
@ -369,14 +512,6 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
| PrimDef::FunNpEye
|
| PrimDef::FunNpEye
|
||||||
| PrimDef::FunNpIdentity => self.build_ndarray_other_factory_function(prim),
|
| PrimDef::FunNpIdentity => self.build_ndarray_other_factory_function(prim),
|
||||||
|
|
||||||
PrimDef::FunNpSize | PrimDef::FunNpShape | PrimDef::FunNpStrides => {
|
|
||||||
self.build_ndarray_property_getter_function(prim)
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimDef::FunNpBroadcastTo | PrimDef::FunNpTranspose | PrimDef::FunNpReshape => {
|
|
||||||
self.build_ndarray_view_function(prim)
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimDef::FunStr => self.build_str_function(),
|
PrimDef::FunStr => self.build_str_function(),
|
||||||
|
|
||||||
PrimDef::FunFloor | PrimDef::FunFloor64 | PrimDef::FunCeil | PrimDef::FunCeil64 => {
|
PrimDef::FunFloor | PrimDef::FunFloor64 | PrimDef::FunCeil | PrimDef::FunCeil64 => {
|
||||||
@ -442,6 +577,10 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
| PrimDef::FunNpHypot
|
| PrimDef::FunNpHypot
|
||||||
| PrimDef::FunNpNextAfter => self.build_np_2ary_function(prim),
|
| PrimDef::FunNpNextAfter => self.build_np_2ary_function(prim),
|
||||||
|
|
||||||
|
PrimDef::FunNpTranspose | PrimDef::FunNpReshape => {
|
||||||
|
self.build_np_sp_ndarray_function(prim)
|
||||||
|
}
|
||||||
|
|
||||||
PrimDef::FunNpDot
|
PrimDef::FunNpDot
|
||||||
| PrimDef::FunNpLinalgCholesky
|
| PrimDef::FunNpLinalgCholesky
|
||||||
| PrimDef::FunNpLinalgQr
|
| PrimDef::FunNpLinalgQr
|
||||||
@ -1247,176 +1386,6 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_ndarray_property_getter_function(&mut self, prim: PrimDef) -> TopLevelDef {
|
|
||||||
debug_assert_prim_is_allowed(
|
|
||||||
prim,
|
|
||||||
&[PrimDef::FunNpSize, PrimDef::FunNpShape, PrimDef::FunNpStrides],
|
|
||||||
);
|
|
||||||
|
|
||||||
let in_ndarray_ty = self.unifier.get_fresh_var_with_range(
|
|
||||||
&[self.primitives.ndarray],
|
|
||||||
Some("T".into()),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
match prim {
|
|
||||||
PrimDef::FunNpSize => create_fn_by_codegen(
|
|
||||||
self.unifier,
|
|
||||||
&into_var_map([in_ndarray_ty]),
|
|
||||||
prim.name(),
|
|
||||||
self.primitives.int32,
|
|
||||||
&[(in_ndarray_ty.ty, "a")],
|
|
||||||
Box::new(|ctx, obj, fun, args, generator| {
|
|
||||||
assert!(obj.is_none());
|
|
||||||
assert_eq!(args.len(), 1);
|
|
||||||
|
|
||||||
let ndarray_ty = fun.0.args[0].ty;
|
|
||||||
let ndarray =
|
|
||||||
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
|
||||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
|
||||||
.map_value(ndarray.into_pointer_value(), None);
|
|
||||||
|
|
||||||
let size = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_truncate_or_bit_cast(
|
|
||||||
ndarray.size(generator, ctx),
|
|
||||||
ctx.ctx.i32_type(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
Ok(Some(size.into()))
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
|
|
||||||
PrimDef::FunNpShape | PrimDef::FunNpStrides => {
|
|
||||||
// The function signatures of `np_shape` an `np_size` are the same.
|
|
||||||
// Mixed together for convenience.
|
|
||||||
|
|
||||||
// The return type is a tuple of variable length depending on the ndims of the input ndarray.
|
|
||||||
let ret_ty = self.unifier.get_dummy_var().ty; // Handled by special folding
|
|
||||||
|
|
||||||
create_fn_by_codegen(
|
|
||||||
self.unifier,
|
|
||||||
&into_var_map([in_ndarray_ty]),
|
|
||||||
prim.name(),
|
|
||||||
ret_ty,
|
|
||||||
&[(in_ndarray_ty.ty, "a")],
|
|
||||||
Box::new(move |ctx, obj, fun, args, generator| {
|
|
||||||
assert!(obj.is_none());
|
|
||||||
assert_eq!(args.len(), 1);
|
|
||||||
|
|
||||||
let ndarray_ty = fun.0.args[0].ty;
|
|
||||||
let ndarray =
|
|
||||||
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
|
||||||
|
|
||||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
|
||||||
.map_value(ndarray.into_pointer_value(), None);
|
|
||||||
|
|
||||||
let result_tuple = match prim {
|
|
||||||
PrimDef::FunNpShape => ndarray.make_shape_tuple(generator, ctx),
|
|
||||||
PrimDef::FunNpStrides => ndarray.make_strides_tuple(generator, ctx),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(result_tuple.as_base_value().into()))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build np/sp functions that take as input `NDArray` only
|
|
||||||
fn build_ndarray_view_function(&mut self, prim: PrimDef) -> TopLevelDef {
|
|
||||||
debug_assert_prim_is_allowed(
|
|
||||||
prim,
|
|
||||||
&[PrimDef::FunNpBroadcastTo, PrimDef::FunNpTranspose, PrimDef::FunNpReshape],
|
|
||||||
);
|
|
||||||
|
|
||||||
let in_ndarray_ty = self.unifier.get_fresh_var_with_range(
|
|
||||||
&[self.primitives.ndarray],
|
|
||||||
Some("T".into()),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
match prim {
|
|
||||||
PrimDef::FunNpTranspose => create_fn_by_codegen(
|
|
||||||
self.unifier,
|
|
||||||
&into_var_map([in_ndarray_ty]),
|
|
||||||
prim.name(),
|
|
||||||
in_ndarray_ty.ty,
|
|
||||||
&[(in_ndarray_ty.ty, "x")],
|
|
||||||
Box::new(move |ctx, _, fun, args, generator| {
|
|
||||||
let arg_ty = fun.0.args[0].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)
|
|
||||||
.map_value(arg_val.into_pointer_value(), None);
|
|
||||||
|
|
||||||
let ndarray = ndarray.transpose(generator, ctx, None); // TODO: Add axes argument
|
|
||||||
Ok(Some(ndarray.as_base_value().into()))
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
|
|
||||||
// NOTE: on `ndarray_factory_fn_shape_arg_tvar` and
|
|
||||||
// the `param_ty` for `create_fn_by_codegen`.
|
|
||||||
//
|
|
||||||
// Similar to `build_ndarray_from_shape_factory_function` we delegate the responsibility of typechecking
|
|
||||||
// to [`typecheck::type_inferencer::Inferencer::fold_numpy_function_call_shape_argument`],
|
|
||||||
// and use a dummy [`TypeVar`] `ndarray_factory_fn_shape_arg_tvar` as a placeholder for `param_ty`.
|
|
||||||
PrimDef::FunNpBroadcastTo | PrimDef::FunNpReshape => {
|
|
||||||
// These two functions have the same function signature.
|
|
||||||
// Mixed together for convenience.
|
|
||||||
|
|
||||||
let ret_ty = self.unifier.get_dummy_var().ty; // Handled by special holding
|
|
||||||
|
|
||||||
create_fn_by_codegen(
|
|
||||||
self.unifier,
|
|
||||||
&VarMap::new(),
|
|
||||||
prim.name(),
|
|
||||||
ret_ty,
|
|
||||||
&[
|
|
||||||
(in_ndarray_ty.ty, "x"),
|
|
||||||
(self.ndarray_factory_fn_shape_arg_tvar.ty, "shape"), // Handled by special folding
|
|
||||||
],
|
|
||||||
Box::new(move |ctx, _, fun, args, generator| {
|
|
||||||
let ndarray_ty = fun.0.args[0].ty;
|
|
||||||
let ndarray_val =
|
|
||||||
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
|
||||||
|
|
||||||
let shape_ty = fun.0.args[1].ty;
|
|
||||||
let shape_val =
|
|
||||||
args[1].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?;
|
|
||||||
|
|
||||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
|
||||||
.map_value(ndarray_val.into_pointer_value(), None);
|
|
||||||
|
|
||||||
let shape = parse_numpy_int_sequence(generator, ctx, (shape_ty, shape_val));
|
|
||||||
|
|
||||||
// The ndims after reshaping is gotten from the return type of the call.
|
|
||||||
let (_, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, fun.0.ret);
|
|
||||||
let ndims = extract_ndims(&ctx.unifier, ndims);
|
|
||||||
|
|
||||||
let new_ndarray = match prim {
|
|
||||||
PrimDef::FunNpBroadcastTo => {
|
|
||||||
ndarray.broadcast_to(generator, ctx, ndims, &shape)
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimDef::FunNpReshape => {
|
|
||||||
ndarray.reshape_or_copy(generator, ctx, ndims, &shape)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Ok(Some(new_ndarray.as_base_value().as_basic_value_enum()))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the `str()` function.
|
/// Build the `str()` function.
|
||||||
fn build_str_function(&mut self) -> TopLevelDef {
|
fn build_str_function(&mut self) -> TopLevelDef {
|
||||||
let prim = PrimDef::FunStr;
|
let prim = PrimDef::FunStr;
|
||||||
@ -1904,6 +1873,57 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build np/sp functions that take as input `NDArray` only
|
||||||
|
fn build_np_sp_ndarray_function(&mut self, prim: PrimDef) -> TopLevelDef {
|
||||||
|
debug_assert_prim_is_allowed(prim, &[PrimDef::FunNpTranspose, PrimDef::FunNpReshape]);
|
||||||
|
|
||||||
|
match prim {
|
||||||
|
PrimDef::FunNpTranspose => {
|
||||||
|
let ndarray_ty = self.unifier.get_fresh_var_with_range(
|
||||||
|
&[self.ndarray_num_ty],
|
||||||
|
Some("T".into()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
create_fn_by_codegen(
|
||||||
|
self.unifier,
|
||||||
|
&into_var_map([ndarray_ty]),
|
||||||
|
prim.name(),
|
||||||
|
ndarray_ty.ty,
|
||||||
|
&[(ndarray_ty.ty, "x")],
|
||||||
|
Box::new(move |ctx, _, fun, args, generator| {
|
||||||
|
let arg_ty = fun.0.args[0].ty;
|
||||||
|
let arg_val =
|
||||||
|
args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||||
|
Ok(Some(ndarray_transpose(generator, ctx, (arg_ty, arg_val))?))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: on `ndarray_factory_fn_shape_arg_tvar` and
|
||||||
|
// the `param_ty` for `create_fn_by_codegen`.
|
||||||
|
//
|
||||||
|
// Similar to `build_ndarray_from_shape_factory_function` we delegate the responsibility of typechecking
|
||||||
|
// to [`typecheck::type_inferencer::Inferencer::fold_numpy_function_call_shape_argument`],
|
||||||
|
// and use a dummy [`TypeVar`] `ndarray_factory_fn_shape_arg_tvar` as a placeholder for `param_ty`.
|
||||||
|
PrimDef::FunNpReshape => create_fn_by_codegen(
|
||||||
|
self.unifier,
|
||||||
|
&VarMap::new(),
|
||||||
|
prim.name(),
|
||||||
|
self.ndarray_num_ty,
|
||||||
|
&[(self.ndarray_num_ty, "x"), (self.ndarray_factory_fn_shape_arg_tvar.ty, "shape")],
|
||||||
|
Box::new(move |ctx, _, fun, args, generator| {
|
||||||
|
let x1_ty = fun.0.args[0].ty;
|
||||||
|
let x1_val = args[0].1.clone().to_basic_value_enum(ctx, generator, x1_ty)?;
|
||||||
|
let x2_ty = fun.0.args[1].ty;
|
||||||
|
let x2_val = args[1].1.clone().to_basic_value_enum(ctx, generator, x2_ty)?;
|
||||||
|
Ok(Some(ndarray_reshape(generator, ctx, (x1_ty, x1_val), (x2_ty, x2_val))?))
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Build `np_linalg` and `sp_linalg` functions
|
/// Build `np_linalg` and `sp_linalg` functions
|
||||||
///
|
///
|
||||||
/// The input to these functions must be floating point `NDArray`
|
/// The input to these functions must be floating point `NDArray`
|
||||||
@ -1935,12 +1955,10 @@ impl<'a> BuiltinBuilder<'a> {
|
|||||||
Box::new(move |ctx, _, fun, args, generator| {
|
Box::new(move |ctx, _, fun, args, generator| {
|
||||||
let x1_ty = fun.0.args[0].ty;
|
let x1_ty = fun.0.args[0].ty;
|
||||||
let x1_val = args[0].1.clone().to_basic_value_enum(ctx, generator, x1_ty)?;
|
let x1_val = args[0].1.clone().to_basic_value_enum(ctx, generator, x1_ty)?;
|
||||||
|
|
||||||
let x2_ty = fun.0.args[1].ty;
|
let x2_ty = fun.0.args[1].ty;
|
||||||
let x2_val = args[1].1.clone().to_basic_value_enum(ctx, generator, x2_ty)?;
|
let x2_val = args[1].1.clone().to_basic_value_enum(ctx, generator, x2_ty)?;
|
||||||
|
|
||||||
let result = ndarray_dot(generator, ctx, (x1_ty, x1_val), (x2_ty, x2_val))?;
|
Ok(Some(ndarray_dot(generator, ctx, (x1_ty, x1_val), (x2_ty, x2_val))?))
|
||||||
Ok(Some(result))
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -1052,7 +1052,7 @@ impl TopLevelComposer {
|
|||||||
}
|
}
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let no_defaults = args.args.len() - args.defaults.len() - 1;
|
let no_defaults = args.args.len() - args.defaults.len() - 1;
|
||||||
for (idx, x) in args.args.iter().skip(1).enumerate() {
|
for (idx, x) in itertools::enumerate(args.args.iter().skip(1)) {
|
||||||
let type_ann = {
|
let type_ann = {
|
||||||
let Some(annotation_expr) = x.node.annotation.as_ref() else {return Err(HashSet::from([format!("type annotation needed for `{}` (at {})", x.node.arg, x.location)]));};
|
let Some(annotation_expr) = x.node.annotation.as_ref() else {return Err(HashSet::from([format!("type annotation needed for `{}` (at {})", x.node.arg, x.location)]));};
|
||||||
parse_ast_to_type_annotation_kinds(
|
parse_ast_to_type_annotation_kinds(
|
||||||
|
@ -54,16 +54,6 @@ pub enum PrimDef {
|
|||||||
FunNpEye,
|
FunNpEye,
|
||||||
FunNpIdentity,
|
FunNpIdentity,
|
||||||
|
|
||||||
// NumPy ndarray property getters
|
|
||||||
FunNpSize,
|
|
||||||
FunNpShape,
|
|
||||||
FunNpStrides,
|
|
||||||
|
|
||||||
// NumPy ndarray view functions
|
|
||||||
FunNpBroadcastTo,
|
|
||||||
FunNpTranspose,
|
|
||||||
FunNpReshape,
|
|
||||||
|
|
||||||
// Miscellaneous NumPy & SciPy functions
|
// Miscellaneous NumPy & SciPy functions
|
||||||
FunNpRound,
|
FunNpRound,
|
||||||
FunNpFloor,
|
FunNpFloor,
|
||||||
@ -111,6 +101,8 @@ pub enum PrimDef {
|
|||||||
FunNpLdExp,
|
FunNpLdExp,
|
||||||
FunNpHypot,
|
FunNpHypot,
|
||||||
FunNpNextAfter,
|
FunNpNextAfter,
|
||||||
|
FunNpTranspose,
|
||||||
|
FunNpReshape,
|
||||||
|
|
||||||
// Linalg functions
|
// Linalg functions
|
||||||
FunNpDot,
|
FunNpDot,
|
||||||
@ -248,16 +240,6 @@ impl PrimDef {
|
|||||||
PrimDef::FunNpEye => fun("np_eye", None),
|
PrimDef::FunNpEye => fun("np_eye", None),
|
||||||
PrimDef::FunNpIdentity => fun("np_identity", None),
|
PrimDef::FunNpIdentity => fun("np_identity", None),
|
||||||
|
|
||||||
// NumPy NDArray property getters,
|
|
||||||
PrimDef::FunNpSize => fun("np_size", None),
|
|
||||||
PrimDef::FunNpShape => fun("np_shape", None),
|
|
||||||
PrimDef::FunNpStrides => fun("np_strides", None),
|
|
||||||
|
|
||||||
// NumPy NDArray view functions
|
|
||||||
PrimDef::FunNpBroadcastTo => fun("np_broadcast_to", None),
|
|
||||||
PrimDef::FunNpTranspose => fun("np_transpose", None),
|
|
||||||
PrimDef::FunNpReshape => fun("np_reshape", None),
|
|
||||||
|
|
||||||
// Miscellaneous NumPy & SciPy functions
|
// Miscellaneous NumPy & SciPy functions
|
||||||
PrimDef::FunNpRound => fun("np_round", None),
|
PrimDef::FunNpRound => fun("np_round", None),
|
||||||
PrimDef::FunNpFloor => fun("np_floor", None),
|
PrimDef::FunNpFloor => fun("np_floor", None),
|
||||||
@ -305,6 +287,8 @@ impl PrimDef {
|
|||||||
PrimDef::FunNpLdExp => fun("np_ldexp", None),
|
PrimDef::FunNpLdExp => fun("np_ldexp", None),
|
||||||
PrimDef::FunNpHypot => fun("np_hypot", None),
|
PrimDef::FunNpHypot => fun("np_hypot", None),
|
||||||
PrimDef::FunNpNextAfter => fun("np_nextafter", None),
|
PrimDef::FunNpNextAfter => fun("np_nextafter", None),
|
||||||
|
PrimDef::FunNpTranspose => fun("np_transpose", None),
|
||||||
|
PrimDef::FunNpReshape => fun("np_reshape", None),
|
||||||
|
|
||||||
// Linalg functions
|
// Linalg functions
|
||||||
PrimDef::FunNpDot => fun("np_dot", None),
|
PrimDef::FunNpDot => fun("np_dot", None),
|
||||||
|
@ -8,5 +8,5 @@ expression: res_vec
|
|||||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
|
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
|
||||||
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(261)]\n}\n",
|
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(246)]\n}\n",
|
||||||
]
|
]
|
||||||
|
@ -7,7 +7,7 @@ expression: res_vec
|
|||||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B[typevar245]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar245\"]\n}\n",
|
"Class {\nname: \"B\",\nancestors: [\"B[typevar230]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar230\"]\n}\n",
|
||||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"C\",\nancestors: [\"C\", \"B[bool]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"C\",\nancestors: [\"C\", \"B[bool]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n",
|
||||||
|
@ -5,8 +5,8 @@ expression: res_vec
|
|||||||
[
|
[
|
||||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"A\",\nancestors: [\"A[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n",
|
"Class {\nname: \"A\",\nancestors: [\"A[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n",
|
||||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [TypeVarId(258)]\n}\n",
|
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [TypeVarId(243)]\n}\n",
|
||||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(263)]\n}\n",
|
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(248)]\n}\n",
|
||||||
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[list[float], int32]], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[list[float], int32]], none]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
|
@ -3,7 +3,7 @@ source: nac3core/src/toplevel/test.rs
|
|||||||
expression: res_vec
|
expression: res_vec
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
"Class {\nname: \"A\",\nancestors: [\"A[typevar244, typevar245]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[float, bool], b:B], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\")],\ntype_vars: [\"typevar244\", \"typevar245\"]\n}\n",
|
"Class {\nname: \"A\",\nancestors: [\"A[typevar229, typevar230]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[float, bool], b:B], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\")],\ntype_vars: [\"typevar229\", \"typevar230\"]\n}\n",
|
||||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[float, bool], b:B], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[float, bool], b:B], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[float, bool]], A[bool, int32]]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[float, bool]], A[bool, int32]]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[list[B], int32]], tuple[A[virtual[A[B, int32]], bool], B]]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[list[B], int32]], tuple[A[virtual[A[B, int32]], bool], B]]\")],\ntype_vars: []\n}\n",
|
||||||
|
@ -6,12 +6,12 @@ expression: res_vec
|
|||||||
"Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(264)]\n}\n",
|
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(249)]\n}\n",
|
||||||
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(272)]\n}\n",
|
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(257)]\n}\n",
|
||||||
]
|
]
|
||||||
|
@ -15,13 +15,14 @@ use crate::{
|
|||||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||||
typecheck::{
|
typecheck::{
|
||||||
type_inferencer::PrimitiveStore,
|
type_inferencer::PrimitiveStore,
|
||||||
typedef::{Type, Unifier},
|
typedef::{into_var_map, Type, Unifier},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResolverInternal {
|
struct ResolverInternal {
|
||||||
id_to_type: Mutex<HashMap<StrRef, Type>>,
|
id_to_type: Mutex<HashMap<StrRef, Type>>,
|
||||||
id_to_def: Mutex<HashMap<StrRef, DefinitionId>>,
|
id_to_def: Mutex<HashMap<StrRef, DefinitionId>>,
|
||||||
|
class_names: Mutex<HashMap<StrRef, Type>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResolverInternal {
|
impl ResolverInternal {
|
||||||
@ -178,8 +179,11 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
|||||||
let mut composer =
|
let mut composer =
|
||||||
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
||||||
|
|
||||||
let internal_resolver =
|
let internal_resolver = Arc::new(ResolverInternal {
|
||||||
Arc::new(ResolverInternal { id_to_def: Mutex::default(), id_to_type: Mutex::default() });
|
id_to_def: Mutex::default(),
|
||||||
|
id_to_type: Mutex::default(),
|
||||||
|
class_names: Mutex::default(),
|
||||||
|
});
|
||||||
let resolver =
|
let resolver =
|
||||||
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
@ -780,6 +784,13 @@ fn make_internal_resolver_with_tvar(
|
|||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
print: bool,
|
print: bool,
|
||||||
) -> Arc<ResolverInternal> {
|
) -> Arc<ResolverInternal> {
|
||||||
|
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||||
|
let list = unifier.add_ty(TypeEnum::TObj {
|
||||||
|
obj_id: PrimDef::List.id(),
|
||||||
|
fields: HashMap::new(),
|
||||||
|
params: into_var_map([list_elem_tvar]),
|
||||||
|
});
|
||||||
|
|
||||||
let res: Arc<ResolverInternal> = ResolverInternal {
|
let res: Arc<ResolverInternal> = ResolverInternal {
|
||||||
id_to_def: Mutex::new(HashMap::from([("list".into(), PrimDef::List.id())])),
|
id_to_def: Mutex::new(HashMap::from([("list".into(), PrimDef::List.id())])),
|
||||||
id_to_type: tvars
|
id_to_type: tvars
|
||||||
@ -795,6 +806,7 @@ fn make_internal_resolver_with_tvar(
|
|||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
.into(),
|
.into(),
|
||||||
|
class_names: Mutex::new(HashMap::from([("list".into(), list)])),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
if print {
|
if print {
|
||||||
@ -807,7 +819,7 @@ struct TypeToStringFolder<'a> {
|
|||||||
unifier: &'a mut Unifier,
|
unifier: &'a mut Unifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold<Option<Type>> for TypeToStringFolder<'_> {
|
impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
|
||||||
type TargetU = String;
|
type TargetU = String;
|
||||||
type Error = String;
|
type Error = String;
|
||||||
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
|
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
|
||||||
|
@ -15,7 +15,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::toplevel::helper::PrimDef;
|
use crate::toplevel::helper::PrimDef;
|
||||||
|
|
||||||
impl Inferencer<'_> {
|
impl<'a> Inferencer<'a> {
|
||||||
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
||||||
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
|
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
|
||||||
Err(HashSet::from([format!("Error at {}: cannot have value none", expr.location)]))
|
Err(HashSet::from([format!("Error at {}: cannot have value none", expr.location)]))
|
||||||
@ -94,7 +94,7 @@ impl Inferencer<'_> {
|
|||||||
// there are some cases where the custom field is None
|
// there are some cases where the custom field is None
|
||||||
if let Some(ty) = &expr.custom {
|
if let Some(ty) = &expr.custom {
|
||||||
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. })
|
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. })
|
||||||
&& ty.obj_id(self.unifier).is_none_or(|id| id != PrimDef::List.id())
|
&& !ty.obj_id(self.unifier).is_some_and(|id| id == PrimDef::List.id())
|
||||||
&& !self.unifier.is_concrete(*ty, &self.function_data.bound_variables)
|
&& !self.unifier.is_concrete(*ty, &self.function_data.bound_variables)
|
||||||
{
|
{
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
|
@ -7,12 +7,12 @@ use nac3parser::ast::{Cmpop, Operator, StrRef, Unaryop};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
type_inferencer::*,
|
type_inferencer::*,
|
||||||
typedef::{into_var_map, FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
|
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
symbol_resolver::SymbolValue,
|
symbol_resolver::SymbolValue,
|
||||||
toplevel::{
|
toplevel::{
|
||||||
helper::{extract_ndims, PrimDef},
|
helper::PrimDef,
|
||||||
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
|
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -175,8 +175,19 @@ pub fn impl_binop(
|
|||||||
ops: &[Operator],
|
ops: &[Operator],
|
||||||
) {
|
) {
|
||||||
with_fields(unifier, ty, |unifier, fields| {
|
with_fields(unifier, ty, |unifier, fields| {
|
||||||
let other_tvar = unifier.get_fresh_var_with_range(other_ty, Some("N".into()), None);
|
let (other_ty, other_var_id) = if other_ty.len() == 1 {
|
||||||
let function_vars = into_var_map([other_tvar]);
|
(other_ty[0], None)
|
||||||
|
} else {
|
||||||
|
let tvar = unifier.get_fresh_var_with_range(other_ty, Some("N".into()), None);
|
||||||
|
(tvar.ty, Some(tvar.id))
|
||||||
|
};
|
||||||
|
|
||||||
|
let function_vars = if let Some(var_id) = other_var_id {
|
||||||
|
vec![(var_id, other_ty)].into_iter().collect::<VarMap>()
|
||||||
|
} else {
|
||||||
|
VarMap::new()
|
||||||
|
};
|
||||||
|
|
||||||
let ret_ty = ret_ty.unwrap_or_else(|| unifier.get_fresh_var(None, None).ty);
|
let ret_ty = ret_ty.unwrap_or_else(|| unifier.get_fresh_var(None, None).ty);
|
||||||
|
|
||||||
for (base_op, variant) in iproduct!(ops, [BinopVariant::Normal, BinopVariant::AugAssign]) {
|
for (base_op, variant) in iproduct!(ops, [BinopVariant::Normal, BinopVariant::AugAssign]) {
|
||||||
@ -187,7 +198,7 @@ pub fn impl_binop(
|
|||||||
ret: ret_ty,
|
ret: ret_ty,
|
||||||
vars: function_vars.clone(),
|
vars: function_vars.clone(),
|
||||||
args: vec![FuncArg {
|
args: vec![FuncArg {
|
||||||
ty: other_tvar.ty,
|
ty: other_ty,
|
||||||
default_value: None,
|
default_value: None,
|
||||||
name: "other".into(),
|
name: "other".into(),
|
||||||
is_vararg: false,
|
is_vararg: false,
|
||||||
@ -530,43 +541,36 @@ pub fn typeof_binop(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (lhs_dtype, lhs_ndims) = unpack_ndarray_var_tys(unifier, lhs);
|
let (_, lhs_ndims) = unpack_ndarray_var_tys(unifier, lhs);
|
||||||
let lhs_ndims = extract_ndims(unifier, lhs_ndims);
|
let lhs_ndims = match &*unifier.get_ty_immutable(lhs_ndims) {
|
||||||
|
TypeEnum::TLiteral { values, .. } => {
|
||||||
let (rhs_dtype, rhs_ndims) = unpack_ndarray_var_tys(unifier, rhs);
|
assert_eq!(values.len(), 1);
|
||||||
let rhs_ndims = extract_ndims(unifier, rhs_ndims);
|
u64::try_from(values[0].clone()).unwrap()
|
||||||
|
|
||||||
if !(unifier.unioned(lhs_dtype, primitives.float)
|
|
||||||
&& unifier.unioned(rhs_dtype, primitives.float))
|
|
||||||
{
|
|
||||||
return Err(format!(
|
|
||||||
"ndarray.__matmul__ only supports float64 operations, but LHS has type {} and RHS has type {}",
|
|
||||||
unifier.stringify(lhs),
|
|
||||||
unifier.stringify(rhs)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deduce the ndims of the resulting ndarray.
|
|
||||||
// If this is 0 (an unsized ndarray), matmul returns a scalar just like NumPy.
|
|
||||||
let result_ndims = match (lhs_ndims, rhs_ndims) {
|
|
||||||
(0, _) | (_, 0) => {
|
|
||||||
return Err(
|
|
||||||
"ndarray.__matmul__ does not allow unsized ndarray input".to_string()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
(1, 1) => 0,
|
_ => unreachable!(),
|
||||||
(1, _) => rhs_ndims - 1,
|
};
|
||||||
(_, 1) => lhs_ndims - 1,
|
let (_, rhs_ndims) = unpack_ndarray_var_tys(unifier, rhs);
|
||||||
(m, n) => max(m, n),
|
let rhs_ndims = match &*unifier.get_ty_immutable(rhs_ndims) {
|
||||||
|
TypeEnum::TLiteral { values, .. } => {
|
||||||
|
assert_eq!(values.len(), 1);
|
||||||
|
u64::try_from(values[0].clone()).unwrap()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if result_ndims == 0 {
|
match (lhs_ndims, rhs_ndims) {
|
||||||
// If the result is unsized, NumPy returns a scalar.
|
(2, 2) => typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?,
|
||||||
primitives.float
|
(lhs, rhs) if lhs == 0 || rhs == 0 => {
|
||||||
} else {
|
return Err(format!(
|
||||||
let result_ndims_ty =
|
"Input operand {} does not have enough dimensions (has {lhs}, requires {rhs})",
|
||||||
unifier.get_fresh_literal(vec![SymbolValue::U64(result_ndims)], None);
|
u8::from(rhs == 0)
|
||||||
make_ndarray_ty(unifier, primitives, Some(primitives.float), Some(result_ndims_ty))
|
))
|
||||||
|
}
|
||||||
|
(lhs, rhs) => {
|
||||||
|
return Err(format!(
|
||||||
|
"ndarray.__matmul__ on {lhs}D and {rhs}D operands not supported"
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,7 +773,7 @@ pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifie
|
|||||||
impl_div(unifier, store, ndarray_t, &[ndarray_t, ndarray_dtype_t], None);
|
impl_div(unifier, store, ndarray_t, &[ndarray_t, ndarray_dtype_t], None);
|
||||||
impl_floordiv(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
impl_floordiv(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
||||||
impl_mod(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
impl_mod(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
||||||
impl_matmul(unifier, store, ndarray_t, &[ndarray_unsized_t], None);
|
impl_matmul(unifier, store, ndarray_t, &[ndarray_t], Some(ndarray_t));
|
||||||
impl_sign(unifier, store, ndarray_t, Some(ndarray_t));
|
impl_sign(unifier, store, ndarray_t, Some(ndarray_t));
|
||||||
impl_invert(unifier, store, ndarray_t, Some(ndarray_t));
|
impl_invert(unifier, store, ndarray_t, Some(ndarray_t));
|
||||||
impl_eq(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
impl_eq(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
|
||||||
|
@ -94,7 +94,7 @@ fn loc_to_str(loc: Option<Location>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for DisplayTypeError<'_> {
|
impl<'a> Display for DisplayTypeError<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
use TypeErrorKind::*;
|
use TypeErrorKind::*;
|
||||||
let mut notes = Some(HashMap::new());
|
let mut notes = Some(HashMap::new());
|
||||||
|
@ -3,7 +3,7 @@ use std::{
|
|||||||
cmp::max,
|
cmp::max,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
convert::{From, TryInto},
|
convert::{From, TryInto},
|
||||||
iter::{once, repeat_n},
|
iter::once,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ fn fix_assignment_target_context(node: &mut ast::Located<ExprKind>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold<()> for Inferencer<'_> {
|
impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
type TargetU = Option<Type>;
|
type TargetU = Option<Type>;
|
||||||
type Error = InferenceError;
|
type Error = InferenceError;
|
||||||
|
|
||||||
@ -657,7 +657,7 @@ impl Fold<()> for Inferencer<'_> {
|
|||||||
|
|
||||||
type InferenceResult = Result<Type, InferenceError>;
|
type InferenceResult = Result<Type, InferenceError>;
|
||||||
|
|
||||||
impl Inferencer<'_> {
|
impl<'a> Inferencer<'a> {
|
||||||
/// Constrain a <: b
|
/// Constrain a <: b
|
||||||
/// Currently implemented as unification
|
/// Currently implemented as unification
|
||||||
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), InferenceError> {
|
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), InferenceError> {
|
||||||
@ -1234,45 +1234,6 @@ impl Inferencer<'_> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ["np_shape".into(), "np_strides".into()].contains(id) && args.len() == 1 {
|
|
||||||
let ndarray = self.fold_expr(args.remove(0))?;
|
|
||||||
|
|
||||||
let ndims = arraylike_get_ndims(self.unifier, ndarray.custom.unwrap());
|
|
||||||
|
|
||||||
// Make a tuple of size `ndims` full of int32 (TODO: Make it usize)
|
|
||||||
let ret_ty = TypeEnum::TTuple {
|
|
||||||
ty: repeat_n(self.primitives.int32, ndims as usize).collect_vec(),
|
|
||||||
is_vararg_ctx: false,
|
|
||||||
};
|
|
||||||
let ret_ty = self.unifier.add_ty(ret_ty);
|
|
||||||
|
|
||||||
let func_ty = TypeEnum::TFunc(FunSignature {
|
|
||||||
args: vec![FuncArg {
|
|
||||||
name: "a".into(),
|
|
||||||
default_value: None,
|
|
||||||
ty: ndarray.custom.unwrap(),
|
|
||||||
is_vararg: false,
|
|
||||||
}],
|
|
||||||
ret: ret_ty,
|
|
||||||
vars: VarMap::new(),
|
|
||||||
});
|
|
||||||
let func_ty = self.unifier.add_ty(func_ty);
|
|
||||||
|
|
||||||
return Ok(Some(Located {
|
|
||||||
location,
|
|
||||||
custom: Some(ret_ty),
|
|
||||||
node: ExprKind::Call {
|
|
||||||
func: Box::new(Located {
|
|
||||||
custom: Some(func_ty),
|
|
||||||
location: func.location,
|
|
||||||
node: ExprKind::Name { id: *id, ctx: *ctx },
|
|
||||||
}),
|
|
||||||
args: vec![ndarray],
|
|
||||||
keywords: vec![],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if id == &"np_dot".into() {
|
if id == &"np_dot".into() {
|
||||||
let arg0 = self.fold_expr(args.remove(0))?;
|
let arg0 = self.fold_expr(args.remove(0))?;
|
||||||
let arg1 = self.fold_expr(args.remove(0))?;
|
let arg1 = self.fold_expr(args.remove(0))?;
|
||||||
@ -1594,7 +1555,7 @@ impl Inferencer<'_> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// 2-argument ndarray n-dimensional factory functions
|
// 2-argument ndarray n-dimensional factory functions
|
||||||
if ["np_reshape".into(), "np_broadcast_to".into()].contains(id) && args.len() == 2 {
|
if id == &"np_reshape".into() && args.len() == 2 {
|
||||||
let arg0 = self.fold_expr(args.remove(0))?;
|
let arg0 = self.fold_expr(args.remove(0))?;
|
||||||
|
|
||||||
let shape_expr = args.remove(0);
|
let shape_expr = args.remove(0);
|
||||||
|
@ -18,6 +18,7 @@ use crate::{
|
|||||||
struct Resolver {
|
struct Resolver {
|
||||||
id_to_type: HashMap<StrRef, Type>,
|
id_to_type: HashMap<StrRef, Type>,
|
||||||
id_to_def: HashMap<StrRef, DefinitionId>,
|
id_to_def: HashMap<StrRef, DefinitionId>,
|
||||||
|
class_names: HashMap<StrRef, Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolResolver for Resolver {
|
impl SymbolResolver for Resolver {
|
||||||
@ -197,6 +198,7 @@ impl TestEnvironment {
|
|||||||
let resolver = Arc::new(Resolver {
|
let resolver = Arc::new(Resolver {
|
||||||
id_to_type: identifier_mapping.clone(),
|
id_to_type: identifier_mapping.clone(),
|
||||||
id_to_def: HashMap::default(),
|
id_to_def: HashMap::default(),
|
||||||
|
class_names: HashMap::default(),
|
||||||
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
TestEnvironment {
|
TestEnvironment {
|
||||||
@ -452,6 +454,7 @@ impl TestEnvironment {
|
|||||||
vars: IndexMap::default(),
|
vars: IndexMap::default(),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
let class_names: HashMap<_, _> = [("Bar".into(), bar), ("Bar2".into(), bar2)].into();
|
||||||
|
|
||||||
let id_to_name = [
|
let id_to_name = [
|
||||||
"int32".into(),
|
"int32".into(),
|
||||||
@ -489,6 +492,7 @@ impl TestEnvironment {
|
|||||||
("Bar2".into(), DefinitionId(defs + 3)),
|
("Bar2".into(), DefinitionId(defs + 3)),
|
||||||
]
|
]
|
||||||
.into(),
|
.into(),
|
||||||
|
class_names,
|
||||||
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
TestEnvironment {
|
TestEnvironment {
|
||||||
|
@ -3,13 +3,13 @@ use std::{
|
|||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
iter::{repeat, repeat_n, zip},
|
iter::{repeat, zip},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use itertools::Itertools;
|
use itertools::{repeat_n, Itertools};
|
||||||
|
|
||||||
use nac3parser::ast::{Cmpop, Location, StrRef, Unaryop};
|
use nac3parser::ast::{Cmpop, Location, StrRef, Unaryop};
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ pub struct DwarfReader<'a> {
|
|||||||
pub virt_addr: u32,
|
pub virt_addr: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DwarfReader<'_> {
|
impl<'a> DwarfReader<'a> {
|
||||||
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
|
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
|
||||||
DwarfReader { slice, virt_addr }
|
DwarfReader { slice, virt_addr }
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ pub struct DwarfWriter<'a> {
|
|||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DwarfWriter<'_> {
|
impl<'a> DwarfWriter<'a> {
|
||||||
pub fn new(slice: &mut [u8]) -> DwarfWriter {
|
pub fn new(slice: &mut [u8]) -> DwarfWriter {
|
||||||
DwarfWriter { slice, offset: 0 }
|
DwarfWriter { slice, offset: 0 }
|
||||||
}
|
}
|
||||||
@ -375,7 +375,7 @@ pub struct FDE_Records<'a> {
|
|||||||
available: usize,
|
available: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for FDE_Records<'_> {
|
impl<'a> Iterator for FDE_Records<'a> {
|
||||||
type Item = (u32, u32);
|
type Item = (u32, u32);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@ -423,7 +423,7 @@ pub struct EH_Frame_Hdr<'a> {
|
|||||||
fdes: Vec<(u32, u32)>,
|
fdes: Vec<(u32, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EH_Frame_Hdr<'_> {
|
impl<'a> EH_Frame_Hdr<'a> {
|
||||||
/// Create a [EH_Frame_Hdr] object, and write out the fixed fields of `.eh_frame_hdr` to memory.
|
/// Create a [EH_Frame_Hdr] object, and write out the fixed fields of `.eh_frame_hdr` to memory.
|
||||||
///
|
///
|
||||||
/// Load address is not known at this point.
|
/// Load address is not known at this point.
|
||||||
|
@ -159,7 +159,7 @@ struct SymbolTableReader<'a> {
|
|||||||
strtab: &'a [u8],
|
strtab: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTableReader<'_> {
|
impl<'a> SymbolTableReader<'a> {
|
||||||
pub fn find_index_by_name(&self, sym_name: &[u8]) -> Option<usize> {
|
pub fn find_index_by_name(&self, sym_name: &[u8]) -> Option<usize> {
|
||||||
self.symtab.iter().position(|sym| {
|
self.symtab.iter().position(|sym| {
|
||||||
if let Ok(dynsym_name) = name_starting_at_slice(self.strtab, sym.st_name as usize) {
|
if let Ok(dynsym_name) = name_starting_at_slice(self.strtab, sym.st_name as usize) {
|
||||||
|
@ -67,7 +67,7 @@ def _bool(x):
|
|||||||
|
|
||||||
def _float(x):
|
def _float(x):
|
||||||
if isinstance(x, np.ndarray):
|
if isinstance(x, np.ndarray):
|
||||||
return np.float64(x)
|
return np.float_(x)
|
||||||
else:
|
else:
|
||||||
return float(x)
|
return float(x)
|
||||||
|
|
||||||
@ -111,9 +111,6 @@ def patch(module):
|
|||||||
def output_strln(x):
|
def output_strln(x):
|
||||||
print(x, end='')
|
print(x, end='')
|
||||||
|
|
||||||
def output_int32_list(x):
|
|
||||||
print([int(e) for e in x])
|
|
||||||
|
|
||||||
def dbg_stack_address(_):
|
def dbg_stack_address(_):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -129,12 +126,11 @@ def patch(module):
|
|||||||
return output_float
|
return output_float
|
||||||
elif name == "output_str":
|
elif name == "output_str":
|
||||||
return output_strln
|
return output_strln
|
||||||
elif name == "output_int32_list":
|
|
||||||
return output_int32_list
|
|
||||||
elif name in {
|
elif name in {
|
||||||
"output_bool",
|
"output_bool",
|
||||||
"output_int32",
|
"output_int32",
|
||||||
"output_int64",
|
"output_int64",
|
||||||
|
"output_int32_list",
|
||||||
"output_uint32",
|
"output_uint32",
|
||||||
"output_uint64",
|
"output_uint64",
|
||||||
"output_strln",
|
"output_strln",
|
||||||
@ -183,16 +179,6 @@ def patch(module):
|
|||||||
module.np_identity = np.identity
|
module.np_identity = np.identity
|
||||||
module.np_array = np.array
|
module.np_array = np.array
|
||||||
|
|
||||||
# NumPy NDArray view functions
|
|
||||||
module.np_broadcast_to = np.broadcast_to
|
|
||||||
module.np_transpose = np.transpose
|
|
||||||
module.np_reshape = np.reshape
|
|
||||||
|
|
||||||
# NumPy NDArray property getters
|
|
||||||
module.np_size = np.size
|
|
||||||
module.np_shape = np.shape
|
|
||||||
module.np_strides = lambda ndarray: ndarray.strides
|
|
||||||
|
|
||||||
# NumPy Math functions
|
# NumPy Math functions
|
||||||
module.np_isnan = np.isnan
|
module.np_isnan = np.isnan
|
||||||
module.np_isinf = np.isinf
|
module.np_isinf = np.isinf
|
||||||
@ -232,6 +218,8 @@ def patch(module):
|
|||||||
module.np_ldexp = np.ldexp
|
module.np_ldexp = np.ldexp
|
||||||
module.np_hypot = np.hypot
|
module.np_hypot = np.hypot
|
||||||
module.np_nextafter = np.nextafter
|
module.np_nextafter = np.nextafter
|
||||||
|
module.np_transpose = np.transpose
|
||||||
|
module.np_reshape = np.reshape
|
||||||
|
|
||||||
# SciPy Math functions
|
# SciPy Math functions
|
||||||
module.sp_spec_erf = special.erf
|
module.sp_spec_erf = special.erf
|
||||||
|
@ -68,19 +68,6 @@ def output_ndarray_float_2(n: ndarray[float, Literal[2]]):
|
|||||||
for c in range(len(n[r])):
|
for c in range(len(n[r])):
|
||||||
output_float64(n[r][c])
|
output_float64(n[r][c])
|
||||||
|
|
||||||
def output_ndarray_float_3(n: ndarray[float, Literal[3]]):
|
|
||||||
for d in range(len(n)):
|
|
||||||
for r in range(len(n[d])):
|
|
||||||
for c in range(len(n[d][r])):
|
|
||||||
output_float64(n[d][r][c])
|
|
||||||
|
|
||||||
def output_ndarray_float_4(n: ndarray[float, Literal[4]]):
|
|
||||||
for x in range(len(n)):
|
|
||||||
for y in range(len(n[x])):
|
|
||||||
for z in range(len(n[x][y])):
|
|
||||||
for w in range(len(n[x][y][z])):
|
|
||||||
output_float64(n[x][y][z][w])
|
|
||||||
|
|
||||||
def consume_ndarray_1(n: ndarray[float, Literal[1]]):
|
def consume_ndarray_1(n: ndarray[float, Literal[1]]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -210,104 +197,6 @@ def test_ndarray_nd_idx():
|
|||||||
output_float64(x[1, 0])
|
output_float64(x[1, 0])
|
||||||
output_float64(x[1, 1])
|
output_float64(x[1, 1])
|
||||||
|
|
||||||
def test_ndarray_transpose():
|
|
||||||
x: ndarray[float, 2] = np_array([[1., 2., 3.], [4., 5., 6.]])
|
|
||||||
y = np_transpose(x)
|
|
||||||
z = np_transpose(y)
|
|
||||||
|
|
||||||
output_int32(np_shape(x)[0])
|
|
||||||
output_int32(np_shape(x)[1])
|
|
||||||
output_ndarray_float_2(x)
|
|
||||||
|
|
||||||
output_int32(np_shape(y)[0])
|
|
||||||
output_int32(np_shape(y)[1])
|
|
||||||
output_ndarray_float_2(y)
|
|
||||||
|
|
||||||
output_int32(np_shape(z)[0])
|
|
||||||
output_int32(np_shape(z)[1])
|
|
||||||
output_ndarray_float_2(z)
|
|
||||||
|
|
||||||
def test_ndarray_reshape():
|
|
||||||
w: ndarray[float, 1] = np_array([1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
|
|
||||||
x = np_reshape(w, (1, 2, 1, -1))
|
|
||||||
y = np_reshape(x, [2, -1])
|
|
||||||
z = np_reshape(y, 10)
|
|
||||||
|
|
||||||
output_int32(np_shape(w)[0])
|
|
||||||
output_ndarray_float_1(w)
|
|
||||||
|
|
||||||
output_int32(np_shape(x)[0])
|
|
||||||
output_int32(np_shape(x)[1])
|
|
||||||
output_int32(np_shape(x)[2])
|
|
||||||
output_int32(np_shape(x)[3])
|
|
||||||
output_ndarray_float_4(x)
|
|
||||||
|
|
||||||
output_int32(np_shape(y)[0])
|
|
||||||
output_int32(np_shape(y)[1])
|
|
||||||
output_ndarray_float_2(y)
|
|
||||||
|
|
||||||
output_int32(np_shape(z)[0])
|
|
||||||
output_ndarray_float_1(z)
|
|
||||||
|
|
||||||
x1: ndarray[int32, 1] = np_array([1, 2, 3, 4])
|
|
||||||
x2: ndarray[int32, 2] = np_reshape(x1, (2, 2))
|
|
||||||
|
|
||||||
output_int32(np_shape(x1)[0])
|
|
||||||
output_ndarray_int32_1(x1)
|
|
||||||
|
|
||||||
output_int32(np_shape(x2)[0])
|
|
||||||
output_int32(np_shape(x2)[1])
|
|
||||||
output_ndarray_int32_2(x2)
|
|
||||||
|
|
||||||
def test_ndarray_broadcast_to():
|
|
||||||
xs = np_array([1.0, 2.0, 3.0])
|
|
||||||
ys = np_broadcast_to(xs, (1, 3))
|
|
||||||
zs = np_broadcast_to(ys, (2, 4, 3))
|
|
||||||
|
|
||||||
output_int32(np_shape(xs)[0])
|
|
||||||
output_ndarray_float_1(xs)
|
|
||||||
|
|
||||||
output_int32(np_shape(ys)[0])
|
|
||||||
output_int32(np_shape(ys)[1])
|
|
||||||
output_ndarray_float_2(ys)
|
|
||||||
|
|
||||||
output_int32(np_shape(zs)[0])
|
|
||||||
output_int32(np_shape(zs)[1])
|
|
||||||
output_int32(np_shape(zs)[2])
|
|
||||||
output_ndarray_float_3(zs)
|
|
||||||
|
|
||||||
def test_ndarray_subscript_assignment():
|
|
||||||
xs = np_array([[11.0, 22.0, 33.0, 44.0], [55.0, 66.0, 77.0, 88.0]])
|
|
||||||
|
|
||||||
xs[0, 0] = 99.0
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
xs[0] = 100.0
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
xs[:, ::2] = 101.0
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
xs[1:, 0] = 102.0
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
xs[0] = np_array([-1.0, -2.0, -3.0, -4.0])
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
xs[:] = np_array([-5.0, -6.0, -7.0, -8.0])
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
|
|
||||||
# Test assignment with memory sharing
|
|
||||||
ys1 = np_reshape(xs, (2, 4))
|
|
||||||
ys2 = np_transpose(ys1)
|
|
||||||
ys3 = ys2[::-1, 0]
|
|
||||||
ys3[0] = -999.0
|
|
||||||
|
|
||||||
output_ndarray_float_2(xs)
|
|
||||||
output_ndarray_float_2(ys1)
|
|
||||||
output_ndarray_float_2(ys2)
|
|
||||||
output_ndarray_float_1(ys3)
|
|
||||||
|
|
||||||
def test_ndarray_add():
|
def test_ndarray_add():
|
||||||
x = np_identity(2)
|
x = np_identity(2)
|
||||||
y = x + np_ones([2, 2])
|
y = x + np_ones([2, 2])
|
||||||
@ -1551,6 +1440,27 @@ def test_ndarray_nextafter_broadcast_rhs_scalar():
|
|||||||
output_ndarray_float_2(nextafter_x_zeros)
|
output_ndarray_float_2(nextafter_x_zeros)
|
||||||
output_ndarray_float_2(nextafter_x_ones)
|
output_ndarray_float_2(nextafter_x_ones)
|
||||||
|
|
||||||
|
def test_ndarray_transpose():
|
||||||
|
x: ndarray[float, 2] = np_array([[1., 2., 3.], [4., 5., 6.]])
|
||||||
|
y = np_transpose(x)
|
||||||
|
z = np_transpose(y)
|
||||||
|
|
||||||
|
output_ndarray_float_2(x)
|
||||||
|
output_ndarray_float_2(y)
|
||||||
|
|
||||||
|
def test_ndarray_reshape():
|
||||||
|
w: ndarray[float, 1] = np_array([1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
|
||||||
|
x = np_reshape(w, (1, 2, 1, -1))
|
||||||
|
y = np_reshape(x, [2, -1])
|
||||||
|
z = np_reshape(y, 10)
|
||||||
|
|
||||||
|
x1: ndarray[int32, 1] = np_array([1, 2, 3, 4])
|
||||||
|
x2: ndarray[int32, 2] = np_reshape(x1, (2, 2))
|
||||||
|
|
||||||
|
output_ndarray_float_1(w)
|
||||||
|
output_ndarray_float_2(y)
|
||||||
|
output_ndarray_float_1(z)
|
||||||
|
|
||||||
def test_ndarray_dot():
|
def test_ndarray_dot():
|
||||||
x1: ndarray[float, 1] = np_array([5.0, 1.0, 4.0, 2.0])
|
x1: ndarray[float, 1] = np_array([5.0, 1.0, 4.0, 2.0])
|
||||||
y1: ndarray[float, 1] = np_array([5.0, 1.0, 6.0, 6.0])
|
y1: ndarray[float, 1] = np_array([5.0, 1.0, 6.0, 6.0])
|
||||||
@ -1682,11 +1592,6 @@ def run() -> int32:
|
|||||||
test_ndarray_slices()
|
test_ndarray_slices()
|
||||||
test_ndarray_nd_idx()
|
test_ndarray_nd_idx()
|
||||||
|
|
||||||
test_ndarray_transpose()
|
|
||||||
test_ndarray_reshape()
|
|
||||||
test_ndarray_broadcast_to()
|
|
||||||
test_ndarray_subscript_assignment()
|
|
||||||
|
|
||||||
test_ndarray_add()
|
test_ndarray_add()
|
||||||
test_ndarray_add_broadcast()
|
test_ndarray_add_broadcast()
|
||||||
test_ndarray_add_broadcast_lhs_scalar()
|
test_ndarray_add_broadcast_lhs_scalar()
|
||||||
@ -1850,6 +1755,8 @@ def run() -> int32:
|
|||||||
test_ndarray_nextafter_broadcast()
|
test_ndarray_nextafter_broadcast()
|
||||||
test_ndarray_nextafter_broadcast_lhs_scalar()
|
test_ndarray_nextafter_broadcast_lhs_scalar()
|
||||||
test_ndarray_nextafter_broadcast_rhs_scalar()
|
test_ndarray_nextafter_broadcast_rhs_scalar()
|
||||||
|
test_ndarray_transpose()
|
||||||
|
test_ndarray_reshape()
|
||||||
|
|
||||||
test_ndarray_dot()
|
test_ndarray_dot()
|
||||||
test_ndarray_cholesky()
|
test_ndarray_cholesky()
|
||||||
|
@ -29,10 +29,10 @@ def u32_max() -> uint32:
|
|||||||
return ~uint32(0)
|
return ~uint32(0)
|
||||||
|
|
||||||
def i32_min() -> int32:
|
def i32_min() -> int32:
|
||||||
return int32(-(1 << 31))
|
return int32(1 << 31)
|
||||||
|
|
||||||
def i32_max() -> int32:
|
def i32_max() -> int32:
|
||||||
return int32((1 << 31)-1)
|
return int32(~(1 << 31))
|
||||||
|
|
||||||
def u64_min() -> uint64:
|
def u64_min() -> uint64:
|
||||||
return uint64(0)
|
return uint64(0)
|
||||||
@ -63,9 +63,8 @@ def test_conv_from_i32():
|
|||||||
i32_max()
|
i32_max()
|
||||||
]:
|
]:
|
||||||
output_int64(int64(x))
|
output_int64(int64(x))
|
||||||
if x >= 0:
|
output_uint32(uint32(x))
|
||||||
output_uint32(uint32(x))
|
output_uint64(uint64(x))
|
||||||
output_uint64(uint64(x))
|
|
||||||
output_float64(float(x))
|
output_float64(float(x))
|
||||||
|
|
||||||
def test_conv_from_u32():
|
def test_conv_from_u32():
|
||||||
@ -109,6 +108,7 @@ def test_conv_from_u64():
|
|||||||
|
|
||||||
def test_f64toi32():
|
def test_f64toi32():
|
||||||
for x in [
|
for x in [
|
||||||
|
float(i32_min()) - 1.0,
|
||||||
float(i32_min()),
|
float(i32_min()),
|
||||||
float(i32_min()) + 1.0,
|
float(i32_min()) + 1.0,
|
||||||
-1.5,
|
-1.5,
|
||||||
@ -117,6 +117,7 @@ def test_f64toi32():
|
|||||||
1.5,
|
1.5,
|
||||||
float(i32_max()) - 1.0,
|
float(i32_max()) - 1.0,
|
||||||
float(i32_max()),
|
float(i32_max()),
|
||||||
|
float(i32_max()) + 1.0
|
||||||
]:
|
]:
|
||||||
output_int32(int32(x))
|
output_int32(int32(x))
|
||||||
|
|
||||||
@ -137,17 +138,24 @@ def test_f64toi64():
|
|||||||
|
|
||||||
def test_f64tou32():
|
def test_f64tou32():
|
||||||
for x in [
|
for x in [
|
||||||
|
-1.5,
|
||||||
|
float(u32_min()) - 1.0,
|
||||||
|
-0.5,
|
||||||
float(u32_min()),
|
float(u32_min()),
|
||||||
0.5,
|
0.5,
|
||||||
float(u32_min()) + 1.0,
|
float(u32_min()) + 1.0,
|
||||||
1.5,
|
1.5,
|
||||||
float(u32_max()) - 1.0,
|
float(u32_max()) - 1.0,
|
||||||
float(u32_max()),
|
float(u32_max()),
|
||||||
|
float(u32_max()) + 1.0
|
||||||
]:
|
]:
|
||||||
output_uint32(uint32(x))
|
output_uint32(uint32(x))
|
||||||
|
|
||||||
def test_f64tou64():
|
def test_f64tou64():
|
||||||
for x in [
|
for x in [
|
||||||
|
-1.5,
|
||||||
|
float(u64_min()) - 1.0,
|
||||||
|
-0.5,
|
||||||
float(u64_min()),
|
float(u64_min()),
|
||||||
0.5,
|
0.5,
|
||||||
float(u64_min()) + 1.0,
|
float(u64_min()) + 1.0,
|
||||||
@ -173,4 +181,4 @@ def run() -> int32:
|
|||||||
test_f64tou32()
|
test_f64tou32()
|
||||||
test_f64tou64()
|
test_f64tou64()
|
||||||
|
|
||||||
return 0
|
return 0
|
Loading…
Reference in New Issue
Block a user