Compare commits

...

100 Commits

Author SHA1 Message Date
ychenfo a32faf78d2 nac3standalone: add more tests 2022-06-01 17:55:03 +08:00
ychenfo d568161169 nac3core: fix codegen error of inheritance 2022-06-01 17:55:03 +08:00
ychenfo 6e578a2ebd nac3core: codegen fix call parameter type error 2022-06-01 17:55:00 +08:00
Sebastien Bourdeauducq 4c39dd240f update all dependencies 2022-05-31 23:09:51 +08:00
occheung 48fc5ceb8e nac3artiq: demote global value to private
... except typeinfo & now symbols.
typeinfo will be read by the runtime linker; now is for now-pinning.
2022-05-30 22:46:41 +08:00
ychenfo c4ab2855e5 nac3core: pretty print codegen panic error 2022-05-30 04:09:21 +08:00
ychenfo ffac37dc48 nac3core: fix exception type in primitive store 2022-05-29 19:14:00 +08:00
ychenfo 76473152e8 nac3core: fix llvm.expect intrinsic name
this might be one of the causes for the random segfault bug
2022-05-27 04:23:49 +08:00
Sebastien Bourdeauducq b04631e935 update dependencies, switch to nixpkgs 22.05 2022-05-24 11:10:29 +08:00
ychenfo 09820e5aed nac3artiq: return err instead of panic for host object attribute error 2022-05-18 02:54:42 +08:00
Sebastien Bourdeauducq 0ec2ed4d91 update dependencies 2022-05-17 12:05:12 +08:00
ychenfo 2cb725b7ac nac3artiq: correct global name for const object 2022-05-16 02:50:42 +08:00
Sebastien Bourdeauducq b9259b1907 update nixpkgs and LLVM 2022-05-14 16:33:03 +08:00
ychenfo 096f4b03c0 nac3core: fix assignment 2022-05-14 02:30:08 +08:00
ychenfo a022005183 nac3core: fix broken tests 2022-05-11 03:53:53 +08:00
ychenfo 325ba0a408 nac3core: add debug info 2022-05-11 03:53:53 +08:00
ychenfo ae6434696c nac3artiq: rename the filename of modinit
rename from __nac3_synthesized_modinit__ to <nac3_synthesized_modinit> to be more idomatic python
2022-05-11 03:52:16 +08:00
Sebastien Bourdeauducq 3f327113b2 update dependencies, use upstream inkwell 2022-04-27 15:41:46 +08:00
Sebastien Bourdeauducq 27d509d70e nac3artiq: get_const_obj should no longer make a pointer. Closes #272 2022-04-27 15:28:58 +08:00
Sebastien Bourdeauducq a321b13bec fix typos 2022-04-27 11:08:10 +08:00
ychenfo 48cb485b89 nac3core: show outer type info in type error messages
Reviewed-on: M-Labs/nac3#274
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-04-22 15:31:55 +08:00
Sebastien Bourdeauducq 837aaa95f1 flake: contain sipyco to nac3artiq-profile 2022-04-19 10:34:55 +08:00
Sebastien Bourdeauducq a19e9c0bec flake: provide llvm-as for IRRT
clang already depends on llvmPackages_13.llvm, so, unlike the statically-linked tools
from llvm-nac3, this does not make the bloat even worse.
2022-04-19 10:23:41 +08:00
Sebastien Bourdeauducq 5dbe1d3d7d llvm: restore llvm-config 2022-04-19 10:23:12 +08:00
Sebastien Bourdeauducq e9bca3c822 llvm: set LLVM_BUILD_TOOLS=OFF 2022-04-19 00:30:11 +08:00
Sebastien Bourdeauducq 42d1aad507 flake: add PGO build to Hydra 2022-04-18 23:58:43 +08:00
Sebastien Bourdeauducq 2777a6e05f flake: use nac3devices example for PGO 2022-04-18 23:57:57 +08:00
Sebastien Bourdeauducq 05be5e93c4 flake: update nixpkgs 2022-04-18 18:48:05 +08:00
Sebastien Bourdeauducq 85f21060e4 update to LLVM 14 2022-04-18 18:47:20 +08:00
Sebastien Bourdeauducq a308d24caa nac3standalone: cleanup 2022-04-18 16:02:48 +08:00
Sebastien Bourdeauducq 1eac111d4c cleanup 2022-04-18 15:55:37 +08:00
ychenfo 44199781dc nac3standalone: add tests for operators 2022-04-18 15:31:56 +08:00
ychenfo 711c3d3303 nac3core: support custom operators 2022-04-18 15:31:56 +08:00
sb10q 0975264482 README: center icon 2022-04-18 15:11:32 +08:00
Sebastien Bourdeauducq 087aded3a3 add icon
Icon is copyright Evgeny Filatov and not covered by any free software license.
2022-04-18 15:07:53 +08:00
ychenfo f14b32be67 nac3artiq: type check host int bound instead of panic when codegen 2022-04-16 03:01:37 +08:00
David Nadlinger 879c66cccf flake.nix: Fix outdated nixConfig keys
The old syntax seems to be silently ignored on (at least)
Nix 2.7.0.
2022-04-13 21:21:18 +01:00
wylited 35b6459c58 nac3core: replace paramter with parameter 2022-04-13 15:42:26 +08:00
wylited e94b25f544 spelling (#264)
Co-authored-by: wylited <ds@m-labs.hk>
Co-committed-by: wylited <ds@m-labs.hk>
2022-04-13 11:32:31 +08:00
Sebastien Bourdeauducq 6972689469 nac3artiq: cleanup demo 2022-04-12 10:34:14 +08:00
Sebastien Bourdeauducq 3fb22c9182 nac3artiq: treat host numpy.float64 as float. Closes #90 2022-04-12 10:33:28 +08:00
Sebastien Bourdeauducq 1e7abf0268 fix tests 2022-04-12 10:06:41 +08:00
Sebastien Bourdeauducq f5a6d29106 update insta snapshots 2022-04-12 09:56:49 +08:00
Sebastien Bourdeauducq ca07cb66cd format typevars consistently 2022-04-12 09:28:17 +08:00
Sebastien Bourdeauducq 93e9a6a38a update dependencies 2022-04-12 09:13:04 +08:00
ychenfo 722e3df086 nac3core, artiq: optimize kernel invariant for tuple index 2022-04-11 14:58:40 +08:00
ychenfo ad9ad22cb8 nac3core: optimize unwrap KernelInvariant 2022-04-11 14:58:35 +08:00
ychenfo f66f66b3a4 nac3core, artiq: remove unnecessary ptr casts 2022-04-10 01:28:46 +08:00
ychenfo 6c485bc9dc nac3artiq: skip attribute writeback for option
option types do not have any fields to be written back to the host so it is ok to skip. If we do not skip, there will be error when getting the value of it since it can be `none`, whose type is not concrete
2022-04-10 01:28:30 +08:00
ychenfo 089bba96a3 nac3artiq: get_obj_value take an additional argument for expected type 2022-04-10 01:28:30 +08:00
ychenfo 0e0871bc38 nac3core, artiq: to_basic_value_enum takes an argument indicating the expected type 2022-04-10 01:28:22 +08:00
ychenfo 26187bff0b nac3core: add missing bound check and negative index handling for list subscription assignment 2022-04-09 04:56:31 +08:00
ychenfo 86ce513cb5 nac3standalone: fix broken test
previously this test unexpectedly passed because it is a slice assignment to extend the list, which is valid in CPython and hence in interpret_demo, and which also happened to give the same output in nac3 by memmove the elements in the list of bool
2022-04-05 18:21:46 +08:00
ychenfo c29cbf6ddd nac3core: add bound check for list slice 2022-04-05 18:21:46 +08:00
ychenfo 7443c5ea0f nac3core: add location information to codegen context 2022-04-05 18:21:46 +08:00
Sebastien Bourdeauducq f55b077e60 README: update Windows instructions 2022-04-05 18:07:38 +08:00
Sebastien Bourdeauducq e05b0bf5dc flake: update nixpkgs 2022-04-05 10:10:08 +08:00
Sebastien Bourdeauducq 8eda0affc9 windows: add wine-msys2-build 2022-04-05 10:06:36 +08:00
Sebastien Bourdeauducq 75c53b40a3 windows: update msys2 packages, add setuptools to environment 2022-04-05 10:06:14 +08:00
pca006132 0d10044d66 Merge pull request 'Fix float**int with negative power' (#254) from neg_powi_fix into master
Reviewed-on: M-Labs/nac3#254
2022-04-04 22:43:20 +08:00
ychenfo 23b7f4ef18 nac3standalone: add tests for power 2022-04-04 22:10:56 +08:00
ychenfo 710904f975 nac3core: fix powi with negative integer power 2022-04-04 22:10:56 +08:00
Sebastien Bourdeauducq 4bf452ec5a windows: do not check dependencies when making package 2022-04-04 16:03:59 +08:00
Sebastien Bourdeauducq 9fdce11efe windows: depend on python 2022-04-04 15:21:34 +08:00
Sebastien Bourdeauducq f24ef85aed hydra: use msys2 type 2022-04-04 15:03:53 +08:00
Sebastien Bourdeauducq 4a19787f10 README: update 2022-04-04 15:03:44 +08:00
Sebastien Bourdeauducq 8209c0a475 windows: create MSYS2 package 2022-04-04 14:24:47 +08:00
pca006132 4f66bdeda9 Merge pull request 'nac3core: do not get llvm value too eagerly for kernel invariant' (#253) from kernel_invariant_fix into master
Reviewed-on: M-Labs/nac3#253
2022-03-31 12:48:49 +08:00
Sebastien Bourdeauducq 57369896d7 update dependencies 2022-03-31 10:40:18 +08:00
ychenfo 2edeb31d21 nac3core: do not get llvm value too eagerly for kernel invariant 2022-03-31 10:28:16 +08:00
ychenfo b8ef44d64e nac3standalone: add test for default param 2022-03-30 04:05:47 +08:00
ychenfo c3156afebd nac3core: fix broken tests 2022-03-30 04:05:47 +08:00
ychenfo 388c9b7241 nac3core: better check and err msg for default param 2022-03-30 04:05:47 +08:00
ychenfo e52d7fc97a nac3artiq: resolve unsigned int host variable as defautl param 2022-03-30 04:05:47 +08:00
ychenfo 6ab73a223c nac3core/artiq: support default param of option type 2022-03-30 04:05:47 +08:00
ychenfo a38cc04444 nac3core: assert statement 2022-03-29 06:56:40 +08:00
ychenfo 1f5826d352 fix ternary if (#250)
Use store and load to handle if expression as the blocks might be changed when generating sub-expressions.

Reviewed-on: M-Labs/nac3#250
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-03-29 06:54:00 +08:00
Sebastien Bourdeauducq 94eebde4ea README: add note about MSVC Python 2022-03-28 10:45:01 +08:00
Sebastien Bourdeauducq 63ec382673 README: update Windows instructions 2022-03-27 19:36:02 +08:00
Sebastien Bourdeauducq 0ca1a7bedb windows: work around broken LLD install script 2022-03-27 19:14:02 +08:00
Sebastien Bourdeauducq 201ca3f63d Revert "nac3artiq: use lld.exe on Windows"
This reverts commit 19182759cd.
2022-03-27 19:09:11 +08:00
Sebastien Bourdeauducq 19182759cd nac3artiq: use lld.exe on Windows 2022-03-27 18:41:38 +08:00
Sebastien Bourdeauducq edd039abdc windows: build LLD 2022-03-27 18:41:23 +08:00
Sebastien Bourdeauducq 3852cc1058 windows: don't fixup LLVM 2022-03-27 18:38:23 +08:00
Sebastien Bourdeauducq 0600ee8efa nac3artiq: use correct lld invokation on Windows 2022-03-27 18:25:14 +08:00
ychenfo bed33a7421 nac3standalone: add tests for tuple 2022-03-27 10:31:20 +08:00
ychenfo 0d2b844a2e nac3artiq: avoid getting tuple as pointer value 2022-03-27 10:31:20 +08:00
ychenfo 8d7e300a4a nac3core: do not use const struct for tuple 2022-03-27 10:13:17 +08:00
ychenfo 10d623e36f nac3core/artiq: fix tuple representation 2022-03-27 07:47:14 +08:00
ychenfo 000b128551 nac3artiq: cast none to correct ptr type (#241) 2022-03-26 23:32:50 +08:00
Sebastien Bourdeauducq e4581a6d9b nac3standalone/demo: fix return type in loop.py 2022-03-26 21:10:12 +08:00
pca006132 1a82d296e7 nac3core/codegen: prevent users from modifying loop counter
Fixes #211
2022-03-26 20:58:37 +08:00
pca006132 bf067e2481 nac3artiq: implement attribute writeback
We will only writeback attributes that are supported by the current RPC
implementation: primitives, tuple and lists of lists... of primitives.
2022-03-26 20:13:58 +08:00
ychenfo ba8ed6c663 nac3artiq: handle recursive types properly 2022-03-26 18:54:21 +08:00
ychenfo 26a4834254 fix warnings 2022-03-26 18:52:08 +08:00
Sebastien Bourdeauducq 1ad4b0227c windows: fix src location 2022-03-26 15:46:21 +08:00
Sebastien Bourdeauducq 6288a66dc5 windows: fix cargo lockfile location 2022-03-26 15:23:31 +08:00
Sebastien Bourdeauducq de4320eefb improve package names 2022-03-26 15:15:59 +08:00
Sebastien Bourdeauducq a380cd5010 move all Nix files to one folder 2022-03-26 15:13:43 +08:00
ychenfo 80631fc92b Option type support (#224)
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-03-26 15:09:15 +08:00
64 changed files with 3284 additions and 1346 deletions

275
Cargo.lock generated
View File

@ -234,9 +234,9 @@ dependencies = [
[[package]]
name = "fixedbitset"
version = "0.2.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
[[package]]
name = "fxhash"
@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
"cfg-if",
"libc",
@ -278,9 +278,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.8.0"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [
"autocfg",
"hashbrown",
@ -298,12 +298,9 @@ dependencies = [
[[package]]
name = "indoc"
version = "1.0.4"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
dependencies = [
"unindent",
]
checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
[[package]]
name = "indoc-impl"
@ -320,24 +317,21 @@ dependencies = [
[[package]]
name = "inkwell"
version = "0.1.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2223d0eba0ae6d40a3e4680c6a3209143471e1f38b41746ea309aa36dde9f90b"
version = "0.1.0"
source = "git+https://github.com/TheDan64/inkwell.git#02fb7045f7a952c80bc983e19093c62c6ccffe3d"
dependencies = [
"either",
"inkwell_internals",
"libc",
"llvm-sys",
"once_cell",
"parking_lot",
"regex",
"parking_lot 0.12.1",
]
[[package]]
name = "inkwell_internals"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c7090af3d300424caa81976b8c97bca41cd70e861272c072e188ae082fb49f9"
source = "git+https://github.com/TheDan64/inkwell.git#02fb7045f7a952c80bc983e19093c62c6ccffe3d"
dependencies = [
"proc-macro2",
"quote",
@ -378,15 +372,15 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "lalrpop"
version = "0.19.7"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852b75a095da6b69da8c5557731c3afd06525d4f655a4fc1c799e2ec8bc4dce4"
checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823"
dependencies = [
"ascii-canvas",
"atty",
@ -407,9 +401,9 @@ dependencies = [
[[package]]
name = "lalrpop-util"
version = "0.19.7"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6d265705249fe209280676d8f68887859fa42e1d34f342fc05bd47726a5e188"
checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4"
dependencies = [
"regex",
]
@ -422,9 +416,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libloading"
@ -444,9 +438,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "llvm-sys"
version = "130.0.3"
version = "140.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95eb03b4f7ae21f48ef7c565a3e3aa22c50616aea64645fb1fd7f6f56b51c274"
checksum = "5a4ad24a72823cb06764e04a008d4aedc1be56d2be87aa4212b12a3e9f77bb10"
dependencies = [
"cc",
"lazy_static",
@ -457,27 +451,28 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
@ -495,7 +490,7 @@ dependencies = [
"inkwell",
"nac3core",
"nac3parser",
"parking_lot",
"parking_lot 0.11.2",
"pyo3",
"tempfile",
]
@ -506,7 +501,7 @@ version = "0.1.0"
dependencies = [
"fxhash",
"lazy_static",
"parking_lot",
"parking_lot 0.11.2",
"string-interner",
]
@ -515,12 +510,12 @@ name = "nac3core"
version = "0.1.0"
dependencies = [
"crossbeam",
"indoc 1.0.4",
"indoc 1.0.6",
"inkwell",
"insta",
"itertools",
"nac3parser",
"parking_lot",
"parking_lot 0.11.2",
"rayon",
"regex",
"test-case",
@ -549,7 +544,7 @@ dependencies = [
"inkwell",
"nac3core",
"nac3parser",
"parking_lot",
"parking_lot 0.11.2",
]
[[package]]
@ -570,9 +565,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.10.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "parking_lot"
@ -582,7 +577,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
"parking_lot_core 0.8.5",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core 0.9.3",
]
[[package]]
@ -599,6 +604,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "paste"
version = "0.1.18"
@ -618,20 +636,11 @@ dependencies = [
"proc-macro-hack",
]
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "petgraph"
version = "0.5.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
dependencies = [
"fixedbitset",
"indexmap",
@ -716,11 +725,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -732,7 +741,7 @@ dependencies = [
"cfg-if",
"indoc 0.3.6",
"libc",
"parking_lot",
"parking_lot 0.11.2",
"paste",
"pyo3-build-config",
"pyo3-macros",
@ -773,9 +782,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.16"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
@ -812,9 +821,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.5.1"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
@ -824,31 +833,30 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.9.1"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.11"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall",
@ -857,9 +865,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.5"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
"aho-corasick",
"memchr",
@ -868,9 +876,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.25"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "remove_dir_all"
@ -896,9 +904,9 @@ checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "ryu"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "scopeguard"
@ -908,36 +916,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.11.0"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
[[package]]
name = "serde"
version = "1.0.136"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
@ -946,9 +942,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.79"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"itoa",
"ryu",
@ -957,9 +953,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc"
dependencies = [
"indexmap",
"ryu",
@ -998,26 +994,26 @@ dependencies = [
[[package]]
name = "string_cache"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"parking_lot",
"once_cell",
"parking_lot 0.12.1",
"phf_shared 0.10.0",
"precomputed-hash",
]
[[package]]
name = "syn"
version = "1.0.89"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -1070,18 +1066,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
@ -1097,12 +1093,6 @@ dependencies = [
"crunchy",
]
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unic-char-property"
version = "0.9.0"
@ -1156,10 +1146,16 @@ dependencies = [
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "unicode_names2"
@ -1169,9 +1165,9 @@ checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1"
[[package]]
name = "unindent"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44"
[[package]]
name = "version_check"
@ -1207,6 +1203,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -1,5 +1,10 @@
# NAC3
<div align="center">
![icon](https://git.m-labs.hk/M-Labs/nac3/raw/branch/master/nac3.svg)
</div>
# NAC3
NAC3 is a major, backward-incompatible rewrite of the compiler for the [ARTIQ](https://m-labs.hk/artiq) physics experiment control and data acquisition system. It features greatly improved compilation speeds, a much better type system, and more predictable and transparent operation.
NAC3 has a modular design and its applicability reaches beyond ARTIQ. The ``nac3core`` module does not contain anything specific to ARTIQ, and can be used in any project that requires compiling Python to machine code.
@ -16,40 +21,28 @@ NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2
After setting up Nix as above, use ``nix shell git+https://github.com/m-labs/artiq.git?ref=nac3`` to get a shell with the NAC3 version of ARTIQ. See the ``examples`` directory in ARTIQ (``nac3`` Git branch) for some samples of NAC3 kernel code.
### Windows (work in progress)
### Windows
NAC3 ARTIQ packaging for MSYS2/Windows is not yet complete so installation involves many manual steps. It is also less tested and you may encounter problems.
Install [MSYS2](https://www.msys2.org/) and run the following commands:
Install [MSYS2](https://www.msys2.org/), and open "MSYS2 MinGW x64". Edit ``/etc/pacman.conf`` to add:
```
pacman -S mingw-w64-x86_64-python-h5py mingw-w64-x86_64-python-pyqt5 mingw-w64-x86_64-python-scipy mingw-w64-x86_64-python-prettytable mingw-w64-x86_64-python-pygit2
pacman -S mingw-w64-x86_64-python-pip
pip install qasync
pip install pyqtgraph
pacman -S patch git
git clone https://github.com/m-labs/sipyco
cd sipyco
git show 20c946aad78872fe60b78d9b57a624d69f3eea47 | patch -p1 -R
python setup.py install
cd ..
git clone -b nac3 https://github.com/m-labs/artiq
cd artiq
python setup.py install
[artiq]
SigLevel = Optional TrustAll
Server = https://lab.m-labs.hk/msys2
```
Locate a recent build of ``nac3artiq-msys2`` from [Hydra](https://nixbld.m-labs.hk) and download ``nac3artiq.zip``. Then extract the contents in the appropriate location:
Then run the following commands:
```
pacman -S unzip
wget https://nixbld.m-labs.hk/build/97899/download/1/nac3artiq.zip # edit the build number
unzip nac3artiq.zip -d C:/msys64/mingw64/lib/python3.9/site-packages
pacman -Syu
pacman -S mingw-w64-x86_64-artiq
```
Install additional NAC3 dependencies:
Install ``lld-msys2`` manually:
```
pacman -S mingw-w64-x86_64-lld
wget https://nixbld.m-labs.hk/build/115527/download/1/ld.lld.exe
mv ld.lld.exe C:/msys64/mingw64/bin
```
And you should be good to go.
Note: This build of NAC3 cannot be used with Anaconda Python nor the python.org binaries for Windows. Those Python versions are compiled with Visual Studio (MSVC) and their ABI is incompatible with the GNU ABI used in this build. We have no plans to support Visual Studio nor the MSVC ABI. If you need a MSVC build, please install the requisite bloated spyware from Microsoft and compile NAC3 yourself.
## For developers

View File

@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1647992509,
"narHash": "sha256-AG40Nt5OWz0LBs5p457emOuwLKOvTtcv/2fUdnEN3Ws=",
"lastModified": 1653920503,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d2caa9377539e3b5ff1272ac3aa2d15f3081069f",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-21.11",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}

View File

@ -1,21 +1,27 @@
{
description = "The third-generation ARTIQ compiler";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.11;
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-22.05;
outputs = { self, nixpkgs }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
in rec {
packages.x86_64-linux = rec {
llvm-nac3 = pkgs.callPackage "${self}/llvm" {};
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
nac3artiq = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
pkgs.rustPlatform.buildRustPackage rec {
name = "nac3artiq";
outputs = [ "out" "runkernel" "standalone" ];
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3 ];
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"inkwell-0.1.0" = "sha256-gDHV3RDuBU/l1viV1G1YO4kdTylRUctlxZcocX1hIIo=";
};
};
passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ])) ];
checkPhase =
@ -48,22 +54,22 @@
};
# LLVM PGO support
llvm-nac3-instrumented = pkgs.callPackage "${self}/llvm" {
stdenv = pkgs.llvmPackages_13.stdenv;
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
};
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq-instrumented";
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3-instrumented ];
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3-instrumented ];
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ];
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
doCheck = false;
configurePhase =
''
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_13.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_14.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
'';
installPhase =
''
@ -75,27 +81,52 @@
);
nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation {
name = "nac3artiq-profile";
src = self;
buildInputs = [ (python3-mimalloc.withPackages(ps: [ ps.numpy nac3artiq-instrumented ])) pkgs.lld_13 pkgs.llvmPackages_13.libllvm ];
srcs = [
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "sipyco";
rev = "939f84f9b5eef7efbf7423c735d1834783b6140e";
sha256 = "sha256-15Nun4EY35j+6SPZkjzZtyH/ncxLS60KuGJjFh5kSTc=";
})
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "artiq";
rev = "dd57fdc530baf926a5f354dc1c2bd90564affd96";
sha256 = "sha256-hcqVcToYWkc3oDFkKr9wZUF65ydiSYVHdmiGiu2Mc1c=";
})
];
buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.jsonschema nac3artiq-instrumented ]))
pkgs.lld_14
pkgs.llvmPackages_14.llvm.out
];
phases = [ "buildPhase" "installPhase" ];
# TODO: get more representative code.
buildPhase = "python $src/nac3artiq/demo/demo.py";
buildPhase =
''
srcs=($srcs)
sipyco=''${srcs[0]}
artiq=''${srcs[1]}
export PYTHONPATH=$sipyco:$artiq
python -m artiq.frontend.artiq_ddb_template $artiq/artiq/examples/nac3devices/nac3devices.json > device_db.py
cp $artiq/artiq/examples/nac3devices/nac3devices.py .
python -m artiq.frontend.artiq_compile nac3devices.py
'';
installPhase =
''
mkdir $out
llvm-profdata merge -o $out/llvm.profdata /build/llvm/build/profiles/*
'';
};
llvm-nac3-pgo = pkgs.callPackage "${self}/llvm" {
stdenv = pkgs.llvmPackages_13.stdenv;
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
};
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq-pgo";
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3-pgo ];
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3-pgo ];
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
cargoBuildFlags = [ "--package" "nac3artiq" ];
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
@ -109,18 +140,19 @@
);
};
packages.x86_64-w64-mingw32 = import ./windows { inherit pkgs; };
packages.x86_64-w64-mingw32 = import ./nix/windows { inherit pkgs; };
devShell.x86_64-linux = pkgs.mkShell {
name = "nac3-dev-shell";
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
llvmPackages_13.clang-unwrapped # IRRT
llvmPackages_14.clang-unwrapped # IRRT
pkgs.llvmPackages_14.llvm.out # IRRT
cargo
rustc
# runtime dependencies
lld_13
lld_14
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ]))
# development tools
cargo-insta
@ -129,7 +161,7 @@
];
};
devShells.x86_64-linux.msys2 = pkgs.mkShell {
name = "nac3-dev-shell";
name = "nac3-dev-shell-msys2";
buildInputs = with pkgs; [
curl
pacman
@ -139,14 +171,16 @@
};
hydraJobs = {
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq;
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq nac3artiq-pgo;
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
lld-msys2 = packages.x86_64-w64-mingw32.lld;
};
};
nixConfig = {
binaryCachePublicKeys = ["nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="];
binaryCaches = ["https://nixbld.m-labs.hk" "https://cache.nixos.org"];
extra-trusted-public-keys = "nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=";
extra-substituters = "https://nixbld.m-labs.hk";
};
}

View File

@ -1,381 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 135036f509d2..265c36f8211b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -270,15 +270,21 @@ if (CMAKE_BUILD_TYPE AND
message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif()
+include(GNUInstallDirs)
+
set(LLVM_LIBDIR_SUFFIX "" CACHE STRING "Define suffix of library directory name (32/64)" )
-set(LLVM_TOOLS_INSTALL_DIR "bin" CACHE STRING "Path for binary subdirectory (defaults to 'bin')")
+set(LLVM_TOOLS_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
+ "Path for binary subdirectory (defaults to 'bin')")
mark_as_advanced(LLVM_TOOLS_INSTALL_DIR)
set(LLVM_UTILS_INSTALL_DIR "${LLVM_TOOLS_INSTALL_DIR}" CACHE STRING
"Path to install LLVM utilities (enabled by LLVM_INSTALL_UTILS=ON) (defaults to LLVM_TOOLS_INSTALL_DIR)")
mark_as_advanced(LLVM_UTILS_INSTALL_DIR)
+set(LLVM_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/cmake/llvm" CACHE STRING
+ "Path for CMake subdirectory (defaults to lib/cmake/llvm)" )
+
# They are used as destination of target generators.
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
@@ -581,9 +587,9 @@ option (LLVM_ENABLE_SPHINX "Use Sphinx to generate llvm documentation." OFF)
option (LLVM_ENABLE_OCAMLDOC "Build OCaml bindings documentation." ON)
option (LLVM_ENABLE_BINDINGS "Build bindings." ON)
-set(LLVM_INSTALL_DOXYGEN_HTML_DIR "share/doc/llvm/doxygen-html"
+set(LLVM_INSTALL_DOXYGEN_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html"
CACHE STRING "Doxygen-generated HTML documentation install directory")
-set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "share/doc/llvm/ocaml-html"
+set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/ocaml-html"
CACHE STRING "OCamldoc-generated HTML documentation install directory")
option (LLVM_BUILD_EXTERNAL_COMPILER_RT
@@ -1048,7 +1054,7 @@ endif()
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1059,7 +1065,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
install(DIRECTORY ${LLVM_INCLUDE_DIR}/llvm ${LLVM_INCLUDE_DIR}/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1073,13 +1079,13 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if (LLVM_INSTALL_MODULEMAPS)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "module.modulemap"
)
install(FILES include/llvm/module.install.modulemap
- DESTINATION include/llvm
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm
COMPONENT llvm-headers
RENAME "module.extern.modulemap"
)
diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake
index 9c2b85374307..5531ceeb2eeb 100644
--- a/cmake/modules/AddLLVM.cmake
+++ b/cmake/modules/AddLLVM.cmake
@@ -818,9 +818,9 @@ macro(add_llvm_library name)
get_target_export_arg(${name} LLVM export_to_llvmexports ${umbrella})
install(TARGETS ${name}
${export_to_llvmexports}
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- RUNTIME DESTINATION bin COMPONENT ${name})
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${name})
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-${name}
@@ -1036,7 +1036,7 @@ function(process_llvm_pass_plugins)
"set(LLVM_STATIC_EXTENSIONS ${LLVM_STATIC_EXTENSIONS})")
install(FILES
${llvm_cmake_builddir}/LLVMConfigExtensions.cmake
- DESTINATION ${LLVM_INSTALL_PACKAGE_DIR}
+ DESTINATION ${LLVM_INSTALL_CMAKE_DIR}
COMPONENT cmake-exports)
set(ExtensionDef "${LLVM_BINARY_DIR}/include/llvm/Support/Extension.def")
@@ -1250,7 +1250,7 @@ macro(add_llvm_example name)
endif()
add_llvm_executable(${name} ${ARGN})
if( LLVM_BUILD_EXAMPLES )
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
endif()
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endmacro(add_llvm_example name)
@@ -1868,7 +1868,7 @@ function(llvm_install_library_symlink name dest type)
set(full_name ${CMAKE_${type}_LIBRARY_PREFIX}${name}${CMAKE_${type}_LIBRARY_SUFFIX})
set(full_dest ${CMAKE_${type}_LIBRARY_PREFIX}${dest}${CMAKE_${type}_LIBRARY_SUFFIX})
- set(output_dir lib${LLVM_LIBDIR_SUFFIX})
+ set(output_dir ${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX})
if(WIN32 AND "${type}" STREQUAL "SHARED")
set(output_dir bin)
endif()
@@ -1879,7 +1879,7 @@ function(llvm_install_library_symlink name dest type)
endfunction()
-function(llvm_install_symlink name dest)
+function(llvm_install_symlink name dest output_dir)
cmake_parse_arguments(ARG "ALWAYS_GENERATE" "COMPONENT" "" ${ARGN})
foreach(path ${CMAKE_MODULE_PATH})
if(EXISTS ${path}/LLVMInstallSymlink.cmake)
@@ -1902,7 +1902,7 @@ function(llvm_install_symlink name dest)
set(full_dest ${dest}${CMAKE_EXECUTABLE_SUFFIX})
install(SCRIPT ${INSTALL_SYMLINK}
- CODE "install_symlink(${full_name} ${full_dest} ${LLVM_TOOLS_INSTALL_DIR})"
+ CODE "install_symlink(${full_name} ${full_dest} ${output_dir})"
COMPONENT ${component})
if (NOT LLVM_ENABLE_IDE AND NOT ARG_ALWAYS_GENERATE)
@@ -1985,7 +1985,8 @@ function(add_llvm_tool_symlink link_name target)
endif()
if ((TOOL_IS_TOOLCHAIN OR NOT LLVM_INSTALL_TOOLCHAIN_ONLY) AND LLVM_BUILD_TOOLS)
- llvm_install_symlink(${link_name} ${target})
+ GNUInstallDirs_get_absolute_install_dir(output_dir LLVM_TOOLS_INSTALL_DIR)
+ llvm_install_symlink(${link_name} ${target} ${output_dir})
endif()
endif()
endfunction()
@@ -2114,9 +2115,9 @@ function(llvm_setup_rpath name)
# Since BUILD_SHARED_LIBS is only recommended for use by developers,
# hardcode the rpath to build/install lib dir first in this mode.
# FIXME: update this when there is better solution.
- set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
elseif(UNIX)
- set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)")
set_property(TARGET ${name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-z,origin ")
diff --git a/cmake/modules/AddOCaml.cmake b/cmake/modules/AddOCaml.cmake
index 554046b20edf..4d1ad980641e 100644
--- a/cmake/modules/AddOCaml.cmake
+++ b/cmake/modules/AddOCaml.cmake
@@ -144,9 +144,9 @@ function(add_ocaml_library name)
endforeach()
if( APPLE )
- set(ocaml_rpath "@executable_path/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "@executable_path/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
elseif( UNIX )
- set(ocaml_rpath "\\$ORIGIN/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "\\$ORIGIN/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
endif()
list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}")
diff --git a/cmake/modules/AddSphinxTarget.cmake b/cmake/modules/AddSphinxTarget.cmake
index e80c3b5c1cac..482f6d715ef5 100644
--- a/cmake/modules/AddSphinxTarget.cmake
+++ b/cmake/modules/AddSphinxTarget.cmake
@@ -90,7 +90,7 @@ function (add_sphinx_target builder project)
endif()
elseif (builder STREQUAL html)
string(TOUPPER "${project}" project_upper)
- set(${project_upper}_INSTALL_SPHINX_HTML_DIR "share/doc/${project}/html"
+ set(${project_upper}_INSTALL_SPHINX_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/html"
CACHE STRING "HTML documentation install directory for ${project}")
# '/.' indicates: copy the contents of the directory directly into
diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt
index 51b6a4fdc284..4adc2acfc074 100644
--- a/cmake/modules/CMakeLists.txt
+++ b/cmake/modules/CMakeLists.txt
@@ -1,6 +1,6 @@
include(LLVMDistributionSupport)
-set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
+set(LLVM_INSTALL_PACKAGE_DIR ${LLVM_INSTALL_CMAKE_DIR} CACHE STRING "Path for CMake subdirectory (defaults to 'cmake/llvm')")
set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
# First for users who use an installed LLVM, create the LLVMExports.cmake file.
@@ -109,13 +109,13 @@ foreach(p ${_count})
set(LLVM_CONFIG_CODE "${LLVM_CONFIG_CODE}
get_filename_component(LLVM_INSTALL_PREFIX \"\${LLVM_INSTALL_PREFIX}\" PATH)")
endforeach(p)
-set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/include")
+set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
set(LLVM_CONFIG_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
set(LLVM_CONFIG_MAIN_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
-set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/lib\${LLVM_LIBDIR_SUFFIX}")
+set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}")
set(LLVM_CONFIG_CMAKE_DIR "\${LLVM_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
set(LLVM_CONFIG_BINARY_DIR "\${LLVM_INSTALL_PREFIX}")
-set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/bin")
+set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
# Generate a default location for lit
if (LLVM_INSTALL_UTILS AND LLVM_BUILD_UTILS)
diff --git a/cmake/modules/LLVMInstallSymlink.cmake b/cmake/modules/LLVMInstallSymlink.cmake
index 3e6a2c9a2648..52e14d955c60 100644
--- a/cmake/modules/LLVMInstallSymlink.cmake
+++ b/cmake/modules/LLVMInstallSymlink.cmake
@@ -4,7 +4,7 @@
function(install_symlink name target outdir)
set(DESTDIR $ENV{DESTDIR})
- set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/${outdir}/")
+ set(bindir "${DESTDIR}${outdir}/")
message(STATUS "Creating ${name}")
diff --git a/docs/CMake.rst b/docs/CMake.rst
index f1ac2c7d4934..c6e1469b5e13 100644
--- a/docs/CMake.rst
+++ b/docs/CMake.rst
@@ -202,7 +202,7 @@ CMake manual, or execute ``cmake --help-variable VARIABLE_NAME``.
**LLVM_LIBDIR_SUFFIX**:STRING
Extra suffix to append to the directory where libraries are to be
installed. On a 64-bit architecture, one could use ``-DLLVM_LIBDIR_SUFFIX=64``
- to install libraries to ``/usr/lib64``.
+ to install libraries to ``/usr/lib64``. See also ``CMAKE_INSTALL_LIBDIR``.
Rarely-used CMake variables
---------------------------
@@ -551,8 +551,8 @@ LLVM-specific variables
**LLVM_INSTALL_DOXYGEN_HTML_DIR**:STRING
The path to install Doxygen-generated HTML documentation to. This path can
- either be absolute or relative to the CMAKE_INSTALL_PREFIX. Defaults to
- `share/doc/llvm/doxygen-html`.
+ either be absolute or relative to the ``CMAKE_INSTALL_PREFIX``. Defaults to
+ `${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html`.
**LLVM_LINK_LLVM_DYLIB**:BOOL
If enabled, tools will be linked with the libLLVM shared library. Defaults
@@ -792,9 +792,11 @@ the ``cmake`` command or by setting it directly in ``ccmake`` or ``cmake-gui``).
This file is available in two different locations.
-* ``<INSTALL_PREFIX>/lib/cmake/llvm/LLVMConfig.cmake`` where
- ``<INSTALL_PREFIX>`` is the install prefix of an installed version of LLVM.
- On Linux typically this is ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
+* ``<LLVM_INSTALL_PACKAGE_DIR>LLVMConfig.cmake`` where
+ ``<LLVM_INSTALL_PACKAGE_DIR>`` is the location where LLVM CMake modules are
+ installed as part of an installed version of LLVM. This is typically
+ ``cmake/llvm/`` within the lib directory. On Linux, this is typically
+ ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
* ``<LLVM_BUILD_ROOT>/lib/cmake/llvm/LLVMConfig.cmake`` where
``<LLVM_BUILD_ROOT>`` is the root of the LLVM build tree. **Note: this is only
diff --git a/examples/Bye/CMakeLists.txt b/examples/Bye/CMakeLists.txt
index bb96edb4b4bf..678c22fb43c8 100644
--- a/examples/Bye/CMakeLists.txt
+++ b/examples/Bye/CMakeLists.txt
@@ -14,6 +14,6 @@ if (NOT WIN32)
BUILDTREE_ONLY
)
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endif()
diff --git a/include/llvm/CMakeLists.txt b/include/llvm/CMakeLists.txt
index b46319f24fc8..2feabd1954e4 100644
--- a/include/llvm/CMakeLists.txt
+++ b/include/llvm/CMakeLists.txt
@@ -5,5 +5,5 @@ add_subdirectory(Frontend)
# If we're doing an out-of-tree build, copy a module map for generated
# header files into the build area.
if (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
- configure_file(module.modulemap.build module.modulemap COPYONLY)
+ configure_file(module.modulemap.build ${LLVM_INCLUDE_DIR}/module.modulemap COPYONLY)
endif (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index ebe5b73a5c65..70c497be12f5 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -23,6 +23,10 @@
#define LLVM_CXXFLAGS "@LLVM_CXXFLAGS@"
#define LLVM_BUILDMODE "@LLVM_BUILDMODE@"
#define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@"
+#define LLVM_INSTALL_BINDIR "@CMAKE_INSTALL_BINDIR@"
+#define LLVM_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@"
+#define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@"
+#define LLVM_INSTALL_CMAKEDIR "@LLVM_INSTALL_CMAKE_DIR@"
#define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@"
#define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@"
#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 1a2f04552d13..44fa7d3eec6b 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -357,12 +357,26 @@ int main(int argc, char **argv) {
("-I" + ActiveIncludeDir + " " + "-I" + ActiveObjRoot + "/include");
} else {
ActivePrefix = CurrentExecPrefix;
- ActiveIncludeDir = ActivePrefix + "/include";
- SmallString<256> path(StringRef(LLVM_TOOLS_INSTALL_DIR));
- sys::fs::make_absolute(ActivePrefix, path);
- ActiveBinDir = std::string(path.str());
- ActiveLibDir = ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX;
- ActiveCMakeDir = ActiveLibDir + "/cmake/llvm";
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_INCLUDEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveIncludeDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_BINDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveBinDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveLibDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_CMAKEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveCMakeDir = std::string(path.str());
+ }
ActiveIncludeOption = "-I" + ActiveIncludeDir;
}
diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt
index 0af29ad762c5..37b99b83e35c 100644
--- a/tools/lto/CMakeLists.txt
+++ b/tools/lto/CMakeLists.txt
@@ -33,7 +33,7 @@ add_llvm_library(${LTO_LIBRARY_NAME} ${LTO_LIBRARY_TYPE} INSTALL_WITH_TOOLCHAIN
${SOURCES} DEPENDS intrinsics_gen)
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT LTO)
if (APPLE)
diff --git a/tools/opt-viewer/CMakeLists.txt b/tools/opt-viewer/CMakeLists.txt
index ead73ec13a8f..250362021f17 100644
--- a/tools/opt-viewer/CMakeLists.txt
+++ b/tools/opt-viewer/CMakeLists.txt
@@ -8,7 +8,7 @@ set (files
foreach (file ${files})
install(PROGRAMS ${file}
- DESTINATION share/opt-viewer
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/opt-viewer
COMPONENT opt-viewer)
endforeach (file)
diff --git a/tools/remarks-shlib/CMakeLists.txt b/tools/remarks-shlib/CMakeLists.txt
index 865436247270..ce1daa62f6ab 100644
--- a/tools/remarks-shlib/CMakeLists.txt
+++ b/tools/remarks-shlib/CMakeLists.txt
@@ -19,7 +19,7 @@ if(LLVM_ENABLE_PIC)
endif()
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/Remarks.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT Remarks)
if (APPLE)

56
nac3.svg Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="a"
width="128"
height="128"
viewBox="0 0 95.99999 95.99999"
version="1.1"
sodipodi:docname="nac3.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs11" />
<sodipodi:namedview
id="namedview9"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="128px"
inkscape:zoom="5.9448568"
inkscape:cx="60.472441"
inkscape:cy="60.556547"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="a" />
<rect
x="40.072601"
y="-26.776209"
width="55.668747"
height="55.668747"
transform="matrix(0.71803815,0.69600374,-0.71803815,0.69600374,0,0)"
style="fill:#be211e;stroke:#000000;stroke-width:4.37375px;stroke-linecap:round;stroke-linejoin:round"
id="rect2" />
<line
x1="38.00692"
y1="63.457153"
x2="57.993061"
y2="63.457153"
style="fill:none;stroke:#000000;stroke-width:4.37269px;stroke-linecap:round;stroke-linejoin:round"
id="line4" />
<path
d="m 48.007301,57.843329 c -1.943097,0 -3.877522,-0.41727 -5.686157,-1.246007 -3.218257,-1.474616 -5.650382,-4.075418 -6.849639,-7.323671 -2.065624,-5.588921 -1.192751,-10.226647 2.575258,-13.827 0.611554,-0.584909 1.518048,-0.773041 2.323689,-0.488206 0.80673,0.286405 1.369495,0.998486 1.447563,1.827234 0.237469,2.549302 2.439719,5.917376 4.28414,6.55273 0.396859,0.13506 0.820953,-0.05859 1.097084,-0.35222 0.339254,-0.360754 0.451065,-0.961893 -1.013597,-3.191372 -2.089851,-3.181137 -4.638728,-8.754903 -0.262407,-15.069853 0.494457,-0.713491 1.384673,-1.068907 2.256469,-0.909156 0.871795,0.161332 1.583757,0.806404 1.752251,1.651189 0.716448,3.591862 2.962357,6.151755 5.199306,8.023138 1.935503,1.61861 4.344688,3.867387 5.435687,7.096643 2.283183,6.758017 -1.202511,14.114988 -8.060822,16.494025 -1.467083,0.509226 -2.98513,0.762536 -4.498836,0.762536 z M 39.358865,40.002192 c -0.304711,0.696206 -0.541636,2.080524 -0.56865,2.237454 -0.330316,1.918771 0.168305,3.803963 0.846157,5.539951 0.856828,2.19436 2.437543,3.942467 4.583411,4.925713 2.143691,0.981675 4.554131,1.097816 6.789992,0.322666 4.571485,-1.586549 6.977584,-6.532238 5.363036,-11.02597 v -5.27e-4 C 55.455481,39.447968 54.023463,38.162043 52.221335,36.65432 50.876945,35.529534 49.409662,33.987726 48.417983,32.135555 48.01343,31.37996 47.79547,30.34303 47.76669,29.413263 c -0.187481,0.669514 -0.212441,2.325923 -0.150396,2.93691 0.179209,1.764456 1.333476,3.644546 2.340611,5.171243 1.311568,1.988179 2.72058,6.037272 0.459681,8.367985 -1.54192,1.58953 -4.038511,2.052034 -5.839973,1.38492 -2.398314,-0.888147 -3.942744,-2.690627 -4.941118,-4.768029 -0.121194,-0.25217 -0.532464,-1.174187 -0.276619,-2.5041 z"
id="path6"
style="stroke-width:1.09317" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -16,9 +16,9 @@ nac3parser = { path = "../nac3parser" }
nac3core = { path = "../nac3core" }
[dependencies.inkwell]
version = "0.1.0-beta.4"
git = "https://github.com/TheDan64/inkwell.git"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[features]
init-llvm-profile = []

View File

@ -1,10 +1,4 @@
from min_artiq import *
from numpy import int32, int64
@extern
def output_int(x: int32):
...
@nac3

View File

@ -5,6 +5,7 @@ class EmbeddingMap:
self.string_map = {}
self.string_reverse_map = {}
self.function_map = {}
self.attributes_writeback = []
# preallocate exception names
self.preallocate_runtime_exception_names(["RuntimeError",
@ -16,7 +17,8 @@ class EmbeddingMap:
"CacheError",
"SPIError",
"0:ZeroDivisionError",
"0:IndexError"])
"0:IndexError",
"0:UnwrapNoneError"])
def preallocate_runtime_exception_names(self, names):
for i, name in enumerate(names):

View File

@ -11,6 +11,7 @@ from embedding_map import EmbeddingMap
__all__ = [
"Kernel", "KernelInvariant", "virtual",
"Option", "Some", "none", "UnwrapNoneError",
"round64", "floor64", "ceil64",
"extern", "kernel", "portable", "nac3",
"rpc", "ms", "us", "ns",
@ -32,6 +33,39 @@ class KernelInvariant(Generic[T]):
class virtual(Generic[T]):
pass
class Option(Generic[T]):
_nac3_option: T
def __init__(self, v: T):
self._nac3_option = v
def is_none(self):
return self._nac3_option is None
def is_some(self):
return not self.is_none()
def unwrap(self):
if self.is_none():
raise UnwrapNoneError()
return self._nac3_option
def __repr__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(str(self._nac3_option))
def Some(v: T) -> Option[T]:
return Option(v)
none = Option(None)
def round64(x):
return round(x)
@ -240,5 +274,10 @@ class KernelContextManager:
def __exit__(self):
pass
@nac3
class UnwrapNoneError(Exception):
"""raised when unwrapping a none value"""
artiq_builtin = True
parallel = KernelContextManager()
sequential = KernelContextManager()

View File

@ -6,7 +6,7 @@ use nac3core::{
},
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, GenCall},
typecheck::typedef::{FunSignature, Type},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}
};
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
@ -15,7 +15,9 @@ use inkwell::{
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
};
use crate::timeline::TimeFns;
use pyo3::{PyObject, PyResult, Python, types::{PyDict, PyList}};
use crate::{symbol_resolver::InnerResolver, timeline::TimeFns};
use std::{
collections::hash_map::DefaultHasher,
@ -66,7 +68,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?;
if let Some(end) = self.end.clone() {
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self)?;
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -86,7 +88,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
ctx.builder.build_store(end_store, max);
}
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self)?;
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
self.timeline.emit_at_mu(ctx, start_val);
}
Ok(result)
@ -118,7 +120,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self)?
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else {
self.timeline.emit_now_mu(ctx)
};
@ -172,8 +174,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val =
self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(ctx, self)?;
let end_val = self
.gen_expr(ctx, &end_expr)?
.unwrap()
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
// inside a sequential block
if old_start.is_none() {
@ -184,7 +188,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let outer_end_val = self
.gen_expr(ctx, old_end)?
.unwrap()
.to_basic_value_enum(ctx, self)?;
.to_basic_value_enum(ctx, self, old_end.custom.unwrap())?;
let smax =
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -270,8 +274,6 @@ fn gen_rpc_tag<'ctx, 'a>(
buffer.push(b'l');
gen_rpc_tag(ctx, *ty, buffer)?;
}
// we should return an error, this will be fixed after improving error message
// as this requires returning an error during codegen
_ => return Err(format!("Unsupported type: {:?}", ctx.unifier.stringify(ty))),
}
}
@ -291,7 +293,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
let int32 = ctx.ctx.i32_type();
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1 .0 as u64, false);
let service_id = int32.const_int(fun.1.0 as u64, false);
// -- setup rpc tags
let mut tag = Vec::new();
if obj.is_some() {
@ -361,14 +363,17 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
}
// default value handling
for k in keys.into_iter() {
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into());
mapping.insert(
k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into()
);
}
// reorder the parameters
let mut real_params = fun
.0
.args
.iter()
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator))
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator, arg.ty))
.collect::<Result<Vec<_>, _>>()?;
if let Some(obj) = obj {
if let ValueEnum::Static(obj) = obj.1 {
@ -486,6 +491,83 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
Ok(Some(result))
}
pub fn attributes_writeback<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
inner_resolver: &InnerResolver,
host_attributes: PyObject,
) -> Result<(), String> {
Python::with_gil(|py| -> PyResult<Result<(), String>> {
let host_attributes = host_attributes.cast_as::<PyList>(py)?;
let top_levels = ctx.top_level.definitions.read();
let globals = inner_resolver.global_value_ids.read();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let mut values = Vec::new();
let mut scratch_buffer = Vec::new();
for (_, val) in globals.iter() {
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(py, val, &mut ctx.unifier, &top_levels, &ctx.primitives)?;
if let Err(ty) = ty {
return Ok(Err(ty))
}
let ty = ty.unwrap();
match &*ctx.unifier.get_ty(ty) {
TypeEnum::TObj { fields, obj_id, .. }
if *obj_id != ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
// we only care about primitive attributes
// for non-primitive attributes, they should be in another global
let mut attributes = Vec::new();
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
for (name, (field_ty, is_mutable)) in fields.iter() {
if !is_mutable {
continue
}
if gen_rpc_tag(ctx, *field_ty, &mut scratch_buffer).is_ok() {
attributes.push(name.to_string());
let index = ctx.get_attr_index(ty, *name);
values.push((*field_ty, ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)])));
}
}
if !attributes.is_empty() {
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
pydict.set_item("fields", attributes)?;
host_attributes.append(pydict)?;
}
},
TypeEnum::TList { ty: elem_ty } => {
if gen_rpc_tag(ctx, *elem_ty, &mut scratch_buffer).is_ok() {
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
host_attributes.append(pydict)?;
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap()));
}
},
_ => {}
}
}
let fun = FunSignature {
args: values.iter().enumerate().map(|(i, (ty, _))| FuncArg {
name: i.to_string().into(),
ty: *ty,
default_value: None
}).collect(),
ret: ctx.primitives.none,
vars: Default::default()
};
let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator) {
return Ok(Err(e));
}
Ok(Ok(()))
}).unwrap()?;
Ok(())
}
pub fn rpc_codegen_callback() -> Arc<GenCall> {
Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| {
rpc_codegen_callback_fn(ctx, obj, fun, args, generator)

View File

@ -6,10 +6,12 @@ use std::sync::Arc;
use inkwell::{
memory_buffer::MemoryBuffer,
module::Linkage,
passes::{PassManager, PassManagerBuilder},
targets::*,
OptimizationLevel,
};
use nac3core::codegen::gen_func_impl;
use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
use nac3parser::{
@ -36,6 +38,7 @@ use nac3core::{
use tempfile::{self, TempDir};
use crate::codegen::attributes_writeback;
use crate::{
codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore},
@ -63,6 +66,7 @@ pub struct PrimitivePythonId {
uint32: u64,
uint64: u64,
float: u64,
float64: u64,
bool: u64,
list: u64,
tuple: u64,
@ -71,6 +75,7 @@ pub struct PrimitivePythonId {
exception: u64,
generic_alias: (u64, u64),
virtual_id: u64,
option: u64,
}
type TopLevelComponent = (Stmt, String, PyObject);
@ -325,8 +330,9 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_at_mu(ctx, arg);
Ok(None)
}))),
@ -342,8 +348,9 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_delay_mu(ctx, arg);
Ok(None)
}))),
@ -373,7 +380,17 @@ impl Nac3 {
get_attr_id(typing_mod, "_GenericAlias"),
get_attr_id(types_mod, "GenericAlias"),
),
none: get_attr_id(builtins_mod, "None"),
none: id_fn
.call1((builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("none")
.unwrap(),))
.unwrap()
.extract()
.unwrap(),
typevar: get_attr_id(typing_mod, "TypeVar"),
int: get_attr_id(builtins_mod, "int"),
int32: get_attr_id(numpy_mod, "int32"),
@ -382,9 +399,21 @@ impl Nac3 {
uint64: get_attr_id(numpy_mod, "uint64"),
bool: get_attr_id(builtins_mod, "bool"),
float: get_attr_id(builtins_mod, "float"),
float64: get_attr_id(numpy_mod, "float64"),
list: get_attr_id(builtins_mod, "list"),
tuple: get_attr_id(builtins_mod, "tuple"),
exception: get_attr_id(builtins_mod, "Exception"),
option: id_fn
.call1((builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("Option")
.unwrap(),))
.unwrap()
.extract()
.unwrap(),
};
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
@ -454,6 +483,8 @@ impl Nac3 {
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py);
let host_attributes = embedding_map.getattr("attributes_writeback").unwrap().to_object(py);
let global_value_ids: Arc<RwLock<HashMap<_, _>>> = Arc::new(RwLock::new(HashMap::new()));
let helper = PythonHelper {
id_fn: builtins.getattr("id").unwrap().to_object(py),
len_fn: builtins.getattr("len").unwrap().to_object(py),
@ -474,13 +505,13 @@ impl Nac3 {
"KeyError",
"NotImplementedError",
"OverflowError",
"IOError"
"IOError",
"UnwrapNoneError",
];
add_exceptions(&mut composer, &mut builtins_def, &mut builtins_ty, &exception_names);
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
let global_value_ids = Arc::new(RwLock::new(HashSet::<u64>::new()));
let mut rpc_ids = vec![];
for (stmt, path, module) in self.top_levels.iter() {
let py_module: &PyAny = module.extract(py)?;
@ -593,8 +624,8 @@ impl Nac3 {
format!("def __modinit__():\n base.{}({})", method_name, arg_names.join(", "))
};
let mut synthesized =
parse_program(&synthesized, "__nac3_synthesized_modinit__".to_string().into()).unwrap();
let resolver = Arc::new(Resolver(Arc::new(InnerResolver {
parse_program(&synthesized, "<nac3_synthesized_modinit>".to_string().into()).unwrap();
let inner_resolver = Arc::new(InnerResolver {
id_to_type: builtins_ty.clone().into(),
id_to_def: builtins_def.clone().into(),
pyid_to_def: self.pyid_to_def.clone(),
@ -611,22 +642,23 @@ impl Nac3 {
string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(),
deferred_eval_store: self.deferred_eval_store.clone(),
}))) as Arc<dyn SymbolResolver + Send + Sync>;
});
let resolver = Arc::new(Resolver(inner_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())
.unwrap();
let signature =
let fun_signature =
FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() };
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &signature, &mut cache);
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.add_cty(signature);
if let Err(e) = composer.start_analysis(true) {
// report error of __modinit__ separately
if !e.contains("__nac3_synthesized_modinit__") {
if !e.contains("<nac3_synthesized_modinit>") {
return Err(CompileError::new_err(format!(
"compilation failed\n----------\n{}",
e
@ -640,7 +672,10 @@ impl Nac3 {
&mut composer.unifier,
&self.primitive,
);
return Err(CompileError::new_err(msg.unwrap_or(e)));
return Err(CompileError::new_err(format!(
"compilation failed\n----------\n{}",
msg.unwrap_or(e)
)));
}
}
let top_level = Arc::new(composer.make_top_level_context());
@ -698,12 +733,29 @@ impl Nac3 {
symbol_name: "__modinit__".to_string(),
body: instance.body,
signature,
resolver,
resolver: resolver.clone(),
store,
unifier_index: instance.unifier_id,
calls: instance.calls,
id: 0,
};
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.add_cty(signature);
let attributes_writeback_task = CodeGenTask {
subst: Default::default(),
symbol_name: "attributes_writeback".to_string(),
body: Arc::new(Default::default()),
signature,
resolver,
store,
unifier_index: instance.unifier_id,
calls: Arc::new(Default::default()),
id: 0,
};
let isa = self.isa;
let working_directory = self.working_directory.path().to_owned();
@ -723,14 +775,27 @@ impl Nac3 {
.map(|s| Box::new(ArtiqCodeGenerator::new(s.to_string(), size_t, self.time_fns)))
.collect();
let membuffer = membuffers.clone();
py.allow_threads(|| {
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level.clone(), f);
registry.add_task(task);
registry.wait_tasks_complete(handles);
let mut generator = ArtiqCodeGenerator::new("attributes_writeback".to_string(), size_t, self.time_fns);
let context = inkwell::context::Context::create();
let module = context.create_module("attributes_writeback");
let builder = context.create_builder();
let (_, module, _) = gen_func_impl(&context, &mut generator, &registry, builder, module,
attributes_writeback_task, |generator, ctx| {
attributes_writeback(ctx, generator, inner_resolver.as_ref(), host_attributes)
}).unwrap();
let buffer = module.write_bitcode_to_memory();
let buffer = buffer.as_slice().into();
membuffer.lock().push(buffer);
});
let buffers = membuffers.lock();
let context = inkwell::context::Context::create();
let buffers = membuffers.lock();
let main = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main"))
.unwrap();
@ -742,6 +807,11 @@ impl Nac3 {
main.link_in_module(other)
.map_err(|err| CompileError::new_err(err.to_string()))?;
}
let builder = context.create_builder();
let modinit_return = main.get_function("__modinit__").unwrap().get_last_basic_block().unwrap().get_terminator().unwrap();
builder.position_before(&modinit_return);
builder.build_call(main.get_function("attributes_writeback").unwrap(), &[], "attributes_writeback");
main.link_in_module(load_irrt(&context))
.map_err(|err| CompileError::new_err(err.to_string()))?;
@ -753,6 +823,19 @@ impl Nac3 {
function_iter = func.get_next_function();
}
// Demote all global variables that will not be referenced in the kernel to private
let preserved_symbols: Vec<&'static [u8]> = vec![
b"typeinfo",
b"now",
];
let mut global_option = main.get_first_global();
while let Some(global) = global_option {
if !preserved_symbols.contains(&(global.get_name().to_bytes())) {
global.set_linkage(Linkage::Private);
}
global_option = global.get_next_global();
}
let builder = PassManagerBuilder::create();
builder.set_optimization_level(OptimizationLevel::Aggressive);
let passes = PassManager::create(());
@ -805,7 +888,11 @@ impl Nac3 {
);
}
if let Ok(linker_status) = Command::new("ld.lld").args(linker_args).status() {
#[cfg(not(windows))]
let lld_command = "ld.lld";
#[cfg(windows)]
let lld_command = "ld.lld.exe";
if let Ok(linker_status) = Command::new(lld_command).args(linker_args).status() {
if !linker_status.success() {
return Err(CompileError::new_err("failed to start linker"));
}

View File

@ -15,7 +15,7 @@ use pyo3::{
PyAny, PyObject, PyResult, Python,
};
use std::{
collections::{HashMap, HashSet},
collections::HashMap,
sync::{
Arc,
atomic::{AtomicBool, Ordering::Relaxed}
@ -54,7 +54,7 @@ pub struct InnerResolver {
pub id_to_pyval: RwLock<HashMap<StrRef, (u64, PyObject)>>,
pub id_to_primitive: RwLock<HashMap<u64, PrimitiveValue>>,
pub field_to_val: RwLock<HashMap<(u64, StrRef), Option<(u64, PyObject)>>>,
pub global_value_ids: Arc<RwLock<HashSet<u64>>>,
pub global_value_ids: Arc<RwLock<HashMap<u64, PyObject>>>,
pub class_names: Mutex<HashMap<StrRef, Type>>,
pub pyid_to_def: Arc<RwLock<HashMap<u64, DefinitionId>>>,
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
@ -99,7 +99,7 @@ impl StaticValue for PythonValue {
_: &mut dyn CodeGenerator,
) -> BasicValueEnum<'ctx> {
ctx.module
.get_global(self.id.to_string().as_str())
.get_global(format!("{}_const", self.id).as_str())
.map(|val| val.as_pointer_value().into())
.unwrap_or_else(|| {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
@ -115,13 +115,7 @@ impl StaticValue for PythonValue {
&[ctx.ctx.i32_type().const_int(id as u64, false).into()],
false,
));
let global2 = ctx.module.add_global(
struct_type.ptr_type(AddressSpace::Generic),
None,
format!("{}_const2", self.id).as_str(),
);
global2.set_initializer(&global.as_pointer_value());
Ok(global2.as_pointer_value().into())
Ok(global.as_pointer_value().into())
})
.unwrap()
})
@ -131,6 +125,7 @@ impl StaticValue for PythonValue {
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String> {
if let Some(val) = self.resolver.id_to_primitive.read().get(&self.id) {
return Ok(match val {
@ -150,7 +145,7 @@ impl StaticValue for PythonValue {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
self.resolver
.get_obj_value(py, self.value.as_ref(py), ctx, generator)
.get_obj_value(py, self.value.as_ref(py), ctx, generator, expected_ty)
.map(Option::unwrap)
}).map_err(|e| e.to_string())
}
@ -169,6 +164,16 @@ impl StaticValue for PythonValue {
let helper = &self.resolver.helper;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
// for optimizing unwrap KernelInvariant
if ty_id == self.resolver.primitive_ids.option && name == "_nac3_option".into() {
let obj = self.value.getattr(py, &name.to_string())?;
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
if self.id == self.resolver.primitive_ids.none {
return Ok(None)
} else {
return Ok(Some((id, obj)))
}
}
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
let mut mutable = true;
let defs = ctx.top_level.definitions.read();
@ -201,6 +206,28 @@ impl StaticValue for PythonValue {
}))
})
}
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>> {
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
let helper = &self.resolver.helper;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
assert_eq!(ty_id, self.resolver.primitive_ids.tuple);
let tup: &PyTuple = self.value.extract(py)?;
let elem = tup.get_item(index as usize);
let id = self.resolver.helper.id_fn.call1(py, (elem,))?.extract(py)?;
Ok(Some((id, elem.into())))
})
.unwrap()
.map(|(id, obj)| {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: obj,
store_obj: self.store_obj.clone(),
resolver: self.resolver.clone(),
}))
})
}
}
impl InnerResolver {
@ -269,6 +296,8 @@ impl InnerResolver {
Ok(Ok((primitives.bool, true)))
} else if ty_id == self.primitive_ids.float {
Ok(Ok((primitives.float, true)))
} else if ty_id == self.primitive_ids.float64 {
Ok(Ok((primitives.float, true)))
} else if ty_id == self.primitive_ids.exception {
Ok(Ok((primitives.exception, true)))
} else if ty_id == self.primitive_ids.list {
@ -279,6 +308,10 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.tuple {
// do not handle type var param and concrete check here
Ok(Ok((unifier.add_ty(TypeEnum::TTuple { ty: vec![] }), false)))
} else if ty_id == self.primitive_ids.option {
Ok(Ok((primitives.option, false)))
} else if ty_id == self.primitive_ids.none {
unreachable!("none cannot be typeid")
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() {
let def = defs[def_id.0].read();
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
@ -499,7 +532,7 @@ impl InnerResolver {
}
}
fn get_obj_type(
pub fn get_obj_type(
&self,
py: Python,
obj: &PyAny,
@ -508,8 +541,8 @@ impl InnerResolver {
primitives: &PrimitiveStore,
) -> PyResult<Result<Type, String>> {
let ty = self.helper.type_fn.call1(py, (obj,)).unwrap();
let ty_id: u64 = self.helper.id_fn.call1(py, (ty.clone(),))?.extract(py)?;
if let Some(ty) = self.pyid_to_type.read().get(&ty_id) {
let py_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if let Some(ty) = self.pyid_to_type.read().get(&py_obj_id) {
return Ok(Ok(*ty))
}
let (extracted_ty, inst_check) = match self.get_pyty_obj_type(
@ -540,7 +573,7 @@ impl InnerResolver {
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
if len == 0 {
assert!(matches!(
&*unifier.get_ty(extracted_ty),
&*unifier.get_ty(*ty),
TypeEnum::TVar { fields: None, range, .. }
if range.is_empty()
));
@ -569,7 +602,54 @@ impl InnerResolver {
let types = types?;
Ok(types.map(|types| unifier.add_ty(TypeEnum::TTuple { ty: types })))
}
// special handling for option type since its class member layout in python side
// is special and cannot be mapped directly to a nac3 type as below
(TypeEnum::TObj { obj_id, params, .. }, false)
if *obj_id == primitives.option.get_obj_id(unifier) =>
{
let field_data = match obj.getattr("_nac3_option") {
Ok(d) => d,
// we use `none = Option(None)`, so the obj always have attr `_nac3_option`
Err(_) => unreachable!("cannot be None")
};
// if is `none`
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if zelf_id == self.primitive_ids.none {
if let TypeEnum::TObj { params, .. } =
unifier.get_ty_immutable(primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
} else {
unreachable!("must be tobj")
}
}
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
Ok(t) => t,
Err(e) => {
return Ok(Err(format!(
"error when getting type of the option object ({})",
e
)))
}
};
let new_var_map: HashMap<_, _> = params.iter().map(|(id, _)| (*id, ty)).collect();
let res = unifier.subst(extracted_ty, &new_var_map).unwrap_or(extracted_ty);
Ok(Ok(res))
}
(TypeEnum::TObj { params, fields, .. }, false) => {
self.pyid_to_type.write().insert(py_obj_id, extracted_ty);
let var_map = params
.iter()
.map(|(id_var, ty)| {
@ -590,7 +670,10 @@ impl InnerResolver {
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1.0) {
continue;
} else {
let field_data = obj.getattr(&name)?;
let field_data = match obj.getattr(&name) {
Ok(d) => d,
Err(e) => return Ok(Err(format!("{}", e))),
};
let ty = match self
.get_obj_type(py, field_data, unifier, defs, primitives)?
{
@ -623,50 +706,86 @@ impl InnerResolver {
let extracted_ty = unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty);
Ok(Ok(extracted_ty))
};
instantiate_obj()
let result = instantiate_obj();
// update/remove the cache according to the result
match result {
Ok(Ok(ty)) => self.pyid_to_type.write().insert(py_obj_id, ty),
_ => self.pyid_to_type.write().remove(&py_obj_id)
};
result
}
_ => {
// check integer bounds
if unifier.unioned(extracted_ty, primitives.int32) {
obj.extract::<i32>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of int32", obj))),
|_| Ok(Ok(extracted_ty))
)
} else if unifier.unioned(extracted_ty, primitives.int64) {
obj.extract::<i64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of int64", obj))),
|_| Ok(Ok(extracted_ty))
)
} else if unifier.unioned(extracted_ty, primitives.uint32) {
obj.extract::<u32>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of uint32", obj))),
|_| Ok(Ok(extracted_ty))
)
} else if unifier.unioned(extracted_ty, primitives.uint64) {
obj.extract::<u64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of uint64", obj))),
|_| Ok(Ok(extracted_ty))
)
} else if unifier.unioned(extracted_ty, primitives.bool) {
obj.extract::<bool>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of bool", obj))),
|_| Ok(Ok(extracted_ty))
)
} else if unifier.unioned(extracted_ty, primitives.float) {
obj.extract::<f64>().map_or_else(
|_| Ok(Err(format!("{} is not in the range of float64", obj))),
|_| Ok(Ok(extracted_ty))
)
} else {
Ok(Ok(extracted_ty))
}
}
_ => Ok(Ok(extracted_ty)),
}
}
fn get_obj_value<'ctx, 'a>(
pub fn get_obj_value<'ctx, 'a>(
&self,
py: Python,
obj: &PyAny,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
let val: i32 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of int32", obj)))?;
let val: i32 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::I32(val));
Ok(Some(ctx.ctx.i32_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.int64 {
let val: i64 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of int64", obj)))?;
let val: i64 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::I64(val));
Ok(Some(ctx.ctx.i64_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.uint32 {
let val: u32 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of uint32", obj)))?;
let val: u32 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::U32(val));
Ok(Some(ctx.ctx.i32_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.uint64 {
let val: u64 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of uint64", obj)))?;
let val: u64 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::U64(val));
Ok(Some(ctx.ctx.i64_type().const_int(val, false).into()))
} else if ty_id == self.primitive_ids.bool {
let val: bool = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of bool", obj)))?;
let val: bool = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
Ok(Some(ctx.ctx.bool_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.float {
let val: f64 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of float64", obj)))?;
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
let val: f64 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));
Ok(Some(ctx.ctx.f64_type().const_float(val).into()))
} else if ty_id == self.primitive_ids.list {
@ -677,40 +796,39 @@ impl InnerResolver {
}
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
let ty = if len == 0 {
ctx.primitives.int32
let elem_ty =
if let TypeEnum::TList { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref()
{
*ty
} else {
self.get_list_elem_type(
py,
obj,
len,
&mut ctx.unifier,
&ctx.top_level.definitions.read(),
&ctx.primitives,
)?
.unwrap()
unreachable!("must be list")
};
let ty = ctx.get_llvm_type(generator, ty);
let ty = ctx.get_llvm_type(generator, elem_ty);
let size_t = generator.get_size_type(ctx.ctx);
let arr_ty = ctx
.ctx
.struct_type(&[ty.ptr_type(AddressSpace::Generic).into(), size_t.into()], false);
{
if self.global_value_ids.read().contains(&id) {
if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id);
self.global_value_ids.write().insert(id, obj.into());
}
}
let arr: Result<Option<Vec<_>>, _> = (0..len)
.map(|i| {
obj.get_item(i).and_then(|elem| self.get_obj_value(py, elem, ctx, generator).map_err(
|e| super::CompileError::new_err(format!("Error getting element {}: {}", i, e))))
obj
.get_item(i)
.and_then(|elem| self.get_obj_value(py, elem, ctx, generator, elem_ty)
.map_err(
|e| super::CompileError::new_err(
format!("Error getting element {}: {}", i, e))
))
})
.collect();
let arr = arr?.unwrap();
@ -751,50 +869,73 @@ impl InnerResolver {
Ok(Some(global.as_pointer_value().into()))
} else if ty_id == self.primitive_ids.tuple {
let id_str = id.to_string();
if let Some(global) = ctx.module.get_global(&id_str) {
return Ok(Some(global.as_pointer_value().into()));
}
if let TypeEnum::TTuple { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
let tup_tys = ty.iter();
let elements: &PyTuple = obj.cast_as()?;
let types: Result<Result<Vec<_>, _>, _> = elements
assert_eq!(elements.len(), tup_tys.len());
let val: Result<Option<Vec<_>>, _> =
elements
.iter()
.enumerate()
.map(|(i, elem)| {
self.get_obj_type(
py,
elem,
&mut ctx.unifier,
&ctx.top_level.definitions.read(),
&ctx.primitives,
.zip(tup_tys)
.map(|((i, elem), ty)| self
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
super::CompileError::new_err(
format!("Error getting element {}: {}", i, e)
)
.map_err(|e| super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))
.map(|ty| ty.map(|ty| ctx.get_llvm_type(generator, ty)))
})
.collect();
let types = types?.unwrap();
let ty = ctx.ctx.struct_type(&types, false);
)
).collect();
let val = val?.unwrap();
let val = ctx.ctx.const_struct(&val, false);
Ok(Some(val.into()))
} else {
unreachable!("must expect tuple type")
}
} else if ty_id == self.primitive_ids.option {
let option_val_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
if self.global_value_ids.read().contains(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type")
};
if id == self.primitive_ids.none {
// for option type, just a null ptr
Ok(Some(
ctx.get_llvm_type(generator, option_val_ty)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
))
} else {
match self
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator, option_val_ty)
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting value of Option object: {}",
e
))
})? {
Some(v) => {
let global_str = format!("{}_option", id);
{
if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&global_str).unwrap_or_else(|| {
ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id);
self.global_value_ids.write().insert(id, obj.into());
}
}
let val: Result<Option<Vec<_>>, _> =
elements.iter().enumerate().map(|(i, elem)| self.get_obj_value(py, elem, ctx, generator).map_err(|e|
super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))).collect();
let val = val?.unwrap();
let val = ctx.ctx.const_struct(&val, false);
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str);
global.set_initializer(&val);
let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str);
global.set_initializer(&v);
Ok(Some(global.as_pointer_value().into()))
},
None => Ok(None),
}
}
} else {
let id_str = id.to_string();
@ -812,13 +953,13 @@ impl InnerResolver {
.get_element_type()
.into_struct_type();
{
if self.global_value_ids.read().contains(&id) {
if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id);
self.global_value_ids.write().insert(id, obj.into());
}
}
// should be classes
@ -827,15 +968,17 @@ impl InnerResolver {
if let TopLevelDef::Class { fields, .. } = &*definition {
let values: Result<Option<Vec<_>>, _> = fields
.iter()
.map(|(name, _, _)| {
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator).map_err(|e|
super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
.map(|(name, ty, _)| {
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator, *ty)
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
})
.collect();
let values = values?;
if let Some(values) = values {
let val = ty.const_named_struct(&values);
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str);
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
});
global.set_initializer(&val);
Ok(Some(global.as_pointer_value().into()))
} else {
@ -852,6 +995,7 @@ impl InnerResolver {
py: Python,
obj: &PyAny,
) -> PyResult<Result<SymbolValue, String>> {
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
Ok(if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
@ -860,23 +1004,33 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.int64 {
let val: i64 = obj.extract()?;
Ok(SymbolValue::I64(val))
} else if ty_id == self.primitive_ids.uint32 {
let val: u32 = obj.extract()?;
Ok(SymbolValue::U32(val))
} else if ty_id == self.primitive_ids.uint64 {
let val: u64 = obj.extract()?;
Ok(SymbolValue::U64(val))
} else if ty_id == self.primitive_ids.bool {
let val: bool = obj.extract()?;
Ok(SymbolValue::Bool(val))
} else if ty_id == self.primitive_ids.float {
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
let val: f64 = obj.extract()?;
Ok(SymbolValue::Double(val))
} else if ty_id == self.primitive_ids.tuple {
let elements: &PyTuple = obj.cast_as()?;
let elements: Result<Result<Vec<_>, String>, _> =
elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect();
let elements = match elements? {
Ok(el) => el,
Err(err) => return Ok(Err(err)),
};
Ok(SymbolValue::Tuple(elements))
elements?.map(SymbolValue::Tuple)
} else if ty_id == self.primitive_ids.option {
if id == self.primitive_ids.none {
Ok(SymbolValue::OptionNone)
} else {
Err("only primitives values and tuple can be default parameter value".into())
self
.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
.map(|v| SymbolValue::OptionSome(Box::new(v)))
}
} else {
Err("only primitives values, option and tuple can be default parameter value".into())
})
}
}
@ -892,8 +1046,9 @@ impl SymbolResolver for Resolver {
for (key, val) in members.iter() {
let key: &str = key.extract()?;
if key == id.to_string() {
sym_value =
Some(self.0.get_default_param_obj_value(py, val).unwrap().unwrap());
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
sym_value = Some(v)
}
break;
}
}
@ -901,7 +1056,7 @@ impl SymbolResolver for Resolver {
})
.unwrap()
}
_ => unimplemented!("other type of expr not supported at {}", expr.location),
_ => unreachable!("only for resolving names"),
}
}

View File

@ -12,9 +12,9 @@ rayon = "1.5.1"
nac3parser = { path = "../nac3parser" }
[dependencies.inkwell]
version = "0.1.0-beta.4"
git = "https://github.com/TheDan64/inkwell.git"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[dev-dependencies]
test-case = "1.2.0"

View File

@ -10,7 +10,10 @@ use crate::{
},
symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
typecheck::{
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name},
},
};
use inkwell::{
AddressSpace,
@ -91,6 +94,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
&mut self,
generator: &mut dyn CodeGenerator,
val: &SymbolValue,
ty: Type,
) -> BasicValueEnum<'ctx> {
match val {
SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(),
@ -107,7 +111,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
ty.const_named_struct(&[str_ptr, size.into()]).into()
}
SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v)).collect_vec();
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
let fields = vals.iter().map(|v| v.get_type()).collect_vec();
let ty = self.ctx.struct_type(&fields, false);
let ptr = self.builder.build_alloca(ty, "tuple");
@ -124,6 +128,33 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
}
self.builder.build_load(ptr, "tup_val")
}
SymbolValue::OptionSome(v) => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.get_obj_id(&self.unifier) =>
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type"),
};
let val = self.gen_symbol_val(generator, v, ty);
let ptr = self.builder.build_alloca(val.get_type(), "default_opt_some");
self.builder.build_store(ptr, val);
ptr.into()
}
SymbolValue::OptionNone => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.get_obj_id(&self.unifier) =>
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type"),
};
let actual_ptr_type =
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::Generic);
actual_ptr_type.const_null().into()
}
}
}
@ -134,10 +165,12 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
) -> BasicTypeEnum<'ctx> {
get_llvm_type(
self.ctx,
&self.module,
generator,
&mut self.unifier,
self.top_level,
&mut self.type_cache,
&self.primitives,
ty,
)
}
@ -205,6 +238,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
pub fn gen_int_ops(
&mut self,
generator: &mut dyn CodeGenerator,
op: &Operator,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
@ -240,7 +274,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),
(Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(),
(Operator::Pow, s) => integer_power(self, lhs, rhs, s).into(),
(Operator::Pow, s) => integer_power(generator, self, lhs, rhs, s).into(),
// special implementation?
(Operator::MatMult, _) => unreachable!(),
}
@ -328,17 +362,31 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
}
}
}
let params = if loc_params.is_empty() {
params
} else {
&loc_params
};
let params = if loc_params.is_empty() { params } else { &loc_params };
let params = fun
.get_type()
.get_param_types()
.into_iter()
.zip(params.iter())
.map(|(ty, val)| match (ty, val.get_type()) {
(BasicTypeEnum::PointerType(arg_ty), BasicTypeEnum::PointerType(val_ty))
if {
ty != val.get_type()
&& arg_ty.get_element_type().is_struct_type()
&& val_ty.get_element_type().is_struct_type()
} =>
{
self.builder.build_bitcast(*val, arg_ty, "call_arg_cast")
}
_ => *val,
})
.collect_vec();
let result = if let Some(target) = self.unwind_target {
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_block = self.ctx.append_basic_block(current, &format!("after.{}", call_name));
let result = self
.builder
.build_invoke(fun, params, then_block, target, call_name)
.build_invoke(fun, &params, then_block, target, call_name)
.try_as_basic_value()
.left();
self.builder.position_at_end(then_block);
@ -354,17 +402,17 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
}
}
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
pub fn gen_string<S: Into<String>>(
&mut self,
generator: &mut G,
generator: &mut dyn CodeGenerator,
s: S,
) -> BasicValueEnum<'ctx> {
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
}
pub fn raise_exn<G: CodeGenerator>(
pub fn raise_exn(
&mut self,
generator: &mut G,
generator: &mut dyn CodeGenerator,
name: &str,
msg: BasicValueEnum<'ctx>,
params: [Option<IntValue<'ctx>>; 3],
@ -401,20 +449,33 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
gen_raise(generator, self, Some(&zelf.into()), loc);
}
pub fn make_assert<G: CodeGenerator>(
pub fn make_assert(
&mut self,
generator: &mut G,
generator: &mut dyn CodeGenerator,
cond: IntValue<'ctx>,
err_name: &str,
err_msg: &str,
params: [Option<IntValue<'ctx>>; 3],
loc: Location,
) {
let err_msg = self.gen_string(generator, err_msg);
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc)
}
pub fn make_assert_impl(
&mut self,
generator: &mut dyn CodeGenerator,
cond: IntValue<'ctx>,
err_name: &str,
err_msg: BasicValueEnum<'ctx>,
params: [Option<IntValue<'ctx>>; 3],
loc: Location,
) {
let i1 = self.ctx.bool_type();
let i1_true = i1.const_all_ones();
let expect_fun = self.module.get_function("llvm.expect.i1").unwrap_or_else(|| {
self.module.add_function(
"llvm.expect",
"llvm.expect.i1",
i1.fn_type(&[i1.into(), i1.into()], false),
None,
)
@ -435,7 +496,6 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
self.builder.build_conditional_branch(cond, then_block, exn_block);
self.builder.position_at_end(exn_block);
let err_msg = self.gen_string(generator, err_msg);
self.raise_exn(generator, err_name, err_msg, params, loc);
self.builder.position_at_end(then_block);
}
@ -451,12 +511,7 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
match def {
TopLevelDef::Class { methods, .. } => {
// TODO: what about other fields that require alloca?
let mut fun_id = None;
for (name, _, id) in methods.iter() {
if name == &"__init__".into() {
fun_id = Some(*id);
}
}
let fun_id = methods.iter().find(|method| method.0 == "__init__".into()).and_then(|method| Some(method.2));
let ty = ctx.get_llvm_type(generator, signature.ret).into_pointer_type();
let zelf_ty: BasicTypeEnum = ty.get_element_type().try_into().unwrap();
let zelf: BasicValueEnum<'ctx> = ctx.builder.build_alloca(zelf_ty, "alloca").into();
@ -592,19 +647,19 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
}
mapping.insert(
k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into(),
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into(),
);
}
// reorder the parameters
let mut real_params =
fun.0.args.iter().map(|arg| mapping.remove(&arg.name).unwrap()).collect_vec();
fun.0.args.iter().map(|arg| (mapping.remove(&arg.name).unwrap(), arg.ty)).collect_vec();
if let Some(obj) = &obj {
real_params.insert(0, obj.1.clone());
real_params.insert(0, (obj.1.clone(), obj.0));
}
let static_params = real_params
.iter()
.enumerate()
.filter_map(|(i, v)| {
.filter_map(|(i, (v, _))| {
if let ValueEnum::Static(s) = v {
Some((i, s.clone()))
} else {
@ -636,7 +691,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
};
param_vals = real_params
.into_iter()
.map(|p| p.to_basic_value_enum(ctx, generator))
.map(|(p, t)| p.to_basic_value_enum(ctx, generator, t))
.collect::<Result<Vec<_>, String>>()?;
instance_to_symbol.get(&key).cloned().ok_or_else(|| "".into())
}
@ -749,7 +804,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
let Comprehension { target, iter, ifs, .. } = &generators[0];
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?;
let int32 = ctx.ctx.i32_type();
let size_t = generator.get_size_type(ctx.ctx);
let zero_size_t = size_t.const_zero();
@ -854,7 +909,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let result = generator
.gen_expr(ctx, cond)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?
.into_int_value();
let succ = ctx.ctx.append_basic_block(current, "then");
ctx.builder.build_conditional_branch(result, succ, test_bb);
@ -863,7 +918,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let elem = generator.gen_expr(ctx, elt)?.unwrap();
let i = ctx.builder.build_load(index, "i").into_int_value();
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
let val = elem.to_basic_value_enum(ctx, generator)?;
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
ctx.builder.build_store(elem_ptr, val);
ctx.builder
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
@ -885,41 +940,94 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
left: &Expr<Option<Type>>,
op: &Operator,
right: &Expr<Option<Type>>,
) -> Result<ValueEnum<'ctx>, String> {
loc: Location,
is_aug_assign: bool,
) -> Result<Option<ValueEnum<'ctx>>, String> {
let ty1 = ctx.unifier.get_representative(left.custom.unwrap());
let ty2 = ctx.unifier.get_representative(right.custom.unwrap());
let left = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(ctx, generator)?;
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator)?;
let left_val = generator
.gen_expr(ctx, left)?
.unwrap()
.to_basic_value_enum(ctx, generator, left.custom.unwrap())?;
let right_val = generator
.gen_expr(ctx, right)?
.unwrap()
.to_basic_value_enum(ctx, generator, right.custom.unwrap())?;
// we can directly compare the types, because we've got their representatives
// which would be unchanged until further unification, which we would never do
// when doing code generation for function instances
Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
ctx.gen_int_ops(op, left, right, true)
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
Ok(Some(ctx.gen_int_ops(generator, op, left_val, right_val, true).into()))
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
ctx.gen_int_ops(op, left, right, false)
Ok(Some(ctx.gen_int_ops(generator, op, left_val, right_val, false).into()))
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
ctx.gen_float_ops(op, left, right)
Ok(Some(ctx.gen_float_ops(op, left_val, right_val).into()))
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int
assert!(*op == Operator::Pow);
// TODO: throw exception when rhs is out of i16 bound
// since llvm intrinsic only support to i16 for f64
let i16_t = ctx.ctx.i16_type();
let pow_intr = ctx.module.get_function("llvm.powi.f64.i16").unwrap_or_else(|| {
let i32_t = ctx.ctx.i32_type();
let pow_intr = ctx.module.get_function("llvm.powi.f64.i32").unwrap_or_else(|| {
let f64_t = ctx.ctx.f64_type();
let ty = f64_t.fn_type(&[f64_t.into(), i16_t.into()], false);
ctx.module.add_function("llvm.powi.f64.i16", ty, None)
let ty = f64_t.fn_type(&[f64_t.into(), i32_t.into()], false);
ctx.module.add_function("llvm.powi.f64.i32", ty, None)
});
let right = ctx.builder.build_int_truncate(right.into_int_value(), i16_t, "r_pow");
ctx.builder
.build_call(pow_intr, &[left.into(), right.into()], "f_pow_i")
let res = ctx.builder
.build_call(pow_intr, &[left_val.into(), right_val.into()], "f_pow_i")
.try_as_basic_value()
.unwrap_left()
.unwrap_left();
Ok(Some(res.into()))
} else {
unimplemented!()
let (op_name, id) = if let TypeEnum::TObj { fields, obj_id, .. } =
ctx.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
{
let (binop_name, binop_assign_name) = (
binop_name(op).into(),
binop_assign_name(op).into()
);
// if is aug_assign, try aug_assign operator first
if is_aug_assign && fields.contains_key(&binop_assign_name) {
(binop_assign_name, *obj_id)
} else {
(binop_name, *obj_id)
}
} else {
unreachable!("must be tobj")
};
let signature = match ctx.calls.get(&loc.into()) {
Some(call) => ctx.unifier.get_call_signature(*call).unwrap(),
None => {
if let TypeEnum::TObj { fields, .. } =
ctx.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
{
let fn_ty = fields.get(&op_name).unwrap().0;
if let TypeEnum::TFunc(sig) = ctx.unifier.get_ty_immutable(fn_ty).as_ref() {
sig.clone()
} else {
unreachable!("must be func sig")
}
} else {
unreachable!("must be tobj")
}
},
};
let fun_id = {
let defs = ctx.top_level.definitions.read();
let obj_def = defs.get(id.0).unwrap().read();
if let TopLevelDef::Class { methods, .. } = &*obj_def {
methods.iter().find(|method| method.0 == op_name).unwrap().2
} else {
unreachable!()
}
};
generator
.gen_call(
ctx,
Some((left.custom.unwrap(), left_val.into())),
(&signature, fun_id),
vec![(None, right_val.into())],
).map(|f| f.map(|f| f.into()))
}
.into())
}
pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
@ -927,31 +1035,46 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, 'a>,
expr: &Expr<Option<Type>>,
) -> Result<Option<ValueEnum<'ctx>>, String> {
ctx.current_loc = expr.location;
let int32 = ctx.ctx.i32_type();
let zero = int32.const_int(0, false);
let loc = ctx.debug_info.0.create_debug_location(
ctx.ctx,
ctx.current_loc.row as u32,
ctx.current_loc.column as u32,
ctx.debug_info.2,
None,
);
ctx.builder.set_current_debug_location(ctx.ctx, loc);
Ok(Some(match &expr.node {
ExprKind::Constant { value, .. } => {
let ty = expr.custom.unwrap();
ctx.gen_const(generator, value, ty).into()
}
ExprKind::Name { id, .. } if id == &"none".into() => {
match (
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
) {
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
_ => unreachable!("must be option type"),
}
}
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
None => {
let resolver = ctx.resolver.clone();
let val = resolver.get_symbol_value(*id, ctx).unwrap();
// if is tuple, need to deref it to handle tuple as value
if let (TypeEnum::TTuple { .. }, BasicValueEnum::PointerValue(ptr)) = (
&*ctx.unifier.get_ty(expr.custom.unwrap()),
resolver
.get_symbol_value(*id, ctx)
.unwrap()
.to_basic_value_enum(ctx, generator)?,
) {
ctx.builder.build_load(ptr, "tup_val").into()
} else {
val
}
resolver.get_symbol_value(*id, ctx).unwrap()
}
},
ExprKind::List { elts, .. } => {
@ -962,7 +1085,10 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(|e| Err(e), |v| v.unwrap().to_basic_value_enum(ctx, generator))
.map_or_else(
Err,
|v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
)
})
.collect::<Result<Vec<_>, _>>()?;
let ty = if elements.is_empty() {
@ -995,7 +1121,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(|e| Err(e), |v| v.unwrap().to_basic_value_enum(ctx, generator))
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap()))
})
.collect::<Result<Vec<_>, _>>()?;
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
@ -1017,13 +1143,13 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
// note that we would handle class methods directly in calls
match generator.gen_expr(ctx, value)?.unwrap() {
ValueEnum::Static(v) => v.get_field(*attr, ctx).map_or_else(|| {
let v = v.to_basic_value_enum(ctx, generator)?;
let v = v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?;
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)],
))) as Result<_, String>
}, |v| Ok(v))?,
}, Ok)?,
ValueEnum::Dynamic(v) => {
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
ValueEnum::Dynamic(ctx.build_gep_and_load(
@ -1038,7 +1164,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let left = generator
.gen_expr(ctx, &values[0])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[0].custom.unwrap())?
.into_int_value();
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let a_bb = ctx.ctx.append_basic_block(current, "a");
@ -1054,7 +1180,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let b = generator
.gen_expr(ctx, &values[1])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
.into_int_value();
ctx.builder.build_unconditional_branch(cont_bb);
(a, b)
@ -1064,7 +1190,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let a = generator
.gen_expr(ctx, &values[1])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
.into_int_value();
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(b_bb);
@ -1078,11 +1204,15 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
phi.add_incoming(&[(&a, a_bb), (&b, b_bb)]);
phi.as_basic_value().into()
}
ExprKind::BinOp { op, left, right } => gen_binop_expr(generator, ctx, left, op, right)?,
ExprKind::BinOp { op, left, right } => {
return gen_binop_expr(generator, ctx, left, op, right, expr.location, false);
}
ExprKind::UnaryOp { op, operand } => {
let ty = ctx.unifier.get_representative(operand.custom.unwrap());
let val =
generator.gen_expr(ctx, operand)?.unwrap().to_basic_value_enum(ctx, generator)?;
generator.gen_expr(ctx, operand)?
.unwrap()
.to_basic_value_enum(ctx, generator, operand.custom.unwrap())?;
if ty == ctx.primitives.bool {
let val = val.into_int_value();
match op {
@ -1142,11 +1272,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
) {
(lhs, rhs)
} else {
@ -1170,11 +1300,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
) {
(lhs, rhs)
} else {
@ -1202,23 +1332,46 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let test = generator
.gen_expr(ctx, test)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, test.custom.unwrap())?
.into_int_value();
let body_ty = body.custom.unwrap();
let is_none = ctx.unifier.get_representative(body_ty) == ctx.primitives.none;
let result = if !is_none {
let llvm_ty = ctx.get_llvm_type(generator, body_ty);
Some(ctx.builder.build_alloca(llvm_ty, "if_exp_result"))
} else {
None
};
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_bb = ctx.ctx.append_basic_block(current, "then");
let else_bb = ctx.ctx.append_basic_block(current, "else");
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(test, then_bb, else_bb);
ctx.builder.position_at_end(then_bb);
let a = generator.gen_expr(ctx, body)?.unwrap().to_basic_value_enum(ctx, generator)?;
let a = generator.gen_expr(ctx, body)?;
match result {
None => None,
Some(v) => {
let a = a.unwrap().to_basic_value_enum(ctx, generator, body.custom.unwrap())?;
Some(ctx.builder.build_store(v, a))
}
};
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(else_bb);
let b = generator.gen_expr(ctx, orelse)?.unwrap().to_basic_value_enum(ctx, generator)?;
let b = generator.gen_expr(ctx, orelse)?;
match result {
None => None,
Some(v) => {
let b = b.unwrap().to_basic_value_enum(ctx, generator, orelse.custom.unwrap())?;
Some(ctx.builder.build_store(v, b))
}
};
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(cont_bb);
let phi = ctx.builder.build_phi(a.get_type(), "ifexpr");
phi.add_incoming(&[(&a, then_bb), (&b, else_bb)]);
phi.as_basic_value().into()
match result {
None => return Ok(None),
Some(v) => return Ok(Some(ctx.builder.build_load(v, "if_exp_val_load").into()))
}
}
ExprKind::Call { func, args, keywords } => {
let mut params = args
@ -1270,17 +1423,74 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let defs = ctx.top_level.definitions.read();
let obj_def = defs.get(id.0).unwrap().read();
if let TopLevelDef::Class { methods, .. } = &*obj_def {
let mut fun_id = None;
for (name, _, id) in methods.iter() {
if name == attr {
fun_id = Some(*id);
}
}
fun_id.unwrap()
methods.iter().find(|method| method.0 == *attr).unwrap().2
} else {
unreachable!()
}
};
// directly generate code for option.unwrap
// since it needs to return static value to optimize for kernel invariant
if attr == &"unwrap".into()
&& id == ctx.primitives.option.get_obj_id(&ctx.unifier)
{
match val {
ValueEnum::Static(v) => match v.get_field("_nac3_option".into(), ctx) {
// if is none, raise exception directly
None => {
let err_msg = ctx.gen_string(generator, "");
let current_fun = ctx
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let unreachable_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_unreachable"
);
let exn_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_exception"
);
ctx.builder.build_unconditional_branch(exn_block);
ctx.builder.position_at_end(exn_block);
ctx.raise_exn(
generator,
"0:UnwrapNoneError",
err_msg,
[None, None, None],
ctx.current_loc
);
ctx.builder.position_at_end(unreachable_block);
let ptr = ctx
.get_llvm_type(generator, value.custom.unwrap())
.into_pointer_type()
.const_null();
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_none_unreachable_load"
).into()));
}
Some(v) => return Ok(Some(v)),
}
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let not_null = ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
ctx.make_assert(
generator,
not_null,
"0:UnwrapNoneError",
"",
[None, None, None],
expr.location,
);
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_some_load"
).into()))
}
_ => unreachable!("option must be static or ptr")
}
}
return Ok(generator
.gen_call(
ctx,
@ -1298,7 +1508,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value();
let ty = ctx.get_llvm_type(generator, *ty);
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]).into_pointer_value();
@ -1307,6 +1517,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let (start, end, step) =
handle_slice_indices(lower, upper, step, ctx, generator, v)?;
let length = calculate_len_for_slice_range(
generator,
ctx,
start,
ctx.builder
@ -1328,8 +1539,8 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let res_ind =
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?;
list_slice_assignment(
generator,
ctx,
generator.get_size_type(ctx.ctx),
ty,
res_array_ret,
res_ind,
@ -1344,7 +1555,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let raw_index = generator
.gen_expr(ctx, slice)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value();
let raw_index = ctx.builder.build_int_s_extend(
raw_index,
@ -1379,26 +1590,39 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
[Some(raw_index), Some(len), None],
expr.location,
);
ctx.build_gep_and_load(arr_ptr, &[index])
ctx.build_gep_and_load(arr_ptr, &[index]).into()
}
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.into_struct_value();
let index: u32 =
if let ExprKind::Constant { value: ast::Constant::Int(v), .. } = &slice.node {
(*v).try_into().unwrap()
} else {
unreachable!("tuple subscript must be const int after type check");
};
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap()
let v = generator
.gen_expr(ctx, value)?
.unwrap();
match v {
ValueEnum::Dynamic(v) => {
let v = v.into_struct_value();
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
}
ValueEnum::Static(v) => {
match v.get_tuple_element(index) {
Some(v) => v,
None => {
let tup = v
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_struct_value();
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
}
}
}
}
} else {
unreachable!("should not be other subscriptable types after type check");
}
}
.into(),
},
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr)?.into(),
_ => unimplemented!(),
}))

View File

@ -36,7 +36,7 @@ pub trait CodeGenerator {
}
/// Generate object constructor and returns the constructed object.
/// - signature: Function signature of the contructor.
/// - signature: Function signature of the constructor.
/// - def: Class definition for the constructor class.
/// - params: Function parameters.
fn gen_constructor<'ctx, 'a>(

View File

@ -6,7 +6,7 @@ use inkwell::{
context::Context,
memory_buffer::MemoryBuffer,
module::Module,
types::{BasicTypeEnum, IntType},
types::BasicTypeEnum,
values::{IntValue, PointerValue},
AddressSpace, IntPredicate,
};
@ -34,6 +34,7 @@ pub fn load_irrt(ctx: &Context) -> Module {
// repeated squaring method adapted from GNU Scientific Library:
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
pub fn integer_power<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
base: IntValue<'ctx>,
exp: IntValue<'ctx>,
@ -51,7 +52,21 @@ pub fn integer_power<'ctx, 'a>(
let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false);
ctx.module.add_function(symbol, fn_type, None)
});
// TODO: throw exception when exp < 0
// throw exception when exp < 0
let ge_zero = ctx.builder.build_int_compare(
IntPredicate::SGE,
exp,
exp.get_type().const_zero(),
"assert_int_pow_ge_0",
);
ctx.make_assert(
generator,
ge_zero,
"0:ValueError",
"integer power must be positive or zero",
[None, None, None],
ctx.current_loc,
);
ctx.builder
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
.try_as_basic_value()
@ -60,6 +75,7 @@ pub fn integer_power<'ctx, 'a>(
}
pub fn calculate_len_for_slice_range<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
start: IntValue<'ctx>,
end: IntValue<'ctx>,
@ -72,7 +88,21 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
ctx.module.add_function(SYMBOL, fn_t, None)
});
// TODO: assert step != 0, throw exception if not
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"step must not be zero",
[None, None, None],
ctx.current_loc,
);
ctx.builder
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
.try_as_basic_value()
@ -129,7 +159,6 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
generator: &mut G,
list: PointerValue<'ctx>,
) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> {
// TODO: throw exception when step is 0
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let one = int32.const_int(1, false);
@ -154,8 +183,23 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
let step = generator
.gen_expr(ctx, step)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?
.into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"slice step cannot be zero",
[None, None, None],
ctx.current_loc,
);
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1");
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg");
(
@ -217,7 +261,7 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator)?;
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator, i.custom.unwrap())?;
Ok(ctx
.builder
.build_call(func, &[i.into(), length.into()], "bounded_ind")
@ -231,14 +275,15 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
/// Negative index should be handled before entering this function
pub fn list_slice_assignment<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
size_ty: IntType<'ctx>,
ty: BasicTypeEnum<'ctx>,
dest_arr: PointerValue<'ctx>,
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
src_arr: PointerValue<'ctx>,
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
) {
let size_ty = generator.get_size_type(ctx.ctx);
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic);
let int32 = ctx.ctx.i32_type();
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
@ -282,8 +327,67 @@ pub fn list_slice_assignment<'ctx, 'a>(
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
// index in bound and positive should be done
// TODO: 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
// throw exception if not satisfied
let src_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
src_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(src_idx.1, one, "e_add_one"),
"final_e",
)
.into_int_value();
let dest_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
dest_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one"),
"final_e",
)
.into_int_value();
let src_slice_len =
calculate_len_for_slice_range(generator, ctx, src_idx.0, src_end, src_idx.2);
let dest_slice_len =
calculate_len_for_slice_range(generator, ctx, dest_idx.0, dest_end, dest_idx.2);
let src_eq_dest = ctx.builder.build_int_compare(
IntPredicate::EQ,
src_slice_len,
dest_slice_len,
"slice_src_eq_dest",
);
let src_slt_dest = ctx.builder.build_int_compare(
IntPredicate::SLT,
src_slice_len,
dest_slice_len,
"slice_src_slt_dest",
);
let dest_step_eq_one = ctx.builder.build_int_compare(
IntPredicate::EQ,
dest_idx.2,
dest_idx.2.get_type().const_int(1, false),
"slice_dest_step_eq_one",
);
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1");
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond");
ctx.make_assert(
generator,
cond,
"0:ValueError",
"attempt to assign sequence of size {0} to slice of size {1} with step size {2}",
[Some(src_slice_len), Some(dest_slice_len), Some(dest_idx.2)],
ctx.current_loc,
);
let new_len = {
let args = vec![
dest_idx.0.into(), // dest start idx

View File

@ -17,10 +17,13 @@ use inkwell::{
module::Module,
passes::{PassManager, PassManagerBuilder},
types::{AnyType, BasicType, BasicTypeEnum},
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue}
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
debug_info::{
DebugInfoBuilder, DICompileUnit, DISubprogram, AsDIScope, DIFlagsConstants, DIScope
},
};
use itertools::Itertools;
use nac3parser::ast::{Stmt, StrRef};
use nac3parser::ast::{Stmt, StrRef, Location};
use parking_lot::{Condvar, Mutex};
use std::collections::{HashMap, HashSet};
use std::sync::{
@ -52,6 +55,7 @@ pub type VarValue<'ctx> = (PointerValue<'ctx>, Option<Arc<dyn StaticValue + Send
pub struct CodeGenContext<'ctx, 'a> {
pub ctx: &'ctx Context,
pub builder: Builder<'ctx>,
pub debug_info: (DebugInfoBuilder<'ctx>, DICompileUnit<'ctx>, DIScope<'ctx>),
pub module: Module<'ctx>,
pub top_level: &'a TopLevelContext,
pub unifier: Unifier,
@ -77,6 +81,7 @@ pub struct CodeGenContext<'ctx, 'a> {
pub outer_catch_clauses:
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
pub need_sret: bool,
pub current_loc: Location,
}
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
@ -200,7 +205,18 @@ impl WorkerRegistry {
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
let context = Context::create();
let mut builder = context.create_builder();
let module = context.create_module(generator.get_name());
let mut module = context.create_module(generator.get_name());
module.add_basic_value_flag(
"Debug Info Version",
inkwell::module::FlagBehavior::Warning,
context.i32_type().const_int(3, false),
);
module.add_basic_value_flag(
"Dwarf Version",
inkwell::module::FlagBehavior::Warning,
context.i32_type().const_int(4, false),
);
let pass_builder = PassManagerBuilder::create();
pass_builder.set_optimization_level(OptimizationLevel::Default);
@ -209,16 +225,17 @@ impl WorkerRegistry {
let mut errors = HashSet::new();
while let Some(task) = self.receiver.recv().unwrap() {
let tmp_module = context.create_module("tmp");
match gen_func(&context, generator, self, builder, tmp_module, task) {
match gen_func(&context, generator, self, builder, module, task) {
Ok(result) => {
builder = result.0;
passes.run_on(&result.2);
module.link_in_module(result.1).unwrap();
module = result.1;
}
Err((old_builder, e)) => {
builder = old_builder;
errors.insert(e);
// create a new empty module just to continue codegen and collect errors
module = context.create_module(&format!("{}_recover", generator.get_name()));
}
}
*self.task_count.lock() -= 1;
@ -231,7 +248,7 @@ impl WorkerRegistry {
let result = module.verify();
if let Err(err) = result {
println!("{}", module.print_to_string().to_str().unwrap());
println!("{}", err);
println!("{}", err.to_string());
panic!()
}
f.run(&module);
@ -255,10 +272,12 @@ pub struct CodeGenTask {
fn get_llvm_type<'ctx>(
ctx: &'ctx Context,
module: &Module<'ctx>,
generator: &mut dyn CodeGenerator,
unifier: &mut Unifier,
top_level: &TopLevelContext,
type_cache: &mut HashMap<Type, BasicTypeEnum<'ctx>>,
primitives: &PrimitiveStore,
ty: Type,
) -> BasicTypeEnum<'ctx> {
use TypeEnum::*;
@ -268,33 +287,64 @@ fn get_llvm_type<'ctx>(
let ty_enum = unifier.get_ty(ty);
let result = match &*ty_enum {
TObj { obj_id, fields, .. } => {
// check to avoid treating primitives as classes
if obj_id.0 <= 7 {
unreachable!();
// check to avoid treating primitives other than Option as classes
if obj_id.0 <= 10 {
match (unifier.get_ty(ty).as_ref(), unifier.get_ty(primitives.option).as_ref())
{
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => {
return get_llvm_type(
ctx,
module,
generator,
unifier,
top_level,
type_cache,
primitives,
*params.iter().next().unwrap().1,
)
.ptr_type(AddressSpace::Generic)
.into();
}
_ => unreachable!("must be option type"),
}
}
// a struct with fields in the order of declaration
let top_level_defs = top_level.definitions.read();
let definition = top_level_defs.get(obj_id.0).unwrap();
let ty = if let TopLevelDef::Class { name, fields: fields_list, .. } =
let ty = if let TopLevelDef::Class { fields: fields_list, .. } =
&*definition.read()
{
let struct_type = ctx.opaque_struct_type(&name.to_string());
type_cache.insert(unifier.get_representative(ty), struct_type.ptr_type(AddressSpace::Generic).into());
let name = unifier.stringify(ty);
match module.get_struct_type(&name) {
Some(t) => t.ptr_type(AddressSpace::Generic).into(),
None => {
let struct_type = ctx.opaque_struct_type(&name);
type_cache.insert(
unifier.get_representative(ty),
struct_type.ptr_type(AddressSpace::Generic).into()
);
let fields = fields_list
.iter()
.map(|f| {
get_llvm_type(
ctx,
module,
generator,
unifier,
top_level,
type_cache,
primitives,
fields[&f.0].0,
)
})
.collect_vec();
struct_type.set_body(&fields, false);
struct_type.ptr_type(AddressSpace::Generic).into()
}
}
} else {
unreachable!()
};
@ -304,14 +354,19 @@ fn get_llvm_type<'ctx>(
// a struct with fields in the order present in the tuple
let fields = ty
.iter()
.map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty))
.map(|ty| {
get_llvm_type(
ctx, module, generator, unifier, top_level, type_cache, primitives, *ty,
)
})
.collect_vec();
ctx.struct_type(&fields, false).into()
}
TList { ty } => {
// a struct with an integer and a pointer to an array
let element_type =
get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty);
let element_type = get_llvm_type(
ctx, module, generator, unifier, top_level, type_cache, primitives, *ty,
);
let fields = [
element_type.ptr_type(AddressSpace::Generic).into(),
generator.get_size_type(ctx).into(),
@ -339,13 +394,14 @@ fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool {
need_sret_impl(ctx, ty, true)
}
pub fn gen_func<'ctx, G: CodeGenerator>(
pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenContext) -> Result<(), String>> (
context: &'ctx Context,
generator: &mut G,
registry: &WorkerRegistry,
builder: Builder<'ctx>,
module: Module<'ctx>,
task: CodeGenTask,
codegen_function: F
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let top_level_ctx = registry.top_level_ctx.clone();
let static_value_store = registry.static_value_store.clone();
@ -385,6 +441,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
range: unifier.get_representative(primitives.range),
str: unifier.get_representative(primitives.str),
exception: unifier.get_representative(primitives.exception),
option: unifier.get_representative(primitives.option),
};
let mut type_cache: HashMap<_, _> = [
@ -395,6 +452,9 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
(primitives.float, context.f64_type().into()),
(primitives.bool, context.bool_type().into()),
(primitives.str, {
let name = "str";
match module.get_struct_type(name) {
None => {
let str_type = context.opaque_struct_type("str");
let fields = [
context.i8_type().ptr_type(AddressSpace::Generic).into(),
@ -402,21 +462,32 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
];
str_type.set_body(&fields, false);
str_type.into()
}
Some(t) => t.as_basic_type_enum()
}
}),
(primitives.range, context.i32_type().array_type(3).ptr_type(AddressSpace::Generic).into()),
(primitives.exception, {
let name = "Exception";
match module.get_struct_type(name) {
Some(t) => t.ptr_type(AddressSpace::Generic).as_basic_type_enum(),
None => {
let exception = context.opaque_struct_type("Exception");
let int32 = context.i32_type().into();
let int64 = context.i64_type().into();
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
exception.set_body(&fields, false);
exception.ptr_type(AddressSpace::Generic).as_basic_type_enum()
}
}
})
]
.iter()
.cloned()
.collect();
type_cache.insert(primitives.exception, {
let exception = context.opaque_struct_type("Exception");
let int32 = context.i32_type().into();
let int64 = context.i64_type().into();
let str_ty = *type_cache.get(&primitives.str).unwrap();
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
exception.set_body(&fields, false);
exception.ptr_type(AddressSpace::Generic).into()
});
// NOTE: special handling of option cannot use this type cache since it contains type var,
// handled inside get_llvm_type instead
let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
task.store.get(task.signature)
@ -437,7 +508,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
let ret_type = if unifier.unioned(ret, primitives.none) {
None
} else {
Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, ret))
Some(get_llvm_type(context, &module, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret))
};
let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty));
@ -446,10 +517,12 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
.map(|arg| {
get_llvm_type(
context,
&module,
generator,
&mut unifier,
top_level_ctx.as_ref(),
&mut type_cache,
&primitives,
arg.ty,
)
.into()
@ -493,10 +566,12 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
let alloca = builder.build_alloca(
get_llvm_type(
context,
&module,
generator,
&mut unifier,
top_level_ctx.as_ref(),
&mut type_cache,
&primitives,
arg.ty,
),
&arg.name.to_string(),
@ -523,6 +598,58 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
builder.build_unconditional_branch(body_bb);
builder.position_at_end(body_bb);
let (dibuilder, compile_unit) = module.create_debug_info_builder(
/* allow_unresolved */ true,
/* language */ inkwell::debug_info::DWARFSourceLanguage::Python,
/* filename */
&task
.body
.get(0)
.map_or_else(
|| "<nac3_internal>".to_string(),
|f| f.location.file.0.to_string(),
),
/* directory */ "",
/* producer */ "NAC3",
/* is_optimized */ true,
/* compiler command line flags */ "",
/* runtime_ver */ 0,
/* split_name */ "",
/* kind */ inkwell::debug_info::DWARFEmissionKind::Full,
/* dwo_id */ 0,
/* split_debug_inling */ true,
/* debug_info_for_profiling */ false,
/* sysroot */ "",
/* sdk */ "",
);
let subroutine_type = dibuilder.create_subroutine_type(
compile_unit.get_file(),
Some(
dibuilder
.create_basic_type("_", 0_u64, 0x00, inkwell::debug_info::DIFlags::PUBLIC)
.unwrap()
.as_type(),
),
&[],
inkwell::debug_info::DIFlags::PUBLIC,
);
let (row, col) =
task.body.get(0).map_or_else(|| (0, 0), |b| (b.location.row, b.location.column));
let func_scope: DISubprogram<'_> = dibuilder.create_function(
/* scope */ compile_unit.as_debug_info_scope(),
/* func name */ symbol,
/* linkage_name */ None,
/* file */ compile_unit.get_file(),
/* line_no */ row as u32,
/* DIType */ subroutine_type,
/* is_local_to_unit */ false,
/* is_definition */ true,
/* scope_line */ row as u32,
/* flags */ inkwell::debug_info::DIFlags::PUBLIC,
/* is_optimized */ true,
);
fn_val.set_subprogram(func_scope);
let mut code_gen_context = CodeGenContext {
ctx: context,
resolver: task.resolver,
@ -543,28 +670,51 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
module,
unifier,
static_value_store,
need_sret: has_sret
need_sret: has_sret,
current_loc: Default::default(),
debug_info: (dibuilder, compile_unit, func_scope.as_debug_info_scope()),
};
let mut err = None;
for stmt in task.body.iter() {
if let Err(e) = generator.gen_stmt(&mut code_gen_context, stmt) {
err = Some(e);
break;
}
if code_gen_context.is_terminated() {
break;
}
}
let loc = code_gen_context.debug_info.0.create_debug_location(
context,
row as u32,
col as u32,
func_scope.as_debug_info_scope(),
None
);
code_gen_context.builder.set_current_debug_location(context, loc);
let result = codegen_function(generator, &mut code_gen_context);
// after static analysis, only void functions can have no return at the end.
if !code_gen_context.is_terminated() {
code_gen_context.builder.build_return(None);
}
code_gen_context.builder.unset_current_debug_location();
code_gen_context.debug_info.0.finalize();
let CodeGenContext { builder, module, .. } = code_gen_context;
if let Some(e) = err {
if let Err(e) = result {
return Err((builder, e));
}
Ok((builder, module, fn_val))
}
pub fn gen_func<'ctx, G: CodeGenerator>(
context: &'ctx Context,
generator: &mut G,
registry: &WorkerRegistry,
builder: Builder<'ctx>,
module: Module<'ctx>,
task: CodeGenTask,
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let body = task.body.clone();
gen_func_impl(context, generator, registry, builder, module, task, |generator, ctx| {
for stmt in body.iter() {
generator.gen_stmt(ctx, stmt)?;
}
Ok(())
})
}

View File

@ -42,19 +42,26 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
// very similar to gen_expr, but we don't do an extra load at the end
// and we flatten nested tuples
Ok(match &pattern.node {
ExprKind::Name { id, .. } => {
ctx.var_assignment.get(id).map(|v| Ok(v.0) as Result<_, String>).unwrap_or_else(
|| {
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
None => {
let ptr_ty = ctx.get_llvm_type(generator, pattern.custom.unwrap());
let ptr = generator.gen_var_alloc(ctx, ptr_ty)?;
ctx.var_assignment.insert(*id, (ptr, None, 0));
Ok(ptr)
},
)?
ptr
}
Some(v) => {
let (ptr, counter) = (v.0, v.2);
ctx.var_assignment.insert(*id, (ptr, None, counter));
ptr
}
}
ExprKind::Attribute { value, attr, .. } => {
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(ctx, generator)?;
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(
ctx,
generator,
value.custom.unwrap(),
)?;
let ptr = if let BasicValueEnum::PointerValue(v) = val {
v
} else {
@ -72,17 +79,58 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
}
}
ExprKind::Subscript { value, slice, .. } => {
assert!(matches!(
ctx.unifier.get_ty_immutable(value.custom.unwrap()).as_ref(),
TypeEnum::TList { .. },
));
let i32_type = ctx.ctx.i32_type();
let zero = i32_type.const_zero();
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value();
let index = generator
let len = ctx
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)])
.into_int_value();
let raw_index = generator
.gen_expr(ctx, slice)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value();
let raw_index = ctx.builder.build_int_s_extend(
raw_index,
generator.get_size_type(ctx.ctx),
"sext",
);
// handle negative index
let is_negative = ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
raw_index,
generator.get_size_type(ctx.ctx).const_zero(),
"is_neg",
);
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
let index = ctx
.builder
.build_select(is_negative, adjusted, raw_index, "index")
.into_int_value();
// unsigned less than is enough, because negative index after adjustment is
// bigger than the length (for unsigned cmp)
let bound_check = ctx.builder.build_int_compare(
inkwell::IntPredicate::ULT,
index,
len,
"inbound",
);
ctx.make_assert(
generator,
bound_check,
"0:IndexError",
"index {0} out of bounds 0:{1}",
[Some(raw_index), Some(len), None],
slice.location,
);
unsafe {
let arr_ptr = ctx
.build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()])
@ -102,7 +150,9 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
) -> Result<(), String> {
match &target.node {
ExprKind::Tuple { elts, .. } => {
if let BasicValueEnum::StructValue(v) = value.to_basic_value_enum(ctx, generator)? {
if let BasicValueEnum::StructValue(v) =
value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
{
for (i, elt) in elts.iter().enumerate() {
let v = ctx
.builder
@ -121,11 +171,13 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
let ls = generator
.gen_expr(ctx, ls)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, ls.custom.unwrap())?
.into_pointer_value();
let (start, end, step) =
handle_slice_indices(lower, upper, step, ctx, generator, ls)?;
let value = value.to_basic_value_enum(ctx, generator)?.into_pointer_value();
let value = value
.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
.into_pointer_value();
let ty =
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(target.custom.unwrap()) {
ctx.get_llvm_type(generator, *ty)
@ -133,15 +185,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
list_slice_assignment(
ctx,
generator.get_size_type(ctx.ctx),
ty,
ls,
(start, end, step),
value,
src_ind,
)
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind)
} else {
unreachable!()
}
@ -155,7 +199,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
*static_value = Some(s.clone());
}
}
let val = value.to_basic_value_enum(ctx, generator)?;
let val = value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?;
ctx.builder.build_store(ptr, val);
}
};
@ -185,11 +229,16 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
// store loop bb information and restore it later
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(
ctx,
generator,
iter.custom.unwrap(),
)?;
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
// setup
let iter_val = iter_val.into_pointer_value();
let i = generator.gen_store_target(ctx, target)?;
let i = generator.gen_var_alloc(ctx, int32.into())?;
let user_i = generator.gen_store_target(ctx, target)?;
let (start, end, step) = destructure_range(ctx, iter_val);
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
ctx.builder.build_unconditional_branch(test_bb);
@ -207,6 +256,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
"start_loop",
);
ctx.builder.build_store(i, tmp);
ctx.builder.build_store(user_i, tmp);
// // if step > 0, continue when i < end
let cmp1 = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, end, "cmp1");
// if step < 0, continue when i > end
@ -296,7 +346,11 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb);
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
if let BasicValueEnum::IntValue(test) = test {
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
} else {
@ -357,7 +411,11 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
};
ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb);
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
if let BasicValueEnum::IntValue(test) = test {
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
} else {
@ -420,8 +478,8 @@ pub fn final_proxy<'ctx, 'a>(
final_paths.push(block);
}
pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
pub fn get_builtins<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
symbol: &str,
) -> FunctionValue<'ctx> {
@ -439,7 +497,7 @@ pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
if symbol == "__nac3_raise" || symbol == "__nac3_resume" {
fun.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 1),
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0),
);
}
fun
@ -454,7 +512,7 @@ pub fn exn_constructor<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let (zelf_ty, zelf) = obj.unwrap();
let zelf = zelf.to_basic_value_enum(ctx, generator)?.into_pointer_value();
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let zelf_id = {
@ -477,14 +535,14 @@ pub fn exn_constructor<'ctx, 'a>(
let ptr =
ctx.builder.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg");
let msg = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)?
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
} else {
empty_string
};
ctx.builder.build_store(ptr, msg);
for i in [6, 7, 8].iter() {
let value = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)?
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
} else {
ctx.ctx.i64_type().const_zero().into()
};
@ -517,8 +575,8 @@ pub fn exn_constructor<'ctx, 'a>(
Ok(Some(zelf.into()))
}
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
pub fn gen_raise<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
exception: Option<&BasicValueEnum<'ctx>>,
loc: Location,
@ -906,7 +964,11 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
) -> Result<(), String> {
let value = value
.as_ref()
.map(|v| generator.gen_expr(ctx, v).and_then(|v| v.unwrap().to_basic_value_enum(ctx, generator)))
.map(|v_expr| {
generator.gen_expr(ctx, v_expr).and_then(|v| {
v.unwrap().to_basic_value_enum(ctx, generator, v_expr.custom.unwrap())
})
})
.transpose()?;
if let Some(return_target) = ctx.return_target {
if let Some(value) = value {
@ -929,6 +991,17 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String> {
ctx.current_loc = stmt.location;
let loc = ctx.debug_info.0.create_debug_location(
ctx.ctx,
ctx.current_loc.row as u32,
ctx.current_loc.column as u32,
ctx.debug_info.2,
None,
);
ctx.builder.set_current_debug_location(ctx.ctx, loc);
match &stmt.node {
StmtKind::Pass { .. } => {}
StmtKind::Expr { value, .. } => {
@ -960,20 +1033,46 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
StmtKind::For { .. } => generator.gen_for(ctx, stmt)?,
StmtKind::With { .. } => generator.gen_with(ctx, stmt)?,
StmtKind::AugAssign { target, op, value, .. } => {
let value = gen_binop_expr(generator, ctx, target, op, value)?;
generator.gen_assign(ctx, target, value)?;
let value = gen_binop_expr(generator, ctx, target, op, value, stmt.location, true)?;
generator.gen_assign(ctx, target, value.unwrap())?;
}
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
StmtKind::Raise { exc, .. } => {
if let Some(exc) = exc {
let exc =
generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(ctx, generator)?;
let exc = generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(
ctx,
generator,
exc.custom.unwrap(),
)?;
gen_raise(generator, ctx, Some(&exc), stmt.location);
} else {
gen_raise(generator, ctx, None, stmt.location);
}
}
_ => unimplemented!(),
StmtKind::Assert { test, msg, .. } => {
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
let err_msg = match msg {
Some(msg) => generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(
ctx,
generator,
msg.custom.unwrap(),
)?,
None => ctx.gen_string(generator, ""),
};
ctx.make_assert_impl(
generator,
test.into_int_value(),
"0:AssertionError",
err_msg,
[None, None, None],
stmt.location,
)
}
_ => unimplemented!()
};
Ok(())
}

View File

@ -71,7 +71,7 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_exception_id(&self, tyid: usize) -> usize {
fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!()
}
}
@ -181,13 +181,41 @@ fn test_primitives() {
; ModuleID = 'test'
source_filename = \"test\"
define i32 @testing(i32 %0, i32 %1) {
define i32 @testing(i32 %0, i32 %1) !dbg !4 {
init:
%add = add i32 %0, %1
%cmp = icmp eq i32 %add, 1
%ifexpr = select i1 %cmp, i32 %0, i32 0
ret i32 %ifexpr
%add = add i32 %0, %1, !dbg !9
%cmp = icmp eq i32 %add, 1, !dbg !10
br i1 %cmp, label %then, label %else, !dbg !10
then: ; preds = %init
br label %cont, !dbg !11
else: ; preds = %init
br label %cont, !dbg !12
cont: ; preds = %else, %then
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ], !dbg !13
ret i32 %if_exp_result.0, !dbg !14
}
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
!6 = !{!7}
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!8 = !{}
!9 = !DILocation(line: 1, column: 9, scope: !4)
!10 = !DILocation(line: 2, column: 15, scope: !4)
!11 = !DILocation(line: 2, column: 5, scope: !4)
!12 = !DILocation(line: 2, column: 22, scope: !4)
!13 = !DILocation(line: 0, scope: !4)
!14 = !DILocation(line: 3, column: 8, scope: !4)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
@ -333,18 +361,36 @@ fn test_simple_call() {
; ModuleID = 'test'
source_filename = \"test\"
define i32 @testing(i32 %0) {
define i32 @testing(i32 %0) !dbg !5 {
init:
%call = call i32 @foo.0(i32 %0)
%mul = mul i32 %call, 2
ret i32 %mul
%call = call i32 @foo.0(i32 %0), !dbg !10
%mul = mul i32 %call, 2, !dbg !11
ret i32 %mul, !dbg !11
}
define i32 @foo.0(i32 %0) {
define i32 @foo.0(i32 %0) !dbg !12 {
init:
%add = add i32 %0, 1
ret i32 %add
%add = add i32 %0, 1, !dbg !13
ret i32 %add, !dbg !13
}
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2, !4}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
!7 = !{!8}
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!9 = !{}
!10 = !DILocation(line: 1, column: 9, scope: !5)
!11 = !DILocation(line: 2, column: 12, scope: !5)
!12 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
!13 = !DILocation(line: 1, column: 12, scope: !12)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());

View File

@ -14,7 +14,7 @@ use crate::{
typedef::{Type, Unifier},
},
};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
use itertools::{chain, izip};
use nac3parser::ast::{Expr, Location, StrRef};
use parking_lot::RwLock;
@ -29,6 +29,8 @@ pub enum SymbolValue {
Double(f64),
Bool(bool),
Tuple(Vec<SymbolValue>),
OptionSome(Box<SymbolValue>),
OptionNone,
}
impl Display for SymbolValue {
@ -50,6 +52,8 @@ impl Display for SymbolValue {
SymbolValue::Tuple(t) => {
write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "))
}
SymbolValue::OptionSome(v) => write!(f, "Some({})", v),
SymbolValue::OptionNone => write!(f, "none"),
}
}
}
@ -67,6 +71,7 @@ pub trait StaticValue {
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String>;
fn get_field<'ctx, 'a>(
@ -74,6 +79,8 @@ pub trait StaticValue {
name: StrRef,
ctx: &mut CodeGenContext<'ctx, 'a>,
) -> Option<ValueEnum<'ctx>>;
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>>;
}
#[derive(Clone)]
@ -106,14 +113,21 @@ impl<'ctx> From<FloatValue<'ctx>> for ValueEnum<'ctx> {
}
}
impl<'ctx> From<StructValue<'ctx>> for ValueEnum<'ctx> {
fn from(v: StructValue<'ctx>) -> Self {
ValueEnum::Dynamic(v.into())
}
}
impl<'ctx> ValueEnum<'ctx> {
pub fn to_basic_value_enum<'a>(
self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String> {
match self {
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator),
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator, expected_ty),
ValueEnum::Dynamic(v) => Ok(v),
}
}
@ -153,12 +167,11 @@ pub trait SymbolResolver {
}
thread_local! {
static IDENTIFIER_ID: [StrRef; 12] = [
static IDENTIFIER_ID: [StrRef; 11] = [
"int32".into(),
"int64".into(),
"float".into(),
"bool".into(),
"None".into(),
"virtual".into(),
"list".into(),
"tuple".into(),
@ -183,14 +196,13 @@ pub fn parse_type_annotation<T>(
let int64_id = ids[1];
let float_id = ids[2];
let bool_id = ids[3];
let none_id = ids[4];
let virtual_id = ids[5];
let list_id = ids[6];
let tuple_id = ids[7];
let str_id = ids[8];
let exn_id = ids[9];
let uint32_id = ids[10];
let uint64_id = ids[11];
let virtual_id = ids[4];
let list_id = ids[5];
let tuple_id = ids[6];
let str_id = ids[7];
let exn_id = ids[8];
let uint32_id = ids[9];
let uint64_id = ids[10];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
if *id == int32_id {
@ -205,8 +217,6 @@ pub fn parse_type_annotation<T>(
Ok(primitives.float)
} else if *id == bool_id {
Ok(primitives.bool)
} else if *id == none_id {
Ok(primitives.none)
} else if *id == str_id {
Ok(primitives.str)
} else if *id == exn_id {
@ -357,7 +367,7 @@ impl dyn SymbolResolver + Send + Sync {
unreachable!("expected class definition")
}
},
&mut |id| format!("var{}", id),
&mut |id| format!("typevar{}", id),
&mut None,
)
}

View File

@ -105,6 +105,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
("__param2__".into(), int64, true),
];
// for Option, is_some and is_none share the same type: () -> bool,
// and they are methods under the same class `Option`
let (is_some_ty, unwrap_ty, (option_ty_var, option_ty_var_id)) =
if let TypeEnum::TObj { fields, params, .. } =
primitives.1.get_ty(primitives.0.option).as_ref()
{
(
*fields.get(&"is_some".into()).unwrap(),
*fields.get(&"unwrap".into()).unwrap(),
(*params.iter().next().unwrap().1, *params.iter().next().unwrap().0),
)
} else {
unreachable!()
};
let top_level_def_list = vec![
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
0,
@ -180,6 +194,91 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
None,
None,
))),
Arc::new(RwLock::new({
TopLevelDef::Class {
name: "Option".into(),
object_id: DefinitionId(10),
type_vars: vec![option_ty_var],
fields: vec![],
methods: vec![
("is_some".into(), is_some_ty.0, DefinitionId(11)),
("is_none".into(), is_some_ty.0, DefinitionId(12)),
("unwrap".into(), unwrap_ty.0, DefinitionId(13)),
],
ancestors: vec![TypeAnnotation::CustomClass {
id: DefinitionId(10),
params: Default::default(),
}],
constructor: None,
resolver: None,
loc: None,
}
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_some".into(),
simple_name: "is_some".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
} else {
unreachable!("option must be ptr")
}
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_none".into(),
simple_name: "is_none".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
} else {
unreachable!("option must be ptr")
}
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.unwrap".into(),
simple_name: "unwrap".into(),
signature: unwrap_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|_, _, _, _, _| {
unreachable!("handled in gen_expr")
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "int32".into(),
simple_name: "int32".into(),
@ -201,7 +300,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
Some(
ctx.builder
@ -266,7 +365,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, uint32)
@ -333,7 +432,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, boolean) {
ctx.builder
.build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext")
@ -385,7 +484,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, int32)
|| ctx.unifier.unioned(arg_ty, uint32)
|| ctx.unifier.unioned(arg_ty, boolean)
@ -432,7 +531,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let boolean = ctx.primitives.bool;
let float = ctx.primitives.float;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, int32)
@ -468,7 +567,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -508,7 +607,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -566,23 +665,44 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let mut step = None;
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let ty_i32 = ctx.primitives.int32;
for (i, arg) in args.iter().enumerate() {
if arg.0 == Some("start".into()) {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("stop".into()) {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("step".into()) {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 0 {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 1 {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 2 {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
}
}
// TODO: error when step == 0
let step = step.unwrap_or_else(|| int32.const_int(1, false).into());
let step = match step {
Some(step) => {
let step = step.into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"range() step must not be zero",
[None, None, None],
ctx.current_loc,
);
step
}
None => int32.const_int(1, false),
};
let stop = stop.unwrap_or_else(|| {
let v = start.unwrap();
start = None;
@ -625,8 +745,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator)?))
|ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?))
},
)))),
loc: None,
@ -650,7 +771,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
Some(arg)
} else if ctx.unifier.unioned(arg_ty, int32) {
@ -708,7 +829,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -748,7 +869,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -788,7 +909,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -828,7 +949,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -880,11 +1001,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|ctx, _, fun, args, generator| {
let range_ty = ctx.primitives.range;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg);
Some(calculate_len_for_slice_range(ctx, start, end, step).into())
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
} else {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
@ -934,8 +1055,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umin.i1", llvm_i1)
@ -996,8 +1117,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umax.i1", llvm_i1)
@ -1054,7 +1175,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let n_ty = fun.0.args[0].ty;
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let mut is_float = false;
let (fun_name, arg_ty) =
@ -1098,6 +1219,29 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Some".into(),
simple_name: "Some".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: option_ty_var, default_value: None }],
ret: primitives.0.option,
vars: HashMap::from([(option_ty_var_id, option_ty_var)]),
})),
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|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 alloca = ctx.builder.build_alloca(arg_val.get_type(), "alloca_some");
ctx.builder.build_store(alloca, arg_val);
Ok(Some(alloca.into()))
},
)))),
loc: None,
})),
];
let ast_list: Vec<Option<ast::Stmt<()>>> =
@ -1123,6 +1267,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
"min",
"max",
"abs",
"Some",
],
)
}

View File

@ -74,6 +74,8 @@ impl TopLevelComposer {
"self".into(),
"Kernel".into(),
"KernelInvariant".into(),
"Some".into(),
"Option".into(),
]);
let defined_names: HashSet<String> = Default::default();
let method_class: HashMap<DefinitionId, DefinitionId> = Default::default();
@ -89,10 +91,9 @@ impl TopLevelComposer {
assert!(name == *simple_name);
builtin_ty.insert(name, *signature);
builtin_id.insert(name, DefinitionId(id));
} else if let TopLevelDef::Class { name, constructor, object_id, type_vars, .. } = &*def
} else if let TopLevelDef::Class { name, constructor, object_id, .. } = &*def
{
assert!(id == object_id.0);
assert!(type_vars.is_empty());
if let Some(constructor) = constructor {
builtin_ty.insert(*name, *constructor);
}
@ -433,11 +434,11 @@ impl TopLevelComposer {
// check if all are unique type vars
let all_unique_type_var = {
let mut occured_type_var_id: HashSet<u32> = HashSet::new();
let mut occurred_type_var_id: HashSet<u32> = HashSet::new();
type_vars.iter().all(|x| {
let ty = unifier.get_ty(*x);
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
occured_type_var_id.insert(*id)
occurred_type_var_id.insert(*id)
} else {
false
}
@ -535,7 +536,7 @@ impl TopLevelComposer {
}
has_base = true;
// the function parse_ast_to make sure that no type var occured in
// the function parse_ast_to make sure that no type var occurred in
// bast_ty if it is a CustomClassKind
let base_ty = parse_ast_to_type_annotation_kinds(
class_resolver,
@ -695,7 +696,7 @@ impl TopLevelComposer {
return Err(errors.into_iter().sorted().join("\n----------\n"));
}
// handle the inheritanced methods and fields
// handle the inherited methods and fields
// Note: we cannot defer error handling til the end of the loop, because there is loop
// carried dependency, ignoring the error (temporarily) will cause all assumptions to break
// and produce weird error messages
@ -824,9 +825,9 @@ impl TopLevelComposer {
let mut function_var_map: HashMap<u32, Type> = HashMap::new();
let arg_types = {
// make sure no duplicate parameter
let mut defined_paramter_name: HashSet<_> = HashSet::new();
let mut defined_parameter_name: HashSet<_> = HashSet::new();
for x in args.args.iter() {
if !defined_paramter_name.insert(x.node.arg)
if !defined_parameter_name.insert(x.node.arg)
|| keyword_list.contains(&x.node.arg)
{
return Err(format!(
@ -1073,10 +1074,10 @@ impl TopLevelComposer {
let arg_types: Vec<FuncArg> = {
// check method parameters cannot have same name
let mut defined_paramter_name: HashSet<_> = HashSet::new();
let mut defined_parameter_name: HashSet<_> = HashSet::new();
let zelf: StrRef = "self".into();
for x in args.args.iter() {
if !defined_paramter_name.insert(x.node.arg)
if !defined_parameter_name.insert(x.node.arg)
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
{
return Err(format!(
@ -1087,13 +1088,13 @@ impl TopLevelComposer {
}
}
if name == &"__init__".into() && !defined_paramter_name.contains(&zelf) {
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
return Err(format!(
"__init__ method must have a `self` parameter (at {})",
b.location
));
}
if !defined_paramter_name.contains(&zelf) {
if !defined_parameter_name.contains(&zelf) {
return Err(format!(
"class method must have a `self` parameter (at {})",
b.location
@ -1226,7 +1227,7 @@ impl TopLevelComposer {
dummy_return_type
} else {
// if do not have return annotation, return none
// for uniform handling, still use type annoatation
// for uniform handling, still use type annotation
let dummy_return_type = unifier.get_dummy_var().0;
type_var_to_concrete_def.insert(
dummy_return_type,
@ -1467,9 +1468,9 @@ impl TopLevelComposer {
Ok(())
}
/// step 5, analyze and call type inferecer to fill the `instance_to_stmt` of topleveldef::function
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of topleveldef::function
fn analyze_function_instance(&mut self) -> Result<(), String> {
// first get the class contructor type correct for the following type check in function body
// first get the class constructor type correct for the following type check in function body
// also do class field instantiation check
let init_str_id = "__init__".into();
let mut definition_extension = Vec::new();
@ -1580,7 +1581,7 @@ impl TopLevelComposer {
return Ok(());
}
let mut init_id: Option<DefinitionId> = None;
// get the class contructor type correct
// get the class constructor type correct
let (contor_args, contor_type_vars) = {
let mut constructor_args: Vec<FuncArg> = Vec::new();
let mut type_vars: HashMap<u32, Type> = HashMap::new();
@ -1783,9 +1784,7 @@ impl TopLevelComposer {
})
};
let mut identifiers = {
// NOTE: none and function args?
let mut result: HashSet<_> = HashSet::new();
result.insert("None".into());
if self_type.is_some() {
result.insert("self".into());
}
@ -1808,9 +1807,7 @@ impl TopLevelComposer {
},
unifier,
variable_mapping: {
// NOTE: none and function args?
let mut result: HashMap<StrRef, Type> = HashMap::new();
result.insert("None".into(), primitives_ty.none);
if let Some(self_ty) = self_type {
result.insert("self".into(), self_ty);
}
@ -1909,7 +1906,7 @@ impl TopLevelComposer {
unreachable!("must be class id here")
}
},
&mut |id| format!("tvar{}", id),
&mut |id| format!("typevar{}", id),
&mut None,
);
return Err(format!(

View File

@ -84,7 +84,7 @@ impl TopLevelComposer {
obj_id: DefinitionId(7),
fields: vec![
("__name__".into(), (int32, true)),
("__file__".into(), (int32, true)),
("__file__".into(), (str, true)),
("__line__".into(), (int32, true)),
("__col__".into(), (int32, true)),
("__func__".into(), (str, true)),
@ -107,13 +107,49 @@ impl TopLevelComposer {
fields: HashMap::new(),
params: HashMap::new(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 };
let option_type_var = unifier.get_fresh_var(Some("option_type_var".into()), None);
let is_some_type_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: bool,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
}));
let unwrap_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: option_type_var.0,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
}));
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: vec![
("is_some".into(), (is_some_type_fun_ty, true)),
("is_none".into(), (is_some_type_fun_ty, true)),
("unwrap".into(), (unwrap_fun_ty, true)),
]
.into_iter()
.collect::<HashMap<_, _>>(),
params: HashMap::from([(option_type_var.1, option_type_var.0)]),
});
let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
(primitives, unifier)
}
/// already include the definition_id of itself inside the ancestors vector
/// when first regitering, the type_vars, fields, methods, ancestors are invalid
/// when first registering, the type_vars, fields, methods, ancestors are invalid
pub fn make_top_level_class_def(
index: usize,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
@ -380,76 +416,77 @@ impl TopLevelComposer {
primitive: &PrimitiveStore,
unifier: &mut Unifier,
) -> Result<(), String> {
let res = match val {
SymbolValue::Bool(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.bool) {
None
} else {
Some("bool".to_string())
fn type_default_param(
val: &SymbolValue,
primitive: &PrimitiveStore,
unifier: &mut Unifier,
) -> TypeAnnotation {
match val {
SymbolValue::Bool(..) => TypeAnnotation::Primitive(primitive.bool),
SymbolValue::Double(..) => TypeAnnotation::Primitive(primitive.float),
SymbolValue::I32(..) => TypeAnnotation::Primitive(primitive.int32),
SymbolValue::I64(..) => TypeAnnotation::Primitive(primitive.int64),
SymbolValue::U32(..) => TypeAnnotation::Primitive(primitive.uint32),
SymbolValue::U64(..) => TypeAnnotation::Primitive(primitive.uint64),
SymbolValue::Str(..) => TypeAnnotation::Primitive(primitive.str),
SymbolValue::Tuple(vs) => {
let vs_tys = vs
.iter()
.map(|v| type_default_param(v, primitive, unifier))
.collect::<Vec<_>>();
TypeAnnotation::Tuple(vs_tys)
}
SymbolValue::OptionNone => TypeAnnotation::CustomClass {
id: primitive.option.get_obj_id(unifier),
params: Default::default(),
},
SymbolValue::OptionSome(v) => {
let ty = type_default_param(v, primitive, unifier);
TypeAnnotation::CustomClass {
id: primitive.option.get_obj_id(unifier),
params: vec![ty],
}
}
SymbolValue::Double(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.float) {
None
} else {
Some("float".to_string())
}
}
SymbolValue::I32(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int32) {
None
} else {
Some("int32".to_string())
fn is_compatible(
found: &TypeAnnotation,
expect: &TypeAnnotation,
unifier: &mut Unifier,
primitive: &PrimitiveStore,
) -> bool {
match (found, expect) {
(TypeAnnotation::Primitive(f), TypeAnnotation::Primitive(e)) => {
unifier.unioned(*f, *e)
}
(
TypeAnnotation::CustomClass { id: f_id, params: f_param },
TypeAnnotation::CustomClass { id: e_id, params: e_param },
) => {
*f_id == *e_id
&& *f_id == primitive.option.get_obj_id(unifier)
&& (f_param.is_empty()
|| (f_param.len() == 1
&& e_param.len() == 1
&& is_compatible(&f_param[0], &e_param[0], unifier, primitive)))
}
(TypeAnnotation::Tuple(f), TypeAnnotation::Tuple(e)) => {
f.len() == e.len()
&& f.iter()
.zip(e.iter())
.all(|(f, e)| is_compatible(f, e, unifier, primitive))
}
_ => false,
}
}
SymbolValue::I64(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int64) {
None
} else {
Some("int64".to_string())
}
}
SymbolValue::U32(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint32) {
None
} else {
Some("uint32".to_string())
}
}
SymbolValue::U64(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint64) {
None
} else {
Some("uint64".to_string())
}
}
SymbolValue::Str(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) {
None
} else {
Some("str".to_string())
}
}
SymbolValue::Tuple(elts) => {
if let TypeAnnotation::Tuple(elts_ty) = ty {
for (e, t) in elts.iter().zip(elts_ty.iter()) {
Self::check_default_param_type(e, t, primitive, unifier)?
}
if elts.len() != elts_ty.len() {
Some(format!("tuple of length {}", elts.len()))
} else {
None
}
} else {
Some("tuple".to_string())
}
}
};
if let Some(found) = res {
let found = type_default_param(val, primitive, unifier);
if !is_compatible(&found, ty, unifier, primitive) {
Err(format!(
"incompatible default parameter type, expect {}, found {}",
ty.stringify(unifier),
found
found.stringify(unifier),
))
} else {
Ok(())
@ -475,6 +512,10 @@ pub fn parse_parameter_default_value(
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
)),
Constant::None => Err(format!(
"`None` is not supported, use `none` for option type instead ({})",
loc
)),
_ => unimplemented!("this constant is not supported at {}", loc),
}
}
@ -512,6 +553,11 @@ pub fn parse_parameter_default_value(
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?)
)
),
_ => Err(format!("unsupported default parameter at {}", default.location)),
}
}
@ -520,15 +566,20 @@ pub fn parse_parameter_default_value(
.map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()?
)),
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
ast::ExprKind::Name { id, .. } => {
resolver.get_default_param_value(default).ok_or_else(
|| format!(
"`{}` cannot be used as a default parameter at {} (not primitive type or tuple / not defined?)",
"`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id,
default.location
)
)
}
_ => Err(format!("unsupported default parameter at {}", default.location))
_ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
))
}
}

View File

@ -5,10 +5,10 @@ expression: res_vec
---
[
"Class {\nname: \"Generic_A\",\nancestors: [\"{class: Generic_A, params: [\\\"V\\\"]}\", \"{class: B, params: []}\"],\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.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [17]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [18]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
]

View File

@ -5,13 +5,13 @@ expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"T\\\"]}\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A[T]\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\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.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: [\\\"var6\\\"]}\", \"{class: A, params: [\\\"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: [\"var6\"]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B[typevar7]\", \"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: [\"typevar7\"]\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",
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: B, params: [\\\"bool\\\"]}\", \"{class: A, params: [\\\"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",
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
]

View File

@ -6,10 +6,10 @@ expression: res_vec
---
[
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"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: [19]\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [24]\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: [20]\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [25]\n}\n",
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\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",
]

View File

@ -5,10 +5,10 @@ expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A[typevar6, typevar7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"typevar6\", \"typevar7\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: A, params: [\\\"int64\\\", \\\"bool\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:B], B]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\",\nvar_id: []\n}\n",

View File

@ -1,19 +1,19 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 540
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\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.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [25]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: C, params: []}\", \"{class: A, params: []}\"],\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.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [26]\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",
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: A, params: []}\"],\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.fun\",\nsig: \"fn[[b:B], 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: [33]\n}\n",
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [34]\n}\n",
]

View File

@ -1,9 +1,9 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 541
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\nfields: [],\nmethods: [],\ntype_vars: []\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [],\nmethods: [],\ntype_vars: []\n}\n",
]

View File

@ -65,14 +65,14 @@ impl SymbolResolver for Resolver {
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.0.id_to_def.lock().get(&id).cloned().ok_or("Unknown identifier".to_string())
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
fn get_exception_id(&self, tyid: usize) -> usize {
fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!()
}
}
@ -763,7 +763,7 @@ fn make_internal_resolver_with_tvar(
(name, {
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice(), None, None);
if print {
println!("{}: {:?}, tvar{}", name, ty, id);
println!("{}: {:?}, typevar{}", name, ty, id);
}
ty
})
@ -791,7 +791,7 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
self.unifier.internal_stringify(
ty,
&mut |id| format!("class{}", id.to_string()),
&mut |id| format!("tvar{}", id.to_string()),
&mut |id| format!("typevar{}", id.to_string()),
&mut None,
)
} else {

View File

@ -29,20 +29,28 @@ impl TypeAnnotation {
{
(*name).into()
} else {
format!("def_{}", id.0)
unreachable!()
}
}
None => format!("def_{}", id.0),
None => format!("class_def_{}", id.0),
};
format!(
"{{class: {}, params: {:?}}}",
"{}{}",
class_name,
params.iter().map(|p| p.stringify(unifier)).collect_vec()
{
let param_list = params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
if param_list.is_empty() {
"".into()
} else {
format!("[{}]", param_list)
}
}
)
}
Virtual(ty) | List(ty) => ty.stringify(unifier),
Virtual(ty) => format!("virtual[{}]", ty.stringify(unifier)),
List(ty) => format!("list[{}]", ty.stringify(unifier)),
Tuple(types) => {
format!("({:?})", types.iter().map(|p| p.stringify(unifier)).collect_vec())
format!("tuple[{}]", types.iter().map(|p| p.stringify(unifier)).collect_vec().join(", "))
}
}
}
@ -72,8 +80,6 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::Primitive(primitives.float))
} else if id == &"bool".into() {
Ok(TypeAnnotation::Primitive(primitives.bool))
} else if id == &"None".into() {
Ok(TypeAnnotation::Primitive(primitives.none))
} else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str))
} else if id == &"Exception".into() {
@ -223,6 +229,29 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::List(def_ann.into()))
}
// option
ast::ExprKind::Subscript { value, slice, .. }
if {
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Option".into())
} =>
{
let def_ann = parse_ast_to_type_annotation_kinds(
resolver,
top_level_defs,
unifier,
primitives,
slice.as_ref(),
locked,
)?;
let id =
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() {
*obj_id
} else {
unreachable!()
};
Ok(TypeAnnotation::CustomClass { id, params: vec![def_ann] })
}
// tuple
ast::ExprKind::Subscript { value, slice, .. }
if {
@ -327,7 +356,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier.internal_stringify(
p,
&mut |id| format!("class{}", id),
&mut |id| format!("tvar{}", id),
&mut |id| format!("typevar{}", id),
&mut None
),
*id
@ -407,7 +436,7 @@ pub fn get_type_from_type_annotation_kinds(
/// the type of `self` should be similar to `A[T, V]`, where `T`, `V`
/// considered to be type variables associated with the class \
/// \
/// But note that here we do not make a duplication of `T`, `V`, we direclty
/// But note that here we do not make a duplication of `T`, `V`, we directly
/// use them as they are in the TopLevelDef::Class since those in the
/// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations
/// the Type of their fields and methods will also be subst when application/instantiation

View File

@ -20,6 +20,8 @@ impl<'a> Inferencer<'a> {
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
ExprKind::Name { id, .. } => {
if !defined_identifiers.contains(id) {
defined_identifiers.insert(*id);
@ -70,6 +72,9 @@ impl<'a> Inferencer<'a> {
}
match &expr.node {
ExprKind::Name { id, .. } => {
if id == &"none".into() {
return Ok(());
}
self.should_have_value(expr)?;
if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type(

View File

@ -83,7 +83,7 @@ where
pub fn impl_binop(
unifier: &mut Unifier,
store: &PrimitiveStore,
_store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
@ -120,7 +120,7 @@ pub fn impl_binop(
fields.insert(binop_assign_name(op).into(), {
(
unifier.add_ty(TypeEnum::TFunc(FunSignature {
ret: store.none,
ret: ret_ty,
vars: function_vars.clone(),
args: vec![FuncArg {
ty: other_ty,

View File

@ -40,6 +40,7 @@ pub struct PrimitiveStore {
pub range: Type,
pub str: Type,
pub exception: Type,
pub option: Type,
}
pub struct FunctionData {
@ -422,9 +423,16 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
(None, None) => {}
},
ast::StmtKind::AugAssign { target, op, value, .. } => {
let res_ty = self.infer_bin_ops(stmt.location, target, op, value)?;
let res_ty = self.infer_bin_ops(stmt.location, target, op, value, true)?;
self.unify(res_ty, target.custom.unwrap(), &stmt.location)?;
}
ast::StmtKind::Assert { test, msg, .. } => {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
match msg {
Some(m) => self.unify(m.custom.unwrap(), self.primitives.str, &m.location)?,
None => ()
}
}
_ => return report_error("Unsupported statement type", stmt.location),
};
Ok(stmt)
@ -448,6 +456,27 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
Some(self.infer_constant(value, &expr.location)?)
}
ast::ExprKind::Name { id, .. } => {
// the name `none` is special since it may have different types
if id == &"none".into() {
if let TypeEnum::TObj { params, .. } =
self.unifier.get_ty_immutable(self.primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
Some(self.unifier.subst(self.primitives.option, &var_map).unwrap())
} else {
unreachable!("must be tobj")
}
} else {
if !self.defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type(
self.unifier,
@ -468,6 +497,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
}
Some(self.infer_identifier(*id)?)
}
}
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
ast::ExprKind::Attribute { value, attr, ctx } => {
@ -475,7 +505,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
}
ast::ExprKind::BoolOp { values, .. } => Some(self.infer_bool_ops(values)?),
ast::ExprKind::BinOp { left, op, right } => {
Some(self.infer_bin_ops(expr.location, left, op, right)?)
Some(self.infer_bin_ops(expr.location, left, op, right, false)?)
}
ast::ExprKind::UnaryOp { op, operand } => Some(self.infer_unary_ops(op, operand)?),
ast::ExprKind::Compare { left, ops, comparators } => {
@ -932,6 +962,8 @@ impl<'a> Inferencer<'a> {
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? }))
}
ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None
=> report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
_ => report_error("not supported", *loc),
}
}
@ -996,8 +1028,24 @@ impl<'a> Inferencer<'a> {
left: &ast::Expr<Option<Type>>,
op: &ast::Operator,
right: &ast::Expr<Option<Type>>,
is_aug_assign: bool,
) -> InferenceResult {
let method = binop_name(op).into();
let method = if let TypeEnum::TObj { fields, .. } =
self.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
{
let (binop_name, binop_assign_name) = (
binop_name(op).into(),
binop_assign_name(op).into()
);
// if is aug_assign, try aug_assign operator first
if is_aug_assign && fields.contains_key(&binop_assign_name) {
binop_assign_name
} else {
binop_name
}
} else {
binop_name(op).into()
};
self.build_method_call(
location,
method,

View File

@ -44,14 +44,14 @@ impl SymbolResolver for Resolver {
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.id_to_def.get(&id).cloned().ok_or("Unknown identifier".to_string())
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
fn get_exception_id(&self, tyid: usize) -> usize {
fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!()
}
}
@ -129,7 +129,24 @@ impl TestEnvironment {
fields: HashMap::new(),
params: HashMap::new(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 };
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: HashMap::new(),
params: HashMap::new(),
});
let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
set_primitives_magic_methods(&primitives, &mut unifier);
let id_to_name = [
@ -237,6 +254,11 @@ impl TestEnvironment {
fields: HashMap::new(),
params: HashMap::new(),
});
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: HashMap::new(),
params: HashMap::new(),
});
identifier_mapping.insert("None".into(), none);
for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"]
.iter()
@ -259,7 +281,19 @@ impl TestEnvironment {
}
let defs = 7;
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 };
let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
let (v0, id) = unifier.get_dummy_var();

View File

@ -16,7 +16,7 @@ use crate::toplevel::{DefinitionId, TopLevelContext, TopLevelDef};
#[cfg(test)]
mod test;
/// Handle for a type, implementated as a key in the unification table.
/// Handle for a type, implemented as a key in the unification table.
pub type Type = UnificationKey;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@ -54,6 +54,18 @@ pub enum RecordKey {
Int(i32),
}
impl Type {
// a wrapper function for cleaner code so that we don't need to
// write this long pattern matching just to get the field `obj_id`
pub fn get_obj_id(self, unifier: &Unifier) -> DefinitionId {
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty_immutable(self).as_ref() {
*obj_id
} else {
unreachable!("expect a object type")
}
}
}
impl From<&RecordKey> for StrRef {
fn from(r: &RecordKey) -> Self {
match r {
@ -645,12 +657,16 @@ impl Unifier {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
for (x, y) in ty1.iter().zip(ty2.iter()) {
self.unify_impl(*x, *y, false)?;
if self.unify_impl(*x, *y, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
}
self.set_a_to_b(a, b);
}
(TList { ty: ty1 }, TList { ty: ty2 }) => {
self.unify_impl(*ty1, *ty2, false)?;
if self.unify_impl(*ty1, *ty2, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
self.set_a_to_b(a, b);
}
(TVar { fields: Some(map), range, .. }, TObj { fields, .. }) => {
@ -731,12 +747,16 @@ impl Unifier {
self.incompatible_types(a, b)?;
}
for (x, y) in zip(params1.values(), params2.values()) {
self.unify_impl(*x, *y, false)?;
if self.unify_impl(*x, *y, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
}
self.set_a_to_b(a, b);
}
(TVirtual { ty: ty1 }, TVirtual { ty: ty2 }) => {
self.unify_impl(*ty1, *ty2, false)?;
if self.unify_impl(*ty1, *ty2, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
self.set_a_to_b(a, b);
}
(TCall(calls1), TCall(calls2)) => {
@ -772,9 +792,13 @@ impl Unifier {
if x.name != y.name || x.default_value != y.default_value {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
self.unify_impl(x.ty, y.ty, false)?;
if self.unify_impl(x.ty, y.ty, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
}
self.unify_impl(sign1.ret, sign2.ret, false)?;
if self.unify_impl(sign1.ret, sign2.ret, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
self.set_a_to_b(a, b);
}
(TVar { fields: Some(fields), .. }, _) => {
@ -818,7 +842,7 @@ impl Unifier {
},
)
},
&mut |id| format!("var{}", id),
&mut |id| format!("typevar{}", id),
notes,
)
}

View File

@ -286,7 +286,7 @@ fn test_unify(
("v1", "tuple[int]"),
("v2", "tuple[float]"),
],
(("v1", "v2"), "Incompatible types: 0 and 1")
(("v1", "v2"), "Incompatible types: tuple[0] and tuple[1]")
; "tuple parameter mismatch"
)]
#[test_case(2,
@ -302,14 +302,14 @@ fn test_unify(
("v1", "Record[a=float,b=int]"),
("v2", "Foo[v3]"),
],
(("v1", "v2"), "`3[var4]::b` field/method does not exist")
(("v1", "v2"), "`3[typevar4]::b` field/method does not exist")
; "record obj merge"
)]
/// Test cases for invalid unifications.
fn test_invalid_unification(
variable_count: u32,
unify_pairs: &[(&'static str, &'static str)],
errornous_pair: ((&'static str, &'static str), &'static str),
erroneous_pair: ((&'static str, &'static str), &'static str),
) {
let mut env = TestEnvironment::new();
let mut mapping = HashMap::new();
@ -326,11 +326,11 @@ fn test_invalid_unification(
pairs.push((t1, t2));
}
let (t1, t2) =
(env.parse(errornous_pair.0 .0, &mapping), env.parse(errornous_pair.0 .1, &mapping));
(env.parse(erroneous_pair.0 .0, &mapping), env.parse(erroneous_pair.0 .1, &mapping));
for (a, b) in pairs {
env.unifier.unify(a, b).unwrap();
}
assert_eq!(env.unify(t1, t2), Err(errornous_pair.1.to_string()));
assert_eq!(env.unify(t1, t2), Err(erroneous_pair.1.to_string()));
}
#[test]
@ -445,7 +445,7 @@ fn test_typevar_range() {
// where v in (int, list[v1]), v1 in (int, bool)
assert_eq!(
env.unify(float_list, v),
Err("Expected any one of these types: 0, list[var5], but got list[1]\n\nNotes:\n var5 ∈ {0, 2}".to_string())
Err("Expected any one of these types: 0, list[typevar5], but got list[1]\n\nNotes:\n typevar5 ∈ {0, 2}".to_string())
);
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
@ -478,7 +478,8 @@ fn test_typevar_range() {
let int_list = env.unifier.add_ty(TypeEnum::TList { ty: int });
assert_eq!(
env.unify(a_list, int_list),
Err("Expected any one of these types: 1, but got 0".into())
Err("Incompatible types: list[typevar22] and list[0]\
\n\nNotes:\n typevar22 {1}".into())
);
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
@ -504,9 +505,9 @@ fn test_rigid_var() {
let int = env.parse("int", &HashMap::new());
let list_int = env.parse("list[int]", &HashMap::new());
assert_eq!(env.unify(a, b), Err("Incompatible types: var3 and var2".to_string()));
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
env.unifier.unify(list_a, list_x).unwrap();
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and var2".to_string()));
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: list[typevar2] and list[0]".to_string()));
env.unifier.replace_rigid_var(a, int);
env.unifier.unify(list_x, list_int).unwrap();

View File

@ -121,7 +121,7 @@ mod tests {
#[test]
fn test_parse_lambda() {
let source = "lambda x, y: x * y"; // lambda(x, y): x * y";
let parse_ast = parse_program(&source, Default::default()).unwrap();
let parse_ast = parse_program(source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast);
}
@ -129,7 +129,7 @@ mod tests {
fn test_parse_tuples() {
let source = "a, b = 4, 5";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
@ -140,7 +140,7 @@ class Foo(A, B):
pass
def method_with_default(self, arg='default'):
pass";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
@ -183,7 +183,7 @@ while i < 2: # nac3: 4
# nac3: if1
if 1: # nac3: if2
3";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
@ -196,7 +196,7 @@ while test: # nac3: while3
# nac3: simple assign0
a = 3 # nac3: simple assign1
";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
@ -215,7 +215,7 @@ if a: # nac3: small2
for i in a: # nac3: for1
pass
";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
}
#[test]
@ -224,6 +224,6 @@ for i in a: # nac3: for1
if a: # nac3: something
a = 3
";
assert!(parse_program(&source, Default::default()).is_err());
assert!(parse_program(source, Default::default()).is_err());
}
}

View File

@ -10,6 +10,6 @@ nac3parser = { path = "../nac3parser" }
nac3core = { path = "../nac3core" }
[dependencies.inkwell]
version = "0.1.0-beta.4"
git = "https://github.com/TheDan64/inkwell.git"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]

View File

@ -74,6 +74,11 @@ pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _conte
unimplemented!();
}
#[no_mangle]
pub extern "C" fn __nac3_raise(_state: u32, _exception_object: u32, _context: u32) -> u32 {
unimplemented!();
}
extern "C" {
fn run() -> i32;
}

View File

@ -8,6 +8,38 @@ import pathlib
from numpy import int32, int64, uint32, uint64
from typing import TypeVar, Generic
T = TypeVar('T')
class Option(Generic[T]):
_nac3_option: T
def __init__(self, v: T):
self._nac3_option = v
def is_none(self):
return self._nac3_option is None
def is_some(self):
return not self.is_none()
def unwrap(self):
return self._nac3_option
def __repr__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(str(self._nac3_option))
def Some(v: T) -> Option[T]:
return Option(v)
none = Option(None)
def patch(module):
def output_asciiart(x):
@ -39,6 +71,9 @@ def patch(module):
module.TypeVar = TypeVar
module.Generic = Generic
module.extern = extern
module.Option = Option
module.Some = Some
module.none = none
def file_import(filename, prefix="file_import_"):

View File

@ -0,0 +1,43 @@
@extern
def output_int32(x: int32):
...
def f1(a: int32 = 4):
output_int32(a)
def f2(a: int64 = int64(123)):
output_int32(int32(a))
def f3(a: uint32 = uint32(234)):
output_int32(int32(a))
def f4(a: tuple[int32, tuple[int32, int32], int64] = (4, (5, 6), int64(7))):
output_int32(a[0])
output_int32(a[1][0])
output_int32(a[1][1])
output_int32(int32(a[2]))
def f5(a: float = 3.45):
output_int32(int32(a))
def f6(a: Option[list[int32]] = none):
if a.is_none():
a = Some([11,22,33])
output_int32(a.unwrap()[2])
def f7(a: Option[tuple[int32, int64]] = Some((3, int64(123)))):
if a.is_some():
a_unwrap = a.unwrap()
output_int32(a_unwrap[0])
output_int32(int32(a_unwrap[1]))
def run() -> int32:
f1()
f2()
f3()
f4()
f5()
f6()
f7()
return 0

View File

@ -0,0 +1,32 @@
from __future__ import annotations
@extern
def output_int32(x: int32):
...
class A:
a: int32
def __init__(self, a: int32):
self.a = a
def f1(self):
self.f2()
def f2(self):
output_int32(self.a)
class B(A):
b: int32
def __init__(self, b: int32):
self.a = b + 1
self.b = b
def run() -> int32:
aaa = A(5)
bbb = B(2)
aaa.f1()
bbb.f1()
return 0

View File

@ -229,7 +229,7 @@ def list_slice_assignment():
bl5[3:-5] = []
output_int32_list([int32(b) for b in bl5])
bl6 = bl[:]
bl6[3:-5] = [True, False, False]
bl6[3:-5] = [True, False]
output_int32_list([int32(b) for b in bl6])
bl7 = bl[:]
bl7[:-2] = [False]

View File

@ -0,0 +1,9 @@
@extern
def output_int32(x: int32):
...
def run() -> int32:
for _ in range(10):
output_int32(_)
_ = 0
return 0

View File

@ -0,0 +1,257 @@
from __future__ import annotations
@extern
def output_int32(x: int32):
...
@extern
def output_uint32(x: uint32):
...
@extern
def output_int64(x: int64):
...
@extern
def output_uint64(x: uint64):
...
@extern
def output_float64(x: float):
...
def run() -> int32:
test_int32()
test_uint32()
test_int64()
test_uint64()
test_A()
test_B()
return 0
def test_int32():
a = 17
b = 3
output_int32(a + b)
output_int32(a - b)
output_int32(a * b)
output_int32(a // b)
output_int32(a % b)
output_int32(a | b)
output_int32(a ^ b)
output_int32(a & b)
output_int32(a << b)
output_int32(a >> b)
output_float64(a / b)
a += b
output_int32(a)
a -= b
output_int32(a)
a *= b
output_int32(a)
a //= b
output_int32(a)
a %= b
output_int32(a)
a |= b
output_int32(a)
a ^= b
output_int32(a)
a &= b
output_int32(a)
a <<= b
output_int32(a)
a >>= b
output_int32(a)
# fail because (a / b) is float
# a /= b
def test_uint32():
a = uint32(17)
b = uint32(3)
output_uint32(a + b)
output_uint32(a - b)
output_uint32(a * b)
output_uint32(a // b)
output_uint32(a % b)
output_uint32(a | b)
output_uint32(a ^ b)
output_uint32(a & b)
output_uint32(a << b)
output_uint32(a >> b)
output_float64(a / b)
a += b
output_uint32(a)
a -= b
output_uint32(a)
a *= b
output_uint32(a)
a //= b
output_uint32(a)
a %= b
output_uint32(a)
a |= b
output_uint32(a)
a ^= b
output_uint32(a)
a &= b
output_uint32(a)
a <<= b
output_uint32(a)
a >>= b
output_uint32(a)
def test_int64():
a = int64(17)
b = int64(3)
output_int64(a + b)
output_int64(a - b)
output_int64(a * b)
output_int64(a // b)
output_int64(a % b)
output_int64(a | b)
output_int64(a ^ b)
output_int64(a & b)
output_int64(a << b)
output_int64(a >> b)
output_float64(a / b)
a += b
output_int64(a)
a -= b
output_int64(a)
a *= b
output_int64(a)
a //= b
output_int64(a)
a %= b
output_int64(a)
a |= b
output_int64(a)
a ^= b
output_int64(a)
a &= b
output_int64(a)
a <<= b
output_int64(a)
a >>= b
output_int64(a)
def test_uint64():
a = uint64(17)
b = uint64(3)
output_uint64(a + b)
output_uint64(a - b)
output_uint64(a * b)
output_uint64(a // b)
output_uint64(a % b)
output_uint64(a | b)
output_uint64(a ^ b)
output_uint64(a & b)
output_uint64(a << b)
output_uint64(a >> b)
output_float64(a / b)
a += b
output_uint64(a)
a -= b
output_uint64(a)
a *= b
output_uint64(a)
a //= b
output_uint64(a)
a %= b
output_uint64(a)
a |= b
output_uint64(a)
a ^= b
output_uint64(a)
a &= b
output_uint64(a)
a <<= b
output_uint64(a)
a >>= b
output_uint64(a)
class A:
a: int32
def __init__(self, a: int32):
self.a = a
def __add__(self, other: A) -> A:
output_int32(self.a + other.a)
return A(self.a + other.a)
def __sub__(self, other: A) -> A:
output_int32(self.a - other.a)
return A(self.a - other.a)
def test_A():
a = A(17)
b = A(3)
c = a + b
# fail due to alloca in __add__ function
# output_int32(c.a)
a += b
# fail due to alloca in __add__ function
# output_int32(a.a)
a = A(17)
b = A(3)
d = a - b
# fail due to alloca in __add__ function
# output_int32(c.a)
a -= b
# fail due to alloca in __add__ function
# output_int32(a.a)
a = A(17)
b = A(3)
a.__add__(b)
a.__sub__(b)
class B:
a: int32
def __init__(self, a: int32):
self.a = a
def __add__(self, other: B) -> B:
output_int32(self.a + other.a)
return B(self.a + other.a)
def __sub__(self, other: B) -> B:
output_int32(self.a - other.a)
return B(self.a - other.a)
def __iadd__(self, other: B) -> B:
output_int32(self.a + other.a + 24)
return B(self.a + other.a + 24)
def __isub__(self, other: B) -> B:
output_int32(self.a - other.a - 24)
return B(self.a - other.a - 24)
def test_B():
a = B(17)
b = B(3)
c = a + b
# fail due to alloca in __add__ function
# output_int32(c.a)
a += b
# fail due to alloca in __add__ function
# output_int32(a.a)
a = B(17)
b = B(3)
d = a - b
# fail due to alloca in __add__ function
# output_int32(c.a)
a -= b
# fail due to alloca in __add__ function
# output_int32(a.a)
a = B(17)
b = B(3)
a.__add__(b)
a.__sub__(b)

View File

@ -0,0 +1,40 @@
@extern
def output_int32(x: int32):
...
class A:
d: Option[int32]
e: Option[Option[int32]]
def __init__(self, a: Option[int32], b: Option[Option[int32]]):
self.d = a
self.e = b
def run() -> int32:
a = Some(3)
if a.is_some():
d = a.unwrap()
output_int32(a.unwrap())
a = none
if a.is_none():
output_int32(d + 2)
else:
a = Some(5)
c = Some(6)
output_int32(a.unwrap() + c.unwrap())
f = Some(4.3)
output_int32(int32(f.unwrap()))
obj = A(Some(6), none)
output_int32(obj.d.unwrap())
obj2 = Some(A(Some(7), none))
output_int32(obj2.unwrap().d.unwrap())
obj3 = Some(A(Some(8), Some(none)))
if obj3.unwrap().e.unwrap().is_none():
obj3.unwrap().e = Some(Some(9))
output_int32(obj3.unwrap().d.unwrap())
output_int32(obj3.unwrap().e.unwrap().unwrap())
return 0

View File

@ -0,0 +1,26 @@
@extern
def output_float64(f: float):
...
def run() -> int32:
output_float64(float(3 ** 1))
output_float64(float(3 ** 0))
output_float64(float(3 ** 19))
output_float64(1.0 ** -100)
output_float64(1.0 ** -2)
output_float64(1.0 ** 0)
output_float64(1.0 ** 1)
output_float64(1.0 ** 100)
output_float64(3.0 ** 0)
output_float64(3.0 ** 1)
output_float64(3.0 ** 2)
output_float64(3.0 ** -1)
output_float64(3.0 ** -2)
output_float64(3.0 ** -32767)
output_float64(3.0 ** -3.0)
output_float64(3.0 ** -0.0)
output_float64(3.0 ** 0.0)
output_float64(4.0 ** 0.5)
output_float64(4.0 ** -0.5)
return 0

View File

@ -0,0 +1,36 @@
from __future__ import annotations
@extern
def output_int32(a: int32):
...
class A:
d: int32
a: list[B]
def __init__(self, b: list[B]):
self.d = 123
self.a = b
def f(self):
output_int32(self.d)
class B:
a: A
def __init__(self, a: A):
self.a = a
def ff(self):
self.a.f()
class Demo:
a: A
def __init__(self, a: A):
self.a = a
def run() -> int32:
aa = A([])
bb = B(aa)
aa.a = [bb]
d = Demo(aa)
d.a.a[0].ff()
return 0

View File

@ -0,0 +1,29 @@
@extern
def output_int32_list(x: list[int32]):
...
@extern
def output_int32(x: int32):
...
class A:
a: int32
b: bool
def __init__(self, a: int32, b: bool):
self.a = a
self.b = b
def run() -> int32:
data = [0, 1, 2, 3]
t = [(d, d + d) for d in data]
for i in t:
tt = (Some(i[1]), i[0])
tl = ([i[0], i[1] + i[0]], i[1])
output_int32(tt[0].unwrap())
output_int32(tt[1])
output_int32(tl[0][0])
output_int32(tl[0][1])
output_int32(tl[1])
return 0

View File

@ -34,5 +34,9 @@ def run() -> int32:
insta = A()
inst = C(insta)
inst.foo()
insta2 = B()
inst2 = C(insta2)
inst2.foo()
return 0

View File

@ -30,46 +30,13 @@ use nac3parser::{
mod basic_symbol_resolver;
use basic_symbol_resolver::*;
fn main() {
let file_name = env::args().nth(1).unwrap();
let threads: u32 = env::args().nth(2).map(|s| str::parse(&s).unwrap()).unwrap_or(1);
Target::initialize_all(&InitializationConfig::default());
let program = match fs::read_to_string(file_name.clone()) {
Ok(program) => program,
Err(err) => {
println!("Cannot open input file: {}", err);
return;
}
};
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
let (mut composer, builtins_def, builtins_ty) =
TopLevelComposer::new(vec![], Default::default());
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
id_to_type: builtins_ty.into(),
id_to_def: builtins_def.into(),
class_names: Default::default(),
module_globals: Default::default(),
str_store: Default::default(),
}
.into();
let resolver =
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
for stmt in parser_result.into_iter() {
if let StmtKind::Assign { targets, value, .. } = &stmt.node {
fn handle_typevar_definition(
fn handle_typevar_definition(
var: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<Type, String> {
) -> Result<Type, String> {
if let ExprKind::Call { func, args, .. } = &var.node {
if matches!(&func.node, ExprKind::Name { id, .. } if id == &"TypeVar".into()) {
let constraints = args
@ -102,9 +69,9 @@ fn main() {
var
))
}
}
}
fn handle_assignment_pattern(
fn handle_assignment_pattern(
targets: &[Expr],
value: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
@ -112,7 +79,7 @@ fn main() {
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<(), String> {
) -> Result<(), String> {
if targets.len() == 1 {
match &targets[0].node {
ExprKind::Name { id, .. } => {
@ -185,8 +152,41 @@ fn main() {
)),
}
}
}
}
fn main() {
let file_name = env::args().nth(1).unwrap();
let threads: u32 = env::args().nth(2).map(|s| str::parse(&s).unwrap()).unwrap_or(1);
Target::initialize_all(&InitializationConfig::default());
let program = match fs::read_to_string(file_name.clone()) {
Ok(program) => program,
Err(err) => {
println!("Cannot open input file: {}", err);
return;
}
};
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
let (mut composer, builtins_def, builtins_ty) =
TopLevelComposer::new(vec![], Default::default());
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
id_to_type: builtins_ty.into(),
id_to_def: builtins_def.into(),
class_names: Default::default(),
module_globals: Default::default(),
str_store: Default::default(),
}.into();
let resolver =
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
for stmt in parser_result.into_iter() {
match &stmt.node {
StmtKind::Assign { targets, value, .. } => {
let def_list = composer.extract_def_list();
let unifier = &mut composer.unifier;
let primitives = &composer.primitives_ty;
@ -202,17 +202,20 @@ fn main() {
eprintln!("{}", err);
return;
}
continue;
}
},
// allow (and ignore) "from __future__ import annotations"
StmtKind::ImportFrom { module, names, .. }
if module == &Some("__future__".into()) && names.len() == 1 && names[0].name == "annotations".into() => (),
_ => {
let (name, def_id, ty) =
composer.register_top_level(stmt, Some(resolver.clone()), "__main__".into()).unwrap();
internal_resolver.add_id_def(name, def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(name, ty);
}
}
}
}
let signature = FunSignature { args: vec![], ret: primitive.int32, vars: HashMap::new() };
let mut store = ConcreteTypeStore::new();

View File

@ -8,7 +8,6 @@
, ncurses
, zlib
, which
, llvmPackages_13
, debugVersion ? false
, enableManpages ? false
, enableSharedLibraries ? false
@ -18,7 +17,7 @@
let
inherit (lib) optional optionals optionalString;
release_version = "13.0.1";
release_version = "14.0.4";
candidate = ""; # empty or "rcN"
dash-candidate = lib.optionalString (candidate != "") "-${candidate}";
version = "${release_version}${dash-candidate}"; # differentiating these (variables) is important for RCs
@ -35,7 +34,7 @@ in stdenv.mkDerivation (rec {
pname = "llvm";
inherit version;
src = fetch pname "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ=";
src = fetch pname "sha256-646Q362uQHOn+PxjhLrMDdoHJADYK50l2rtSgKc3uiI=";
unpackPhase = ''
unpackFile $src
@ -50,19 +49,12 @@ in stdenv.mkDerivation (rec {
buildInputs = [ ];
propagatedBuildInputs = optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ ncurses ]
++ [ zlib ];
propagatedBuildInputs = [ ncurses zlib ];
checkInputs = [ which ];
patches = [
./gnu-install-dirs.patch
# Fix random compiler crashes: https://bugs.llvm.org/show_bug.cgi?id=50611
(fetchpatch {
url = "https://raw.githubusercontent.com/archlinux/svntogit-packages/4764a4f8c920912a2bfd8b0eea57273acfe0d8a8/trunk/no-strict-aliasing-DwarfCompileUnit.patch";
sha256 = "18l6mrvm2vmwm77ckcnbjvh6ybvn72rhrb799d4qzwac4x2ifl7g";
stripLen = 1;
})
./llvm-future-riscv-abi.diff
];
@ -121,6 +113,8 @@ in stdenv.mkDerivation (rec {
"-DLLVM_DEFAULT_TARGET_TRIPLE=${stdenv.hostPlatform.config}"
"-DLLVM_ENABLE_UNWIND_TABLES=OFF"
"-DLLVM_ENABLE_THREADS=OFF"
"-DLLVM_INCLUDE_BENCHMARKS=OFF"
"-DLLVM_BUILD_TOOLS=OFF"
"-DLLVM_TARGETS_TO_BUILD=X86;ARM;RISCV"
] ++ optionals enableSharedLibraries [
"-DLLVM_LINK_LLVM_DYLIB=ON"
@ -137,7 +131,6 @@ in stdenv.mkDerivation (rec {
"-DCAN_TARGET_i386=false"
] ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"-DCMAKE_CROSSCOMPILING=True"
"-DLLVM_TABLEGEN=${llvmPackages_13.tools.llvm}/bin/llvm-tblgen"
(
let
nativeCC = pkgsBuildBuild.targetPackages.stdenv.cc;
@ -154,6 +147,7 @@ in stdenv.mkDerivation (rec {
] ++ extraCmakeFlags;
postBuild = ''
make llvm-config
rm -fR $out
'';
@ -162,6 +156,7 @@ in stdenv.mkDerivation (rec {
'';
postInstall = ''
cp bin/llvm-config $out/bin
mkdir -p $python/share
mv $out/share/opt-viewer $python/share/opt-viewer
moveToOutput "bin/llvm-config*" "$dev"

View File

@ -0,0 +1,220 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fec956091cd5..5a766f5c5d7c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -303,6 +303,9 @@ set(LLVM_EXAMPLES_INSTALL_DIR "examples" CACHE STRING
"Path for examples subdirectory (enabled by LLVM_BUILD_EXAMPLES=ON) (defaults to 'examples')")
mark_as_advanced(LLVM_EXAMPLES_INSTALL_DIR)
+set(LLVM_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/cmake/llvm" CACHE STRING
+ "Path for CMake subdirectory (defaults to lib/cmake/llvm)" )
+
# They are used as destination of target generators.
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake
index fed1fec7d72e..4baed19b9e98 100644
--- a/cmake/modules/AddLLVM.cmake
+++ b/cmake/modules/AddLLVM.cmake
@@ -838,8 +838,8 @@ macro(add_llvm_library name)
get_target_export_arg(${name} LLVM export_to_llvmexports ${umbrella})
install(TARGETS ${name}
${export_to_llvmexports}
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" COMPONENT ${name}
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" COMPONENT ${name}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT ${name})
if (NOT LLVM_ENABLE_IDE)
@@ -1056,7 +1056,7 @@ function(process_llvm_pass_plugins)
"set(LLVM_STATIC_EXTENSIONS ${LLVM_STATIC_EXTENSIONS})")
install(FILES
${llvm_cmake_builddir}/LLVMConfigExtensions.cmake
- DESTINATION ${LLVM_INSTALL_PACKAGE_DIR}
+ DESTINATION ${LLVM_INSTALL_CMAKE_DIR}
COMPONENT cmake-exports)
set(ExtensionDef "${LLVM_BINARY_DIR}/include/llvm/Support/Extension.def")
@@ -1902,7 +1902,7 @@ function(llvm_install_library_symlink name dest type)
set(full_name ${CMAKE_${type}_LIBRARY_PREFIX}${name}${CMAKE_${type}_LIBRARY_SUFFIX})
set(full_dest ${CMAKE_${type}_LIBRARY_PREFIX}${dest}${CMAKE_${type}_LIBRARY_SUFFIX})
- set(output_dir lib${LLVM_LIBDIR_SUFFIX})
+ set(output_dir ${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX})
if(WIN32 AND "${type}" STREQUAL "SHARED")
set(output_dir bin)
endif()
@@ -1913,7 +1913,7 @@ function(llvm_install_library_symlink name dest type)
endfunction()
-function(llvm_install_symlink name dest)
+function(llvm_install_symlink name dest output_dir)
cmake_parse_arguments(ARG "ALWAYS_GENERATE" "COMPONENT" "" ${ARGN})
foreach(path ${CMAKE_MODULE_PATH})
if(EXISTS ${path}/LLVMInstallSymlink.cmake)
@@ -1936,7 +1936,7 @@ function(llvm_install_symlink name dest)
set(full_dest ${dest}${CMAKE_EXECUTABLE_SUFFIX})
install(SCRIPT ${INSTALL_SYMLINK}
- CODE "install_symlink(${full_name} ${full_dest} ${LLVM_TOOLS_INSTALL_DIR})"
+ CODE "install_symlink(${full_name} ${full_dest} ${output_dir})"
COMPONENT ${component})
if (NOT LLVM_ENABLE_IDE AND NOT ARG_ALWAYS_GENERATE)
@@ -2019,7 +2019,8 @@ function(add_llvm_tool_symlink link_name target)
endif()
if ((TOOL_IS_TOOLCHAIN OR NOT LLVM_INSTALL_TOOLCHAIN_ONLY) AND LLVM_BUILD_TOOLS)
- llvm_install_symlink(${link_name} ${target})
+ GNUInstallDirs_get_absolute_install_dir(output_dir LLVM_TOOLS_INSTALL_DIR)
+ llvm_install_symlink(${link_name} ${target} ${output_dir})
endif()
endif()
endfunction()
@@ -2148,9 +2149,9 @@ function(llvm_setup_rpath name)
# Since BUILD_SHARED_LIBS is only recommended for use by developers,
# hardcode the rpath to build/install lib dir first in this mode.
# FIXME: update this when there is better solution.
- set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
elseif(UNIX)
- set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)")
set_property(TARGET ${name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-z,origin ")
diff --git a/cmake/modules/AddOCaml.cmake b/cmake/modules/AddOCaml.cmake
index 891c9e6d618c..8d963f3b0069 100644
--- a/cmake/modules/AddOCaml.cmake
+++ b/cmake/modules/AddOCaml.cmake
@@ -147,9 +147,9 @@ function(add_ocaml_library name)
endforeach()
if( APPLE )
- set(ocaml_rpath "@executable_path/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "@executable_path/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
elseif( UNIX )
- set(ocaml_rpath "\\$ORIGIN/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "\\$ORIGIN/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
endif()
list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}")
diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt
index cea0c1df0a14..eedcd9450312 100644
--- a/cmake/modules/CMakeLists.txt
+++ b/cmake/modules/CMakeLists.txt
@@ -2,7 +2,7 @@ include(ExtendPath)
include(LLVMDistributionSupport)
include(FindPrefixFromConfig)
-set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
+set(LLVM_INSTALL_PACKAGE_DIR ${LLVM_INSTALL_CMAKE_DIR} CACHE STRING "Path for CMake subdirectory (defaults to 'cmake/llvm')")
set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
# First for users who use an installed LLVM, create the LLVMExports.cmake file.
@@ -122,7 +122,7 @@ set(LLVM_CONFIG_INCLUDE_DIRS
)
list(REMOVE_DUPLICATES LLVM_CONFIG_INCLUDE_DIRS)
-extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "lib\${LLVM_LIBDIR_SUFFIX}")
+extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}")
set(LLVM_CONFIG_LIBRARY_DIRS
"${LLVM_CONFIG_LIBRARY_DIR}"
# FIXME: Should there be other entries here?
diff --git a/cmake/modules/LLVMInstallSymlink.cmake b/cmake/modules/LLVMInstallSymlink.cmake
index b5c35f706cb7..9261ab797de6 100644
--- a/cmake/modules/LLVMInstallSymlink.cmake
+++ b/cmake/modules/LLVMInstallSymlink.cmake
@@ -6,7 +6,7 @@ include(GNUInstallDirs)
function(install_symlink name target outdir)
set(DESTDIR $ENV{DESTDIR})
- set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/${outdir}")
+ set(bindir "${DESTDIR}${outdir}/")
message(STATUS "Creating ${name}")
diff --git a/docs/CMake.rst b/docs/CMake.rst
index 044ec8a4d39d..504d0eac3ade 100644
--- a/docs/CMake.rst
+++ b/docs/CMake.rst
@@ -224,7 +224,7 @@ description is in `LLVM-related variables`_ below.
**LLVM_LIBDIR_SUFFIX**:STRING
Extra suffix to append to the directory where libraries are to be
installed. On a 64-bit architecture, one could use ``-DLLVM_LIBDIR_SUFFIX=64``
- to install libraries to ``/usr/lib64``.
+ to install libraries to ``/usr/lib64``. See also ``CMAKE_INSTALL_LIBDIR``.
**LLVM_PARALLEL_{COMPILE,LINK}_JOBS**:STRING
Building the llvm toolchain can use a lot of resources, particularly
@@ -910,9 +910,11 @@ the ``cmake`` command or by setting it directly in ``ccmake`` or ``cmake-gui``).
This file is available in two different locations.
-* ``<INSTALL_PREFIX>/lib/cmake/llvm/LLVMConfig.cmake`` where
- ``<INSTALL_PREFIX>`` is the install prefix of an installed version of LLVM.
- On Linux typically this is ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
+* ``<LLVM_INSTALL_PACKAGE_DIR>LLVMConfig.cmake`` where
+ ``<LLVM_INSTALL_PACKAGE_DIR>`` is the location where LLVM CMake modules are
+ installed as part of an installed version of LLVM. This is typically
+ ``cmake/llvm/`` within the lib directory. On Linux, this is typically
+ ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
* ``<LLVM_BUILD_ROOT>/lib/cmake/llvm/LLVMConfig.cmake`` where
``<LLVM_BUILD_ROOT>`` is the root of the LLVM build tree. **Note: this is only
diff --git a/include/llvm/CMakeLists.txt b/include/llvm/CMakeLists.txt
index b46319f24fc8..2feabd1954e4 100644
--- a/include/llvm/CMakeLists.txt
+++ b/include/llvm/CMakeLists.txt
@@ -5,5 +5,5 @@ add_subdirectory(Frontend)
# If we're doing an out-of-tree build, copy a module map for generated
# header files into the build area.
if (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
- configure_file(module.modulemap.build module.modulemap COPYONLY)
+ configure_file(module.modulemap.build ${LLVM_INCLUDE_DIR}/module.modulemap COPYONLY)
endif (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index abbb8a450da6..70c497be12f5 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -23,7 +23,10 @@
#define LLVM_CXXFLAGS "@LLVM_CXXFLAGS@"
#define LLVM_BUILDMODE "@LLVM_BUILDMODE@"
#define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@"
+#define LLVM_INSTALL_BINDIR "@CMAKE_INSTALL_BINDIR@"
+#define LLVM_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@"
#define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@"
+#define LLVM_INSTALL_CMAKEDIR "@LLVM_INSTALL_CMAKE_DIR@"
#define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@"
#define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@"
#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 8ed88f33ead4..5e7184bab90d 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -363,12 +363,20 @@ int main(int argc, char **argv) {
ActiveIncludeDir = std::string(Path.str());
}
{
- SmallString<256> Path(LLVM_TOOLS_INSTALL_DIR);
+ SmallString<256> Path(LLVM_INSTALL_BINDIR);
sys::fs::make_absolute(ActivePrefix, Path);
ActiveBinDir = std::string(Path.str());
}
- ActiveLibDir = ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX;
- ActiveCMakeDir = ActiveLibDir + "/cmake/llvm";
+ {
+ SmallString<256> Path(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveLibDir = std::string(Path.str());
+ }
+ {
+ SmallString<256> Path(LLVM_INSTALL_CMAKEDIR);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveCMakeDir = std::string(Path.str());
+ }
ActiveIncludeOption = "-I" + ActiveIncludeDir;
}

26
nix/windows/PKGBUILD Normal file
View File

@ -0,0 +1,26 @@
pkgbase="mingw-w64-nac3artiq"
pkgname="mingw-w64-x86_64-nac3artiq"
pkgver=1.0
pkgrel=1
pkgdesc="New ARTIQ compiler 3"
arch=("any")
mingw_arch=("mingw64")
url="https://m-labs.hk"
license=("LGPL")
source=("nac3artiq.pyd")
noextract=("nac3artiq.pyd")
sha256sums=("SKIP")
depends=("mingw-w64-x86_64-python")
prepare() {
true
}
build() {
true
}
package() {
mkdir -p $pkgdir/mingw64/lib/python3.9/site-packages
cp ${srcdir}/nac3artiq.pyd $pkgdir/mingw64/lib/python3.9/site-packages
}

View File

@ -33,14 +33,15 @@ let
};
in rec {
llvm-nac3 = pkgs.stdenvNoCC.mkDerivation rec {
name = "llvm-nac3-msys2";
pname = "llvm-nac3-msys2";
version = "14.0.4";
src-llvm = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/llvm-13.0.1.src.tar.xz";
sha256 = "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ=";
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/llvm-${version}.src.tar.xz";
sha256 = "sha256-646Q362uQHOn+PxjhLrMDdoHJADYK50l2rtSgKc3uiI=";
};
src-clang = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/clang-13.0.1.src.tar.xz";
sha256 = "sha256-eHqeLZn1yHIKoXc+S+AJRhzTDTvUD90kWR5HNGfJF8k=";
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/clang-${version}.src.tar.xz";
sha256 = "sha256-kivcg0FJHU9UVIxRuv13wOc3oxRv4zx2K84xoP0VFZE=";
};
buildInputs = [ pkgs.wineWowPackages.stable ];
phases = [ "unpackPhase" "patchPhase" "configurePhase" "buildPhase" "installPhase" ];
@ -48,6 +49,7 @@ in rec {
''
mkdir llvm
tar xf ${src-llvm} -C llvm --strip-components=1
mv llvm/Modules/* llvm/cmake/modules # work around https://github.com/llvm/llvm-project/issues/53281
mkdir clang
tar xf ${src-clang} -C clang --strip-components=1
cd llvm
@ -63,7 +65,7 @@ in rec {
${silenceFontconfig}
mkdir build
cd build
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_UNWIND_TABLES=OFF -DLLVM_ENABLE_THREADS=OFF -DLLVM_TARGETS_TO_BUILD=X86\;ARM\;RISCV -DLLVM_LINK_LLVM_DYLIB=OFF -DLLVM_ENABLE_FFI=OFF -DFFI_INCLUDE_DIR=fck-cmake -DFFI_LIBRARY_DIR=fck-cmake -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_INSTALL_PREFIX=Z:$out
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_UNWIND_TABLES=OFF -DLLVM_ENABLE_THREADS=OFF -DLLVM_TARGETS_TO_BUILD=X86\;ARM\;RISCV -DLLVM_LINK_LLVM_DYLIB=OFF -DLLVM_ENABLE_FFI=OFF -DFFI_INCLUDE_DIR=fck-cmake -DFFI_LIBRARY_DIR=fck-cmake -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_INSTALL_PREFIX=Z:$out
'';
buildPhase =
''
@ -73,12 +75,18 @@ in rec {
''
wine64 ninja install
'';
dontFixup = true;
};
nac3artiq = pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq";
src = ../.;
cargoLock = { lockFile = ../Cargo.lock; };
nativeBuildInputs = [ pkgs.wineWowPackages.stable pkgs.zip ];
name = "nac3artiq-msys2";
src = ../../.;
cargoLock = {
lockFile = ../../Cargo.lock;
outputHashes = {
"inkwell-0.1.0" = "sha256-gDHV3RDuBU/l1viV1G1YO4kdTylRUctlxZcocX1hIIo=";
};
};
nativeBuildInputs = [ pkgs.wineWowPackages.stable ];
buildPhase =
''
export HOME=`mktemp -d`
@ -90,10 +98,9 @@ in rec {
'';
installPhase =
''
mkdir -p $out $out/nix-support
ln -s target/release/nac3artiq.dll nac3artiq.pyd
zip $out/nac3artiq.zip nac3artiq.pyd
echo file binary-dist $out/nac3artiq.zip >> $out/nix-support/hydra-build-products
mkdir $out $out/nix-support
cp target/release/nac3artiq.dll $out/nac3artiq.pyd
echo file binary-dist $out/nac3artiq.pyd >> $out/nix-support/hydra-build-products
'';
checkPhase =
''
@ -101,6 +108,61 @@ in rec {
'';
dontFixup = true;
};
nac3artiq-pkg = pkgs.stdenvNoCC.mkDerivation {
name = "nac3artiq-msys2-pkg";
nativeBuildInputs = [ pkgs.pacman pkgs.fakeroot pkgs.libarchive pkgs.zstd ];
src = nac3artiq;
phases = [ "buildPhase" "installPhase" ];
buildPhase =
''
ln -s ${./PKGBUILD} PKGBUILD
ln -s $src/nac3artiq.pyd nac3artiq.pyd
makepkg --config ${./makepkg.conf} --nodeps
'';
installPhase =
''
mkdir $out $out/nix-support
cp *.pkg.tar.zst $out
echo file msys2 $out/*.pkg.tar.zst >> $out/nix-support/hydra-build-products
'';
};
lld = pkgs.stdenvNoCC.mkDerivation rec {
pname = "lld-msys2";
version = "14.0.4";
src = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/lld-${version}.src.tar.xz";
sha256 = "sha256-BeEoSmUWbQo6ETm5mT8cYyuCkwUBMWriDIiiBBcMK6Y=";
};
buildInputs = [ pkgs.wineWowPackages.stable ];
phases = [ "unpackPhase" "patchPhase" "configurePhase" "buildPhase" "installPhase" ];
patches = [ ./lld-disable-macho.diff ];
setSourceRoot = # work around https://github.com/llvm/llvm-project/issues/53281
''
mv cmake/Modules/* lld-14.0.4.src/cmake/modules
sourceRoot=lld-14.0.4.src
'';
configurePhase =
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin\;Z:${llvm-nac3}/bin
${silenceFontconfig}
mkdir build
cd build
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=Z:$out
'';
buildPhase =
''
wine64 ninja
'';
installPhase =
''
mkdir -p $out $out/nix-support
cp bin/ld.lld.exe $out
echo file binary-dist $out/ld.lld.exe >> $out/nix-support/hydra-build-products
'';
dontFixup = true;
};
wine-msys2 = pkgs.writeShellScriptBin "wine-msys2"
''
export WINEDEBUG=-all
@ -108,4 +170,12 @@ in rec {
export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 cmd
'';
wine-msys2-build = pkgs.writeShellScriptBin "wine-msys2-build"
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin
${silenceFontconfig}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 $@
'';
}

View File

@ -0,0 +1,34 @@
diff '--color=auto' -Naur lld-14.0.1.src.orig/CMakeLists.txt lld-14.0.1.src/CMakeLists.txt
--- lld-14.0.1.src.orig/CMakeLists.txt 2022-04-12 07:44:22.000000000 +0800
+++ lld-14.0.1.src/CMakeLists.txt 2022-04-18 18:03:22.530746411 +0800
@@ -248,7 +248,6 @@
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
-add_subdirectory(MachO)
add_subdirectory(MinGW)
add_subdirectory(wasm)
diff '--color=auto' -Naur lld-14.0.1.src.orig/tools/lld/CMakeLists.txt lld-14.0.1.src/tools/lld/CMakeLists.txt
--- lld-14.0.1.src.orig/tools/lld/CMakeLists.txt 2022-04-12 07:44:22.000000000 +0800
+++ lld-14.0.1.src/tools/lld/CMakeLists.txt 2022-04-18 18:03:34.434692221 +0800
@@ -14,7 +14,6 @@
lldCommon
lldCOFF
lldELF
- lldMachO
lldMinGW
lldWasm
)
diff '--color=auto' -Naur lld-14.0.1.src.orig/tools/lld/lld.cpp lld-14.0.1.src/tools/lld/lld.cpp
--- lld-14.0.1.src.orig/tools/lld/lld.cpp 2022-04-12 07:44:22.000000000 +0800
+++ lld-14.0.1.src/tools/lld/lld.cpp 2022-04-18 18:04:08.517537288 +0800
@@ -151,8 +151,6 @@
return elf::link;
else if (f == WinLink)
return coff::link;
- else if (f == Darwin)
- return macho::link;
else if (f == Wasm)
return lld::wasm::link;
else

View File

@ -10,7 +10,7 @@ curl -L https://mirror.msys2.org/msys/x86_64/pacman-mirrors-20220205-1-any.pkg.t
curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | grep -v SigLevel | sed s\|/etc/pacman.d\|$MSYS2DIR/etc/pacman.d\|g > $MSYS2DIR/etc/pacman.conf
fakeroot pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf -Syy
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy > $MSYS2DIR/packages.txt
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-setuptools > $MSYS2DIR/packages.txt
echo "{ pkgs } : [" > msys2_packages.nix
while read package; do

2
nix/windows/makepkg.conf Normal file
View File

@ -0,0 +1,2 @@
PKGEXT='.pkg.tar.zst'
SRCEXT='.src.tar.gz'

View File

@ -6,8 +6,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.11-9-any.pkg.tar.zst";
sha256 = "0fb3xbw9a0ah4viwp3a7hr5phnc7mvcl9ba2yjidncpqmspypacx";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.12-1-any.pkg.tar.zst";
sha256 = "1b461ic5s3hjk3y70ldik82ny08rdywn1zfqa8d2jyyvnh4dya77";
})
(pkgs.fetchurl {
@ -16,13 +16,13 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
sha256 = "1ngnjb9vgk295wlwqandm0nhqqdfrp584kx3nfshxkhwmj5gpzxk";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0wyg5ad3fh2lwd7avxvpncipj5wxmp647l43wzr1l3rrkd820yy3";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
sha256 = "1ccipidbsjncdhr48k50ia53dwn7v3ghdl8f1svgwvnh3mrx0bww";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0bnzwgf395fbwbsq8900prj409b081hi0dd76kak6d971xqyy2r4";
})
(pkgs.fetchurl {
@ -46,8 +46,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
sha256 = "0qdy79l5y02lw2xa8i3j6yayhz8a7awfgyyd82pcmbzwx57q2xqb";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "181fm72bi6cs348hixkqjzivizzcsyn2lxvnbqprx4366prjf7nn";
})
(pkgs.fetchurl {
@ -61,8 +61,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
sha256 = "08zwgkrp45y5ry8avz61krasjkk4k4a5rrdz4nd78bbbah84mpgz";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0n3pim85wlsc93y9sh06rnfraqgzbz300sp9hd8n7wgvcsmpj9rx";
})
(pkgs.fetchurl {
@ -86,8 +86,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.7-1-any.pkg.tar.zst";
sha256 = "19qh3kk2kmkkzxirpx1swgfsfb29gy9q8qgdmrzzbwrqrn8vs77j";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.8-1-any.pkg.tar.zst";
sha256 = "1qkw4k61ddaflns5ms0xh0czbx99wxhs0dfbk8sv8by2rkshl51k";
})
(pkgs.fetchurl {
@ -101,8 +101,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.1-1-any.pkg.tar.zst";
sha256 = "00vm6d56ldr1f4h0dn15j0ja17dif45qxlxaqv4x5nw555frklf5";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.2-1-any.pkg.tar.zst";
sha256 = "0p694g8g716zyvcxc48xmdanjyrkp3ir4naqlradrw2szy9bj800";
})
(pkgs.fetchurl {
@ -231,8 +231,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.22.3-2-any.pkg.tar.zst";
sha256 = "1xp8n5s98va7a9cq36d9p49lk32yv9vkvlayc4d4j465xzm21hgp";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.23.0-1-any.pkg.tar.zst";
sha256 = "1gcmm3bd29zfd88ack6xdqwmbskyvhb4ymrfspayk4jfcj4svky5";
})
(pkgs.fetchurl {
@ -266,8 +266,8 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.1-1-any.pkg.tar.zst";
sha256 = "04h4m72mwmad82nzrl5qj9wlsinjs7z7bsbbq17dxkx92aj75p26";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.2-1-any.pkg.tar.zst";
sha256 = "14agi4611h5400j7bqh1w5i5qbhp4hp6apzaddilxq97y37vj90q";
})
(pkgs.fetchurl {
@ -291,12 +291,17 @@
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-1-any.pkg.tar.zst";
sha256 = "1adbbycbvs2nkjhgap92fk3x0vqfjb3ghhvyd1xlnn56c5n0iphf";
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-2-any.pkg.tar.zst";
sha256 = "1cj0j06b2xhcvq9v6jx5cn130r8pkv2xa885f3z3z98kmrifjw0l";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-numpy-1.21.5-1-any.pkg.tar.zst";
sha256 = "10bhfq65nrzxipgy75bqaad74daif4ay06phwvbx70b9j0wm33c3";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-setuptools-59.8.0-2-any.pkg.tar.zst";
sha256 = "1jbvsmh1r00yb6fm8by6z7xp9069f9lkj73jrsnxmflmvpdkjl5c";
})
]