forked from AbleOS/holey-bytes
Compare commits
200 commits
Author | SHA1 | Date | |
---|---|---|---|
08fc9d6ab6 | |||
abcb3434f8 | |||
4ebf1c7996 | |||
3f11e19a91 | |||
51bfbdf7ae | |||
5c8f7c9c79 | |||
3491814b4f | |||
ee434e6135 | |||
9afe191bca | |||
8ededb8612 | |||
9c4b84ce33 | |||
5909837015 | |||
9f67b22aa2 | |||
939d0807fb | |||
888b38ad4c | |||
5275a7e0fd | |||
418fd0039e | |||
d220823d78 | |||
af19f4e30d | |||
1621d93e86 | |||
4b3b6af70e | |||
f59c0c1092 | |||
8ad58ee6b6 | |||
6e8eb059f6 | |||
969ea57e3f | |||
cfd3eac0a8 | |||
a8aba7e7c2 | |||
f05c61a99e | |||
e769fa8dba | |||
b3f858f64b | |||
1584ec7563 | |||
6085177982 | |||
47014c6164 | |||
3702a99d03 | |||
248bdf003a | |||
d3f3fe98e3 | |||
14cf5efaa5 | |||
95496116b0 | |||
86f7d70747 | |||
0516ce68f4 | |||
945e5c70f6 | |||
ec9bb886f8 | |||
127fdb3cc5 | |||
e65c72e19f | |||
1ca9529302 | |||
c0d957e70c | |||
b9b8233a53 | |||
d2fa41039b | |||
9fe8d6bbff | |||
04680c8b7c | |||
a1e692eac7 | |||
8bf2d1a266 | |||
1571938e9f | |||
f7d5bccdd9 | |||
07d4fe416a | |||
b2be007ef0 | |||
bfac81c807 | |||
ef36e21475 | |||
8138d6664f | |||
9f43e3bb92 | |||
6fba7da782 | |||
7837eeb90d | |||
5a7a01ca02 | |||
f9c47f86ad | |||
48a0c8d0b9 | |||
00f6729d31 | |||
dc96c8b10a | |||
91e35b72ee | |||
5aeeedbdce | |||
fae75072f4 | |||
71ba2c2486 | |||
c5d5301b7b | |||
c553c3d9e9 | |||
9ce446b507 | |||
3b4b30b2bd | |||
cf672beb79 | |||
3f6ebdd009 | |||
19aca050ed | |||
d368ac023b | |||
8ea6c5cfcc | |||
e7cd2c0129 | |||
e44d003e7f | |||
a2ca8d98df | |||
a3355a59c0 | |||
b63b832383 | |||
116f045a5f | |||
784d552c1d | |||
24a3aed360 | |||
58ee5c0a56 | |||
9dfb2eb606 | |||
5df4fb8882 | |||
86ca959ea3 | |||
f353bd5882 | |||
cad0a828d0 | |||
fb119bc6eb | |||
aa83ed2ec9 | |||
fb11c94af4 | |||
b030b1eeb7 | |||
4856533b22 | |||
d8d039b67a | |||
b760d9ef75 | |||
e587de1778 | |||
b12579ff65 | |||
0aa355695a | |||
4a857d2317 | |||
2253ac6198 | |||
05a7bd0583 | |||
ab55ec0240 | |||
able | 8353ab58a5 | ||
f527d61c1e | |||
f3879cb013 | |||
e89511b14c | |||
1c135a3050 | |||
f83194359c | |||
becd5c4b7f | |||
37dd13cab2 | |||
bc2dd82eb7 | |||
aa2de502cc | |||
542c69fd60 | |||
95e9270fef | |||
fe5a8631f6 | |||
8892dd729a | |||
a7718e1220 | |||
e079bbd312 | |||
12b9d43754 | |||
397b2a4b1b | |||
12bb7029b4 | |||
a64383e72b | |||
2034152c83 | |||
13714eb513 | |||
4088bd18b1 | |||
e94b812b3b | |||
e5d6b35f66 | |||
e6df9b6b01 | |||
baa70d3f12 | |||
ec4499e519 | |||
085c593add | |||
867a750d8f | |||
b1b6d9eba1 | |||
12be64965f | |||
7058efe75c | |||
afc1c5aac5 | |||
83146cfd61 | |||
bb625a9e19 | |||
81cf39b602 | |||
e4da9cc927 | |||
454b0ffd1c | |||
981c17ff19 | |||
d01e31b203 | |||
9cb273a04b | |||
2e2b7612d9 | |||
f493c2776f | |||
f77bc52465 | |||
f524013c34 | |||
3c86eafe72 | |||
0d87bf8f09 | |||
e5a4561f07 | |||
b71031c146 | |||
dd51961fbb | |||
63f2a0dac0 | |||
4ec88e3397 | |||
f1e715e9bd | |||
80fd0e89b4 | |||
9949086011 | |||
c701eb7b6d | |||
f1deab11c9 | |||
f079daa42d | |||
7cac9382ad | |||
ce2f7d2059 | |||
f5f9060803 | |||
ad7fb5d0fc | |||
d99672b751 | |||
7def052749 | |||
b2eefa5b83 | |||
3c35557872 | |||
b6274f3455 | |||
c61efc3933 | |||
654005eea2 | |||
335e6ec20a | |||
1e02efc1eb | |||
8b98c2ed1b | |||
c353d28be0 | |||
7865d692a1 | |||
29a23cec0c | |||
5dce4df2a1 | |||
42a713aeae | |||
823c78bf74 | |||
c657084451 | |||
63a1c7feb4 | |||
bedffa9b32 | |||
b8032aa840 | |||
65e9f272a8 | |||
d2052cd2a3 | |||
29367d8f8b | |||
a299bad75b | |||
7d48d3beb1 | |||
68c0248189 | |||
0ef74d89cb | |||
1b2b9f899d | |||
455f70db6e |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,5 +9,7 @@ db.sqlite-journal
|
|||
# assets
|
||||
/depell/src/*.gz
|
||||
/depell/src/*.wasm
|
||||
/depell/src/static-pages/*.html
|
||||
#**/*-sv.rs
|
||||
/bytecode/src/instrs.rs
|
||||
/lang/src/testcases.rs
|
||||
|
|
108
Cargo.lock
generated
108
Cargo.lock
generated
|
@ -38,17 +38,11 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
|
@ -235,7 +229,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
"which",
|
||||
|
@ -265,15 +259,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
|
@ -282,9 +267,9 @@ checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.0"
|
||||
version = "1.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
|
||||
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -348,9 +333,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.15"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -591,12 +576,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
|
@ -615,11 +597,10 @@ version = "0.1.0"
|
|||
name = "hblang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.1",
|
||||
"hashbrown 0.15.0",
|
||||
"hbbytecode",
|
||||
"hbvm",
|
||||
"log",
|
||||
"regalloc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -778,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.1",
|
||||
"hashbrown 0.15.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -826,9 +807,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.162"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -863,6 +844,15 @@ version = "0.4.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "1.0.0-alpha.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81"
|
||||
dependencies = [
|
||||
"unicode-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
|
@ -1023,9 +1013,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.25"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
|
||||
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
|
@ -1033,9 +1023,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -1058,19 +1048,6 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.10.2"
|
||||
source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#21c43e3ee182824e92e2b25f1d3c03ed47f9c02b"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
"hashbrown 0.14.5",
|
||||
"log",
|
||||
"rustc-hash 2.0.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
|
@ -1085,9 +1062,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1141,17 +1118,11 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.40"
|
||||
version = "0.38.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
|
@ -1221,18 +1192,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1324,9 +1295,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1484,6 +1455,12 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-id"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
|
@ -1686,6 +1663,7 @@ name = "xtask"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"markdown",
|
||||
"walrus",
|
||||
]
|
||||
|
||||
|
|
|
@ -17,9 +17,7 @@ members = [
|
|||
[workspace.dependencies]
|
||||
hbbytecode = { path = "bytecode", default-features = false }
|
||||
hbvm = { path = "vm", default-features = false }
|
||||
hbxrt = { path = "xrt" }
|
||||
hblang = { path = "lang", default-features = false }
|
||||
hbjit = { path = "jit" }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
@ -5,6 +5,6 @@ edition = "2018"
|
|||
|
||||
[features]
|
||||
default = ["disasm"]
|
||||
std = []
|
||||
disasm = ["std"]
|
||||
disasm = ["alloc"]
|
||||
alloc = []
|
||||
|
||||
|
|
|
@ -98,6 +98,27 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
|
|||
writeln!(generated, " {name} = {id},")?;
|
||||
}
|
||||
writeln!(generated, "}}")?;
|
||||
|
||||
writeln!(generated, "impl {instr} {{")?;
|
||||
writeln!(generated, " pub fn size(self) -> usize {{")?;
|
||||
writeln!(generated, " match self {{")?;
|
||||
let mut instrs = instructions().collect::<Vec<_>>();
|
||||
instrs.sort_unstable_by_key(|&[.., ty, _]| iter_args(ty).map(arg_to_width).sum::<usize>());
|
||||
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| {
|
||||
iter_args(a).map(arg_to_width).sum::<usize>()
|
||||
== iter_args(b).map(arg_to_width).sum::<usize>()
|
||||
}) {
|
||||
let ty = group[0][2];
|
||||
for &[_, name, ..] in group {
|
||||
writeln!(generated, " | {instr}::{name}")?;
|
||||
}
|
||||
generated.pop();
|
||||
let size = iter_args(ty).map(arg_to_width).sum::<usize>() + 1;
|
||||
writeln!(generated, " => {size},")?;
|
||||
}
|
||||
writeln!(generated, " }}")?;
|
||||
writeln!(generated, " }}")?;
|
||||
writeln!(generated, "}}")?;
|
||||
}
|
||||
|
||||
'_arg_kind: {
|
||||
|
|
|
@ -254,8 +254,7 @@ pub fn disasm<'a>(
|
|||
|| global_offset > off + len
|
||||
|| prev
|
||||
.get(global_offset as usize)
|
||||
.map_or(true, |&b| instr_from_byte(b).is_err())
|
||||
|| prev[global_offset as usize] == 0;
|
||||
.is_none_or(|&b| instr_from_byte(b).is_err());
|
||||
has_oob |= local_has_oob;
|
||||
let label = labels.get(&global_offset).unwrap();
|
||||
if local_has_oob {
|
||||
|
|
1
depell/src/icons/download.svg
Normal file
1
depell/src/icons/download.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
|
After Width: | Height: | Size: 279 B |
1
depell/src/icons/run.svg
Normal file
1
depell/src/icons/run.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M320-200v-560l440 280-440 280Zm80-280Zm0 134 210-134-210-134v268Z"/></svg>
|
After Width: | Height: | Size: 190 B |
|
@ -1,16 +1,15 @@
|
|||
* {
|
||||
font-family: var(--font);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
body {
|
||||
--primary: white;
|
||||
--secondary: #EFEFEF;
|
||||
--timestamp: #777777;
|
||||
--primary: light-dark(white, #181A1B);
|
||||
--secondary: light-dark(#EFEFEF, #212425);
|
||||
--timestamp: light-dark(#555555, #AAAAAA);
|
||||
--error: #ff3333;
|
||||
--placeholder: #333333;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
--small-gap: 5px;
|
||||
--font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
|
@ -36,6 +35,11 @@ body {
|
|||
}
|
||||
|
||||
div.preview {
|
||||
margin: var(--small-gap) 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--small-gap);
|
||||
|
||||
div.info {
|
||||
display: flex;
|
||||
gap: var(--small-gap);
|
||||
|
@ -45,10 +49,32 @@ div.preview {
|
|||
}
|
||||
}
|
||||
|
||||
div.stats {
|
||||
div.stat {
|
||||
display: flex;
|
||||
gap: var(--small-gap);
|
||||
|
||||
svg {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
div.code {
|
||||
position: relative;
|
||||
|
||||
nav {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: var(--small-gap);
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
form {
|
||||
|
@ -83,6 +109,8 @@ pre {
|
|||
font-family: var(--monospace);
|
||||
tab-size: 4;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
input {
|
||||
|
@ -108,6 +136,11 @@ button:hover:not(:active) {
|
|||
background: var(--primary);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--monospace);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div#code-editor {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
@ -141,3 +174,40 @@ div#dep-list {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.syn {
|
||||
font-family: var(--monospace);
|
||||
|
||||
&.Comment {
|
||||
color: #939f91;
|
||||
}
|
||||
|
||||
&.Keyword {
|
||||
color: #f85552;
|
||||
}
|
||||
|
||||
&.Identifier,
|
||||
&.Directive {
|
||||
color: #3a94c5;
|
||||
}
|
||||
|
||||
/* &.Number {} */
|
||||
|
||||
&.String {
|
||||
color: #8da101;
|
||||
}
|
||||
|
||||
&.Op,
|
||||
&.Assign {
|
||||
color: #f57d26;
|
||||
}
|
||||
|
||||
&.Paren,
|
||||
&.Bracket,
|
||||
&.Comma,
|
||||
&.Dot,
|
||||
&.Ctor,
|
||||
&.Colon {
|
||||
color: light-dark(#5c6a72, #999999);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ const stack_pointer_offset = 1 << 20;
|
|||
|
||||
/** @param {WebAssembly.Instance} instance @param {Post[]} packages @param {number} fuel
|
||||
* @returns {string} */
|
||||
function compileCode(instance, packages, fuel) {
|
||||
function compileCode(instance, packages, fuel = 100) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
LOG_MESSAGES, LOG_MESSAGES_LEN,
|
||||
|
@ -34,7 +34,7 @@ function compileCode(instance, packages, fuel) {
|
|||
new DataView(memory.buffer).setUint32(INPUT_LEN.value, codeLength, true);
|
||||
|
||||
runWasmFunction(instance, compile_and_run, fuel);
|
||||
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
|
||||
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN).trim();
|
||||
}
|
||||
|
||||
/**@type{WebAssembly.Instance}*/ let fmtInstance;
|
||||
|
@ -44,23 +44,25 @@ async function getFmtInstance() {
|
|||
return fmtInstance ??= (await fmtInstaceFuture).instance;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action
|
||||
* @returns {string | undefined} */
|
||||
/** @param {WebAssembly.Instance} instance @param {string} code @param {"tok" | "fmt" | "minify"} action
|
||||
* @returns {string | Uint8Array | undefined} */
|
||||
function modifyCode(instance, code, action) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
OUTPUT, OUTPUT_LEN,
|
||||
memory, fmt, minify
|
||||
memory, fmt, tok, minify
|
||||
} = instance.exports;
|
||||
|
||||
let funs = { fmt, tok, minify };
|
||||
let fun = funs[action];
|
||||
if (!(true
|
||||
&& memory instanceof WebAssembly.Memory
|
||||
&& INPUT instanceof WebAssembly.Global
|
||||
&& INPUT_LEN instanceof WebAssembly.Global
|
||||
&& OUTPUT instanceof WebAssembly.Global
|
||||
&& OUTPUT_LEN instanceof WebAssembly.Global
|
||||
&& typeof fmt === "function"
|
||||
&& typeof minify === "function"
|
||||
&& funs.hasOwnProperty(action)
|
||||
&& typeof fun === "function"
|
||||
)) never();
|
||||
|
||||
if (action !== "fmt") {
|
||||
|
@ -72,8 +74,14 @@ function modifyCode(instance, code, action) {
|
|||
dw.setUint32(INPUT_LEN.value, code.length, true);
|
||||
new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code));
|
||||
|
||||
return runWasmFunction(instance, action === "fmt" ? fmt : minify) ?
|
||||
bufToString(memory, OUTPUT, OUTPUT_LEN) : undefined;
|
||||
if (!runWasmFunction(instance, fun)) {
|
||||
return undefined;
|
||||
}
|
||||
if (action === "tok") {
|
||||
return bufSlice(memory, OUTPUT, OUTPUT_LEN);
|
||||
} else {
|
||||
return bufToString(memory, OUTPUT, OUTPUT_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,6 +127,15 @@ function packPosts(posts, view) {
|
|||
return len;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Memory} mem
|
||||
* @param {WebAssembly.Global} ptr
|
||||
* @param {WebAssembly.Global} len
|
||||
* @return {Uint8Array} */
|
||||
function bufSlice(mem, ptr, len) {
|
||||
return new Uint8Array(mem.buffer, ptr.value,
|
||||
new DataView(mem.buffer).getUint32(len.value, true));
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Memory} mem
|
||||
* @param {WebAssembly.Global} ptr
|
||||
* @param {WebAssembly.Global} len
|
||||
|
@ -141,12 +158,13 @@ function wireUp(target) {
|
|||
|
||||
const importRe = /@use\s*\(\s*"(([^"]|\\")+)"\s*\)/g;
|
||||
|
||||
/** @param {string} code
|
||||
/** @param {WebAssembly.Instance} fmt
|
||||
* @param {string} code
|
||||
* @param {string[]} roots
|
||||
* @param {Post[]} buf
|
||||
* @param {Set<string>} prevRoots
|
||||
* @returns {void} */
|
||||
function loadCachedPackages(code, roots, buf, prevRoots) {
|
||||
function loadCachedPackages(fmt, code, roots, buf, prevRoots) {
|
||||
buf[0].code = code;
|
||||
|
||||
roots.length = 0;
|
||||
|
@ -162,7 +180,10 @@ function loadCachedPackages(code, roots, buf, prevRoots) {
|
|||
|
||||
for (let imp = roots.pop(); imp !== undefined; imp = roots.pop()) {
|
||||
if (prevRoots.has(imp)) continue; prevRoots.add(imp);
|
||||
buf.push({ path: imp, code: localStorage.getItem("package-" + imp) ?? never() });
|
||||
|
||||
const fmtd = modifyCode(fmt, localStorage.getItem("package-" + imp) ?? never(), "fmt");
|
||||
if (typeof fmtd != "string") never();
|
||||
buf.push({ path: imp, code: fmtd });
|
||||
for (const match of buf[buf.length - 1].code.matchAll(importRe)) {
|
||||
roots.push(match[1]);
|
||||
}
|
||||
|
@ -170,6 +191,61 @@ function loadCachedPackages(code, roots, buf, prevRoots) {
|
|||
}
|
||||
|
||||
/**@type{Set<string>}*/ const prevRoots = new Set();
|
||||
/**@typedef {Object} PackageCtx
|
||||
* @property {AbortController} [cancelation]
|
||||
* @property {string[]} keyBuf
|
||||
* @property {Set<string>} prevParams
|
||||
* @property {HTMLTextAreaElement} [edit] */
|
||||
|
||||
/** @param {string} source @param {Set<string>} importDiff @param {HTMLPreElement} errors @param {PackageCtx} ctx */
|
||||
async function fetchPackages(source, importDiff, errors, ctx) {
|
||||
importDiff.clear();
|
||||
for (const match of source.matchAll(importRe)) {
|
||||
if (localStorage["package-" + match[1]]) continue;
|
||||
importDiff.add(match[1]);
|
||||
}
|
||||
|
||||
if (importDiff.size !== 0 && (ctx.prevParams.size != importDiff.size
|
||||
|| [...ctx.prevParams.keys()].every(e => importDiff.has(e)))) {
|
||||
if (ctx.cancelation) ctx.cancelation.abort();
|
||||
ctx.prevParams.clear();
|
||||
ctx.prevParams = new Set([...importDiff]);
|
||||
ctx.cancelation = new AbortController();
|
||||
|
||||
ctx.keyBuf.length = 0;
|
||||
ctx.keyBuf.push(...importDiff.keys());
|
||||
|
||||
errors.textContent = "fetching: " + ctx.keyBuf.join(", ");
|
||||
|
||||
await fetch(`/code`, {
|
||||
method: "POST",
|
||||
signal: ctx.cancelation.signal,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(ctx.keyBuf),
|
||||
}).then(async e => {
|
||||
try {
|
||||
const json = await e.json();
|
||||
if (e.status == 200) {
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
localStorage["package-" + key] = value;
|
||||
}
|
||||
const missing = ctx.keyBuf.filter(i => json[i] === undefined);
|
||||
if (missing.length !== 0) {
|
||||
errors.textContent = "deps not found: " + missing.join(", ");
|
||||
} else {
|
||||
ctx.cancelation = undefined;
|
||||
ctx.edit?.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
}
|
||||
} catch (er) {
|
||||
errors.textContent = "completely failed to fetch ("
|
||||
+ e.status + "): " + ctx.keyBuf.join(", ");
|
||||
console.error(e, er);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
async function bindCodeEdit(target) {
|
||||
|
@ -188,72 +264,29 @@ async function bindCodeEdit(target) {
|
|||
|
||||
const hbc = await getHbcInstance(), fmt = await getFmtInstance();
|
||||
let importDiff = new Set();
|
||||
const keyBuf = [];
|
||||
/**@type{Post[]}*/
|
||||
const packages = [{ path: "local.hb", code: "" }];
|
||||
const debounce = 100;
|
||||
/**@type{AbortController|undefined}*/
|
||||
let cancelation = undefined;
|
||||
let timeout = 0;
|
||||
const ctx = { keyBuf: [], prevParams: new Set(), edit };
|
||||
|
||||
prevRoots.clear();
|
||||
|
||||
const onInput = () => {
|
||||
importDiff.clear();
|
||||
for (const match of edit.value.matchAll(importRe)) {
|
||||
if (localStorage["package-" + match[1]]) continue;
|
||||
importDiff.add(match[1]);
|
||||
}
|
||||
fetchPackages(edit.value, importDiff, errors, ctx);
|
||||
|
||||
if (importDiff.size !== 0) {
|
||||
if (cancelation) cancelation.abort();
|
||||
cancelation = new AbortController();
|
||||
|
||||
keyBuf.length = 0;
|
||||
keyBuf.push(...importDiff.keys());
|
||||
|
||||
errors.textContent = "fetching: " + keyBuf.join(", ");
|
||||
|
||||
fetch(`/code`, {
|
||||
method: "POST",
|
||||
signal: cancelation.signal,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(keyBuf),
|
||||
}).then(async e => {
|
||||
try {
|
||||
const json = await e.json();
|
||||
if (e.status == 200) {
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
localStorage["package-" + key] = value;
|
||||
}
|
||||
const missing = keyBuf.filter(i => json[i] === undefined);
|
||||
if (missing.length !== 0) {
|
||||
errors.textContent = "deps not found: " + missing.join(", ");
|
||||
} else {
|
||||
cancelation = undefined;
|
||||
edit.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
}
|
||||
} catch (er) {
|
||||
errors.textContent = "completely failed to fetch ("
|
||||
+ e.status + "): " + keyBuf.join(", ");
|
||||
console.error(e, er);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelation && importDiff.size !== 0) {
|
||||
if (ctx.cancelation && importDiff.size !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadCachedPackages(edit.value, keyBuf, packages, prevRoots);
|
||||
loadCachedPackages(fmt, edit.value, ctx.keyBuf, packages, prevRoots);
|
||||
|
||||
errors.textContent = compileCode(hbc, packages, 1);
|
||||
errors.textContent = compileCode(hbc, packages);
|
||||
const minified_size = modifyCode(fmt, edit.value, "minify")?.length;
|
||||
if (minified_size) {
|
||||
codeSize.textContent = (MAX_CODE_SIZE - minified_size) + "";
|
||||
const perc = Math.min(100, Math.floor(100 * (minified_size / MAX_CODE_SIZE)));
|
||||
codeSize.style.color = `color-mix(in srgb, white, var(--error) ${perc}%)`;
|
||||
codeSize.style.color = `color-mix(in srgb, light-dark(black, white), var(--error) ${perc}%)`;
|
||||
}
|
||||
timeout = 0;
|
||||
};
|
||||
|
@ -265,19 +298,86 @@ async function bindCodeEdit(target) {
|
|||
edit.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
|
||||
/** @type {{ [key: string]: (content: string) => Promise<string> | string }} */
|
||||
/**
|
||||
* @type {Array<string>}
|
||||
* to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */
|
||||
const TOK_CLASSES = [
|
||||
'Blank',
|
||||
'Comment',
|
||||
'Keyword',
|
||||
'Identifier',
|
||||
'Directive',
|
||||
'Number',
|
||||
'String',
|
||||
'Op',
|
||||
'Assign',
|
||||
'Paren',
|
||||
'Bracket',
|
||||
'Colon',
|
||||
'Comma',
|
||||
'Dot',
|
||||
'Ctor',
|
||||
];
|
||||
|
||||
/** @type {{ [key: string]: (el: HTMLElement) => void | Promise<void> }} */
|
||||
const applyFns = {
|
||||
timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(),
|
||||
fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"),
|
||||
timestamp: (el) => {
|
||||
const timestamp = el.innerText;
|
||||
const date = new Date(parseInt(timestamp) * 1000);
|
||||
el.innerText = date.toLocaleString();
|
||||
},
|
||||
fmt,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target */
|
||||
async function fmt(target) {
|
||||
const code = target.innerText;
|
||||
const instance = await getFmtInstance();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const fmt = modifyCode(instance, code, 'fmt');
|
||||
if (typeof fmt !== "string") return;
|
||||
const codeBytes = new TextEncoder().encode(fmt);
|
||||
const tok = modifyCode(instance, fmt, 'tok');
|
||||
if (!(tok instanceof Uint8Array)) return;
|
||||
target.innerHTML = '';
|
||||
let start = 0;
|
||||
let kind = tok[0];
|
||||
for (let ii = 1; ii <= tok.length; ii += 1) {
|
||||
// split over same tokens and buffer end
|
||||
if (tok[ii] === kind && ii < tok.length) {
|
||||
continue;
|
||||
}
|
||||
const text = decoder.decode(codeBytes.subarray(start, ii));
|
||||
const textNode = document.createTextNode(text);;
|
||||
if (kind === 0) {
|
||||
target.appendChild(textNode);
|
||||
} else {
|
||||
const el = document.createElement('span');
|
||||
el.classList.add('syn');
|
||||
el.classList.add(TOK_CLASSES[kind]);
|
||||
el.appendChild(textNode);
|
||||
target.appendChild(el);
|
||||
}
|
||||
if (ii == tok.length) {
|
||||
break;
|
||||
}
|
||||
start = ii;
|
||||
kind = tok[ii];
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function execApply(target) {
|
||||
const proises = [];
|
||||
for (const elem of target.querySelectorAll('[apply]')) {
|
||||
if (!(elem instanceof HTMLElement)) continue;
|
||||
const funcname = elem.getAttribute('apply') ?? never();
|
||||
let res = applyFns[funcname](elem.textContent ?? "");
|
||||
if (res instanceof Promise) res.then(c => elem.textContent = c);
|
||||
else elem.textContent = res;
|
||||
const vl = applyFns[funcname](elem);
|
||||
if (vl instanceof Promise) proises.push(vl);
|
||||
}
|
||||
if (target === document.body) {
|
||||
Promise.all(proises).then(() => document.body.hidden = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,10 +432,13 @@ function cacheInputs(target) {
|
|||
}
|
||||
|
||||
/** @param {string} [path] */
|
||||
function updaetTab(path) {
|
||||
function updateTab(path) {
|
||||
console.log(path);
|
||||
for (const elem of document.querySelectorAll("button[hx-push-url]")) {
|
||||
if (elem instanceof HTMLButtonElement)
|
||||
elem.disabled = elem.getAttribute("hx-push-url") === (path ?? window.location.pathname);
|
||||
elem.disabled =
|
||||
elem.getAttribute("hx-push-url") === path
|
||||
|| elem.getAttribute("hx-push-url") === window.location.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,6 +454,7 @@ if (window.location.hostname === 'localhost') {
|
|||
const code = "main:=fn():void{return}";
|
||||
const inst = await getFmtInstance()
|
||||
const fmtd = modifyCode(inst, code, "fmt") ?? never();
|
||||
if (typeof fmtd !== "string") never();
|
||||
const prev = modifyCode(inst, fmtd, "minify") ?? never();
|
||||
if (code != prev) console.error(code, prev);
|
||||
}
|
||||
|
@ -360,7 +464,7 @@ if (window.location.hostname === 'localhost') {
|
|||
code: "main:=fn():int{return 42}",
|
||||
}];
|
||||
const res = compileCode(await getHbcInstance(), posts, 1) ?? never();
|
||||
const expected = "exit code: 42\n";
|
||||
const expected = "exit code: 42";
|
||||
if (expected != res) console.error(expected, res);
|
||||
}
|
||||
})()
|
||||
|
@ -370,8 +474,7 @@ document.body.addEventListener('htmx:afterSwap', (ev) => {
|
|||
if (!(ev.target instanceof HTMLElement)) never();
|
||||
wireUp(ev.target);
|
||||
if (ev.target.tagName == "MAIN" || ev.target.tagName == "BODY")
|
||||
updaetTab(ev['detail'].pathInfo.finalRequestPath);
|
||||
console.log(ev);
|
||||
updateTab(ev['detail'].pathInfo.finalRequestPath);
|
||||
});
|
||||
|
||||
getFmtInstance().then(inst => {
|
||||
|
@ -422,6 +525,30 @@ getFmtInstance().then(inst => {
|
|||
Object.assign(window, { filterCodeDeps });
|
||||
});
|
||||
|
||||
updaetTab();
|
||||
/** @param {HTMLElement} target */
|
||||
function runPost(target) {
|
||||
while (!target.matches("div[class=preview]")) target = target.parentElement ?? never();
|
||||
const code = target.querySelector("pre[apply=fmt]");
|
||||
if (!(code instanceof HTMLPreElement)) never();
|
||||
const output = target.querySelector("pre[id=compiler-output]");
|
||||
if (!(output instanceof HTMLPreElement)) never();
|
||||
|
||||
Promise.all([getHbcInstance(), getFmtInstance()]).then(async ([hbc, fmt]) => {
|
||||
const ctx = { keyBuf: [], prevParams: new Set() };
|
||||
await fetchPackages(code.innerText ?? never(), new Set(), output, ctx);
|
||||
const posts = [{ path: "this", code: "" }];
|
||||
loadCachedPackages(fmt, code.innerText ?? never(), ctx.keyBuf, posts, new Set());
|
||||
output.textContent = compileCode(hbc, posts);
|
||||
output.hidden = false;
|
||||
});
|
||||
|
||||
let author = encodeURIComponent(target.dataset.author ?? never());
|
||||
let name = encodeURIComponent(target.dataset.name ?? never());
|
||||
fetch(`/post/run?author=${author}&name=${name}`, { method: "POST" })
|
||||
}
|
||||
|
||||
Object.assign(window, { runPost });
|
||||
|
||||
updateTab();
|
||||
wireUp(document.body);
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#![feature(iter_collect_into)]
|
||||
#![feature(iter_collect_into, macro_metavar_expr)]
|
||||
use {
|
||||
argon2::{password_hash::SaltString, PasswordVerifier},
|
||||
axum::{
|
||||
body::Bytes,
|
||||
extract::Path,
|
||||
http::{header::COOKIE, request::Parts},
|
||||
extract::{DefaultBodyLimit, Path},
|
||||
http::{header::COOKIE, request::Parts, StatusCode},
|
||||
response::{AppendHeaders, Html},
|
||||
},
|
||||
const_format::formatcp,
|
||||
|
@ -52,22 +52,27 @@ async fn amain() {
|
|||
db::init();
|
||||
|
||||
let router = axum::Router::new()
|
||||
.route("/", get(Index::page))
|
||||
.route("/index.css", static_asset!("text/css", "index.css"))
|
||||
.route("/index.js", static_asset!("text/javascript", "index.js"))
|
||||
.route("/hbfmt.wasm", static_asset!("application/wasm", "hbfmt.wasm"))
|
||||
.route("/hbc.wasm", static_asset!("application/wasm", "hbc.wasm"))
|
||||
.route("/index-view", get(Index::get))
|
||||
.route("/", get(Index::page))
|
||||
.route("/index-view", get(Index::get_with_blog))
|
||||
.route("/blogs/index-view", get(Index::get))
|
||||
.route("/blogs/developing-hblang", get(DevelopingHblang::page))
|
||||
.route("/blogs/developing-hblang-view", get(DevelopingHblang::get))
|
||||
.route("/feed", get(Feed::page))
|
||||
.route("/feed-view", get(Feed::get))
|
||||
.route("/feed-more", post(Feed::more))
|
||||
.route("/profile", get(Profile::page))
|
||||
.route("/profile-view", get(Profile::get))
|
||||
.route("/profile/:name", get(Profile::get_other_page))
|
||||
.route("/profile/password", post(PasswordChange::post))
|
||||
.route("/profile-view/:name", get(Profile::get_other))
|
||||
.route("/post", get(Post::page))
|
||||
.route("/post-view", get(Post::get))
|
||||
.route("/post", post(Post::post))
|
||||
.route("/post/run", post(Post::run))
|
||||
.route("/code", post(fetch_code))
|
||||
.route("/login", get(Login::page))
|
||||
.route("/login-view", get(Login::get))
|
||||
|
@ -85,7 +90,8 @@ async fn amain() {
|
|||
.as_millis();
|
||||
move || async move { id.to_string() }
|
||||
}),
|
||||
);
|
||||
)
|
||||
.layer(DefaultBodyLimit::max(16 * 1024));
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
{
|
||||
|
@ -196,12 +202,45 @@ impl Page for Feed {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Index;
|
||||
macro_rules! decl_static_pages {
|
||||
($(
|
||||
#[derive(PublicPage)]
|
||||
#[page(static = $file:literal)]
|
||||
struct $name:ident;
|
||||
)*) => {
|
||||
const ALL_STATIC_PAGES: [&str; ${count($file)}] = [$($file),*];
|
||||
|
||||
impl PublicPage for Index {
|
||||
fn render_to_buf(self, buf: &mut String) {
|
||||
buf.push_str(include_str!("welcome-page.html"));
|
||||
$(
|
||||
#[derive(Default)]
|
||||
struct $name;
|
||||
|
||||
impl PublicPage for $name {
|
||||
fn render_to_buf(self, buf: &mut String) {
|
||||
buf.push_str(include_str!(concat!("static-pages/", $file, ".html")));
|
||||
}
|
||||
|
||||
async fn page(session: Option<Session>) -> Html<String> {
|
||||
base(|s| blog_base(s, |s| Self::default().render_to_buf(s)), session.as_ref())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
decl_static_pages! {
|
||||
#[derive(PublicPage)]
|
||||
#[page(static = "welcome")]
|
||||
struct Index;
|
||||
#[derive(PublicPage)]
|
||||
#[page(static = "developing-hblang")]
|
||||
struct DevelopingHblang;
|
||||
}
|
||||
|
||||
impl Index {
|
||||
async fn get_with_blog() -> Html<String> {
|
||||
let mut buf = String::new();
|
||||
blog_base(&mut buf, |s| Index.render_to_buf(s));
|
||||
Html(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,11 +278,27 @@ impl Page for Post {
|
|||
<input type="submit" value="submit">
|
||||
<pre id="compiler-output"></pre>
|
||||
</form>
|
||||
!{include_str!("post-page.html")}
|
||||
|
||||
<div id="dep-list">
|
||||
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
|
||||
<section id="deps">
|
||||
"results show here..."
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
!{include_str!("static-pages/post.html")}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Run {
|
||||
author: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Post {
|
||||
pub fn from_row(r: &rusqlite::Row) -> rusqlite::Result<Self> {
|
||||
Ok(Post {
|
||||
|
@ -251,10 +306,25 @@ impl Post {
|
|||
name: r.get(1)?,
|
||||
timestamp: r.get(2)?,
|
||||
code: r.get(3)?,
|
||||
imports: r.get(4)?,
|
||||
runs: r.get(5)?,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
async fn run(
|
||||
session: Session,
|
||||
axum::extract::Query(run): axum::extract::Query<Run>,
|
||||
) -> StatusCode {
|
||||
match db::with(|qes| qes.creata_run.insert((run.name, run.author, session.name))) {
|
||||
Ok(_) => StatusCode::OK,
|
||||
Err(e) => {
|
||||
log::error!("creating run record failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn post(
|
||||
session: Session,
|
||||
axum::Form(mut data): axum::Form<Self>,
|
||||
|
@ -310,7 +380,7 @@ impl Post {
|
|||
impl fmt::Display for Post {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { author, name, timestamp, imports, runs, dependencies, code, .. } = self;
|
||||
write_html! { f <div class="preview">
|
||||
write_html! { f <div class="preview" "data-author"=author "data-name"=name>
|
||||
<div class="info">
|
||||
<span>
|
||||
<a "hx-get"={format_args!("/profile-view/{author}")} href="" "hx-target"="main"
|
||||
|
@ -320,16 +390,21 @@ impl fmt::Display for Post {
|
|||
name
|
||||
</span>
|
||||
<span apply="timestamp">timestamp</span>
|
||||
</div>
|
||||
<div class="stats">
|
||||
for (name, count) in "inps runs deps".split(' ')
|
||||
for (name, count) in [include_str!("icons/download.svg"), include_str!("icons/run.svg"), "deps"]
|
||||
.iter()
|
||||
.zip([imports, runs, dependencies])
|
||||
.filter(|(_, &c)| c != 0)
|
||||
{
|
||||
name ": "<span>count</span>
|
||||
<div class="stat">!name count</div>
|
||||
}
|
||||
</div>
|
||||
<pre apply="fmt">code</pre>
|
||||
<div class="code">
|
||||
<nav>
|
||||
<button onmousedown="runPost(this)">!{include_str!("icons/run.svg")}</button>
|
||||
</nav>
|
||||
<pre apply="fmt">code</pre>
|
||||
</div>
|
||||
<pre hidden id="compiler-output"></pre>
|
||||
if *timestamp == 0 {
|
||||
<button "hx-get"="/post" "hx-swap"="outerHTML"
|
||||
"hx-target"="[preview]">"edit"</button>
|
||||
|
@ -339,6 +414,70 @@ impl fmt::Display for Post {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
struct PasswordChange {
|
||||
old_password: String,
|
||||
new_password: String,
|
||||
#[serde(skip)]
|
||||
error: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl PasswordChange {
|
||||
async fn post(
|
||||
session: Session,
|
||||
axum::Form(mut change): axum::Form<PasswordChange>,
|
||||
) -> Html<String> {
|
||||
db::with(|que| {
|
||||
match que.authenticate.query_row((&session.name,), |r| r.get::<_, String>(1)) {
|
||||
Ok(hash) if verify_password(&hash, &change.old_password).is_err() => {
|
||||
change.error = Some("invalid credentials");
|
||||
}
|
||||
Ok(_) => {
|
||||
let new_hashed = hash_password(&change.new_password);
|
||||
match que
|
||||
.change_passowrd
|
||||
.execute((new_hashed, &session.name))
|
||||
.log("execute update")
|
||||
{
|
||||
None => change.error = Some("intenal server error"),
|
||||
Some(0) => change.error = Some("password is incorrect"),
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
||||
change.error = Some("invalid credentials");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("login queri failed: {e}");
|
||||
change.error = Some("internal server error");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if change.error.is_some() {
|
||||
change.render(&session)
|
||||
} else {
|
||||
PasswordChange::default().render(&session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Page for PasswordChange {
|
||||
fn render_to_buf(self, _: &Session, buf: &mut String) {
|
||||
let Self { old_password, new_password, error } = self;
|
||||
write_html! { (buf)
|
||||
<form "hx-post"="/profile/password" "hx-swap"="outerHTML">
|
||||
if let Some(e) = error { <div class="error">e</div> }
|
||||
<input name="old_password" type="password" autocomplete="old-password"
|
||||
placeholder="old password" value=old_password>
|
||||
<input name="new_password" type="password" autocomplete="new-password" placeholder="new password"
|
||||
value=new_password>
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Profile {
|
||||
other: Option<String>,
|
||||
|
@ -357,20 +496,24 @@ impl Profile {
|
|||
impl Page for Profile {
|
||||
fn render_to_buf(self, session: &Session, buf: &mut String) {
|
||||
db::with(|db| {
|
||||
let name = self.other.as_ref().unwrap_or(&session.name);
|
||||
let iter = db
|
||||
.get_user_posts
|
||||
.query_map((self.other.as_ref().unwrap_or(&session.name),), Post::from_row)
|
||||
.query_map((name,), Post::from_row)
|
||||
.log("get user posts query")
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|p| p.log("user post row"));
|
||||
write_html! { (buf)
|
||||
write_html! { (*buf)
|
||||
if name == &session.name {
|
||||
|b|{PasswordChange::default().render_to_buf(session, b)}
|
||||
}
|
||||
|
||||
for post in iter {
|
||||
!{post}
|
||||
} else {
|
||||
"no posts"
|
||||
}
|
||||
!{include_str!("profile-page.html")}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -399,7 +542,7 @@ struct Login {
|
|||
|
||||
impl PublicPage for Login {
|
||||
fn render_to_buf(self, buf: &mut String) {
|
||||
let Login { name, password, error } = self;
|
||||
let Self { name, password, error } = self;
|
||||
write_html! { (buf)
|
||||
<form "hx-post"="/login" "hx-swap"="outerHTML">
|
||||
if let Some(e) = error { <div class="error">e</div> }
|
||||
|
@ -439,13 +582,12 @@ impl Login {
|
|||
data.error = Some("invalid credentials");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("foo {e}");
|
||||
log::error!("login queri failed: {e}");
|
||||
data.error = Some("internal server error");
|
||||
}
|
||||
});
|
||||
|
||||
if data.error.is_some() {
|
||||
log::error!("what {:?}", data);
|
||||
Err(data.render())
|
||||
} else {
|
||||
Ok(AppendHeaders([
|
||||
|
@ -534,6 +676,29 @@ impl Signup {
|
|||
}
|
||||
}
|
||||
|
||||
fn blog_base(s: &mut String, body: impl FnOnce(&mut String)) {
|
||||
let nav_button = |f: &mut String, name: &str| {
|
||||
write_html! {(f)
|
||||
<button "hx-push-url"={format_args!("/blogs/{name}")}
|
||||
"hx-get"={format_args!("/blogs/{name}-view")}
|
||||
"hx-target"="main#blog"
|
||||
"hx-swap"="innerHTML">name</button>
|
||||
}
|
||||
};
|
||||
|
||||
write_html! {(*s)
|
||||
<nav><section>
|
||||
<button "hx-push-url"="/" "hx-get"="/blogs/index-view"
|
||||
"hx-target"="main#blog" "hx-swap"="innerHTML">"welcome"</button>
|
||||
for name in &ALL_STATIC_PAGES[1..] {
|
||||
|f|{nav_button(f, name)}
|
||||
}
|
||||
</section></nav>
|
||||
<section id="post-form"></section>
|
||||
<main id="blog">|f|{body(f)}</main>
|
||||
}
|
||||
}
|
||||
|
||||
fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<String> {
|
||||
let username = session.map(|s| &s.name);
|
||||
|
||||
|
@ -551,15 +716,17 @@ fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<Strin
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta name="charset" content="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="code dependency hell socila media hblang">
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
<title>"depell"</title>
|
||||
</head>
|
||||
<body>
|
||||
<body hidden>
|
||||
<nav>
|
||||
<button "hx-push-url"="/" "hx-get"="/index-view" "hx-target"="main" "hx-swap"="innerHTML">"depell"</button>
|
||||
<section>
|
||||
if let Some(username) = username {
|
||||
<button "hx-push-url"="/profile" "hx-get"="/profile-view" "hx-target"="main"
|
||||
<button "hx-push-url"={format_args!("/profile/{username}")} "hx-get"="/profile-view" "hx-target"="main"
|
||||
"hx-swap"="innerHTML">username</button>
|
||||
|f|{nav_button(f, "feed"); nav_button(f, "post")}
|
||||
<button "hx-delete"="/login">"logout"</button>
|
||||
|
@ -682,13 +849,58 @@ mod db {
|
|||
gen_queries! {
|
||||
pub struct Queries {
|
||||
register: "INSERT INTO user (name, password_hash) VALUES(?, ?)",
|
||||
change_passowrd: "UPDATE user SET password_hash = ? WHERE name = ?",
|
||||
authenticate: "SELECT name, password_hash FROM user WHERE name = ?",
|
||||
login: "INSERT OR REPLACE INTO session (id, username, expiration) VALUES(?, ?, ?)",
|
||||
logout: "DELETE FROM session WHERE id = ?",
|
||||
get_session: "SELECT username, expiration FROM session WHERE id = ?",
|
||||
get_user_posts: "SELECT author, name, timestamp, code FROM post WHERE author = ?
|
||||
ORDER BY timestamp DESC",
|
||||
get_pots_before: "SELECT author, name, timestamp, code FROM post WHERE timestamp < ?",
|
||||
get_user_posts: "SELECT author, name, timestamp, code, (
|
||||
WITH RECURSIVE roots(name, author, code) AS (
|
||||
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
|
||||
UNION
|
||||
SELECT post.name, post.author, post.code FROM
|
||||
post JOIN import ON post.name = import.from_name
|
||||
AND post.author = import.from_author
|
||||
JOIN roots ON import.to_name = roots.name
|
||||
AND import.to_author = roots.author
|
||||
) SELECT (count(*) - 1) FROM roots
|
||||
) AS imports, (
|
||||
WITH RECURSIVE roots(name, author, code) AS (
|
||||
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
|
||||
UNION
|
||||
SELECT post.name, post.author, post.code FROM post
|
||||
JOIN import ON post.name = import.from_name
|
||||
AND post.author = import.from_author
|
||||
JOIN roots ON import.to_name = roots.name
|
||||
AND import.to_author = roots.author
|
||||
) SELECT count(*) FROM roots
|
||||
JOIN run ON roots.name = run.code_name
|
||||
AND roots.author = run.code_author
|
||||
) AS runs FROM post as outher WHERE author = ? ORDER BY timestamp DESC",
|
||||
// TODO: we might want to cache the recursive queries
|
||||
get_pots_before: "SELECT author, name, timestamp, code, (
|
||||
WITH RECURSIVE roots(name, author, code) AS (
|
||||
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
|
||||
UNION
|
||||
SELECT post.name, post.author, post.code FROM
|
||||
post JOIN import ON post.name = import.from_name
|
||||
AND post.author = import.from_author
|
||||
JOIN roots ON import.to_name = roots.name
|
||||
AND import.to_author = roots.author
|
||||
) SELECT (count(*) - 1) FROM roots
|
||||
) AS imports, (
|
||||
WITH RECURSIVE roots(name, author, code) AS (
|
||||
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
|
||||
UNION
|
||||
SELECT post.name, post.author, post.code FROM post
|
||||
JOIN import ON post.name = import.from_name
|
||||
AND post.author = import.from_author
|
||||
JOIN roots ON import.to_name = roots.name
|
||||
AND import.to_author = roots.author
|
||||
) SELECT count(*) FROM roots
|
||||
JOIN run ON roots.name = run.code_name
|
||||
AND roots.author = run.code_author
|
||||
) as runs FROM post AS outher WHERE timestamp < ?",
|
||||
create_post: "INSERT INTO post (name, author, timestamp, code) VALUES(?, ?, ?, ?)",
|
||||
fetch_deps: "
|
||||
WITH RECURSIVE roots(name, author, code) AS (
|
||||
|
@ -703,6 +915,7 @@ mod db {
|
|||
",
|
||||
create_import: "INSERT INTO import(to_author, to_name, from_author, from_name)
|
||||
VALUES(?, ?, ?, ?)",
|
||||
creata_run: "INSERT OR IGNORE INTO run(code_name, code_author, runner) VALUES(?, ?, ?)",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,8 +942,22 @@ mod db {
|
|||
}
|
||||
|
||||
pub fn init() {
|
||||
const SCHEMA_VERSION: usize = 0;
|
||||
const MIGRATIONS: &[&str] = &[include_str!("migrations/1.sql")];
|
||||
|
||||
let db = rusqlite::Connection::open("db.sqlite").unwrap();
|
||||
db.execute_batch(include_str!("schema.sql")).unwrap();
|
||||
|
||||
let schema_version =
|
||||
db.pragma_query_value(None, "user_version", |v| v.get::<_, usize>(0)).unwrap();
|
||||
|
||||
if schema_version != SCHEMA_VERSION {
|
||||
for &mig in &MIGRATIONS[schema_version..] {
|
||||
db.execute_batch(mig).expect(mig);
|
||||
}
|
||||
db.pragma_update(None, "user_version", SCHEMA_VERSION).unwrap();
|
||||
}
|
||||
|
||||
Queries::new(&db);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<div id="dep-list">
|
||||
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
|
||||
<section id="deps">
|
||||
results show here...
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>About posting code</h3>
|
||||
<p>
|
||||
If you are unfammiliar with <a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a>, refer to the
|
||||
<strong>hblang/README.md</strong> or
|
||||
vizit <a href="/profile/mlokis">mlokis'es posts</a>. Preferably don't edit the code here.
|
||||
</p>
|
||||
|
||||
<h3>Extra textarea features</h3>
|
||||
<ul>
|
||||
<li>proper tab behaviour</li>
|
||||
<li>snap to previous tab boundary on "empty" lines</li>
|
||||
</ul>
|
61
depell/src/static-pages/developing-hblang.md
Normal file
61
depell/src/static-pages/developing-hblang.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# The journey to an optimizing compiler
|
||||
|
||||
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
|
||||
|
||||
Besides being extremely ambitions, the problem with my earliest attempts at making a compiler, is that literally nobody, not even me, was using the language, and so retroactively I am confident, what I implemented was a complex test-case implementation, and not a compiler. I often fall into a trap of implementing edge cases instead of an algorithm that would handle not only the very few thing the tests do but also all the other stuff that users of the language would try.
|
||||
|
||||
Another part of why I was failing for all that time, is that I did the hardest thing first without understanding the core concepts involved in translating written language to IR, god forbid assembly. I wasted a lot of time like this, but at least I learned Rust well. At some point I found a job where I started developing a decentralized network and that fully drawn me away from language development.
|
||||
|
||||
## Completely new approach
|
||||
|
||||
At some point the company I was working for started having financial issues and they were unable to pay me. During that period, I discovered that my love for networking was majorly fueled by the monetary gains associated with it. I burned out, and started to look for things to do with the free time.
|
||||
|
||||
One could say timing was perfect because [`ableos`](https://git.ablecorp.us/AbleOS/ableos) was desperately in need of a sane programming language that compiles to the home made VM ISA used for all software ran in `ableos`, but there was nobody crazy enough to do this. I got terribly nerd sniped, tho I don't regret it. Process of making a language for `ableos` was completely different. Firstly, it needed to be done asap, the lack of a good language blocked everyone form writing drivers for `ableos`, secondly, the moment the language is at least a little bit usable, people other then me will start using it, and lastly, the ISA the language compiles to very simple to emit, understand, and run.
|
||||
|
||||
### Urgency is a bliss
|
||||
|
||||
I actually managed to make the language somewhat work in one week, mainly because my mind set changed. I no longer spent a lot of time designing syntax for elegance, I designed it so that it incredibly easy to parse, meaning I can spent minimal effort implementing the parser, and fully focus on the hard problem of translating AST to instructions. Surprisingly, making everything an expression and not enforcing any arbitrary rules, makes the code you can write incredibly flexible and (most) people love it. One of the decisions I made to save time (or maybe it was an accident) was to make `,;` not enforced, meaning, you are allowed to write delimiters in lists but, as long as it does not change the intent of the code, you can leave them out. In practice, you actually don't need semicolons, unless the next line starts with something sticky like `*x`, int that case you put a semicolon on the previous line to tell the parser where the current expression ends.
|
||||
|
||||
### Only the problem I care about
|
||||
|
||||
Its good to note that writing a parser is no longer interesting for me. I wrote many parsers before and writing one no longer feel rewarding, but more like a chore. The real problem I was excited about was translating AST to instructions, I always ended up overcomplicating this step wit edge cases for every possible scenario that can happen in code, for which there are infinite. But why did I succeed this time? Well all the friction related to getting something that I can execute was so low, I could iterate quickly and realize what I am doing wrong before I burn out. In a week I managed to understand what I was failing to do for years, partly because of all the previous suffering, but mainly because it was so easy to pivot and try new things. And so I managed to make my first single pass compiler, and people immediately started using it.
|
||||
|
||||
### Don't implement features nobody asked for
|
||||
|
||||
Immediately after someone else then me wrote something in `hb` stuff started breaking, over the course of a month I kept fixing bugs and adding new features just fine, and more people started to use the language. All was good and well until I looked into the code. It was incredibly cursed, full of tricks to work around the compiler not doing any optimizations. At that moment I realized the whole compiler after parser needs to be rewritten, I had to implement optimizations, otherwise people wont be able to write readable code that runs fast. All of the features I have added up until now, were a technical dept now. Unless they are all working with optimizations, can't compile the existing code. Yes, if feature exists, be sure as hell it will be used.
|
||||
|
||||
It took around 4 months to reimplement everything make make the optimal code look like what you are used to in other languages. I am really thankful for [sea of nodes](https://github.com/SeaOfNodes), and all the amazing work Cliff Click and others do to make demystify optimizers, It would have taken much longer to for me to figure all the principles out without the exhaustive [tutorial](https://github.com/SeaOfNodes/Simple?tab=readme-ov-file).
|
||||
|
||||
## How my understanding of optimizations changed
|
||||
|
||||
### Optimizations allow us to scale software
|
||||
|
||||
I need to admit, before writing a single pass compiler and later upgrading it to optimizing one, I thought optimizations only affect the quality of final assembly emitted by the compiler. It never occur to me that what the optimizations actually do, is reduce the impact of how you decide to write the code. In a single pass compiler (with zero optimizations), the machine code reflects:
|
||||
|
||||
- order of operations as written in code
|
||||
- whether the value was stored in intermediate locations
|
||||
- exact structure of the control flow and at which point the operations are placed
|
||||
- how many times is something recomputed
|
||||
- operations that only help to convey intent for the reader of the source code
|
||||
- and more I can't think of...
|
||||
|
||||
If you took some code you wrote and then modified it to obfuscate these aspects (in reference to the original code), you would to a subset of what optimizing compiler does. Of course, a good compiler would try hard to improve the metrics its optimizing for, it would:
|
||||
|
||||
- reorder operations to allow the CPU to parallelize them
|
||||
- remove needless stores, or store values directly to places you cant express in code
|
||||
- pull operations out of the loops and into the branches (if it can)
|
||||
- find all common sub-expressions and compute them only once
|
||||
- fold constants as much as possible and use obscure tricks to replace slow instructions if any of the operands are constant
|
||||
- and more...
|
||||
|
||||
In the end, compiler optimizations try to reduce correlation between how the code happens to be written and how well it performs, which is extremely important when you want humans to read the code.
|
||||
|
||||
### Optimizing compilers know more then you
|
||||
|
||||
Optimizing code is a search problem, an optimizer searches the code for patterns that can be rewritten so something more practical for the computer, while preserving the observable behavior of the program. This means it needs enough context about the code to not make a mistake. In fact, the optimizer has so much context, it is able to determine your code is useless. But wait, didn't you write the code because you needed it to do something? Maybe your intention was to break out of the loop after you are done, but the optimizer looked at the code and said, "great, we are so lucky that this integer is always small enough to miss this check by one, DELETE", and then he goes "jackpot, since this loop is now infinite, we don't need this code after it, DELETE". Notice that the optimizer is eager to delete dead code, it did not ask you "Brah, why did you place all your code after an infinite loop?". This is just an example, there are many more cases where modern optimizers just delete all your code because they proven it does something invalid without running it.
|
||||
|
||||
Its stupid but its the world we live in, optimizers are usually a black box you import and feed it the code in a format they understand, they then proceed to optimize it, and if they find a glaring bug they wont tell you, god forbid, they will just molest the code in unspecified ways and spit out whats left. Before writing an optimizer, I did no know this can happen and I did not know this is a problem I pay for with my time, spent figuring out why noting is happening when I run the program.
|
||||
|
||||
But wait its worse! Since optimizers wont ever share the fact you are stupid, we end up with other people painstakingly writing complex linters, that will do a shitty job detecting things that matter, and instead whine about style and other bullcrap (and they suck even at that). If the people who write linters and people who write optimizers swapped the roles, I would be ranting about optimizers instead.
|
||||
|
||||
And so, this is the area where I want to innovate, lets report the dead code to the frontend, and let the compiler frontend filter out the noise and show relevant information in the diagnostics. Refuse to compile the program if you `i /= 0`. Refuse to compile if you `arr[arr.len]`. This is the level of stupid optimizer sees, once it normalizes your code, but proceeds to protect your feelings. My goal so for hblang to relay this to you as much as possible. If we can query for optimizations, we can query for bugs too.
|
8
depell/src/static-pages/post.md
Normal file
8
depell/src/static-pages/post.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
### About posting code
|
||||
|
||||
If you are unfammiliar with [hblang](https://git.ablecorp.us/AbleOS/holey-bytes), refer to the **hblang/README.md** or vizit [mlokis'es posts](/profile/mlokis). Preferably don't edit the code here.
|
||||
|
||||
### Extra textarea features
|
||||
|
||||
- proper tab behaviour
|
||||
- snap to previous tab boundary on "empty" lines
|
11
depell/src/static-pages/welcome.md
Normal file
11
depell/src/static-pages/welcome.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
## Welcome to depell
|
||||
|
||||
Depell (dependency hell) is a simple "social" media site, except that all you can post is [hblang](https://git.ablecorp.us/AbleOS/holey-bytes) code. Instead of likes you run the program, and instead of mentions you import the program as dependency. Run counts even when ran indirectly.
|
||||
|
||||
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
|
||||
|
||||
## Security?
|
||||
|
||||
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
|
||||
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
|
||||
arbitrary JS code, WASM can act as a container inside the JS.
|
|
@ -1,17 +0,0 @@
|
|||
<h1>Welcome to depell</h1>
|
||||
<p>
|
||||
Depell (dependency hell) is a simple "social" media site best compared to twitter, except that all you can post is
|
||||
<a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a> code with no comments allowed. Instead of likes you
|
||||
run the program, and instead of retweets you import the program as dependency. Run counts even when ran indirectly.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
|
||||
</p>
|
||||
|
||||
<h2>Security?</h2>
|
||||
<p>
|
||||
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
|
||||
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
|
||||
arbitrary JS code, WASM can act as a container inside the JS.
|
||||
</p>
|
|
@ -27,8 +27,16 @@ unsafe extern "C" fn fmt() {
|
|||
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tok() {
|
||||
let code = core::slice::from_raw_parts_mut(
|
||||
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||
OUTPUT_LEN = fmt::get_token_kinds(code);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn minify() {
|
||||
let code = core::str::from_raw_parts_mut(core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||
let code = core::str::from_raw_parts_mut(
|
||||
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||
OUTPUT_LEN = fmt::minify(code);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
use {
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::ffi::CStr,
|
||||
hblang::{
|
||||
parser::FileId,
|
||||
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
|
||||
backend::hbvm::HbvmBackend,
|
||||
son::{Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
Ent,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -60,7 +63,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
|
|||
let files = {
|
||||
let paths = files.iter().map(|f| f.path).collect::<Vec<_>>();
|
||||
let mut loader = |path: &str, _: &str, kind| match kind {
|
||||
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap() as FileId),
|
||||
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap()),
|
||||
hblang::parser::FileKind::Embed => Err("embeds are not supported".into()),
|
||||
};
|
||||
files
|
||||
|
@ -79,7 +82,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
|
|||
|
||||
let mut ct = {
|
||||
let mut backend = HbvmBackend::default();
|
||||
Codegen::new(&mut backend, &files, &mut ctx).generate(root as FileId);
|
||||
Codegen::new(&mut backend, &files, &mut ctx).generate(Module::new(root));
|
||||
|
||||
if !ctx.parser.errors.borrow().is_empty() {
|
||||
log::error!("{}", ctx.parser.errors.borrow());
|
||||
|
@ -97,8 +100,15 @@ unsafe fn compile_and_run(mut fuel: usize) {
|
|||
break;
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Ecall) => {
|
||||
let unknown = ct.vm.read_reg(2).0;
|
||||
log::error!("unknown ecall: {unknown}")
|
||||
let kind = ct.vm.read_reg(2).0;
|
||||
match kind {
|
||||
0 => {
|
||||
let str = ct.vm.read_reg(3).0;
|
||||
let str = unsafe { CStr::from_ptr(str as _) };
|
||||
log::error!("{}", str.to_str().unwrap());
|
||||
}
|
||||
unknown => log::error!("unknown ecall: {unknown}"),
|
||||
}
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Timer) => {
|
||||
fuel -= 1;
|
||||
|
|
|
@ -12,17 +12,12 @@ name = "fuzz"
|
|||
path = "src/fuzz_main.rs"
|
||||
|
||||
[dependencies]
|
||||
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry", "allocator-api2"] }
|
||||
hbbytecode = { workspace = true, features = ["disasm"] }
|
||||
hbvm = { workspace = true, features = ["nightly"] }
|
||||
hbvm = { workspace = true, features = ["nightly", "alloc"] }
|
||||
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry"] }
|
||||
log = "0.4.22"
|
||||
|
||||
[dependencies.regalloc2]
|
||||
git = "https://github.com/jakubDoka/regalloc2"
|
||||
branch = "reuse-allocations"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["std", "regalloc2/trace-log"]
|
||||
default = ["std"]
|
||||
std = []
|
||||
no_log = ["log/max_level_off"]
|
||||
|
|
1040
lang/README.md
1040
lang/README.md
File diff suppressed because one or more lines are too long
35
lang/build.rs
Normal file
35
lang/build.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use std::{fmt::Write, iter};
|
||||
|
||||
fn main() {
|
||||
const TEST_FILE: &str = "src/testcases.rs";
|
||||
const INPUT: &str = include_str!("./README.md");
|
||||
|
||||
let mut out = String::new();
|
||||
for (name, code) in block_iter(INPUT) {
|
||||
let name = name.replace(' ', "_");
|
||||
_ = writeln!(
|
||||
out,
|
||||
"#[test] fn {name}() {{ run_codegen_test(\"{name}\", r##\"{code}\"##) }}"
|
||||
);
|
||||
}
|
||||
|
||||
std::fs::write(TEST_FILE, out).unwrap();
|
||||
}
|
||||
|
||||
fn block_iter(mut input: &str) -> impl Iterator<Item = (&str, &str)> {
|
||||
const CASE_PREFIX: &str = "#### ";
|
||||
const CASE_SUFFIX: &str = "\n```hb";
|
||||
|
||||
iter::from_fn(move || loop {
|
||||
let pos = input.find(CASE_PREFIX)?;
|
||||
input = unsafe { input.get_unchecked(pos + CASE_PREFIX.len()..) };
|
||||
let Some((test_name, rest)) = input.split_once(CASE_SUFFIX) else { continue };
|
||||
if !test_name.chars().all(|c| c.is_alphanumeric() || c == '_') {
|
||||
continue;
|
||||
}
|
||||
input = rest;
|
||||
let (body, rest) = input.split_once("```").unwrap_or((input, ""));
|
||||
input = rest;
|
||||
break Some((test_name, body));
|
||||
})
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
--fmt - format all imported source files
|
||||
--fmt-stdout - dont write the formatted file but print it
|
||||
--dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler)
|
||||
--threads <1...> - number of extra threads compiler can use [default: 0]
|
||||
--fmt - format all imported source files
|
||||
--fmt-stdout - dont write the formatted file but print it
|
||||
--dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler)
|
||||
--threads <1...> - number of extra threads compiler can use [default: 0]
|
||||
--path-resolver <name> - choose between builtin path resolvers, options are: ableos
|
||||
|
|
|
@ -1,19 +1,66 @@
|
|||
use {
|
||||
super::{AssemblySpec, Backend, Nid, Node, Nodes},
|
||||
super::{AssemblySpec, Backend},
|
||||
crate::{
|
||||
lexer::TokenKind,
|
||||
parser, reg,
|
||||
son::{debug_assert_matches, write_reloc, Kind, MEM},
|
||||
ty::{self, Loc},
|
||||
Offset, Reloc, Size, TypedReloc, Types,
|
||||
nodes::{Kind, Nid, Nodes, MEM},
|
||||
parser,
|
||||
ty::{self, Loc, Module, Offset, Size, Types},
|
||||
utils::{EntSlice, EntVec},
|
||||
},
|
||||
alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec},
|
||||
core::mem,
|
||||
core::{assert_matches::debug_assert_matches, mem, ops::Range},
|
||||
hbbytecode::{self as instrs, *},
|
||||
reg::Reg,
|
||||
};
|
||||
|
||||
mod my_regalloc;
|
||||
mod their_regalloc;
|
||||
mod regalloc;
|
||||
|
||||
mod reg {
|
||||
pub const STACK_PTR: Reg = 254;
|
||||
pub const ZERO: Reg = 0;
|
||||
pub const RET: Reg = 1;
|
||||
pub const RET_ADDR: Reg = 31;
|
||||
|
||||
pub type Reg = u8;
|
||||
}
|
||||
|
||||
fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) {
|
||||
let value = value.to_ne_bytes();
|
||||
doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TypedReloc {
|
||||
target: ty::Id,
|
||||
reloc: Reloc,
|
||||
}
|
||||
|
||||
// TODO: make into bit struct (width: u2, sub_offset: u3, offset: u27)
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Reloc {
|
||||
offset: Offset,
|
||||
sub_offset: u8,
|
||||
width: u8,
|
||||
}
|
||||
|
||||
impl Reloc {
|
||||
fn new(offset: usize, sub_offset: u8, width: u8) -> Self {
|
||||
Self { offset: offset as u32, sub_offset, width }
|
||||
}
|
||||
|
||||
fn apply_jump(mut self, code: &mut [u8], to: u32, from: u32) -> i64 {
|
||||
self.offset += from;
|
||||
let offset = to as i64 - self.offset as i64;
|
||||
self.write_offset(code, offset);
|
||||
offset
|
||||
}
|
||||
|
||||
fn write_offset(&self, code: &mut [u8], offset: i64) {
|
||||
let bytes = offset.to_ne_bytes();
|
||||
let slice = &mut code[self.offset as usize + self.sub_offset as usize..];
|
||||
slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
struct FuncDt {
|
||||
offset: Offset,
|
||||
|
@ -47,11 +94,10 @@ struct Assembler {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct HbvmBackend {
|
||||
funcs: Vec<FuncDt>,
|
||||
globals: Vec<GlobalDt>,
|
||||
funcs: EntVec<ty::Func, FuncDt>,
|
||||
globals: EntVec<ty::Global, GlobalDt>,
|
||||
asm: Assembler,
|
||||
ralloc: their_regalloc::Regalloc,
|
||||
ralloc_my: my_regalloc::Res,
|
||||
ralloc: regalloc::Res,
|
||||
|
||||
ret_relocs: Vec<Reloc>,
|
||||
relocs: Vec<TypedReloc>,
|
||||
|
@ -98,13 +144,13 @@ impl Backend for HbvmBackend {
|
|||
debug_assert!(self.asm.funcs.is_empty());
|
||||
debug_assert!(self.asm.globals.is_empty());
|
||||
|
||||
self.globals.resize_with(types.ins.globals.len(), Default::default);
|
||||
self.globals.shadow(types.ins.globals.len());
|
||||
|
||||
self.asm.frontier.push(ty::Kind::Func(from).compress());
|
||||
self.asm.frontier.push(from.into());
|
||||
while let Some(itm) = self.asm.frontier.pop() {
|
||||
match itm.expand() {
|
||||
ty::Kind::Func(func) => {
|
||||
let fuc = &mut self.funcs[func as usize];
|
||||
let fuc = &mut self.funcs[func];
|
||||
debug_assert!(!fuc.code.is_empty());
|
||||
if fuc.offset != u32::MAX {
|
||||
continue;
|
||||
|
@ -114,7 +160,7 @@ impl Backend for HbvmBackend {
|
|||
self.asm.frontier.extend(fuc.relocs.iter().map(|r| r.target));
|
||||
}
|
||||
ty::Kind::Global(glob) => {
|
||||
let glb = &mut self.globals[glob as usize];
|
||||
let glb = &mut self.globals[glob];
|
||||
if glb.offset != u32::MAX {
|
||||
continue;
|
||||
}
|
||||
|
@ -128,7 +174,7 @@ impl Backend for HbvmBackend {
|
|||
let init_len = to.len();
|
||||
|
||||
for &func in &self.asm.funcs {
|
||||
let fuc = &mut self.funcs[func as usize];
|
||||
let fuc = &mut self.funcs[func];
|
||||
fuc.offset = to.len() as _;
|
||||
debug_assert!(!fuc.code.is_empty());
|
||||
to.extend(&fuc.code);
|
||||
|
@ -137,18 +183,18 @@ impl Backend for HbvmBackend {
|
|||
let code_length = to.len() - init_len;
|
||||
|
||||
for global in self.asm.globals.drain(..) {
|
||||
self.globals[global as usize].offset = to.len() as _;
|
||||
to.extend(&types.ins.globals[global as usize].data);
|
||||
self.globals[global].offset = to.len() as _;
|
||||
to.extend(&types.ins.globals[global].data);
|
||||
}
|
||||
|
||||
let data_length = to.len() - code_length - init_len;
|
||||
|
||||
for func in self.asm.funcs.drain(..) {
|
||||
let fuc = &self.funcs[func as usize];
|
||||
let fuc = &self.funcs[func];
|
||||
for rel in &fuc.relocs {
|
||||
let offset = match rel.target.expand() {
|
||||
ty::Kind::Func(fun) => self.funcs[fun as usize].offset,
|
||||
ty::Kind::Global(glo) => self.globals[glo as usize].offset,
|
||||
ty::Kind::Func(fun) => self.funcs[fun].offset,
|
||||
ty::Kind::Global(glo) => self.globals[glo].offset,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
rel.reloc.apply_jump(to, offset, fuc.offset);
|
||||
|
@ -158,7 +204,7 @@ impl Backend for HbvmBackend {
|
|||
AssemblySpec {
|
||||
code_length: code_length as _,
|
||||
data_length: data_length as _,
|
||||
entry: self.funcs[from as usize].offset,
|
||||
entry: self.funcs[from].offset,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +213,7 @@ impl Backend for HbvmBackend {
|
|||
mut sluce: &[u8],
|
||||
eca_handler: &mut dyn FnMut(&mut &[u8]),
|
||||
types: &'a Types,
|
||||
files: &'a [parser::Ast],
|
||||
files: &'a EntSlice<Module, parser::Ast>,
|
||||
output: &mut String,
|
||||
) -> Result<(), hbbytecode::DisasmError<'a>> {
|
||||
use hbbytecode::DisasmItem;
|
||||
|
@ -175,11 +221,11 @@ impl Backend for HbvmBackend {
|
|||
.ins
|
||||
.funcs
|
||||
.iter()
|
||||
.zip(&self.funcs)
|
||||
.zip(self.funcs.iter())
|
||||
.filter(|(_, f)| f.offset != u32::MAX)
|
||||
.map(|(f, fd)| {
|
||||
let name = if f.file != u32::MAX {
|
||||
let file = &files[f.file as usize];
|
||||
let name = if f.file != Module::default() {
|
||||
let file = &files[f.file];
|
||||
file.ident_str(f.name)
|
||||
} else {
|
||||
"target_fn"
|
||||
|
@ -191,13 +237,13 @@ impl Backend for HbvmBackend {
|
|||
.ins
|
||||
.globals
|
||||
.iter()
|
||||
.zip(&self.globals)
|
||||
.zip(self.globals.iter())
|
||||
.filter(|(_, g)| g.offset != u32::MAX)
|
||||
.map(|(g, gd)| {
|
||||
let name = if g.file == u32::MAX {
|
||||
let name = if g.file == Module::default() {
|
||||
core::str::from_utf8(&g.data).unwrap_or("invalid utf-8")
|
||||
} else {
|
||||
let file = &files[g.file as usize];
|
||||
let file = &files[g.file];
|
||||
file.ident_str(g.name)
|
||||
};
|
||||
(gd.offset, (name, g.data.len() as Size, DisasmItem::Global))
|
||||
|
@ -210,32 +256,43 @@ impl Backend for HbvmBackend {
|
|||
fn emit_ct_body(
|
||||
&mut self,
|
||||
id: ty::Func,
|
||||
nodes: &mut Nodes,
|
||||
nodes: &Nodes,
|
||||
tys: &Types,
|
||||
files: &[parser::Ast],
|
||||
files: &EntSlice<Module, parser::Ast>,
|
||||
) {
|
||||
self.emit_body(id, nodes, tys, files);
|
||||
let fd = &mut self.funcs[id as usize];
|
||||
let fd = &mut self.funcs[id];
|
||||
fd.code.truncate(fd.code.len() - instrs::jala(0, 0, 0).0);
|
||||
emit(&mut fd.code, instrs::tx());
|
||||
}
|
||||
|
||||
fn emit_body(&mut self, id: ty::Func, nodes: &mut Nodes, tys: &Types, files: &[parser::Ast]) {
|
||||
let sig = tys.ins.funcs[id as usize].sig.unwrap();
|
||||
fn emit_body(
|
||||
&mut self,
|
||||
id: ty::Func,
|
||||
nodes: &Nodes,
|
||||
tys: &Types,
|
||||
files: &EntSlice<Module, parser::Ast>,
|
||||
) {
|
||||
let sig = tys.ins.funcs[id].sig;
|
||||
|
||||
debug_assert!(self.code.is_empty());
|
||||
|
||||
self.offsets.clear();
|
||||
self.offsets.resize(nodes.values.len(), Offset::MAX);
|
||||
self.offsets.resize(nodes.len(), Offset::MAX);
|
||||
|
||||
let mut stack_size = 0;
|
||||
'_compute_stack: {
|
||||
let mems = mem::take(&mut nodes[MEM].outputs);
|
||||
let mems = &nodes[MEM].outputs;
|
||||
for &stck in mems.iter() {
|
||||
if !matches!(nodes[stck].kind, Kind::Stck | Kind::Arg) {
|
||||
debug_assert_matches!(
|
||||
nodes[stck].kind,
|
||||
Kind::Phi | Kind::Return | Kind::Load | Kind::Call { .. } | Kind::Stre
|
||||
Kind::Phi
|
||||
| Kind::Return { .. }
|
||||
| Kind::Load
|
||||
| Kind::Call { .. }
|
||||
| Kind::Stre
|
||||
| Kind::Join
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -248,27 +305,23 @@ impl Backend for HbvmBackend {
|
|||
}
|
||||
self.offsets[stck as usize] = stack_size - self.offsets[stck as usize];
|
||||
}
|
||||
nodes[MEM].outputs = mems;
|
||||
}
|
||||
|
||||
let (saved, tail) = self.emit_body_code(nodes, sig, tys, files);
|
||||
//let (saved, tail) = self.emit_body_code_my(nodes, sig, tys, files);
|
||||
|
||||
if let Some(last_ret) = self.ret_relocs.last()
|
||||
&& last_ret.offset as usize == self.code.len() - 5
|
||||
&& self
|
||||
.jump_relocs
|
||||
.last()
|
||||
.map_or(true, |&(r, _)| self.offsets[r as usize] as usize != self.code.len())
|
||||
.is_none_or(|&(r, _)| self.offsets[r as usize] as usize != self.code.len())
|
||||
{
|
||||
self.code.truncate(self.code.len() - 5);
|
||||
self.ret_relocs.pop();
|
||||
}
|
||||
|
||||
// FIXME: maybe do this incrementally
|
||||
for (nd, rel) in self.jump_relocs.drain(..) {
|
||||
let offset = self.offsets[nd as usize];
|
||||
//debug_assert!(offset < self.code.len() as u32 - 1);
|
||||
rel.apply_jump(&mut self.code, offset, 0);
|
||||
}
|
||||
|
||||
|
@ -319,11 +372,9 @@ impl Backend for HbvmBackend {
|
|||
self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
|
||||
}
|
||||
|
||||
if self.funcs.get(id as usize).is_none() {
|
||||
self.funcs.resize_with(id as usize + 1, Default::default);
|
||||
}
|
||||
self.funcs[id as usize].code = mem::take(&mut self.code);
|
||||
self.funcs[id as usize].relocs = mem::take(&mut self.relocs);
|
||||
self.funcs.shadow(tys.ins.funcs.len());
|
||||
self.funcs[id].code = mem::take(&mut self.code);
|
||||
self.funcs[id].relocs = mem::take(&mut self.relocs);
|
||||
|
||||
debug_assert_eq!(self.ret_relocs.len(), 0);
|
||||
debug_assert_eq!(self.relocs.len(), 0);
|
||||
|
@ -333,27 +384,52 @@ impl Backend for HbvmBackend {
|
|||
}
|
||||
|
||||
impl Nodes {
|
||||
fn cond_op(&self, cnd: Nid) -> CondRet {
|
||||
let Kind::BinOp { op } = self[cnd].kind else { return None };
|
||||
if self.is_unlocked(cnd) {
|
||||
return None;
|
||||
}
|
||||
op.cond_op(self[self[cnd].inputs[1]].ty)
|
||||
}
|
||||
|
||||
fn strip_offset(&self, region: Nid) -> (Nid, Offset) {
|
||||
if matches!(self[region].kind, Kind::BinOp { op: TokenKind::Add | TokenKind::Sub })
|
||||
&& self.is_locked(region)
|
||||
&& let Kind::CInt { value } = self[self[region].inputs[2]].kind
|
||||
{
|
||||
(self[region].inputs[1], value as _)
|
||||
} else {
|
||||
(region, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_never_used(&self, nid: Nid, tys: &Types) -> bool {
|
||||
let node = &self[nid];
|
||||
match node.kind {
|
||||
Kind::CInt { .. } => node.outputs.iter().all(|&o| {
|
||||
Kind::CInt { value: 0 } => false,
|
||||
Kind::CInt { value: 1.. } => node.outputs.iter().all(|&o| {
|
||||
matches!(self[o].kind, Kind::BinOp { op }
|
||||
if op.imm_binop(self[o].ty).is_some()
|
||||
&& self.is_const(self[o].inputs[2])
|
||||
&& op.cond_op(self[o].ty).is_none())
|
||||
}),
|
||||
Kind::BinOp { op: TokenKind::Mul } if node.ty.is_float() => {
|
||||
node.outputs.iter().all(|&n| {
|
||||
self[n].kind == Kind::BinOp { op: TokenKind::Add } && self[n].inputs[1] == nid
|
||||
})
|
||||
}
|
||||
Kind::BinOp { op: TokenKind::Add | TokenKind::Sub } => {
|
||||
self[node.inputs[1]].lock_rc != 0
|
||||
(self.is_locked(node.inputs[1]) && !self[node.inputs[1]].ty.is_float())
|
||||
|| (self.is_const(node.inputs[2])
|
||||
&& node.outputs.iter().all(|&n| self[n].uses_direct_offset_of(nid, tys)))
|
||||
&& node.outputs.iter().all(|&n| self.uses_direct_offset_of(n, nid, tys)))
|
||||
}
|
||||
Kind::BinOp { op } => {
|
||||
op.cond_op(node.ty).is_some()
|
||||
op.cond_op(self[node.inputs[1]].ty).is_some()
|
||||
&& node.outputs.iter().all(|&n| self[n].kind == Kind::If)
|
||||
}
|
||||
Kind::Stck if tys.size_of(node.ty) == 0 => true,
|
||||
Kind::Stck | Kind::Arg => node.outputs.iter().all(|&n| {
|
||||
self[n].uses_direct_offset_of(nid, tys)
|
||||
self.uses_direct_offset_of(n, nid, tys)
|
||||
|| (matches!(self[n].kind, Kind::BinOp { op: TokenKind::Add })
|
||||
&& self.is_never_used(n, tys))
|
||||
}),
|
||||
|
@ -361,22 +437,59 @@ impl Nodes {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn uses_direct_offset_of(&self, nid: Nid, tys: &Types) -> bool {
|
||||
((self.kind == Kind::Stre && self.inputs[2] == nid)
|
||||
|| (self.kind == Kind::Load && self.inputs[1] == nid))
|
||||
&& self.ty.loc(tys) == Loc::Reg
|
||||
fn uses_direct_offset_of(&self, user: Nid, target: Nid, tys: &Types) -> bool {
|
||||
let node = &self[user];
|
||||
((node.kind == Kind::Stre && node.inputs[2] == target)
|
||||
|| (node.kind == Kind::Load && node.inputs[1] == target))
|
||||
&& (node.ty.loc(tys) == Loc::Reg
|
||||
// this means the struct is actually loaded into a register so no BMC needed
|
||||
|| (node.kind == Kind::Load
|
||||
&& !matches!(tys.parama(node.ty).0, Some(PLoc::Ref(..)))
|
||||
&& node.outputs.iter().all(|&o| matches!(self[o].kind, Kind::Call { .. } | Kind::Return { .. }))))
|
||||
}
|
||||
}
|
||||
|
||||
impl HbvmBackend {
|
||||
fn extend(
|
||||
&mut self,
|
||||
base: ty::Id,
|
||||
dest: ty::Id,
|
||||
reg: Reg,
|
||||
tys: &Types,
|
||||
files: &EntSlice<Module, parser::Ast>,
|
||||
) {
|
||||
if reg == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
|
||||
debug_assert!(bsize <= 8, "{}", ty::Display::new(tys, files, base));
|
||||
debug_assert!(dsize <= 8, "{}", ty::Display::new(tys, files, dest));
|
||||
if bsize == dsize {
|
||||
return Default::default();
|
||||
}
|
||||
self.emit(match (base.is_signed(), dest.is_signed()) {
|
||||
(true, true) => {
|
||||
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32][bsize.ilog2() as usize];
|
||||
op(reg, reg)
|
||||
}
|
||||
_ => {
|
||||
let mask = (1u64 << (bsize * 8)) - 1;
|
||||
instrs::andi(reg, reg, mask)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type CondRet = Option<(fn(u8, u8, i16) -> EncodedInstr, bool)>;
|
||||
|
||||
impl TokenKind {
|
||||
fn cmp_against(self) -> Option<u64> {
|
||||
Some(match self {
|
||||
TokenKind::Le | TokenKind::Gt => 1,
|
||||
TokenKind::Ne | TokenKind::Eq => 0,
|
||||
TokenKind::Ge | TokenKind::Lt => (-1i64) as _,
|
||||
Self::Le | Self::Gt => 1,
|
||||
Self::Ne | Self::Eq => 0,
|
||||
Self::Ge | Self::Lt => (-1i64) as _,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -388,22 +501,21 @@ impl TokenKind {
|
|||
let size = ty.simple_size().unwrap();
|
||||
|
||||
let ops = match self {
|
||||
TokenKind::Gt => [instrs::fcmpgt32, instrs::fcmpgt64],
|
||||
TokenKind::Lt => [instrs::fcmplt32, instrs::fcmplt64],
|
||||
Self::Gt => [instrs::fcmpgt32, instrs::fcmpgt64],
|
||||
Self::Lt => [instrs::fcmplt32, instrs::fcmplt64],
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(ops[size.ilog2() as usize - 2])
|
||||
}
|
||||
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn cond_op(self, ty: ty::Id) -> Option<(fn(u8, u8, i16) -> EncodedInstr, bool)> {
|
||||
if ty.is_float() {
|
||||
return None;
|
||||
}
|
||||
fn cond_op(self, ty: ty::Id) -> CondRet {
|
||||
let signed = ty.is_signed();
|
||||
Some((
|
||||
match self {
|
||||
Self::Eq => instrs::jne,
|
||||
Self::Ne => instrs::jeq,
|
||||
_ if ty.is_float() => return None,
|
||||
Self::Le if signed => instrs::jgts,
|
||||
Self::Le => instrs::jgtu,
|
||||
Self::Lt if signed => instrs::jlts,
|
||||
|
@ -412,16 +524,14 @@ impl TokenKind {
|
|||
Self::Ge => instrs::jltu,
|
||||
Self::Gt if signed => instrs::jgts,
|
||||
Self::Gt => instrs::jgtu,
|
||||
Self::Eq => instrs::jne,
|
||||
Self::Ne => instrs::jeq,
|
||||
_ => return None,
|
||||
},
|
||||
matches!(self, Self::Lt | TokenKind::Gt),
|
||||
matches!(self, Self::Lt | Self::Gt),
|
||||
))
|
||||
}
|
||||
|
||||
fn binop(self, ty: ty::Id) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
|
||||
let size = ty.simple_size().unwrap();
|
||||
let size = ty.simple_size().unwrap_or_else(|| panic!("{:?}", ty.expand()));
|
||||
if ty.is_integer() || ty == ty::Id::BOOL || ty.is_pointer() {
|
||||
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, 0, b, c)),*]}; }
|
||||
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(0, a, b, c)),*]}; }
|
||||
|
@ -486,7 +596,7 @@ impl TokenKind {
|
|||
Self::Band => return Some(andi),
|
||||
Self::Bor => return Some(ori),
|
||||
Self::Xor => return Some(xori),
|
||||
Self::Shr if signed => basic_op!(srui8, srui16, srui32, srui64),
|
||||
Self::Shr if signed => basic_op!(srsi8, srsi16, srsi32, srsi64),
|
||||
Self::Shr => basic_op!(srui8, srui16, srui32, srui64),
|
||||
Self::Shl => basic_op!(slui8, slui16, slui32, slui64),
|
||||
_ => return None,
|
||||
|
@ -496,25 +606,84 @@ impl TokenKind {
|
|||
Some(ops[size.ilog2() as usize])
|
||||
}
|
||||
|
||||
fn unop(&self, dst: ty::Id, src: ty::Id) -> Option<fn(u8, u8) -> EncodedInstr> {
|
||||
let src_idx = src.simple_size().unwrap().ilog2() as usize - 2;
|
||||
fn unop(&self, dst: ty::Id, src: ty::Id, tys: &Types) -> Option<fn(u8, u8) -> EncodedInstr> {
|
||||
let src_idx = tys.size_of(src).ilog2() as usize;
|
||||
Some(match self {
|
||||
Self::Sub => instrs::neg,
|
||||
Self::Sub => [
|
||||
|a, b| sub8(a, reg::ZERO, b),
|
||||
|a, b| sub16(a, reg::ZERO, b),
|
||||
|a, b| sub32(a, reg::ZERO, b),
|
||||
|a, b| sub64(a, reg::ZERO, b),
|
||||
][src_idx],
|
||||
Self::Not => instrs::not,
|
||||
Self::Float if dst.is_float() && src.is_integer() => {
|
||||
debug_assert_eq!(dst.simple_size(), src.simple_size());
|
||||
[instrs::itf32, instrs::itf64][src_idx]
|
||||
debug_assert_matches!(
|
||||
(dst.simple_size(), src.simple_size()),
|
||||
(Some(4 | 8), Some(8))
|
||||
);
|
||||
[instrs::itf32, instrs::itf64][dst.simple_size().unwrap().ilog2() as usize - 2]
|
||||
}
|
||||
Self::Number if src.is_float() && dst.is_integer() => {
|
||||
[|a, b| instrs::fti32(a, b, 1), |a, b| instrs::fti64(a, b, 1)][src_idx]
|
||||
[|a, b| instrs::fti32(a, b, 1), |a, b| instrs::fti64(a, b, 1)][src_idx - 2]
|
||||
}
|
||||
Self::Number if src.is_signed() && (dst.is_integer() || dst.is_pointer()) => {
|
||||
[instrs::sxt8, instrs::sxt16, instrs::sxt32][src_idx]
|
||||
}
|
||||
Self::Number
|
||||
if (src.is_unsigned() || src == ty::Id::BOOL)
|
||||
&& (dst.is_integer() || dst.is_pointer()) =>
|
||||
{
|
||||
[
|
||||
|a, b| instrs::andi(a, b, 0xff),
|
||||
|a, b| instrs::andi(a, b, 0xffff),
|
||||
|a, b| instrs::andi(a, b, 0xffffffff),
|
||||
][src_idx]
|
||||
}
|
||||
Self::Float if dst.is_float() && src.is_float() => {
|
||||
[instrs::fc32t64, |a, b| instrs::fc64t32(a, b, 1)][src_idx]
|
||||
[instrs::fc32t64, |a, b| instrs::fc64t32(a, b, 1)][src_idx - 2]
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum PLoc {
|
||||
Reg(Reg, u16),
|
||||
WideReg(Reg, u16),
|
||||
Ref(Reg, u32),
|
||||
}
|
||||
|
||||
impl PLoc {
|
||||
fn reg(self) -> u8 {
|
||||
match self {
|
||||
PLoc::Reg(r, _) | PLoc::WideReg(r, _) | PLoc::Ref(r, _) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ParamAlloc(Range<Reg>);
|
||||
|
||||
impl ParamAlloc {
|
||||
pub fn next(&mut self, ty: ty::Id, tys: &Types) -> Option<PLoc> {
|
||||
Some(match tys.size_of(ty) {
|
||||
0 => return None,
|
||||
size @ 1..=8 => PLoc::Reg(self.0.next().unwrap(), size as _),
|
||||
size @ 9..=16 => PLoc::WideReg(self.0.next_chunk::<2>().unwrap()[0], size as _),
|
||||
size @ 17.. => PLoc::Ref(self.0.next().unwrap(), size),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Types {
|
||||
fn parama(&self, ret: ty::Id) -> (Option<PLoc>, ParamAlloc) {
|
||||
let mut iter = ParamAlloc(1..12);
|
||||
let ret = iter.next(ret, self);
|
||||
iter.0.start += ret.is_none() as u8;
|
||||
(ret, iter)
|
||||
}
|
||||
}
|
||||
|
||||
type EncodedInstr = (usize, [u8; instrs::MAX_SIZE]);
|
||||
fn emit(out: &mut Vec<u8>, (len, instr): EncodedInstr) {
|
||||
out.extend_from_slice(&instr[..len]);
|
||||
|
@ -528,42 +697,7 @@ fn binary_prelude(to: &mut Vec<u8>) {
|
|||
#[derive(Default)]
|
||||
pub struct LoggedMem {
|
||||
pub mem: hbvm::mem::HostMemory,
|
||||
op_buf: Vec<hbbytecode::Oper>,
|
||||
disp_buf: String,
|
||||
prev_instr: Option<hbbytecode::Instr>,
|
||||
}
|
||||
|
||||
impl LoggedMem {
|
||||
unsafe fn display_instr<T>(&mut self, instr: hbbytecode::Instr, addr: hbvm::mem::Address) {
|
||||
let novm: *const hbvm::Vm<Self, 0> = core::ptr::null();
|
||||
let offset = core::ptr::addr_of!((*novm).memory) as usize;
|
||||
let regs = unsafe {
|
||||
&*core::ptr::addr_of!(
|
||||
(*(((self as *mut _ as *mut u8).sub(offset)) as *const hbvm::Vm<Self, 0>))
|
||||
.registers
|
||||
)
|
||||
};
|
||||
|
||||
let mut bytes = core::slice::from_raw_parts(
|
||||
(addr.get() - 1) as *const u8,
|
||||
core::mem::size_of::<T>() + 1,
|
||||
);
|
||||
use core::fmt::Write;
|
||||
hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap();
|
||||
debug_assert!(bytes.is_empty());
|
||||
self.disp_buf.clear();
|
||||
write!(self.disp_buf, "{:<10}", format!("{instr:?}")).unwrap();
|
||||
for (i, op) in self.op_buf.drain(..).enumerate() {
|
||||
if i != 0 {
|
||||
write!(self.disp_buf, ", ").unwrap();
|
||||
}
|
||||
write!(self.disp_buf, "{op:?}").unwrap();
|
||||
if let hbbytecode::Oper::R(r) = op {
|
||||
write!(self.disp_buf, "({})", regs[r as usize].0).unwrap()
|
||||
}
|
||||
}
|
||||
log::trace!("read-typed: {:x}: {}", addr.get(), self.disp_buf);
|
||||
}
|
||||
logger: hbvm::mem::InstrLogger,
|
||||
}
|
||||
|
||||
impl hbvm::mem::Memory for LoggedMem {
|
||||
|
@ -596,20 +730,14 @@ impl hbvm::mem::Memory for LoggedMem {
|
|||
}
|
||||
|
||||
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: hbvm::mem::Address) -> T {
|
||||
if log::log_enabled!(log::Level::Trace) {
|
||||
if core::any::TypeId::of::<u8>() == core::any::TypeId::of::<T>() {
|
||||
if let Some(instr) = self.prev_instr {
|
||||
self.display_instr::<()>(instr, addr);
|
||||
}
|
||||
self.prev_instr = hbbytecode::Instr::try_from(*(addr.get() as *const u8)).ok();
|
||||
} else {
|
||||
let instr = self.prev_instr.take().unwrap();
|
||||
self.display_instr::<T>(instr, addr);
|
||||
}
|
||||
}
|
||||
|
||||
self.mem.prog_read(addr)
|
||||
}
|
||||
|
||||
fn log_instr(&mut self, at: hbvm::mem::Address, regs: &[hbvm::value::Value]) {
|
||||
log::trace!("read-typed: {:x}: {}", at.get(), unsafe {
|
||||
self.logger.display_instr(at, regs)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct AsHex<'a>(&'a [u8]);
|
||||
|
@ -715,7 +843,7 @@ pub struct AbleOsExecutableHeader {
|
|||
|
||||
#[cfg(test)]
|
||||
pub fn test_run_vm(out: &[u8], output: &mut String) {
|
||||
use core::fmt::Write;
|
||||
use core::{ffi::CStr, fmt::Write};
|
||||
|
||||
let mut stack = [0_u64; 1024 * 20];
|
||||
|
||||
|
@ -732,6 +860,12 @@ pub fn test_run_vm(out: &[u8], output: &mut String) {
|
|||
match vm.run() {
|
||||
Ok(hbvm::VmRunOk::End) => break Ok(()),
|
||||
Ok(hbvm::VmRunOk::Ecall) => match vm.read_reg(2).0 {
|
||||
37 => writeln!(
|
||||
output,
|
||||
"{}",
|
||||
unsafe { CStr::from_ptr(vm.read_reg(3).0 as _) }.to_str().unwrap()
|
||||
)
|
||||
.unwrap(),
|
||||
1 => writeln!(output, "ev: Ecall").unwrap(), // compatibility with a test
|
||||
69 => {
|
||||
let [size, align] = [vm.read_reg(3).0 as usize, vm.read_reg(4).0 as usize];
|
1148
lang/src/backend/hbvm/regalloc.rs
Normal file
1148
lang/src/backend/hbvm/regalloc.rs
Normal file
File diff suppressed because it is too large
Load diff
270
lang/src/fmt.rs
270
lang/src/fmt.rs
|
@ -1,9 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
lexer::{self, Lexer, TokenKind},
|
||||
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
|
||||
parser::{
|
||||
self, CommentOr, CtorField, EnumField, Expr, FieldList, ListKind, Poser, Radix,
|
||||
StructField, UnionField,
|
||||
},
|
||||
},
|
||||
core::{
|
||||
fmt::{self},
|
||||
mem,
|
||||
},
|
||||
core::fmt::{self},
|
||||
};
|
||||
|
||||
pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
||||
|
@ -26,6 +32,71 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum TokenGroup {
|
||||
Blank,
|
||||
Comment,
|
||||
Keyword,
|
||||
Identifier,
|
||||
Directive,
|
||||
Number,
|
||||
String,
|
||||
Op,
|
||||
Assign,
|
||||
Paren,
|
||||
Bracket,
|
||||
Colon,
|
||||
Comma,
|
||||
Dot,
|
||||
Ctor,
|
||||
}
|
||||
|
||||
impl TokenKind {
|
||||
fn to_higlight_group(self) -> TokenGroup {
|
||||
use {TokenGroup as TG, TokenKind::*};
|
||||
match self {
|
||||
BSlash | Pound | Eof | Ct => TG::Blank,
|
||||
Comment => TG::Comment,
|
||||
Directive => TG::Directive,
|
||||
Colon => TG::Colon,
|
||||
Semi | Comma => TG::Comma,
|
||||
Dot => TG::Dot,
|
||||
Ctor | Arr | Tupl | TArrow | Range => TG::Ctor,
|
||||
LParen | RParen => TG::Paren,
|
||||
LBrace | RBrace | LBrack | RBrack => TG::Bracket,
|
||||
Number | Float => TG::Number,
|
||||
Under | CtIdent | Ident => TG::Identifier,
|
||||
Tick | Tilde | Que | Not | Mod | Band | Bor | Xor | Mul | Add | Sub | Div | Shl
|
||||
| Shr | Or | And | Lt | Gt | Eq | Le | Ge | Ne => TG::Op,
|
||||
Decl | Assign | BorAss | XorAss | BandAss | AddAss | SubAss | MulAss | DivAss
|
||||
| ModAss | ShrAss | ShlAss => TG::Assign,
|
||||
DQuote | Quote => TG::String,
|
||||
Slf | Defer | Return | If | Else | Loop | Break | Continue | Fn | Idk | Die
|
||||
| Struct | Packed | True | False | Null | Match | Enum | Union | CtLoop => TG::Keyword,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_token_kinds(mut source: &mut [u8]) -> usize {
|
||||
let len = source.len();
|
||||
loop {
|
||||
let src = unsafe { core::str::from_utf8_unchecked(source) };
|
||||
let mut token = lexer::Lexer::new(src).eat();
|
||||
match token.kind {
|
||||
TokenKind::Eof => break,
|
||||
// ???
|
||||
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
||||
_ => {}
|
||||
}
|
||||
let start = token.start as usize;
|
||||
let end = token.end as usize;
|
||||
source[..start].fill(0);
|
||||
source[start..end].fill(token.kind.to_higlight_group() as u8);
|
||||
source = &mut source[end..];
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
pub fn minify(source: &mut str) -> usize {
|
||||
fn needs_space(c: u8) -> bool {
|
||||
matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | 127..)
|
||||
|
@ -39,7 +110,7 @@ pub fn minify(source: &mut str) -> usize {
|
|||
let mut token = lexer::Lexer::new(reader).eat();
|
||||
match token.kind {
|
||||
TokenKind::Eof => break,
|
||||
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
||||
TokenKind::CtIdent | TokenKind::CtLoop | TokenKind::Directive => token.start -= 1,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -135,24 +206,30 @@ impl<'a> Formatter<'a> {
|
|||
return f.write_str(end);
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
self.depth += 1;
|
||||
if !end.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
self.depth += !end.is_empty() as usize;
|
||||
let mut already_indented = end.is_empty();
|
||||
let res = (|| {
|
||||
for (i, stmt) in list.iter().enumerate() {
|
||||
for _ in 0..self.depth {
|
||||
f.write_str("\t")?;
|
||||
if !mem::take(&mut already_indented) {
|
||||
for _ in 0..self.depth {
|
||||
f.write_str("\t")?;
|
||||
}
|
||||
}
|
||||
let add_sep = fmt(self, stmt, f)?;
|
||||
if add_sep {
|
||||
f.write_str(sep)?;
|
||||
}
|
||||
if let Some(expr) = list.get(i + 1)
|
||||
&& let Some(rest) = self.source.get(expr.posi() as usize..)
|
||||
&& let Some(prev) = self.source.get(..expr.posi() as usize)
|
||||
{
|
||||
if insert_needed_semicolon(rest) {
|
||||
if sep.is_empty() && prev.trim_end().ends_with(';') {
|
||||
f.write_str(";")?;
|
||||
}
|
||||
if preserve_newlines(&self.source[..expr.posi() as usize]) > 1 {
|
||||
if count_trailing_newlines(prev) > 1 {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
}
|
||||
|
@ -162,12 +239,14 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
Ok(())
|
||||
})();
|
||||
self.depth -= 1;
|
||||
self.depth -= !end.is_empty() as usize;
|
||||
|
||||
for _ in 0..self.depth {
|
||||
f.write_str("\t")?;
|
||||
if !end.is_empty() {
|
||||
for _ in 0..self.depth {
|
||||
f.write_str("\t")?;
|
||||
}
|
||||
f.write_str(end)?;
|
||||
}
|
||||
f.write_str(end)?;
|
||||
res
|
||||
}
|
||||
|
||||
|
@ -186,6 +265,32 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_fields<F: core::fmt::Write, T: Poser + Copy>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
keyword: &str,
|
||||
trailing_comma: bool,
|
||||
fields: FieldList<T>,
|
||||
fmt: impl Fn(&mut Self, &T, &mut F) -> Result<(), fmt::Error>,
|
||||
) -> fmt::Result {
|
||||
f.write_str(keyword)?;
|
||||
f.write_str(" {")?;
|
||||
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
|
||||
match field {
|
||||
CommentOr::Or(Ok(field)) => fmt(s, field, f)?,
|
||||
CommentOr::Or(Err(scope)) => {
|
||||
s.fmt_list(f, true, "", "", scope, Self::fmt)?;
|
||||
return Ok(false);
|
||||
}
|
||||
CommentOr::Comment { literal, .. } => {
|
||||
f.write_str(literal)?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
}
|
||||
Ok(field.or().is_some())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fmt<F: core::fmt::Write>(&mut self, expr: &Expr, f: &mut F) -> fmt::Result {
|
||||
macro_rules! impl_parenter {
|
||||
($($name:ident => $pat:pat,)*) => {
|
||||
|
@ -202,11 +307,13 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
|
||||
match *expr {
|
||||
Expr::Ct { value, .. } => {
|
||||
f.write_str("$: ")?;
|
||||
Expr::Defer { value, .. } => {
|
||||
f.write_str("defer ")?;
|
||||
self.fmt(value, f)
|
||||
}
|
||||
Expr::Slf { .. } => f.write_str("Self"),
|
||||
Expr::String { literal, .. } => f.write_str(literal),
|
||||
Expr::Char { literal, .. } => f.write_str(literal),
|
||||
Expr::Comment { literal, .. } => f.write_str(literal),
|
||||
Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"),
|
||||
Expr::Embed { path, .. } => write!(f, "@embed(\"{path}\")"),
|
||||
|
@ -215,6 +322,16 @@ impl<'a> Formatter<'a> {
|
|||
f.write_str(".")?;
|
||||
f.write_str(field)
|
||||
}
|
||||
Expr::Range { start, end, .. } => {
|
||||
if let Some(start) = start {
|
||||
self.fmt(start, f)?;
|
||||
}
|
||||
f.write_str("..")?;
|
||||
if let Some(end) = end {
|
||||
self.fmt(end, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Directive { name, args, .. } => {
|
||||
f.write_str("@")?;
|
||||
f.write_str(name)?;
|
||||
|
@ -226,25 +343,44 @@ impl<'a> Formatter<'a> {
|
|||
f.write_str("packed ")?;
|
||||
}
|
||||
|
||||
write!(f, "struct {{")?;
|
||||
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
|
||||
match field {
|
||||
CommentOr::Or(StructField { name, ty, .. }) => {
|
||||
f.write_str(name)?;
|
||||
f.write_str(": ")?;
|
||||
s.fmt(ty, f)?
|
||||
self.fmt_fields(
|
||||
f,
|
||||
"struct",
|
||||
trailing_comma,
|
||||
fields,
|
||||
|s, StructField { name, ty, default_value, .. }, f| {
|
||||
f.write_str(name)?;
|
||||
f.write_str(": ")?;
|
||||
s.fmt(ty, f)?;
|
||||
if let Some(deva) = default_value {
|
||||
f.write_str(" = ")?;
|
||||
s.fmt(deva, f)?;
|
||||
}
|
||||
CommentOr::Comment { literal, .. } => {
|
||||
f.write_str(literal)?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
}
|
||||
Ok(field.or().is_some())
|
||||
})
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
Expr::Union { fields, trailing_comma, .. } => self.fmt_fields(
|
||||
f,
|
||||
"union",
|
||||
trailing_comma,
|
||||
fields,
|
||||
|s, UnionField { name, ty, .. }, f| {
|
||||
f.write_str(name)?;
|
||||
f.write_str(": ")?;
|
||||
s.fmt(ty, f)
|
||||
},
|
||||
),
|
||||
Expr::Enum { variants, trailing_comma, .. } => self.fmt_fields(
|
||||
f,
|
||||
"enum",
|
||||
trailing_comma,
|
||||
variants,
|
||||
|_, EnumField { name, .. }, f| f.write_str(name),
|
||||
),
|
||||
Expr::Ctor { ty, fields, trailing_comma, .. } => {
|
||||
if let Some(ty) = ty {
|
||||
self.fmt_paren(ty, f, unary)?;
|
||||
self.fmt_paren(ty, f, postfix)?;
|
||||
}
|
||||
f.write_str(".{")?;
|
||||
self.fmt_list(
|
||||
|
@ -263,38 +399,43 @@ impl<'a> Formatter<'a> {
|
|||
},
|
||||
)
|
||||
}
|
||||
Expr::Tupl {
|
||||
Expr::List {
|
||||
pos,
|
||||
kind: term,
|
||||
ty: Some(&Expr::Slice { pos: spos, size: Some(&Expr::Number { value, .. }), item }),
|
||||
fields,
|
||||
trailing_comma,
|
||||
} if value as usize == fields.len() => self.fmt(
|
||||
&Expr::Tupl {
|
||||
&Expr::List {
|
||||
pos,
|
||||
kind: term,
|
||||
ty: Some(&Expr::Slice { pos: spos, size: None, item }),
|
||||
fields,
|
||||
trailing_comma,
|
||||
},
|
||||
f,
|
||||
),
|
||||
Expr::Tupl { ty, fields, trailing_comma, .. } => {
|
||||
Expr::List { ty, kind: term, fields, trailing_comma, .. } => {
|
||||
if let Some(ty) = ty {
|
||||
self.fmt_paren(ty, f, unary)?;
|
||||
self.fmt_paren(ty, f, postfix)?;
|
||||
}
|
||||
f.write_str(".(")?;
|
||||
self.fmt_list(f, trailing_comma, ")", ",", fields, Self::fmt)
|
||||
let (start, end) = match term {
|
||||
ListKind::Tuple => (".(", ")"),
|
||||
ListKind::Array => (".[", "]"),
|
||||
};
|
||||
f.write_str(start)?;
|
||||
self.fmt_list(f, trailing_comma, end, ",", fields, Self::fmt)
|
||||
}
|
||||
Expr::Slice { item, size, .. } => {
|
||||
f.write_str("[")?;
|
||||
self.fmt(item, f)?;
|
||||
if let Some(size) = size {
|
||||
f.write_str("; ")?;
|
||||
self.fmt(size, f)?;
|
||||
}
|
||||
f.write_str("]")
|
||||
f.write_str("]")?;
|
||||
self.fmt_paren(item, f, unary)
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
self.fmt(base, f)?;
|
||||
self.fmt_paren(base, f, postfix)?;
|
||||
f.write_str("[")?;
|
||||
self.fmt(index, f)?;
|
||||
f.write_str("]")
|
||||
|
@ -316,8 +457,18 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Loop { body, .. } => {
|
||||
f.write_str("loop ")?;
|
||||
Expr::Match { value, branches, .. } => {
|
||||
f.write_str("match ")?;
|
||||
self.fmt(value, f)?;
|
||||
f.write_str(" {")?;
|
||||
self.fmt_list(f, true, "}", ",", branches, |s, br, f| {
|
||||
s.fmt(&br.pat, f)?;
|
||||
f.write_str(" => ")?;
|
||||
s.fmt(&br.body, f)
|
||||
})
|
||||
}
|
||||
Expr::Loop { body, unrolled, .. } => {
|
||||
f.write_str(if unrolled { "$loop " } else { "loop " })?;
|
||||
self.fmt(body, f)
|
||||
}
|
||||
Expr::Closure { ret, body, args, .. } => {
|
||||
|
@ -407,7 +558,7 @@ impl<'a> Formatter<'a> {
|
|||
prev.rfind(|c: char| c.is_ascii_whitespace()).map_or(prev.len(), |i| i + 1);
|
||||
let exact_bound = lexer::Lexer::new(&prev[estimate_bound..]).last().start;
|
||||
prev = &prev[..exact_bound as usize + estimate_bound];
|
||||
if preserve_newlines(prev) > 0 {
|
||||
if count_trailing_newlines(prev) > 0 {
|
||||
f.write_str("\n")?;
|
||||
for _ in 0..self.depth + 1 {
|
||||
f.write_str("\t")?;
|
||||
|
@ -415,7 +566,9 @@ impl<'a> Formatter<'a> {
|
|||
f.write_str(op.name())?;
|
||||
f.write_str(" ")?;
|
||||
} else {
|
||||
f.write_str(" ")?;
|
||||
if op != TokenKind::Colon {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
f.write_str(op.name())?;
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
|
@ -430,15 +583,10 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn preserve_newlines(source: &str) -> usize {
|
||||
pub fn count_trailing_newlines(source: &str) -> usize {
|
||||
source[source.trim_end().len()..].bytes().filter(|&c| c == b'\n').count()
|
||||
}
|
||||
|
||||
pub fn insert_needed_semicolon(source: &str) -> bool {
|
||||
let kind = lexer::Lexer::new(source).eat().kind;
|
||||
kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl)
|
||||
}
|
||||
|
||||
impl core::fmt::Display for parser::Ast {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt_file(self.exprs(), &self.file, f)
|
||||
|
@ -449,14 +597,14 @@ pub fn fmt_file(exprs: &[Expr], file: &str, f: &mut impl fmt::Write) -> fmt::Res
|
|||
for (i, expr) in exprs.iter().enumerate() {
|
||||
Formatter::new(file).fmt(expr, f)?;
|
||||
if let Some(expr) = exprs.get(i + 1)
|
||||
&& let Some(rest) = file.get(expr.pos() as usize..)
|
||||
&& let Some(prefix) = file.get(..expr.pos() as usize)
|
||||
{
|
||||
if insert_needed_semicolon(rest) {
|
||||
write!(f, ";")?;
|
||||
if prefix.trim_end().ends_with(';') {
|
||||
f.write_str(";")?;
|
||||
}
|
||||
|
||||
if preserve_newlines(&file[..expr.pos() as usize]) > 1 {
|
||||
writeln!(f)?;
|
||||
if count_trailing_newlines(prefix) > 1 {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,15 +630,7 @@ pub mod test {
|
|||
|
||||
let mut ctx = Ctx::default();
|
||||
let ast = parser::Ast::new(ident, minned, &mut ctx, &mut parser::no_loader);
|
||||
//log::error!(
|
||||
// "{} / {} = {} | {} / {} = {}",
|
||||
// ast.mem.size(),
|
||||
// input.len(),
|
||||
// ast.mem.size() as f32 / input.len() as f32,
|
||||
// ast.mem.size(),
|
||||
// ast.file.len(),
|
||||
// ast.mem.size() as f32 / ast.file.len() as f32
|
||||
//);
|
||||
log::info!("{}", ctx.errors.borrow());
|
||||
let mut output = String::new();
|
||||
write!(output, "{ast}").unwrap();
|
||||
|
||||
|
|
219
lang/src/fs.rs
219
lang/src/fs.rs
|
@ -1,12 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
parser::{self, Ast, Ctx, FileKind},
|
||||
son::{self, hbvm::HbvmBackend},
|
||||
backend::hbvm::HbvmBackend,
|
||||
parser::{Ast, Ctx, FileKind},
|
||||
son::{self},
|
||||
ty, FnvBuildHasher,
|
||||
},
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::{fmt::Write, num::NonZeroUsize, ops::Deref},
|
||||
hashbrown::hash_map,
|
||||
std::{
|
||||
borrow::ToOwned,
|
||||
collections::VecDeque,
|
||||
eprintln,
|
||||
ffi::OsStr,
|
||||
|
@ -17,6 +20,8 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
type HashMap<K, V> = hashbrown::HashMap<K, V, FnvBuildHasher>;
|
||||
|
||||
pub struct Logger;
|
||||
|
||||
impl log::Log for Logger {
|
||||
|
@ -33,19 +38,51 @@ impl log::Log for Logger {
|
|||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
pub const ABLEOS_PATH_RESOLVER: PathResolver =
|
||||
&|mut path: &str, mut from: &str, tmp: &mut PathBuf| {
|
||||
tmp.clear();
|
||||
|
||||
path = match path {
|
||||
"stn" => {
|
||||
from = "";
|
||||
"./sysdata/libraries/stn/src/lib.hb"
|
||||
}
|
||||
_ => path,
|
||||
};
|
||||
|
||||
match path.split_once(':') {
|
||||
Some(("lib", p)) => tmp.extend(["./sysdata/libraries", p, "src/lib.hb"]),
|
||||
Some(("stn", p)) => {
|
||||
tmp.extend(["./sysdata/libraries/stn/src", &(p.to_owned() + ".hb")])
|
||||
}
|
||||
Some(("sysdata", p)) => tmp.extend(["./sysdata", p]),
|
||||
None => match Path::new(from).parent() {
|
||||
Some(parent) => tmp.extend([parent, Path::new(path)]),
|
||||
None => tmp.push(path),
|
||||
},
|
||||
_ => panic!("path: '{path}' is invalid: unexpected ':'"),
|
||||
};
|
||||
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Options {
|
||||
pub struct Options<'a> {
|
||||
pub fmt: bool,
|
||||
pub fmt_stdout: bool,
|
||||
pub dump_asm: bool,
|
||||
pub extra_threads: usize,
|
||||
pub resolver: Option<PathResolver<'a>>,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn from_args(args: &[&str]) -> std::io::Result<Self> {
|
||||
impl<'a> Options<'a> {
|
||||
pub fn from_args(
|
||||
args: &[&str],
|
||||
out: &mut Vec<u8>,
|
||||
resolvers: &'a [(&str, PathResolver)],
|
||||
) -> std::io::Result<Self> {
|
||||
if args.contains(&"--help") || args.contains(&"-h") {
|
||||
log::error!("Usage: hbc [OPTIONS...] <FILE>");
|
||||
log::error!(include_str!("../command-help.txt"));
|
||||
writeln!(out, "Usage: hbc [OPTIONS...] <FILE>")?;
|
||||
writeln!(out, include_str!("../command-help.txt"))?;
|
||||
return Err(std::io::ErrorKind::Other.into());
|
||||
}
|
||||
|
||||
|
@ -58,65 +95,91 @@ impl Options {
|
|||
.position(|&a| a == "--threads")
|
||||
.map(|i| {
|
||||
args[i + 1].parse::<NonZeroUsize>().map_err(|e| {
|
||||
std::io::Error::other(format!("--threads expects non zero integer: {e}"))
|
||||
writeln!(out, "--threads expects non zero integer: {e}")
|
||||
.err()
|
||||
.unwrap_or(std::io::ErrorKind::Other.into())
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
.map_or(1, NonZeroUsize::get)
|
||||
- 1,
|
||||
resolver: args
|
||||
.iter()
|
||||
.position(|&a| a == "--path-resolver")
|
||||
.map(|i| {
|
||||
resolvers.iter().find(|&&(n, _)| args[i + 1] == n).map(|&(_, r)| r).ok_or_else(
|
||||
|| {
|
||||
writeln!(
|
||||
out,
|
||||
"--path-resolver can only be one of: {}",
|
||||
resolvers
|
||||
.iter()
|
||||
.map(|&(n, _)| n)
|
||||
.intersperse(", ")
|
||||
.collect::<String>()
|
||||
)
|
||||
.err()
|
||||
.unwrap_or(std::io::ErrorKind::Other.into())
|
||||
},
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std::io::Result<()> {
|
||||
let parsed = parse_from_fs(options.extra_threads, root_file)?;
|
||||
pub fn run_compiler(
|
||||
root_file: &str,
|
||||
options: Options,
|
||||
out: &mut Vec<u8>,
|
||||
warnings: &mut String,
|
||||
) -> std::io::Result<()> {
|
||||
let parsed = parse_from_fs(
|
||||
options.extra_threads,
|
||||
root_file,
|
||||
options.resolver.unwrap_or(&default_resolve),
|
||||
)?;
|
||||
|
||||
fn format_ast(ast: parser::Ast) -> std::io::Result<()> {
|
||||
let mut output = String::new();
|
||||
write!(output, "{ast}").unwrap();
|
||||
if ast.file.deref().trim() != output.as_str().trim() {
|
||||
std::fs::write(&*ast.path, output)?;
|
||||
}
|
||||
Ok(())
|
||||
if (options.fmt || options.fmt_stdout) && !parsed.errors.is_empty() {
|
||||
*out = parsed.errors.into_bytes();
|
||||
return Err(std::io::Error::other("fmt fialed (errors are in out)"));
|
||||
}
|
||||
|
||||
if options.fmt {
|
||||
if !parsed.errors.is_empty() {
|
||||
*out = parsed.errors.into_bytes();
|
||||
return Err(std::io::Error::other("parsing fialed"));
|
||||
}
|
||||
|
||||
for parsed in parsed.ast {
|
||||
format_ast(parsed)?;
|
||||
let mut output = String::new();
|
||||
for ast in parsed.ast {
|
||||
write!(output, "{ast}").unwrap();
|
||||
if ast.file.deref().trim() != output.as_str().trim() {
|
||||
std::fs::write(&*ast.path, &output)?;
|
||||
}
|
||||
output.clear();
|
||||
}
|
||||
} else if options.fmt_stdout {
|
||||
if !parsed.errors.is_empty() {
|
||||
*out = parsed.errors.into_bytes();
|
||||
return Err(std::io::Error::other("parsing fialed"));
|
||||
}
|
||||
|
||||
let ast = parsed.ast.into_iter().next().unwrap();
|
||||
write!(out, "{ast}").unwrap();
|
||||
write!(out, "{}", &parsed.ast[0])?;
|
||||
} else {
|
||||
let mut backend = HbvmBackend::default();
|
||||
|
||||
let mut ctx = crate::son::CodegenCtx::default();
|
||||
*ctx.parser.errors.get_mut() = parsed.errors;
|
||||
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
|
||||
|
||||
codegen.push_embeds(parsed.embeds);
|
||||
codegen.generate(0);
|
||||
codegen.generate(ty::Module::MAIN);
|
||||
|
||||
*warnings = core::mem::take(&mut *codegen.warnings.borrow_mut());
|
||||
|
||||
if !codegen.errors.borrow().is_empty() {
|
||||
log::error!("{}", codegen.errors.borrow());
|
||||
return Err(std::io::Error::other("compilation faoled"));
|
||||
drop(codegen);
|
||||
*out = ctx.parser.errors.into_inner().into_bytes();
|
||||
return Err(std::io::Error::other("compilation faoled (errors are in out)"));
|
||||
}
|
||||
|
||||
codegen.assemble(out);
|
||||
|
||||
if options.dump_asm {
|
||||
let mut disasm = String::new();
|
||||
codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()))?;
|
||||
let err = codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()));
|
||||
*out = disasm.into_bytes();
|
||||
err?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,45 +277,53 @@ pub struct Loaded {
|
|||
errors: String,
|
||||
}
|
||||
|
||||
pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
||||
fn resolve(path: &str, from: &str, tmp: &mut PathBuf) -> Result<PathBuf, CantLoadFile> {
|
||||
tmp.clear();
|
||||
match Path::new(from).parent() {
|
||||
Some(parent) => tmp.extend([parent, Path::new(path)]),
|
||||
None => tmp.push(path),
|
||||
};
|
||||
fn default_resolve(path: &str, from: &str, tmp: &mut PathBuf) -> Result<PathBuf, CantLoadFile> {
|
||||
tmp.clear();
|
||||
match Path::new(from).parent() {
|
||||
Some(parent) => tmp.extend([parent, Path::new(path)]),
|
||||
None => tmp.push(path),
|
||||
};
|
||||
|
||||
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
|
||||
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
|
||||
}
|
||||
|
||||
/// fn(path, from, tmp)
|
||||
pub type PathResolver<'a> =
|
||||
&'a (dyn Fn(&str, &str, &mut PathBuf) -> Result<PathBuf, CantLoadFile> + Send + Sync);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CantLoadFile {
|
||||
pub path: PathBuf,
|
||||
pub source: io::Error,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CantLoadFile {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "can't load file: {}", display_rel_path(&self.path),)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CantLoadFile {
|
||||
path: PathBuf,
|
||||
source: io::Error,
|
||||
impl core::error::Error for CantLoadFile {
|
||||
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
|
||||
Some(&self.source)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CantLoadFile {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "can't load file: {}", display_rel_path(&self.path),)
|
||||
}
|
||||
impl From<CantLoadFile> for io::Error {
|
||||
fn from(e: CantLoadFile) -> Self {
|
||||
io::Error::new(io::ErrorKind::InvalidData, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::error::Error for CantLoadFile {
|
||||
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
|
||||
Some(&self.source)
|
||||
}
|
||||
}
|
||||
pub fn parse_from_fs(
|
||||
extra_threads: usize,
|
||||
root: &str,
|
||||
resolve: PathResolver,
|
||||
) -> io::Result<Loaded> {
|
||||
type Task = (usize, PathBuf);
|
||||
|
||||
impl From<CantLoadFile> for io::Error {
|
||||
fn from(e: CantLoadFile) -> Self {
|
||||
io::Error::new(io::ErrorKind::InvalidData, e)
|
||||
}
|
||||
}
|
||||
|
||||
type Task = (u32, PathBuf);
|
||||
|
||||
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
|
||||
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
|
||||
let seen_modules = Mutex::new(HashMap::<PathBuf, usize>::default());
|
||||
let seen_embeds = Mutex::new(HashMap::<PathBuf, usize>::default());
|
||||
let tasks = TaskQueue::<Task>::new(extra_threads + 1);
|
||||
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
|
||||
let embeds = Mutex::new(Vec::<Vec<u8>>::new());
|
||||
|
@ -271,7 +342,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
|||
}
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
physiscal_path = entry.insert_entry(len as _).key().clone();
|
||||
len as u32
|
||||
len
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -296,7 +367,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
|||
}
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
physiscal_path = entry.insert_entry(len as _).key().clone();
|
||||
len as u32
|
||||
len
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -311,10 +382,10 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
|||
)
|
||||
})?;
|
||||
let mut embeds = embeds.lock().unwrap();
|
||||
if id as usize >= embeds.len() {
|
||||
embeds.resize(id as usize + 1, Default::default());
|
||||
if id >= embeds.len() {
|
||||
embeds.resize(id + 1, Default::default());
|
||||
}
|
||||
embeds[id as usize] = content;
|
||||
embeds[id] = content;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
@ -338,9 +409,9 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
|||
while let Some(task @ (indx, ..)) = tasks.pop() {
|
||||
let res = execute_task(&mut ctx, task, &mut tmp);
|
||||
let mut ast = ast.lock().unwrap();
|
||||
let len = ast.len().max(indx as usize + 1);
|
||||
let len = ast.len().max(indx + 1);
|
||||
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
|
||||
ast[indx as usize] = res;
|
||||
ast[indx] = res;
|
||||
}
|
||||
ctx.errors.into_inner()
|
||||
};
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::hbvm::HbvmBackend,
|
||||
lexer::TokenKind,
|
||||
parser,
|
||||
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
|
||||
son::{Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
},
|
||||
alloc::string::String,
|
||||
core::{fmt::Write, hash::BuildHasher, ops::Range},
|
||||
|
@ -135,6 +137,6 @@ pub fn fuzz(seed_range: Range<u64>) {
|
|||
|
||||
let mut backend = HbvmBackend::default();
|
||||
let mut cdg = Codegen::new(&mut backend, core::slice::from_ref(&parsed), &mut ctx);
|
||||
cdg.generate(0);
|
||||
cdg.generate(Module::MAIN);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ macro_rules! gen_token_kind {
|
|||
#[keywords] $(
|
||||
$keyword:ident = $keyword_lit:literal,
|
||||
)*
|
||||
#[const_keywords] $(
|
||||
$const_keyword:ident = $const_keyword_lit:literal,
|
||||
)*
|
||||
#[punkt] $(
|
||||
$punkt:ident = $punkt_lit:literal,
|
||||
)*
|
||||
|
@ -56,6 +59,7 @@ macro_rules! gen_token_kind {
|
|||
match *self {
|
||||
$( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
|
||||
$( Self::$keyword => stringify!($keyword_lit), )*
|
||||
$( Self::$const_keyword => concat!('$', $const_keyword_lit), )*
|
||||
$( Self::$punkt => stringify!($punkt_lit), )*
|
||||
$($( Self::$op => $op_lit,
|
||||
$(Self::$assign => concat!($op_lit, "="),)?)*)*
|
||||
|
@ -72,12 +76,23 @@ macro_rules! gen_token_kind {
|
|||
} + 1)
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
fn from_ident(ident: &[u8]) -> Self {
|
||||
$(const $keyword: &[u8] = $keyword_lit.as_bytes();)*
|
||||
match ident {
|
||||
$($keyword_lit => Self::$keyword,)*
|
||||
$($keyword => Self::$keyword,)*
|
||||
_ => Self::Ident,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
fn from_ct_ident(ident: &[u8]) -> Self {
|
||||
$(const $const_keyword: &[u8] = $const_keyword_lit.as_bytes();)*
|
||||
match ident {
|
||||
$($const_keyword => Self::$const_keyword,)*
|
||||
_ => Self::CtIdent,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -121,23 +136,11 @@ pub enum TokenKind {
|
|||
|
||||
Ct,
|
||||
|
||||
Return,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
Packed,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
|
||||
Ctor,
|
||||
Tupl,
|
||||
Arr,
|
||||
TArrow,
|
||||
Range,
|
||||
|
||||
Or,
|
||||
And,
|
||||
|
@ -147,8 +150,31 @@ pub enum TokenKind {
|
|||
BSlash = b'\\',
|
||||
RBrack = b']',
|
||||
Xor = b'^',
|
||||
Tick = b'`',
|
||||
Under = b'_',
|
||||
Tick = b'`',
|
||||
|
||||
Slf,
|
||||
Return,
|
||||
If,
|
||||
Match,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
Packed,
|
||||
Enum,
|
||||
Union,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
Defer,
|
||||
|
||||
CtLoop,
|
||||
|
||||
// Unused = a-z
|
||||
LBrace = b'{',
|
||||
Bor = b'|',
|
||||
|
@ -193,6 +219,10 @@ impl TokenKind {
|
|||
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
|
||||
}
|
||||
|
||||
pub fn is_compatison(self) -> bool {
|
||||
matches!(self, Self::Lt | Self::Gt | Self::Ge | Self::Le | Self::Ne | Self::Eq)
|
||||
}
|
||||
|
||||
pub fn is_supported_float_op(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
|
@ -263,12 +293,11 @@ impl TokenKind {
|
|||
match self {
|
||||
Self::Sub if float => (-f64::from_bits(value as _)).to_bits() as _,
|
||||
Self::Sub => value.wrapping_neg(),
|
||||
Self::Not => (value == 0) as _,
|
||||
Self::Float if float => value,
|
||||
Self::Float => (value as f64).to_bits() as _,
|
||||
Self::Number => {
|
||||
debug_assert!(float);
|
||||
f64::from_bits(value as _).to_bits() as _
|
||||
}
|
||||
Self::Number if float => f64::from_bits(value as _) as _,
|
||||
Self::Number => value,
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
}
|
||||
|
@ -295,24 +324,34 @@ gen_token_kind! {
|
|||
Eof,
|
||||
Directive,
|
||||
#[keywords]
|
||||
Return = b"return",
|
||||
If = b"if",
|
||||
Else = b"else",
|
||||
Loop = b"loop",
|
||||
Break = b"break",
|
||||
Continue = b"continue",
|
||||
Fn = b"fn",
|
||||
Struct = b"struct",
|
||||
Packed = b"packed",
|
||||
True = b"true",
|
||||
False = b"false",
|
||||
Null = b"null",
|
||||
Idk = b"idk",
|
||||
Die = b"die",
|
||||
Under = b"_",
|
||||
Slf = "Self",
|
||||
Return = "return",
|
||||
If = "if",
|
||||
Match = "match",
|
||||
Else = "else",
|
||||
Loop = "loop",
|
||||
Break = "break",
|
||||
Continue = "continue",
|
||||
Fn = "fn",
|
||||
Struct = "struct",
|
||||
Packed = "packed",
|
||||
Enum = "enum",
|
||||
Union = "union",
|
||||
True = "true",
|
||||
False = "false",
|
||||
Null = "null",
|
||||
Idk = "idk",
|
||||
Die = "die",
|
||||
Defer = "defer",
|
||||
Under = "_",
|
||||
#[const_keywords]
|
||||
CtLoop = "loop",
|
||||
#[punkt]
|
||||
Ctor = ".{",
|
||||
Tupl = ".(",
|
||||
Arr = ".[",
|
||||
TArrow = "=>",
|
||||
Range = "..",
|
||||
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
|
||||
#[ops]
|
||||
#[prec]
|
||||
|
@ -391,6 +430,23 @@ impl<'a> Lexer<'a> {
|
|||
unsafe { core::str::from_utf8_unchecked(&self.source[tok]) }
|
||||
}
|
||||
|
||||
pub fn taste(&self) -> Token {
|
||||
Lexer { pos: self.pos, source: self.source }.eat()
|
||||
}
|
||||
|
||||
fn peek_n<const N: usize>(&self) -> Option<&[u8; N]> {
|
||||
if core::intrinsics::unlikely(self.pos as usize + N > self.source.len()) {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe {
|
||||
self.source
|
||||
.get_unchecked(self.pos as usize..self.pos as usize + N)
|
||||
.first_chunk()
|
||||
.unwrap_unchecked()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<u8> {
|
||||
if core::intrinsics::unlikely(self.pos >= self.source.len() as u32) {
|
||||
None
|
||||
|
@ -459,7 +515,11 @@ impl<'a> Lexer<'a> {
|
|||
self.advance();
|
||||
}
|
||||
|
||||
if self.advance_if(b'.') {
|
||||
if self
|
||||
.peek_n()
|
||||
.map_or_else(|| self.peek() == Some(b'.'), |&[a, b]| a == b'.' && b != b'.')
|
||||
{
|
||||
self.pos += 1;
|
||||
while let Some(b'0'..=b'9') = self.peek() {
|
||||
self.advance();
|
||||
}
|
||||
|
@ -511,14 +571,23 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
b'.' if self.advance_if(b'{') => T::Ctor,
|
||||
b'.' if self.advance_if(b'(') => T::Tupl,
|
||||
b'.' if self.advance_if(b'[') => T::Arr,
|
||||
b'.' if self.advance_if(b'.') => T::Range,
|
||||
b'=' if self.advance_if(b'>') => T::TArrow,
|
||||
b'&' if self.advance_if(b'&') => T::And,
|
||||
b'|' if self.advance_if(b'|') => T::Or,
|
||||
b'$' if self.advance_if(b':') => T::Ct,
|
||||
b'@' | b'$' => {
|
||||
b'@' => {
|
||||
start += 1;
|
||||
advance_ident(self);
|
||||
identity(c)
|
||||
}
|
||||
b'$' => {
|
||||
start += 1;
|
||||
advance_ident(self);
|
||||
let ident = &self.source[start as usize..self.pos as usize];
|
||||
T::from_ct_ident(ident)
|
||||
}
|
||||
b'<' | b'>' if self.advance_if(c) => {
|
||||
identity(c - 5 + 128 * self.advance_if(b'=') as u8)
|
||||
}
|
||||
|
|
1423
lang/src/lib.rs
1423
lang/src/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,31 @@
|
|||
#[cfg(feature = "std")]
|
||||
fn main() -> std::io::Result<()> {
|
||||
fn main() {
|
||||
use std::io::Write;
|
||||
|
||||
log::set_logger(&hblang::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)];
|
||||
let opts = hblang::Options::from_args(&args, out, resolvers)?;
|
||||
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
|
||||
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
hblang::run_compiler(file, opts, out, warnings)
|
||||
}
|
||||
|
||||
let opts = hblang::Options::from_args(&args)?;
|
||||
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
|
||||
log::set_logger(&hblang::fs::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
let mut out = Vec::new();
|
||||
hblang::run_compiler(file, opts, &mut out)?;
|
||||
std::io::stdout().write_all(&out)
|
||||
let mut warnings = String::new();
|
||||
match run(&mut out, &mut warnings) {
|
||||
Ok(_) => {
|
||||
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
|
||||
std::io::stdout().write_all(&out).unwrap()
|
||||
}
|
||||
Err(_) => {
|
||||
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
|
||||
std::io::stderr().write_all(&out).unwrap();
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2253
lang/src/nodes.rs
Normal file
2253
lang/src/nodes.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,8 @@ use {
|
|||
crate::{
|
||||
fmt::Formatter,
|
||||
lexer::{self, Lexer, Token, TokenKind},
|
||||
ty::{Global, Module},
|
||||
utils::Ent as _,
|
||||
Ident,
|
||||
},
|
||||
alloc::{boxed::Box, string::String, vec::Vec},
|
||||
|
@ -19,10 +21,9 @@ use {
|
|||
|
||||
pub type Pos = u32;
|
||||
pub type IdentFlags = u32;
|
||||
pub type FileId = u32;
|
||||
pub type IdentIndex = u16;
|
||||
pub type LoaderError = String;
|
||||
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<FileId, LoaderError> + 'a);
|
||||
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<usize, LoaderError> + 'a);
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum FileKind {
|
||||
|
@ -30,7 +31,7 @@ pub enum FileKind {
|
|||
Embed,
|
||||
}
|
||||
|
||||
trait Trans {
|
||||
pub trait Trans {
|
||||
fn trans(self) -> Self;
|
||||
}
|
||||
|
||||
|
@ -63,7 +64,7 @@ pub mod idfl {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<FileId, LoaderError> {
|
||||
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<usize, LoaderError> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
@ -78,6 +79,8 @@ struct ScopeIdent {
|
|||
ident: Ident,
|
||||
declared: bool,
|
||||
ordered: bool,
|
||||
used: bool,
|
||||
is_ct: bool,
|
||||
flags: IdentFlags,
|
||||
}
|
||||
|
||||
|
@ -194,8 +197,8 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
|
||||
fn declare_rec(&mut self, expr: &Expr, top_level: bool) {
|
||||
match *expr {
|
||||
Expr::Ident { pos, id, is_first, .. } => {
|
||||
self.declare(pos, id, !top_level, is_first || top_level)
|
||||
Expr::Ident { pos, id, is_first, is_ct, .. } => {
|
||||
self.declare(pos, id, !top_level, is_first || top_level, is_ct)
|
||||
}
|
||||
Expr::Ctor { fields, .. } => {
|
||||
for CtorField { value, .. } in fields {
|
||||
|
@ -206,7 +209,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool) {
|
||||
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool, is_ct: bool) {
|
||||
if !valid_order {
|
||||
self.report(
|
||||
pos,
|
||||
|
@ -228,7 +231,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.ctx.idents[index].is_ct = is_ct;
|
||||
self.ctx.idents[index].ordered = ordered;
|
||||
}
|
||||
|
||||
|
@ -247,7 +250,10 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
.enumerate()
|
||||
.rfind(|(_, elem)| self.lexer.slice(elem.ident.range()) == name)
|
||||
{
|
||||
Some((i, elem)) => (i, elem, false),
|
||||
Some((i, elem)) => {
|
||||
elem.used = true;
|
||||
(i, elem, false)
|
||||
}
|
||||
None => {
|
||||
let ident = match Ident::new(token.start, name.len() as _) {
|
||||
None => {
|
||||
|
@ -260,7 +266,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
self.ctx.idents.push(ScopeIdent {
|
||||
ident,
|
||||
declared: false,
|
||||
used: false,
|
||||
ordered: false,
|
||||
is_ct: false,
|
||||
flags: 0,
|
||||
});
|
||||
(self.ctx.idents.len() - 1, self.ctx.idents.last_mut().unwrap(), true)
|
||||
|
@ -270,7 +278,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
id.flags |= idfl::COMPTIME * is_ct as u32;
|
||||
if id.declared && id.ordered && self.ns_bound > i {
|
||||
id.flags |= idfl::COMPTIME;
|
||||
self.ctx.captured.push(id.ident);
|
||||
self.ctx.captured.push(CapturedIdent { id: id.ident, is_ct: id.is_ct });
|
||||
}
|
||||
|
||||
(id.ident, bl)
|
||||
|
@ -281,13 +289,27 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
|
||||
fn unit_expr(&mut self) -> Option<Expr<'a>> {
|
||||
self.unit_expr_low(true)
|
||||
}
|
||||
|
||||
fn unit_expr_low(&mut self, eat_tail: bool) -> Option<Expr<'a>> {
|
||||
use {Expr as E, TokenKind as T};
|
||||
|
||||
if matches!(
|
||||
self.token.kind,
|
||||
T::RParen | T::RBrace | T::RBrack | T::Comma | T::Semi | T::Else
|
||||
) {
|
||||
self.report(self.token.start, "expected expression")?;
|
||||
}
|
||||
|
||||
let frame = self.ctx.idents.len();
|
||||
let token @ Token { start: pos, .. } = self.next();
|
||||
let prev_boundary = self.ns_bound;
|
||||
let prev_captured = self.ctx.captured.len();
|
||||
let mut must_trail = false;
|
||||
let mut expr = match token.kind {
|
||||
T::Ct => E::Ct { pos, value: self.ptr_expr()? },
|
||||
T::Defer => E::Defer { pos, value: self.ptr_expr()? },
|
||||
T::Slf => E::Slf { pos },
|
||||
T::Directive if self.lexer.slice(token.range()) == "use" => {
|
||||
self.expect_advance(TokenKind::LParen)?;
|
||||
let str = self.expect_advance(TokenKind::DQuote)?;
|
||||
|
@ -299,7 +321,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
pos,
|
||||
path,
|
||||
id: match (self.loader)(path, self.path, FileKind::Module) {
|
||||
Ok(id) => id,
|
||||
Ok(id) => Module::new(id),
|
||||
Err(e) => {
|
||||
self.report(str.start, format_args!("error loading dependency: {e:#}"))?
|
||||
}
|
||||
|
@ -317,7 +339,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
pos,
|
||||
path,
|
||||
id: match (self.loader)(path, self.path, FileKind::Embed) {
|
||||
Ok(id) => id,
|
||||
Ok(id) => Global::new(id),
|
||||
Err(e) => self.report(
|
||||
str.start,
|
||||
format_args!("error loading embedded file: {e:#}"),
|
||||
|
@ -339,6 +361,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
T::Idk => E::Idk { pos },
|
||||
T::Die => E::Die { pos },
|
||||
T::DQuote => E::String { pos, literal: self.tok_str(token) },
|
||||
T::Quote => E::Char { pos, literal: self.tok_str(token) },
|
||||
T::Packed => {
|
||||
self.packed = true;
|
||||
let expr = self.unit_expr()?;
|
||||
|
@ -352,45 +375,62 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
expr
|
||||
}
|
||||
T::Struct => E::Struct {
|
||||
pos,
|
||||
packed: core::mem::take(&mut self.packed),
|
||||
fields: {
|
||||
self.ns_bound = self.ctx.idents.len();
|
||||
self.expect_advance(T::LBrace)?;
|
||||
self.collect_list(T::Comma, T::RBrace, |s| {
|
||||
let tok = s.token;
|
||||
Some(if s.advance_if(T::Comment) {
|
||||
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
|
||||
} else {
|
||||
let name = s.expect_advance(T::Ident)?;
|
||||
s.expect_advance(T::Colon)?;
|
||||
CommentOr::Or(StructField {
|
||||
pos: name.start,
|
||||
name: s.tok_str(name),
|
||||
ty: s.expr()?,
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
captured: {
|
||||
self.ns_bound = prev_boundary;
|
||||
let captured = &mut self.ctx.captured[prev_captured..];
|
||||
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
||||
let preserved = captured.partition_dedup().0.len();
|
||||
self.ctx.captured.truncate(prev_captured + preserved);
|
||||
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
|
||||
},
|
||||
pos: {
|
||||
if self.ns_bound == 0 {
|
||||
// we might save some memory
|
||||
self.ctx.captured.clear();
|
||||
fields: self.collect_fields(&mut must_trail, |s| {
|
||||
if s.lexer.taste().kind != T::Colon {
|
||||
return Some(None);
|
||||
}
|
||||
pos
|
||||
},
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
let name = s.expect_advance(T::Ident)?;
|
||||
s.expect_advance(T::Colon)?;
|
||||
let (ty, default_value) = match s.expr()? {
|
||||
Expr::BinOp { left, op: T::Assign, right, .. } => (*left, Some(*right)),
|
||||
ty => (ty, None),
|
||||
};
|
||||
Some(Some(StructField {
|
||||
pos: name.start,
|
||||
name: s.tok_str(name),
|
||||
ty,
|
||||
default_value,
|
||||
}))
|
||||
})?,
|
||||
captured: self.collect_captures(prev_boundary, prev_captured),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
|
||||
},
|
||||
T::Union => E::Union {
|
||||
pos,
|
||||
fields: self.collect_fields(&mut must_trail, |s| {
|
||||
if s.lexer.taste().kind != T::Colon {
|
||||
return Some(None);
|
||||
}
|
||||
let name = s.expect_advance(T::Ident)?;
|
||||
s.expect_advance(T::Colon)?;
|
||||
Some(Some(UnionField { pos: name.start, name: s.tok_str(name), ty: s.expr()? }))
|
||||
})?,
|
||||
captured: self.collect_captures(prev_boundary, prev_captured),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
|
||||
},
|
||||
T::Enum => E::Enum {
|
||||
pos,
|
||||
variants: self.collect_fields(&mut must_trail, |s| {
|
||||
if !matches!(s.lexer.taste().kind, T::Comma | T::RBrace) {
|
||||
return Some(None);
|
||||
}
|
||||
|
||||
let name = s.expect_advance(T::Ident)?;
|
||||
Some(Some(EnumField { pos: name.start, name: s.tok_str(name) }))
|
||||
})?,
|
||||
captured: self.collect_captures(prev_boundary, prev_captured),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
|
||||
},
|
||||
T::Ident | T::CtIdent => {
|
||||
let (id, is_first) = self.resolve_ident(token);
|
||||
E::Ident { pos, is_ct: token.kind == T::CtIdent, id, is_first }
|
||||
E::Ident {
|
||||
pos: pos - (token.kind == T::CtIdent) as Pos,
|
||||
is_ct: token.kind == T::CtIdent,
|
||||
id,
|
||||
is_first,
|
||||
}
|
||||
}
|
||||
T::Under => E::Wildcard { pos },
|
||||
T::If => E::If {
|
||||
|
@ -399,7 +439,22 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
then: self.ptr_expr()?,
|
||||
else_: self.advance_if(T::Else).then(|| self.ptr_expr()).trans()?,
|
||||
},
|
||||
T::Loop => E::Loop { pos, body: self.ptr_expr()? },
|
||||
T::Match => E::Match {
|
||||
pos,
|
||||
value: self.ptr_expr()?,
|
||||
branches: {
|
||||
self.expect_advance(T::LBrace)?;
|
||||
self.collect_list(T::Comma, T::RBrace, |s| {
|
||||
Some(MatchBranch {
|
||||
pat: s.expr()?,
|
||||
pos: s.expect_advance(T::TArrow)?.start,
|
||||
body: s.expr()?,
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
T::Loop => E::Loop { pos, unrolled: false, body: self.ptr_expr()? },
|
||||
T::CtLoop => E::Loop { pos, unrolled: true, body: self.ptr_expr()? },
|
||||
T::Break => E::Break { pos },
|
||||
T::Continue => E::Continue { pos },
|
||||
T::Return => E::Return {
|
||||
|
@ -418,7 +473,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
self.collect_list(T::Comma, T::RParen, |s| {
|
||||
let name = s.advance_ident()?;
|
||||
let (id, _) = s.resolve_ident(name);
|
||||
s.declare(name.start, id, true, true);
|
||||
s.declare(name.start, id, true, true, name.kind == T::CtIdent);
|
||||
s.expect_advance(T::Colon)?;
|
||||
Some(Arg {
|
||||
pos: name.start,
|
||||
|
@ -436,23 +491,33 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
body: self.ptr_expr()?,
|
||||
},
|
||||
T::Ctor => self.ctor(pos, None),
|
||||
T::Tupl => self.tupl(pos, None),
|
||||
T::Tupl => self.tupl(pos, None, ListKind::Tuple),
|
||||
T::Arr => self.tupl(pos, None, ListKind::Array),
|
||||
T::LBrack => E::Slice {
|
||||
item: self.ptr_unit_expr()?,
|
||||
size: self.advance_if(T::Semi).then(|| self.ptr_expr()).trans()?,
|
||||
pos: {
|
||||
self.expect_advance(T::RBrack)?;
|
||||
pos
|
||||
size: {
|
||||
if self.advance_if(T::RBrack) {
|
||||
None
|
||||
} else {
|
||||
let adv = self.ptr_expr()?;
|
||||
self.expect_advance(T::RBrack)?;
|
||||
Some(adv)
|
||||
}
|
||||
},
|
||||
item: self.arena.alloc(self.unit_expr_low(false)?),
|
||||
pos,
|
||||
},
|
||||
T::Band | T::Mul | T::Xor | T::Sub | T::Que => E::UnOp {
|
||||
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not | T::Dot => E::UnOp {
|
||||
pos,
|
||||
op: token.kind,
|
||||
val: {
|
||||
let prev_ident_stack = self.ctx.idents.len();
|
||||
let expr = self.ptr_unit_expr()?;
|
||||
if token.kind == T::Band {
|
||||
self.flag_idents(*expr, idfl::REFERENCED);
|
||||
}
|
||||
if token.kind == T::Dot {
|
||||
self.ctx.idents.truncate(prev_ident_stack);
|
||||
}
|
||||
expr
|
||||
},
|
||||
},
|
||||
|
@ -491,52 +556,100 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
tok => self.report(token.start, format_args!("unexpected token: {tok}"))?,
|
||||
};
|
||||
|
||||
loop {
|
||||
let token = self.token;
|
||||
if matches!(token.kind, T::LParen | T::Ctor | T::Dot | T::Tupl | T::LBrack) {
|
||||
self.next();
|
||||
}
|
||||
if eat_tail {
|
||||
loop {
|
||||
let token = self.token;
|
||||
if matches!(
|
||||
token.kind,
|
||||
T::LParen | T::Ctor | T::Dot | T::Tupl | T::Arr | T::LBrack | T::Colon
|
||||
) {
|
||||
self.next();
|
||||
}
|
||||
|
||||
expr = match token.kind {
|
||||
T::LParen => Expr::Call {
|
||||
func: self.arena.alloc(expr),
|
||||
args: self.collect_list(T::Comma, T::RParen, Self::expr),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
},
|
||||
T::Ctor => self.ctor(token.start, Some(expr)),
|
||||
T::Tupl => self.tupl(token.start, Some(expr)),
|
||||
T::LBrack => E::Index {
|
||||
base: self.arena.alloc(expr),
|
||||
index: {
|
||||
let index = self.expr()?;
|
||||
self.expect_advance(T::RBrack)?;
|
||||
self.arena.alloc(index)
|
||||
expr = match token.kind {
|
||||
T::LParen => Expr::Call {
|
||||
func: self.arena.alloc(expr),
|
||||
args: self.collect_list(T::Comma, T::RParen, Self::expr),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
},
|
||||
},
|
||||
T::Dot => E::Field {
|
||||
target: self.arena.alloc(expr),
|
||||
pos: token.start,
|
||||
name: {
|
||||
let token = self.expect_advance(T::Ident)?;
|
||||
self.tok_str(token)
|
||||
T::Ctor => self.ctor(token.start, Some(expr)),
|
||||
T::Tupl => self.tupl(token.start, Some(expr), ListKind::Tuple),
|
||||
T::Arr => self.tupl(token.start, Some(expr), ListKind::Array),
|
||||
T::LBrack => E::Index {
|
||||
base: self.arena.alloc(expr),
|
||||
index: self.arena.alloc({
|
||||
if self.advance_if(T::Range) {
|
||||
let pos = self.token.start;
|
||||
if self.advance_if(T::RBrack) {
|
||||
Expr::Range { pos, start: None, end: None }
|
||||
} else {
|
||||
let res = Expr::Range {
|
||||
pos,
|
||||
start: None,
|
||||
end: Some(self.ptr_expr()?),
|
||||
};
|
||||
self.expect_advance(T::RBrack)?;
|
||||
res
|
||||
}
|
||||
} else {
|
||||
let start = self.expr()?;
|
||||
|
||||
let pos = self.token.start;
|
||||
if self.advance_if(T::Range) {
|
||||
let start = self.arena.alloc(start);
|
||||
if self.advance_if(T::RBrack) {
|
||||
Expr::Range { pos, start: Some(start), end: None }
|
||||
} else {
|
||||
let res = Expr::Range {
|
||||
pos,
|
||||
start: Some(start),
|
||||
end: Some(self.ptr_expr()?),
|
||||
};
|
||||
self.expect_advance(T::RBrack)?;
|
||||
res
|
||||
}
|
||||
} else {
|
||||
self.expect_advance(T::RBrack)?;
|
||||
start
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
},
|
||||
_ => break,
|
||||
T::Colon => E::BinOp {
|
||||
left: {
|
||||
self.declare_rec(&expr, false);
|
||||
self.arena.alloc(expr)
|
||||
},
|
||||
pos,
|
||||
op: T::Colon,
|
||||
right: self.ptr_expr()?,
|
||||
},
|
||||
T::Dot => E::Field {
|
||||
target: self.arena.alloc(expr),
|
||||
pos: token.start,
|
||||
name: {
|
||||
let token = self.expect_advance(T::Ident)?;
|
||||
self.tok_str(token)
|
||||
},
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(token.kind, T::Loop | T::LBrace | T::Fn) {
|
||||
if matches!(token.kind, T::Loop | T::LBrace | T::Fn | T::Struct) {
|
||||
self.pop_scope(frame);
|
||||
}
|
||||
|
||||
Some(expr)
|
||||
}
|
||||
|
||||
fn tupl(&mut self, pos: Pos, ty: Option<Expr<'a>>) -> Expr<'a> {
|
||||
Expr::Tupl {
|
||||
fn tupl(&mut self, pos: Pos, ty: Option<Expr<'a>>, kind: ListKind) -> Expr<'a> {
|
||||
Expr::List {
|
||||
pos,
|
||||
kind,
|
||||
ty: ty.map(|ty| self.arena.alloc(ty)),
|
||||
fields: self.collect_list(TokenKind::Comma, TokenKind::RParen, Self::expr),
|
||||
fields: self.collect_list(TokenKind::Comma, kind.term(), Self::expr),
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
}
|
||||
}
|
||||
|
@ -563,6 +676,49 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_fields<T: Copy>(
|
||||
&mut self,
|
||||
must_trail: &mut bool,
|
||||
mut parse_field: impl FnMut(&mut Self) -> Option<Option<T>>,
|
||||
) -> Option<FieldList<'a, T>> {
|
||||
use TokenKind as T;
|
||||
self.ns_bound = self.ctx.idents.len();
|
||||
self.expect_advance(T::LBrace)?;
|
||||
Some(self.collect_list(T::Comma, T::RBrace, |s| {
|
||||
let tok = s.token;
|
||||
Some(if s.advance_if(T::Comment) {
|
||||
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
|
||||
} else if let Some(field) = parse_field(s)? {
|
||||
CommentOr::Or(Ok(field))
|
||||
} else {
|
||||
*must_trail = true;
|
||||
CommentOr::Or(Err(
|
||||
s.collect_list_low(T::Semi, T::RBrace, true, |s| s.expr_low(true))
|
||||
))
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn collect_captures(
|
||||
&mut self,
|
||||
prev_captured: usize,
|
||||
prev_boundary: usize,
|
||||
) -> &'a [CapturedIdent] {
|
||||
self.ns_bound = prev_boundary;
|
||||
let captured = &mut self.ctx.captured[prev_captured..];
|
||||
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
||||
let preserved = captured.partition_dedup().0.len();
|
||||
self.ctx.captured.truncate(prev_captured + preserved);
|
||||
let slc = self.arena.alloc_slice(&self.ctx.captured[prev_captured..]);
|
||||
|
||||
if self.ns_bound == 0 {
|
||||
// we might save some memory
|
||||
self.ctx.captured.clear();
|
||||
}
|
||||
|
||||
slc
|
||||
}
|
||||
|
||||
fn advance_ident(&mut self) -> Option<Token> {
|
||||
let next = self.next();
|
||||
if matches!(next.kind, TokenKind::Ident | TokenKind::CtIdent) {
|
||||
|
@ -578,6 +734,8 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
if !&self.ctx.idents[i].declared {
|
||||
self.ctx.idents.swap(i, undeclared_count);
|
||||
undeclared_count += 1;
|
||||
} else if !self.ctx.idents[i].used {
|
||||
self.warn(self.ctx.idents[i].ident.pos(), "unused identifier");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,11 +754,23 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
&mut self,
|
||||
delim: TokenKind,
|
||||
end: TokenKind,
|
||||
f: impl FnMut(&mut Self) -> Option<T>,
|
||||
) -> &'a [T] {
|
||||
self.collect_list_low(delim, end, false, f)
|
||||
}
|
||||
|
||||
fn collect_list_low<T: Copy>(
|
||||
&mut self,
|
||||
delim: TokenKind,
|
||||
end: TokenKind,
|
||||
keep_end: bool,
|
||||
mut f: impl FnMut(&mut Self) -> Option<T>,
|
||||
) -> &'a [T] {
|
||||
let mut trailing_sep = false;
|
||||
let mut view = self.ctx.stack.view();
|
||||
'o: while !self.advance_if(end) {
|
||||
'o: while (keep_end && self.token.kind != end)
|
||||
|| (!keep_end && !self.advance_if(end)) && self.token.kind != TokenKind::Eof
|
||||
{
|
||||
let val = match f(self) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
|
@ -657,9 +827,25 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn warn(&mut self, pos: Pos, msg: impl fmt::Display) {
|
||||
if log::log_enabled!(log::Level::Error) {
|
||||
use core::fmt::Write;
|
||||
writeln!(
|
||||
self.ctx.warnings.get_mut(),
|
||||
"(W) {}",
|
||||
Report::new(self.lexer.source(), self.path, pos, msg)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn report(&mut self, pos: Pos, msg: impl fmt::Display) -> Option<!> {
|
||||
if log::log_enabled!(log::Level::Error) {
|
||||
if self.ctx.errors.get_mut().len() > 1024 * 10 {
|
||||
panic!("{}", self.ctx.errors.get_mut());
|
||||
}
|
||||
use core::fmt::Write;
|
||||
writeln!(
|
||||
self.ctx.errors.get_mut(),
|
||||
|
@ -673,15 +859,19 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
|
||||
fn flag_idents(&mut self, e: Expr<'a>, flags: IdentFlags) {
|
||||
match e {
|
||||
Expr::Ident { id, .. } => find_ident(&mut self.ctx.idents, id).flags |= flags,
|
||||
Expr::Ident { id, .. } => {
|
||||
if let Some(f) = find_ident(&mut self.ctx.idents, id) {
|
||||
f.flags |= flags;
|
||||
}
|
||||
}
|
||||
Expr::Field { target, .. } => self.flag_idents(*target, flags),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_ident(idents: &mut [ScopeIdent], id: Ident) -> &mut ScopeIdent {
|
||||
idents.binary_search_by_key(&id, |si| si.ident).map(|i| &mut idents[i]).unwrap()
|
||||
fn find_ident(idents: &mut [ScopeIdent], id: Ident) -> Option<&mut ScopeIdent> {
|
||||
idents.binary_search_by_key(&id, |si| si.ident).map(|i| &mut idents[i]).ok()
|
||||
}
|
||||
|
||||
pub fn find_symbol(symbols: &[Symbol], id: Ident) -> &Symbol {
|
||||
|
@ -755,21 +945,32 @@ pub enum Radix {
|
|||
Decimal = 10,
|
||||
}
|
||||
|
||||
pub type FieldList<'a, T> = &'a [CommentOr<'a, Result<T, &'a [Expr<'a>]>>];
|
||||
|
||||
generate_expr! {
|
||||
/// `LIST(start, sep, end, elem) => start { elem sep } [elem] end`
|
||||
/// `OP := grep for `#define OP:`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Expr<'a> {
|
||||
/// `'ct' Expr`
|
||||
Ct {
|
||||
/// `'defer' Expr`
|
||||
Defer {
|
||||
pos: Pos,
|
||||
value: &'a Self,
|
||||
},
|
||||
/// `'Self'`
|
||||
Slf {
|
||||
pos: Pos,
|
||||
},
|
||||
/// `'"([^"]|\\")"'`
|
||||
String {
|
||||
pos: Pos,
|
||||
literal: &'a str,
|
||||
},
|
||||
/// `'\'([^']|\\\')\''`
|
||||
Char {
|
||||
pos: Pos,
|
||||
literal: &'a str,
|
||||
},
|
||||
/// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/'
|
||||
Comment {
|
||||
pos: Pos,
|
||||
|
@ -844,9 +1045,15 @@ generate_expr! {
|
|||
then: &'a Self,
|
||||
else_: Option<&'a Self>,
|
||||
},
|
||||
Match {
|
||||
pos: Pos,
|
||||
value: &'a Self,
|
||||
branches: &'a [MatchBranch<'a>],
|
||||
},
|
||||
/// `'loop' Expr`
|
||||
Loop {
|
||||
pos: Pos,
|
||||
unrolled: bool,
|
||||
body: &'a Self,
|
||||
},
|
||||
/// `('&' | '*' | '^') Expr`
|
||||
|
@ -858,11 +1065,25 @@ generate_expr! {
|
|||
/// `'struct' LIST('{', ',', '}', Ident ':' Expr)`
|
||||
Struct {
|
||||
pos: Pos,
|
||||
fields: &'a [CommentOr<'a, StructField<'a>>],
|
||||
captured: &'a [Ident],
|
||||
fields: FieldList<'a, StructField<'a>>,
|
||||
captured: &'a [CapturedIdent],
|
||||
trailing_comma: bool,
|
||||
packed: bool,
|
||||
},
|
||||
/// `'union' LIST('{', ',', '}', Ident ':' Expr)`
|
||||
Union {
|
||||
pos: Pos,
|
||||
fields: FieldList<'a, UnionField<'a>>,
|
||||
captured: &'a [CapturedIdent],
|
||||
trailing_comma: bool,
|
||||
},
|
||||
/// `'enum' LIST('{', ',', '}', Ident)`
|
||||
Enum {
|
||||
pos: Pos,
|
||||
variants: FieldList<'a, EnumField<'a>>,
|
||||
captured: &'a [CapturedIdent],
|
||||
trailing_comma: bool,
|
||||
},
|
||||
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
||||
Ctor {
|
||||
pos: Pos,
|
||||
|
@ -871,8 +1092,9 @@ generate_expr! {
|
|||
trailing_comma: bool,
|
||||
},
|
||||
/// `[Expr] LIST('.(', ',', ')', Ident [':' Expr])`
|
||||
Tupl {
|
||||
List {
|
||||
pos: Pos,
|
||||
kind: ListKind,
|
||||
ty: Option<&'a Self>,
|
||||
fields: &'a [Self],
|
||||
trailing_comma: bool,
|
||||
|
@ -888,6 +1110,12 @@ generate_expr! {
|
|||
base: &'a Self,
|
||||
index: &'a Self,
|
||||
},
|
||||
/// `[ Expr ] .. [ Expr ]`
|
||||
Range {
|
||||
pos: u32,
|
||||
start: Option<&'a Self>,
|
||||
end: Option<&'a Self>,
|
||||
},
|
||||
/// `Expr '.' Ident`
|
||||
Field {
|
||||
target: &'a Self,
|
||||
|
@ -921,22 +1149,44 @@ generate_expr! {
|
|||
/// `'@use' '(' String ')'`
|
||||
Mod {
|
||||
pos: Pos,
|
||||
id: FileId,
|
||||
id: Module,
|
||||
path: &'a str,
|
||||
},
|
||||
/// `'@use' '(' String ')'`
|
||||
Embed {
|
||||
pos: Pos,
|
||||
id: FileId,
|
||||
id: Global,
|
||||
path: &'a str,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
|
||||
pub struct CapturedIdent {
|
||||
pub id: Ident,
|
||||
pub is_ct: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ListKind {
|
||||
Tuple,
|
||||
Array,
|
||||
}
|
||||
impl ListKind {
|
||||
fn term(self) -> TokenKind {
|
||||
match self {
|
||||
ListKind::Tuple => TokenKind::RParen,
|
||||
ListKind::Array => TokenKind::RBrack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr<'_> {
|
||||
pub fn declares(&self, iden: Result<Ident, &str>, source: &str) -> Option<Ident> {
|
||||
pub fn declares(&self, iden: DeclId, source: &str) -> Option<Ident> {
|
||||
match *self {
|
||||
Self::Ident { id, .. } if iden == Ok(id) || iden == Err(&source[id.range()]) => {
|
||||
Self::Ident { id, .. }
|
||||
if iden == DeclId::Ident(id) || iden == DeclId::Name(&source[id.range()]) =>
|
||||
{
|
||||
Some(id)
|
||||
}
|
||||
Self::Ctor { fields, .. } => fields.iter().find_map(|f| f.value.declares(iden, source)),
|
||||
|
@ -952,14 +1202,14 @@ impl Expr<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_pattern_path<T, F: FnOnce(&Expr) -> T>(
|
||||
pub fn find_pattern_path<T, F: FnOnce(&Expr, bool) -> T>(
|
||||
&self,
|
||||
ident: Ident,
|
||||
target: &Expr,
|
||||
mut with_final: F,
|
||||
) -> Result<T, F> {
|
||||
match *self {
|
||||
Self::Ident { id, .. } if id == ident => Ok(with_final(target)),
|
||||
Self::Ident { id, is_ct, .. } if id == ident => Ok(with_final(target, is_ct)),
|
||||
Self::Ctor { fields, .. } => {
|
||||
for &CtorField { name, value, pos } in fields {
|
||||
match value.find_pattern_path(
|
||||
|
@ -978,11 +1228,50 @@ impl Expr<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct MatchBranch<'a> {
|
||||
pub pat: Expr<'a>,
|
||||
pub pos: Pos,
|
||||
pub body: Expr<'a>,
|
||||
}
|
||||
|
||||
impl Poser for MatchBranch<'_> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.pat.pos()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct EnumField<'a> {
|
||||
pub pos: Pos,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
impl Poser for EnumField<'_> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct UnionField<'a> {
|
||||
pub pos: Pos,
|
||||
pub name: &'a str,
|
||||
pub ty: Expr<'a>,
|
||||
}
|
||||
|
||||
impl Poser for UnionField<'_> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct StructField<'a> {
|
||||
pub pos: Pos,
|
||||
pub name: &'a str,
|
||||
pub ty: Expr<'a>,
|
||||
pub default_value: Option<Expr<'a>>,
|
||||
}
|
||||
|
||||
impl Poser for StructField<'_> {
|
||||
|
@ -1008,6 +1297,18 @@ pub trait Poser {
|
|||
fn posi(&self) -> Pos;
|
||||
}
|
||||
|
||||
impl<O: Poser, E: Poser> Poser for Result<O, E> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.as_ref().map_or_else(Poser::posi, Poser::posi)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Poser> Poser for &[T] {
|
||||
fn posi(&self) -> Pos {
|
||||
self[0].posi()
|
||||
}
|
||||
}
|
||||
|
||||
impl Poser for Pos {
|
||||
fn posi(&self) -> Pos {
|
||||
*self
|
||||
|
@ -1035,9 +1336,9 @@ pub enum CommentOr<'a, T> {
|
|||
Comment { literal: &'a str, pos: Pos },
|
||||
}
|
||||
|
||||
impl<T: Copy> CommentOr<'_, T> {
|
||||
pub fn or(&self) -> Option<T> {
|
||||
match *self {
|
||||
impl<T> CommentOr<'_, T> {
|
||||
pub fn or(&self) -> Option<&T> {
|
||||
match self {
|
||||
CommentOr::Or(v) => Some(v),
|
||||
CommentOr::Comment { .. } => None,
|
||||
}
|
||||
|
@ -1064,10 +1365,11 @@ impl core::fmt::Display for Display<'_> {
|
|||
#[derive(Default)]
|
||||
pub struct Ctx {
|
||||
pub errors: RefCell<String>,
|
||||
pub warnings: RefCell<String>,
|
||||
symbols: Vec<Symbol>,
|
||||
stack: StackAlloc,
|
||||
idents: Vec<ScopeIdent>,
|
||||
captured: Vec<Ident>,
|
||||
captured: Vec<CapturedIdent>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
|
@ -1206,13 +1508,8 @@ impl Ast {
|
|||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
|
||||
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<(&Expr, Ident)> {
|
||||
self.exprs().iter().find_map(|expr| match expr {
|
||||
Expr::BinOp { left, op: TokenKind::Decl, .. } => {
|
||||
left.declares(id, &self.file).map(|id| (expr, id))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
pub fn find_decl(&self, id: DeclId) -> Option<(&Expr, Ident)> {
|
||||
find_decl(self.exprs(), &self.file, id)
|
||||
}
|
||||
|
||||
pub fn ident_str(&self, ident: Ident) -> &str {
|
||||
|
@ -1220,13 +1517,44 @@ impl Ast {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_decl<'a>(
|
||||
exprs: &'a [Expr<'a>],
|
||||
file: &str,
|
||||
id: DeclId,
|
||||
) -> Option<(&'a Expr<'a>, Ident)> {
|
||||
exprs.iter().find_map(|expr| match expr {
|
||||
Expr::BinOp { left, op: TokenKind::Decl | TokenKind::Colon, .. } => {
|
||||
left.declares(id, file).map(|id| (expr, id))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DeclId<'a> {
|
||||
Ident(Ident),
|
||||
Name(&'a str),
|
||||
}
|
||||
|
||||
impl From<Ident> for DeclId<'_> {
|
||||
fn from(value: Ident) -> Self {
|
||||
Self::Ident(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for DeclId<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self::Name(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Ast {
|
||||
fn default() -> Self {
|
||||
Self(AstInner::new("".into(), "".into(), &mut Ctx::default(), &mut no_loader))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(packed)]
|
||||
pub struct ExprRef(NonNull<Expr<'static>>);
|
||||
|
||||
|
@ -1492,7 +1820,7 @@ impl ArenaChunk {
|
|||
|
||||
fn contains(&self, arg: *mut u8) -> bool {
|
||||
(self.base <= arg && unsafe { self.base.add(self.size) } > arg)
|
||||
|| self.next().map_or(false, |s| s.contains(arg))
|
||||
|| self.next().is_some_and(|s| s.contains(arg))
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
|
|
5424
lang/src/son.rs
5424
lang/src/son.rs
File diff suppressed because it is too large
Load diff
|
@ -1,903 +0,0 @@
|
|||
use {
|
||||
super::{HbvmBackend, Nid, Nodes},
|
||||
crate::{
|
||||
lexer::TokenKind,
|
||||
parser,
|
||||
reg::{self, Reg},
|
||||
son::{debug_assert_matches, Kind, ARG_START, MEM, VOID},
|
||||
ty::{self, Arg, Loc},
|
||||
utils::{BitSet, Vc},
|
||||
Offset, PLoc, Reloc, Sig, TypedReloc, Types,
|
||||
},
|
||||
alloc::{borrow::ToOwned, vec::Vec},
|
||||
core::{mem, ops::Range},
|
||||
hbbytecode::{self as instrs},
|
||||
};
|
||||
|
||||
impl HbvmBackend {
|
||||
pub fn emit_body_code_my(
|
||||
&mut self,
|
||||
nodes: &mut Nodes,
|
||||
sig: Sig,
|
||||
tys: &Types,
|
||||
files: &[parser::Ast],
|
||||
) -> (usize, bool) {
|
||||
let mut fuc = Function::new(nodes, tys, sig);
|
||||
log::info!("{fuc:?}");
|
||||
|
||||
let mut res = mem::take(&mut self.ralloc_my);
|
||||
|
||||
Env::new(&fuc, &fuc.func, &mut res).run();
|
||||
|
||||
'_open_function: {
|
||||
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
|
||||
self.emit(instrs::st(reg::RET_ADDR + fuc.tail as u8, reg::STACK_PTR, 0, 0));
|
||||
}
|
||||
|
||||
let reg_offset = if fuc.tail { reg::RET + 12 } else { reg::RET_ADDR + 1 };
|
||||
|
||||
res.node_to_reg.iter_mut().filter(|r| **r != 0).for_each(|r| {
|
||||
*r += reg_offset - 1;
|
||||
if fuc.tail && *r >= reg::RET_ADDR {
|
||||
*r += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let atr = |allc: Nid| res.node_to_reg[allc as usize];
|
||||
|
||||
//for (id, node) in fuc.nodes.iter() {
|
||||
// if node.kind == Kind::Phi {
|
||||
// debug_assert_eq!(atr(node.inputs[1]), atr(node.inputs[2]));
|
||||
// debug_assert_eq!(atr(id), atr(node.inputs[2]));
|
||||
// }
|
||||
//}
|
||||
|
||||
let (retl, mut parama) = tys.parama(sig.ret);
|
||||
let mut typs = sig.args.args();
|
||||
let mut args = fuc.nodes[VOID].outputs[ARG_START..].iter();
|
||||
while let Some(aty) = typs.next(tys) {
|
||||
let Arg::Value(ty) = aty else { continue };
|
||||
let Some(loc) = parama.next(ty, tys) else { continue };
|
||||
let &arg = args.next().unwrap();
|
||||
let (rg, size) = match loc {
|
||||
PLoc::WideReg(rg, size) => (rg, size),
|
||||
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
|
||||
PLoc::Reg(r, ..) | PLoc::Ref(r, ..) => {
|
||||
self.emit(instrs::cp(atr(arg), r));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
self.emit(instrs::st(rg, reg::STACK_PTR, self.offsets[arg as usize] as _, size));
|
||||
if fuc.nodes[arg].lock_rc == 0 {
|
||||
self.emit(instrs::addi64(rg, reg::STACK_PTR, self.offsets[arg as usize] as _));
|
||||
}
|
||||
self.emit(instrs::cp(atr(arg), rg));
|
||||
}
|
||||
|
||||
for (i, block) in fuc.func.blocks.iter().enumerate() {
|
||||
self.offsets[block.entry as usize] = self.code.len() as _;
|
||||
for &nid in &fuc.func.instrs[block.range.clone()] {
|
||||
if nid == VOID {
|
||||
continue;
|
||||
}
|
||||
|
||||
let node = &fuc.nodes[nid];
|
||||
|
||||
let extend = |base: ty::Id, dest: ty::Id, from: Nid, to: Nid| {
|
||||
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
|
||||
debug_assert!(bsize <= 8, "{}", ty::Display::new(tys, files, base));
|
||||
debug_assert!(dsize <= 8, "{}", ty::Display::new(tys, files, dest));
|
||||
if bsize == dsize {
|
||||
return Default::default();
|
||||
}
|
||||
match (base.is_signed(), dest.is_signed()) {
|
||||
(true, true) => {
|
||||
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
|
||||
[bsize.ilog2() as usize];
|
||||
op(atr(to), atr(from))
|
||||
}
|
||||
_ => {
|
||||
let mask = (1u64 << (bsize * 8)) - 1;
|
||||
instrs::andi(atr(to), atr(from), mask)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match node.kind {
|
||||
Kind::If => {
|
||||
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
|
||||
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
|
||||
&& let Some((op, swapped)) =
|
||||
op.cond_op(fuc.nodes[fuc.nodes[cnd].inputs[1]].ty)
|
||||
{
|
||||
let &[_, lhs, rhs] = fuc.nodes[cnd].inputs.as_slice() else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
|
||||
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
|
||||
|
||||
let rel = Reloc::new(self.code.len(), 3, 2);
|
||||
self.jump_relocs.push((node.outputs[!swapped as usize], rel));
|
||||
self.emit(op(atr(lhs), atr(rhs), 0));
|
||||
} else {
|
||||
self.emit(extend(fuc.nodes[cnd].ty, fuc.nodes[cnd].ty.extend(), 0, 0));
|
||||
let rel = Reloc::new(self.code.len(), 3, 2);
|
||||
self.jump_relocs.push((node.outputs[0], rel));
|
||||
self.emit(instrs::jne(atr(cnd), reg::ZERO, 0));
|
||||
}
|
||||
}
|
||||
Kind::Loop | Kind::Region => {
|
||||
if (mem::replace(&mut fuc.backrefs[nid as usize], u16::MAX) != u16::MAX)
|
||||
^ (node.kind == Kind::Loop)
|
||||
{
|
||||
let index = (node.kind == Kind::Loop) as usize + 1;
|
||||
for &out in node.outputs.iter() {
|
||||
if fuc.nodes[out].is_data_phi()
|
||||
&& atr(out) != atr(fuc.nodes[out].inputs[index])
|
||||
{
|
||||
self.emit(instrs::cp(
|
||||
atr(out),
|
||||
atr(fuc.nodes[out].inputs[index]),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let rel = Reloc::new(self.code.len(), 1, 4);
|
||||
self.jump_relocs.push((nid, rel));
|
||||
self.emit(instrs::jmp(0));
|
||||
} else {
|
||||
let index = (node.kind != Kind::Loop) as usize + 1;
|
||||
for &out in node.outputs.iter() {
|
||||
if fuc.nodes[out].is_data_phi()
|
||||
&& atr(out) != atr(fuc.nodes[out].inputs[index])
|
||||
{
|
||||
self.emit(instrs::cp(
|
||||
atr(out),
|
||||
atr(fuc.nodes[out].inputs[index]),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::Return => {
|
||||
let &[_, mut ret, ..] = node.inputs.as_slice() else { unreachable!() };
|
||||
match retl {
|
||||
None => {}
|
||||
Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => {
|
||||
self.emit(instrs::cp(r, atr(ret)));
|
||||
}
|
||||
Some(PLoc::Reg(r, size)) | Some(PLoc::WideReg(r, size)) => {
|
||||
ret = match fuc.nodes[ret].kind {
|
||||
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
|
||||
_ => ret,
|
||||
};
|
||||
self.emit(instrs::ld(r, atr(ret), 0, size))
|
||||
}
|
||||
Some(PLoc::Ref(_, size)) => {
|
||||
ret = match fuc.nodes[ret].kind {
|
||||
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
|
||||
_ => ret,
|
||||
};
|
||||
|
||||
let [src, dst] = [atr(ret), atr(MEM)];
|
||||
if let Ok(size) = u16::try_from(size) {
|
||||
self.emit(instrs::bmc(src, dst, size));
|
||||
} else {
|
||||
for _ in 0..size / u16::MAX as u32 {
|
||||
self.emit(instrs::bmc(src, dst, u16::MAX));
|
||||
self.emit(instrs::addi64(src, src, u16::MAX as _));
|
||||
self.emit(instrs::addi64(dst, dst, u16::MAX as _));
|
||||
}
|
||||
self.emit(instrs::bmc(src, dst, size as u16));
|
||||
self.emit(instrs::addi64(src, src, size.wrapping_neg() as _));
|
||||
self.emit(instrs::addi64(dst, dst, size.wrapping_neg() as _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i != fuc.func.blocks.len() - 1 {
|
||||
let rel = Reloc::new(self.code.len(), 1, 4);
|
||||
self.ret_relocs.push(rel);
|
||||
self.emit(instrs::jmp(0));
|
||||
}
|
||||
}
|
||||
Kind::Die => self.emit(instrs::un()),
|
||||
Kind::CInt { value } if node.ty.is_float() => {
|
||||
self.emit(match node.ty {
|
||||
ty::Id::F32 => instrs::li32(
|
||||
atr(nid),
|
||||
(f64::from_bits(value as _) as f32).to_bits(),
|
||||
),
|
||||
ty::Id::F64 => instrs::li64(atr(nid), value as _),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
}
|
||||
Kind::CInt { value } => self.emit(match tys.size_of(node.ty) {
|
||||
1 => instrs::li8(atr(nid), value as _),
|
||||
2 => instrs::li16(atr(nid), value as _),
|
||||
4 => instrs::li32(atr(nid), value as _),
|
||||
_ => instrs::li64(atr(nid), value as _),
|
||||
}),
|
||||
Kind::UnOp { op } => {
|
||||
let op = op
|
||||
.unop(node.ty, fuc.nodes[node.inputs[1]].ty)
|
||||
.expect("TODO: unary operator not supported");
|
||||
self.emit(op(atr(nid), atr(node.inputs[1])));
|
||||
}
|
||||
Kind::BinOp { .. } if node.lock_rc != 0 => {}
|
||||
Kind::BinOp { op } => {
|
||||
let &[.., lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
|
||||
|
||||
if let Kind::CInt { value } = fuc.nodes[rhs].kind
|
||||
&& fuc.nodes[rhs].lock_rc != 0
|
||||
&& let Some(op) = op.imm_binop(node.ty)
|
||||
{
|
||||
self.emit(op(atr(nid), atr(lhs), value as _));
|
||||
} else if let Some(op) =
|
||||
op.binop(node.ty).or(op.float_cmp(fuc.nodes[lhs].ty))
|
||||
{
|
||||
self.emit(op(atr(nid), atr(lhs), atr(rhs)));
|
||||
} else if let Some(against) = op.cmp_against() {
|
||||
let op_ty = fuc.nodes[lhs].ty;
|
||||
|
||||
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
|
||||
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
|
||||
|
||||
if op_ty.is_float() && matches!(op, TokenKind::Le | TokenKind::Ge) {
|
||||
let opop = match op {
|
||||
TokenKind::Le => TokenKind::Gt,
|
||||
TokenKind::Ge => TokenKind::Lt,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let op_fn = opop.float_cmp(op_ty).unwrap();
|
||||
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
|
||||
self.emit(instrs::not(atr(nid), atr(nid)));
|
||||
} else if op_ty.is_integer() {
|
||||
let op_fn =
|
||||
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
|
||||
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
|
||||
self.emit(instrs::cmpui(atr(nid), atr(nid), against));
|
||||
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
|
||||
self.emit(instrs::not(atr(nid), atr(nid)));
|
||||
}
|
||||
} else {
|
||||
todo!("unhandled operator: {op}");
|
||||
}
|
||||
} else {
|
||||
todo!("unhandled operator: {op}");
|
||||
}
|
||||
}
|
||||
Kind::Call { args, func } => {
|
||||
let (ret, mut parama) = tys.parama(node.ty);
|
||||
let mut args = args.args();
|
||||
let mut allocs = node.inputs[1..].iter();
|
||||
while let Some(arg) = args.next(tys) {
|
||||
let Arg::Value(ty) = arg else { continue };
|
||||
let Some(loc) = parama.next(ty, tys) else { continue };
|
||||
|
||||
let mut arg = *allocs.next().unwrap();
|
||||
let (rg, size) = match loc {
|
||||
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
|
||||
PLoc::WideReg(rg, size) => (rg, size),
|
||||
PLoc::Ref(r, ..) => {
|
||||
arg = match fuc.nodes[arg].kind {
|
||||
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
|
||||
_ => arg,
|
||||
};
|
||||
self.emit(instrs::cp(r, atr(arg)));
|
||||
continue;
|
||||
}
|
||||
PLoc::Reg(r, ..) => {
|
||||
self.emit(instrs::cp(r, atr(arg)));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
arg = match fuc.nodes[arg].kind {
|
||||
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
|
||||
_ => arg,
|
||||
};
|
||||
self.emit(instrs::ld(rg, atr(arg), 0, size));
|
||||
}
|
||||
|
||||
debug_assert!(
|
||||
!matches!(ret, Some(PLoc::Ref(..))) || allocs.next().is_some()
|
||||
);
|
||||
|
||||
if func == ty::ECA {
|
||||
self.emit(instrs::eca());
|
||||
} else {
|
||||
self.relocs.push(TypedReloc {
|
||||
target: ty::Kind::Func(func).compress(),
|
||||
reloc: Reloc::new(self.code.len(), 3, 4),
|
||||
});
|
||||
self.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
|
||||
}
|
||||
|
||||
match ret {
|
||||
Some(PLoc::WideReg(r, size)) => {
|
||||
debug_assert_eq!(
|
||||
fuc.nodes[*node.inputs.last().unwrap()].kind,
|
||||
Kind::Stck
|
||||
);
|
||||
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
|
||||
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
|
||||
}
|
||||
Some(PLoc::Reg(r, size)) if node.ty.loc(tys) == Loc::Stack => {
|
||||
debug_assert_eq!(
|
||||
fuc.nodes[*node.inputs.last().unwrap()].kind,
|
||||
Kind::Stck
|
||||
);
|
||||
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
|
||||
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
|
||||
}
|
||||
Some(PLoc::Reg(r, ..)) => self.emit(instrs::cp(atr(nid), r)),
|
||||
None | Some(PLoc::Ref(..)) => {}
|
||||
}
|
||||
}
|
||||
Kind::Global { global } => {
|
||||
let reloc = Reloc::new(self.code.len(), 3, 4);
|
||||
self.relocs.push(TypedReloc {
|
||||
target: ty::Kind::Global(global).compress(),
|
||||
reloc,
|
||||
});
|
||||
self.emit(instrs::lra(atr(nid), 0, 0));
|
||||
}
|
||||
Kind::Stck => {
|
||||
let base = reg::STACK_PTR;
|
||||
let offset = self.offsets[nid as usize];
|
||||
self.emit(instrs::addi64(atr(nid), base, offset as _));
|
||||
}
|
||||
Kind::Load => {
|
||||
let mut region = node.inputs[1];
|
||||
let mut offset = 0;
|
||||
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
|
||||
&& let Kind::CInt { value } =
|
||||
fuc.nodes[fuc.nodes[region].inputs[2]].kind
|
||||
{
|
||||
region = fuc.nodes[region].inputs[1];
|
||||
offset = value as Offset;
|
||||
}
|
||||
let size = tys.size_of(node.ty);
|
||||
if node.ty.loc(tys) != Loc::Stack {
|
||||
let (base, offset) = match fuc.nodes[region].kind {
|
||||
Kind::Stck => {
|
||||
(reg::STACK_PTR, self.offsets[region as usize] + offset)
|
||||
}
|
||||
_ => (atr(region), offset),
|
||||
};
|
||||
self.emit(instrs::ld(atr(nid), base, offset as _, size as _));
|
||||
}
|
||||
}
|
||||
Kind::Stre if node.inputs[1] == VOID => {}
|
||||
Kind::Stre => {
|
||||
let mut region = node.inputs[2];
|
||||
let mut offset = 0;
|
||||
let size = u16::try_from(tys.size_of(node.ty)).expect("TODO");
|
||||
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
|
||||
&& let Kind::CInt { value } =
|
||||
fuc.nodes[fuc.nodes[region].inputs[2]].kind
|
||||
&& node.ty.loc(tys) == Loc::Reg
|
||||
{
|
||||
region = fuc.nodes[region].inputs[1];
|
||||
offset = value as Offset;
|
||||
}
|
||||
let nd = &fuc.nodes[region];
|
||||
let value = node.inputs[1];
|
||||
let (base, offset, src) = match nd.kind {
|
||||
Kind::Stck if node.ty.loc(tys) == Loc::Reg => {
|
||||
(reg::STACK_PTR, self.offsets[region as usize] + offset, value)
|
||||
}
|
||||
_ => (atr(region), offset, match fuc.nodes[value].kind {
|
||||
Kind::Load { .. } => fuc.nodes[value].inputs[1],
|
||||
_ => value,
|
||||
}),
|
||||
};
|
||||
|
||||
match node.ty.loc(tys) {
|
||||
Loc::Reg => self.emit(instrs::st(atr(src), base, offset as _, size)),
|
||||
Loc::Stack => {
|
||||
debug_assert_eq!(offset, 0);
|
||||
self.emit(instrs::bmc(atr(src), base, size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kind::Mem => self.emit(instrs::cp(atr(MEM), reg::RET)),
|
||||
Kind::Arg => {}
|
||||
e @ (Kind::Start
|
||||
| Kind::Entry
|
||||
| Kind::End
|
||||
| Kind::Loops
|
||||
| Kind::Then
|
||||
| Kind::Else
|
||||
| Kind::Phi
|
||||
| Kind::Assert { .. }) => unreachable!("{e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.ralloc_my = res;
|
||||
|
||||
let bundle_count = self.ralloc_my.bundles.len() + (reg_offset as usize);
|
||||
(
|
||||
if fuc.tail {
|
||||
bundle_count.saturating_sub(reg::RET_ADDR as _)
|
||||
} else {
|
||||
assert!(bundle_count < reg::STACK_PTR as usize, "TODO: spill memory");
|
||||
self.ralloc_my.bundles.len()
|
||||
},
|
||||
fuc.tail,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Function<'a> {
|
||||
sig: Sig,
|
||||
tail: bool,
|
||||
backrefs: Vec<u16>,
|
||||
nodes: &'a mut Nodes,
|
||||
tys: &'a Types,
|
||||
visited: BitSet,
|
||||
func: Func,
|
||||
}
|
||||
|
||||
impl Function<'_> {
|
||||
fn vreg_count(&self) -> usize {
|
||||
self.nodes.values.len()
|
||||
}
|
||||
|
||||
fn uses_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
|
||||
if self.nodes[nid].kind.is_cfg() && !matches!(self.nodes[nid].kind, Kind::Call { .. }) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.nodes[nid]
|
||||
.outputs
|
||||
.iter()
|
||||
.filter(|&&n| self.nodes.is_data_dep(nid, n))
|
||||
.collect_into(buf);
|
||||
}
|
||||
|
||||
fn phi_inputs_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
|
||||
match self.nodes[nid].kind {
|
||||
Kind::Region => {
|
||||
for &inp in self.nodes[nid].outputs.as_slice() {
|
||||
if self.nodes[inp].is_data_phi() {
|
||||
buf.extend(&self.nodes[inp].inputs[1..]);
|
||||
buf.push(inp);
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::Loop => {
|
||||
for &inp in self.nodes[nid].outputs.as_slice() {
|
||||
if self.nodes[inp].is_data_phi() {
|
||||
buf.push(self.nodes[inp].inputs[1]);
|
||||
buf.push(inp);
|
||||
buf.push(self.nodes[inp].inputs[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn instr_of(&self, nid: Nid) -> Option<Nid> {
|
||||
if self.nodes[nid].kind == Kind::Phi || self.nodes[nid].lock_rc != 0 {
|
||||
return None;
|
||||
}
|
||||
debug_assert_ne!(self.backrefs[nid as usize], Nid::MAX, "{:?}", self.nodes[nid]);
|
||||
Some(self.backrefs[nid as usize])
|
||||
}
|
||||
|
||||
fn block_of(&self, nid: Nid) -> Nid {
|
||||
debug_assert!(self.nodes[nid].kind.starts_basic_block());
|
||||
self.backrefs[nid as usize]
|
||||
}
|
||||
|
||||
fn idom_of(&self, mut nid: Nid) -> Nid {
|
||||
while !self.nodes[nid].kind.starts_basic_block() {
|
||||
nid = self.nodes.idom(nid);
|
||||
}
|
||||
nid
|
||||
}
|
||||
|
||||
fn use_block(&self, inst: Nid, uinst: Nid) -> Nid {
|
||||
let mut block = self.nodes.use_block(inst, uinst);
|
||||
while !self.nodes[block].kind.starts_basic_block() {
|
||||
block = self.nodes.idom(block);
|
||||
}
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Function<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
for block in &self.func.blocks {
|
||||
writeln!(f, "{:?}", self.nodes[block.entry].kind)?;
|
||||
for &instr in &self.func.instrs[block.range.clone()] {
|
||||
writeln!(f, "{:?}", self.nodes[instr].kind)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Function<'a> {
|
||||
fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self {
|
||||
let mut s = Self {
|
||||
backrefs: vec![u16::MAX; nodes.values.len()],
|
||||
tail: true,
|
||||
nodes,
|
||||
tys,
|
||||
sig,
|
||||
visited: Default::default(),
|
||||
func: Default::default(),
|
||||
};
|
||||
s.visited.clear(s.nodes.values.len());
|
||||
s.emit_node(VOID);
|
||||
s
|
||||
}
|
||||
|
||||
fn add_block(&mut self, entry: Nid) {
|
||||
self.func
|
||||
.blocks
|
||||
.push(Block { range: self.func.instrs.len()..self.func.instrs.len(), entry });
|
||||
self.backrefs[entry as usize] = self.func.blocks.len() as u16 - 1;
|
||||
}
|
||||
|
||||
fn close_block(&mut self, exit: Nid) {
|
||||
if !matches!(self.nodes[exit].kind, Kind::Loop | Kind::Region) {
|
||||
self.add_instr(exit);
|
||||
} else {
|
||||
self.func.instrs.push(exit);
|
||||
}
|
||||
let prev = self.func.blocks.last_mut().unwrap();
|
||||
prev.range.end = self.func.instrs.len();
|
||||
}
|
||||
|
||||
fn add_instr(&mut self, nid: Nid) {
|
||||
debug_assert_ne!(self.nodes[nid].kind, Kind::Loop);
|
||||
self.backrefs[nid as usize] = self.func.instrs.len() as u16;
|
||||
self.func.instrs.push(nid);
|
||||
}
|
||||
|
||||
fn emit_node(&mut self, nid: Nid) {
|
||||
if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) {
|
||||
match (self.nodes[nid].kind, self.visited.set(nid)) {
|
||||
(Kind::Loop, false) | (Kind::Region, true) => {
|
||||
self.close_block(nid);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if !self.visited.set(nid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.nodes.is_never_used(nid, self.tys) {
|
||||
self.nodes.lock(nid);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut node = self.nodes[nid].clone();
|
||||
match node.kind {
|
||||
Kind::Start => {
|
||||
debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry);
|
||||
self.add_block(VOID);
|
||||
self.emit_node(node.outputs[0])
|
||||
}
|
||||
Kind::If => {
|
||||
let &[_, cond] = node.inputs.as_slice() else { unreachable!() };
|
||||
let &[mut then, mut else_] = node.outputs.as_slice() else { unreachable!() };
|
||||
|
||||
if let Kind::BinOp { op } = self.nodes[cond].kind
|
||||
&& let Some((_, swapped)) = op.cond_op(node.ty)
|
||||
&& swapped
|
||||
{
|
||||
mem::swap(&mut then, &mut else_);
|
||||
}
|
||||
|
||||
self.close_block(nid);
|
||||
self.emit_node(then);
|
||||
self.emit_node(else_);
|
||||
}
|
||||
Kind::Region | Kind::Loop => {
|
||||
self.close_block(nid);
|
||||
self.add_block(nid);
|
||||
self.reschedule_block(nid, &mut node.outputs);
|
||||
for o in node.outputs.into_iter().rev() {
|
||||
self.emit_node(o);
|
||||
}
|
||||
}
|
||||
Kind::Return | Kind::Die => {
|
||||
self.close_block(nid);
|
||||
self.emit_node(node.outputs[0]);
|
||||
}
|
||||
Kind::Entry => {
|
||||
let (ret, mut parama) = self.tys.parama(self.sig.ret);
|
||||
let mut typs = self.sig.args.args();
|
||||
#[expect(clippy::unnecessary_to_owned)]
|
||||
let mut args = self.nodes[VOID].outputs[ARG_START..].to_owned().into_iter();
|
||||
while let Some(ty) = typs.next_value(self.tys) {
|
||||
let arg = args.next().unwrap();
|
||||
debug_assert_eq!(self.nodes[arg].kind, Kind::Arg);
|
||||
match parama.next(ty, self.tys) {
|
||||
None => {}
|
||||
Some(_) => self.add_instr(arg),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(PLoc::Ref(..)) = ret {
|
||||
self.add_instr(MEM);
|
||||
}
|
||||
|
||||
self.reschedule_block(nid, &mut node.outputs);
|
||||
for o in node.outputs.into_iter().rev() {
|
||||
self.emit_node(o);
|
||||
}
|
||||
}
|
||||
Kind::Then | Kind::Else => {
|
||||
self.add_block(nid);
|
||||
self.reschedule_block(nid, &mut node.outputs);
|
||||
for o in node.outputs.into_iter().rev() {
|
||||
self.emit_node(o);
|
||||
}
|
||||
}
|
||||
Kind::Call { func, .. } => {
|
||||
self.tail &= func == ty::ECA;
|
||||
|
||||
self.add_instr(nid);
|
||||
|
||||
self.reschedule_block(nid, &mut node.outputs);
|
||||
for o in node.outputs.into_iter().rev() {
|
||||
if self.nodes[o].inputs[0] == nid
|
||||
|| (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region)
|
||||
&& self.nodes[o].inputs[1] == nid)
|
||||
{
|
||||
self.emit_node(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::CInt { .. }
|
||||
| Kind::BinOp { .. }
|
||||
| Kind::UnOp { .. }
|
||||
| Kind::Global { .. }
|
||||
| Kind::Load { .. }
|
||||
| Kind::Stre
|
||||
| Kind::Stck => self.add_instr(nid),
|
||||
Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {}
|
||||
Kind::Assert { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reschedule_block(&mut self, from: Nid, outputs: &mut Vc) {
|
||||
let from = Some(&from);
|
||||
let mut buf = Vec::with_capacity(outputs.len());
|
||||
let mut seen = BitSet::default();
|
||||
seen.clear(self.nodes.values.len());
|
||||
|
||||
for &o in outputs.iter() {
|
||||
if !self.nodes.is_cfg(o) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seen.set(o);
|
||||
|
||||
let mut cursor = buf.len();
|
||||
buf.push(o);
|
||||
while let Some(&n) = buf.get(cursor) {
|
||||
for &i in &self.nodes[n].inputs[1..] {
|
||||
if from == self.nodes[i].inputs.first()
|
||||
&& self.nodes[i]
|
||||
.outputs
|
||||
.iter()
|
||||
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
|
||||
&& seen.set(i)
|
||||
{
|
||||
buf.push(i);
|
||||
}
|
||||
}
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for &o in outputs.iter() {
|
||||
if !seen.set(o) {
|
||||
continue;
|
||||
}
|
||||
let mut cursor = buf.len();
|
||||
buf.push(o);
|
||||
while let Some(&n) = buf.get(cursor) {
|
||||
for &i in &self.nodes[n].inputs[1..] {
|
||||
if from == self.nodes[i].inputs.first()
|
||||
&& self.nodes[i]
|
||||
.outputs
|
||||
.iter()
|
||||
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
|
||||
&& seen.set(i)
|
||||
{
|
||||
buf.push(i);
|
||||
}
|
||||
}
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(
|
||||
outputs.len() == buf.len() || outputs.len() == buf.len() + 1,
|
||||
"{:?} {:?}",
|
||||
outputs,
|
||||
buf
|
||||
);
|
||||
|
||||
if buf.len() + 1 == outputs.len() {
|
||||
outputs.remove(outputs.len() - 1);
|
||||
}
|
||||
outputs.copy_from_slice(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
ctx: &'a Function<'a>,
|
||||
func: &'a Func,
|
||||
res: &'a mut Res,
|
||||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
pub fn new(ctx: &'a Function<'a>, func: &'a Func, res: &'a mut Res) -> Self {
|
||||
Self { ctx, func, res }
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.res.bundles.clear();
|
||||
self.res.node_to_reg.clear();
|
||||
self.res.node_to_reg.resize(self.ctx.vreg_count(), 0);
|
||||
|
||||
debug_assert!(self.res.dfs_buf.is_empty());
|
||||
debug_assert!(self.res.use_buf.is_empty());
|
||||
debug_assert!(self.res.phi_input_buf.is_empty());
|
||||
|
||||
let mut bundle = Bundle::new(self.func.instrs.len());
|
||||
let mut visited = BitSet::with_capacity(self.ctx.nodes.values.len());
|
||||
let mut use_buf = mem::take(&mut self.res.use_buf);
|
||||
|
||||
let mut phi_input_buf = mem::take(&mut self.res.phi_input_buf);
|
||||
for block in &self.func.blocks {
|
||||
self.ctx.phi_inputs_of(block.entry, &mut phi_input_buf);
|
||||
for param in phi_input_buf.drain(..) {
|
||||
if !visited.set(param) {
|
||||
continue;
|
||||
}
|
||||
self.append_bundle(param, &mut bundle, &mut use_buf);
|
||||
}
|
||||
}
|
||||
self.res.phi_input_buf = phi_input_buf;
|
||||
|
||||
for &inst in &self.func.instrs {
|
||||
if visited.get(inst) || inst == 0 {
|
||||
continue;
|
||||
}
|
||||
self.append_bundle(inst, &mut bundle, &mut use_buf);
|
||||
}
|
||||
|
||||
self.res.use_buf = use_buf;
|
||||
}
|
||||
|
||||
fn append_bundle(&mut self, inst: Nid, bundle: &mut Bundle, use_buf: &mut Vec<Nid>) {
|
||||
let mut dom = self.ctx.idom_of(inst);
|
||||
if self.ctx.nodes[dom].kind == Kind::Loop && self.ctx.nodes[inst].kind == Kind::Phi {
|
||||
dom = self.ctx.nodes.idom(dom);
|
||||
dom = self.ctx.idom_of(dom);
|
||||
}
|
||||
self.ctx.uses_of(inst, use_buf);
|
||||
for uinst in use_buf.drain(..) {
|
||||
let cursor = self.ctx.use_block(inst, uinst);
|
||||
self.reverse_cfg_dfs(cursor, dom, |_, n, b| {
|
||||
let mut range = b.range.clone();
|
||||
range.start =
|
||||
range.start.max(self.ctx.instr_of(inst).map_or(0, |n| n + 1) as usize);
|
||||
range.end = range.end.min(
|
||||
self.ctx
|
||||
.instr_of(uinst)
|
||||
.filter(|_| self.ctx.nodes.loop_depth(dom) == self.ctx.nodes.loop_depth(n))
|
||||
.map_or(Nid::MAX, |n| n + 1) as usize,
|
||||
);
|
||||
|
||||
bundle.add(range);
|
||||
});
|
||||
}
|
||||
|
||||
match self.res.bundles.iter_mut().enumerate().find(|(_, b)| !b.overlaps(bundle)) {
|
||||
Some((i, other)) => {
|
||||
other.merge(bundle);
|
||||
bundle.clear();
|
||||
self.res.node_to_reg[inst as usize] = i as Reg + 1;
|
||||
}
|
||||
None => {
|
||||
self.res.bundles.push(mem::replace(bundle, Bundle::new(self.func.instrs.len())));
|
||||
self.res.node_to_reg[inst as usize] = self.res.bundles.len() as Reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reverse_cfg_dfs(
|
||||
&mut self,
|
||||
from: Nid,
|
||||
until: Nid,
|
||||
mut each: impl FnMut(&mut Self, Nid, &Block),
|
||||
) {
|
||||
debug_assert!(self.res.dfs_buf.is_empty());
|
||||
self.res.dfs_buf.push(from);
|
||||
self.res.dfs_seem.clear(self.ctx.nodes.values.len());
|
||||
|
||||
while let Some(nid) = self.res.dfs_buf.pop() {
|
||||
each(self, nid, &self.func.blocks[self.ctx.block_of(nid) as usize]);
|
||||
if nid == until {
|
||||
continue;
|
||||
}
|
||||
match self.ctx.nodes[nid].kind {
|
||||
Kind::Then | Kind::Else | Kind::Region | Kind::Loop => {
|
||||
for &n in self.ctx.nodes[nid].inputs.iter() {
|
||||
let d = self.ctx.idom_of(n);
|
||||
if self.res.dfs_seem.set(d) {
|
||||
self.res.dfs_buf.push(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::Start => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Res {
|
||||
pub bundles: Vec<Bundle>,
|
||||
pub node_to_reg: Vec<Reg>,
|
||||
use_buf: Vec<Nid>,
|
||||
phi_input_buf: Vec<Nid>,
|
||||
dfs_buf: Vec<Nid>,
|
||||
dfs_seem: BitSet,
|
||||
}
|
||||
|
||||
pub struct Bundle {
|
||||
taken: Vec<bool>,
|
||||
}
|
||||
|
||||
impl Bundle {
|
||||
fn new(size: usize) -> Self {
|
||||
Self { taken: vec![false; size] }
|
||||
}
|
||||
|
||||
fn add(&mut self, range: Range<usize>) {
|
||||
self.taken[range].fill(true);
|
||||
}
|
||||
|
||||
fn overlaps(&self, other: &Self) -> bool {
|
||||
self.taken.iter().zip(other.taken.iter()).any(|(a, b)| a & b)
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &Self) {
|
||||
debug_assert!(!self.overlaps(other));
|
||||
self.taken.iter_mut().zip(other.taken.iter()).for_each(|(a, b)| *a |= *b);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.taken.fill(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Func {
|
||||
pub blocks: Vec<Block>,
|
||||
pub instrs: Vec<Nid>,
|
||||
}
|
||||
|
||||
pub struct Block {
|
||||
pub range: Range<usize>,
|
||||
pub entry: Nid,
|
||||
}
|
File diff suppressed because it is too large
Load diff
1383
lang/src/ty.rs
Normal file
1383
lang/src/ty.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,20 +1,40 @@
|
|||
#![expect(dead_code)]
|
||||
use {
|
||||
alloc::alloc,
|
||||
core::{
|
||||
alloc::Layout,
|
||||
fmt::Debug,
|
||||
hint::unreachable_unchecked,
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut, Not},
|
||||
ops::{Deref, DerefMut, Not, Range},
|
||||
ptr::Unique,
|
||||
},
|
||||
};
|
||||
|
||||
fn decide(b: bool, name: &'static str) -> Result<(), &'static str> {
|
||||
b.then_some(()).ok_or(name)
|
||||
}
|
||||
|
||||
pub fn is_snake_case(str: &str) -> Result<(), &'static str> {
|
||||
decide(str.bytes().all(|c| matches!(c, b'a'..=b'z' | b'0'..=b'9' | b'_')), "snake_case")
|
||||
}
|
||||
|
||||
pub fn is_pascal_case(str: &str) -> Result<(), &'static str> {
|
||||
decide(
|
||||
str.as_bytes()[0].is_ascii_uppercase() && str.bytes().all(|c| c.is_ascii_alphanumeric()),
|
||||
"PascalCase",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_screaming_case(str: &str) -> Result<(), &'static str> {
|
||||
decide(str.bytes().all(|c| matches!(c, b'A'..=b'Z' | b'0'..=b'9' | b'_')), "SCREAMING_CASE")
|
||||
}
|
||||
|
||||
type Nid = u16;
|
||||
type BitSetUnit = usize;
|
||||
|
||||
pub union BitSet {
|
||||
inline: usize,
|
||||
inline: BitSetUnit,
|
||||
alloced: Unique<AllocedBitSet>,
|
||||
}
|
||||
|
||||
|
@ -58,9 +78,9 @@ impl Default for BitSet {
|
|||
}
|
||||
|
||||
impl BitSet {
|
||||
const FLAG: usize = 1 << (Self::UNIT - 1);
|
||||
const FLAG: BitSetUnit = 1 << (Self::UNIT - 1);
|
||||
const INLINE_ELEMS: usize = Self::UNIT - 1;
|
||||
const UNIT: usize = core::mem::size_of::<usize>() * 8;
|
||||
pub const UNIT: usize = core::mem::size_of::<BitSetUnit>() * 8;
|
||||
|
||||
pub fn with_capacity(len: usize) -> Self {
|
||||
let mut s = Self::default();
|
||||
|
@ -72,7 +92,7 @@ impl BitSet {
|
|||
unsafe { self.inline & Self::FLAG != 0 }
|
||||
}
|
||||
|
||||
fn data_and_len(&self) -> (&[usize], usize) {
|
||||
fn data_and_len(&self) -> (&[BitSetUnit], usize) {
|
||||
unsafe {
|
||||
if self.is_inline() {
|
||||
(core::slice::from_ref(&self.inline), Self::INLINE_ELEMS)
|
||||
|
@ -80,16 +100,16 @@ impl BitSet {
|
|||
let small_vec = self.alloced.as_ref();
|
||||
(
|
||||
core::slice::from_raw_parts(
|
||||
&small_vec.data as *const _ as *const usize,
|
||||
&small_vec.data as *const _ as *const BitSetUnit,
|
||||
small_vec.cap,
|
||||
),
|
||||
small_vec.cap * core::mem::size_of::<usize>() * 8,
|
||||
small_vec.cap * Self::UNIT,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_mut_and_len(&mut self) -> (&mut [usize], usize) {
|
||||
fn data_mut_and_len(&mut self) -> (&mut [BitSetUnit], usize) {
|
||||
unsafe {
|
||||
if self.is_inline() {
|
||||
(core::slice::from_mut(&mut self.inline), INLINE_ELEMS)
|
||||
|
@ -97,7 +117,7 @@ impl BitSet {
|
|||
let small_vec = self.alloced.as_mut();
|
||||
(
|
||||
core::slice::from_raw_parts_mut(
|
||||
&mut small_vec.data as *mut _ as *mut usize,
|
||||
&mut small_vec.data as *mut _ as *mut BitSetUnit,
|
||||
small_vec.cap,
|
||||
),
|
||||
small_vec.cap * Self::UNIT,
|
||||
|
@ -124,11 +144,12 @@ impl BitSet {
|
|||
let index = index as usize;
|
||||
let (mut data, len) = self.data_mut_and_len();
|
||||
if core::intrinsics::unlikely(index >= len) {
|
||||
self.grow(index.next_power_of_two().max(4 * Self::UNIT));
|
||||
self.grow((index + 1).next_power_of_two().max(4 * Self::UNIT));
|
||||
(data, _) = self.data_mut_and_len();
|
||||
}
|
||||
|
||||
let (elem, bit) = Self::indexes(index);
|
||||
debug_assert!(elem < data.len(), "{} < {}", elem, data.len());
|
||||
let elem = unsafe { data.get_unchecked_mut(elem) };
|
||||
let prev = *elem;
|
||||
*elem |= 1 << bit;
|
||||
|
@ -142,7 +163,7 @@ impl BitSet {
|
|||
let (ptr, prev_len) = unsafe {
|
||||
if self.is_inline() {
|
||||
let ptr = alloc::alloc(layout);
|
||||
*ptr.add(off).cast::<usize>() = self.inline & !Self::FLAG;
|
||||
*ptr.add(off).cast::<BitSetUnit>() = self.inline & !Self::FLAG;
|
||||
(ptr, 1)
|
||||
} else {
|
||||
let prev_len = self.alloced.as_ref().cap;
|
||||
|
@ -153,7 +174,7 @@ impl BitSet {
|
|||
unsafe {
|
||||
MaybeUninit::fill(
|
||||
core::slice::from_raw_parts_mut(
|
||||
ptr.add(off).cast::<MaybeUninit<usize>>().add(prev_len),
|
||||
ptr.add(off).cast::<MaybeUninit<BitSetUnit>>().add(prev_len),
|
||||
slot_count - prev_len,
|
||||
),
|
||||
0,
|
||||
|
@ -166,7 +187,7 @@ impl BitSet {
|
|||
fn layout(slot_count: usize) -> (core::alloc::Layout, usize) {
|
||||
unsafe {
|
||||
core::alloc::Layout::new::<AllocedBitSet>()
|
||||
.extend(Layout::array::<usize>(slot_count).unwrap_unchecked())
|
||||
.extend(Layout::array::<BitSetUnit>(slot_count).unwrap_unchecked())
|
||||
.unwrap_unchecked()
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +205,10 @@ impl BitSet {
|
|||
|
||||
pub fn clear(&mut self, len: usize) {
|
||||
self.reserve(len);
|
||||
self.clear_as_is();
|
||||
}
|
||||
|
||||
pub fn clear_as_is(&mut self) {
|
||||
if self.is_inline() {
|
||||
unsafe { self.inline &= Self::FLAG };
|
||||
} else {
|
||||
|
@ -191,7 +216,11 @@ impl BitSet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn units<'a>(&'a self, slot: &'a mut usize) -> &'a [usize] {
|
||||
pub fn approx_unit_cap(&self) -> usize {
|
||||
self.data_and_len().0.len()
|
||||
}
|
||||
|
||||
pub fn units<'a>(&'a self, slot: &'a mut BitSetUnit) -> &'a [BitSetUnit] {
|
||||
if self.is_inline() {
|
||||
*slot = unsafe { self.inline } & !Self::FLAG;
|
||||
core::slice::from_ref(slot)
|
||||
|
@ -200,36 +229,47 @@ impl BitSet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn units_mut(&mut self) -> Option<&mut [BitSetUnit]> {
|
||||
self.is_inline().not().then(|| self.data_mut_and_len().0)
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, len: usize) {
|
||||
if len > self.data_and_len().1 {
|
||||
self.grow(len.next_power_of_two().max(4 * Self::UNIT));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn units_mut(&mut self) -> Result<&mut [usize], &mut InlineBitSetView> {
|
||||
if self.is_inline() {
|
||||
Err(unsafe {
|
||||
core::mem::transmute::<&mut usize, &mut InlineBitSetView>(&mut self.inline)
|
||||
})
|
||||
} else {
|
||||
Ok(self.data_mut_and_len().0)
|
||||
pub fn set_range(&mut self, proj_range: Range<usize>) {
|
||||
if proj_range.is_empty() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InlineBitSetView(usize);
|
||||
self.reserve(proj_range.end);
|
||||
let (units, _) = self.data_mut_and_len();
|
||||
|
||||
impl InlineBitSetView {
|
||||
pub(crate) fn add_mask(&mut self, tmp: usize) {
|
||||
debug_assert!(tmp & BitSet::FLAG == 0);
|
||||
self.0 |= tmp;
|
||||
if proj_range.start / Self::UNIT == (proj_range.end - 1) / Self::UNIT {
|
||||
debug_assert!(proj_range.len() <= Self::UNIT);
|
||||
let mask = ((1 << proj_range.len()) - 1) << (proj_range.start % Self::UNIT);
|
||||
units[proj_range.start / Self::UNIT] |= mask;
|
||||
} else {
|
||||
let fill_range = proj_range.start.div_ceil(Self::UNIT)..proj_range.end / Self::UNIT;
|
||||
units[fill_range].fill(BitSetUnit::MAX);
|
||||
|
||||
let prefix_len = Self::UNIT - proj_range.start % Self::UNIT;
|
||||
let prefix_mask = ((1 << prefix_len) - 1) << (proj_range.start % Self::UNIT);
|
||||
units[proj_range.start / Self::UNIT] |= prefix_mask;
|
||||
|
||||
let postfix_len = proj_range.end % Self::UNIT;
|
||||
let postfix_mask = (1 << postfix_len) - 1;
|
||||
units[proj_range.end / Self::UNIT] |= postfix_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitSetIter<'a> {
|
||||
index: usize,
|
||||
current: usize,
|
||||
remining: &'a [usize],
|
||||
current: BitSetUnit,
|
||||
remining: &'a [BitSetUnit],
|
||||
}
|
||||
|
||||
impl Iterator for BitSetIter<'_> {
|
||||
|
@ -249,7 +289,7 @@ impl Iterator for BitSetIter<'_> {
|
|||
|
||||
struct AllocedBitSet {
|
||||
cap: usize,
|
||||
data: [usize; 0],
|
||||
data: [BitSetUnit; 0],
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -360,25 +400,26 @@ impl Vc {
|
|||
}
|
||||
|
||||
pub fn push(&mut self, value: Nid) {
|
||||
if let Some(layout) = self.layout()
|
||||
&& unsafe { self.alloced.len == self.alloced.cap }
|
||||
{
|
||||
unsafe {
|
||||
self.alloced.cap *= 2;
|
||||
self.alloced.base = Unique::new_unchecked(
|
||||
alloc::realloc(
|
||||
self.alloced.base.as_ptr().cast(),
|
||||
layout,
|
||||
self.alloced.cap as usize * core::mem::size_of::<Nid>(),
|
||||
)
|
||||
.cast(),
|
||||
);
|
||||
if let Some(layout) = self.layout() {
|
||||
if unsafe { self.alloced.len == self.alloced.cap } {
|
||||
unsafe {
|
||||
self.alloced.cap *= 2;
|
||||
self.alloced.base = Unique::new_unchecked(
|
||||
alloc::realloc(
|
||||
self.alloced.base.as_ptr().cast(),
|
||||
layout,
|
||||
self.alloced.cap as usize * core::mem::size_of::<Nid>(),
|
||||
)
|
||||
.cast(),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.len() == INLINE_ELEMS {
|
||||
unsafe {
|
||||
let mut allcd =
|
||||
Self::alloc((self.inline.cap + 1).next_power_of_two() as _, self.len());
|
||||
core::ptr::copy_nonoverlapping(self.as_ptr(), allcd.as_mut_ptr(), self.len());
|
||||
debug_assert!(!allcd.is_inline());
|
||||
*self = allcd;
|
||||
}
|
||||
}
|
||||
|
@ -532,3 +573,117 @@ struct AllocedVc {
|
|||
len: Nid,
|
||||
base: Unique<Nid>,
|
||||
}
|
||||
|
||||
pub trait Ent: Copy {
|
||||
fn new(index: usize) -> Self;
|
||||
fn index(self) -> usize;
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct EntSlice<K: Ent, T> {
|
||||
k: PhantomData<fn(K)>,
|
||||
data: [T],
|
||||
}
|
||||
|
||||
impl<'a, K: Ent, T> From<&'a [T]> for &'a EntSlice<K, T> {
|
||||
fn from(value: &'a [T]) -> Self {
|
||||
unsafe { core::mem::transmute(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ent, T> core::ops::Index<K> for EntSlice<K, T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: K) -> &Self::Output {
|
||||
&self.data[index.index()]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EntVec<K: Ent, T> {
|
||||
data: ::alloc::vec::Vec<T>,
|
||||
k: PhantomData<fn(K)>,
|
||||
}
|
||||
|
||||
impl<K: Ent, T> Default for EntVec<K, T> {
|
||||
fn default() -> Self {
|
||||
Self { data: Default::default(), k: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ent, T> EntVec<K, T> {
|
||||
pub fn clear(&mut self) {
|
||||
self.data.clear();
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> K {
|
||||
let k = K::new(self.data.len());
|
||||
self.data.push(value);
|
||||
k
|
||||
}
|
||||
|
||||
pub fn next(&self, index: K) -> Option<&T> {
|
||||
self.data.get(index.index() + 1)
|
||||
}
|
||||
|
||||
pub fn shadow(&mut self, len: usize)
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
if self.data.len() < len {
|
||||
self.data.resize_with(len, Default::default);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> core::slice::Iter<T> {
|
||||
self.data.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ent, T> core::ops::Index<K> for EntVec<K, T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: K) -> &Self::Output {
|
||||
&self.data[index.index()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ent, T> core::ops::IndexMut<K> for EntVec<K, T> {
|
||||
fn index_mut(&mut self, index: K) -> &mut Self::Output {
|
||||
&mut self.data[index.index()]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! decl_ent {
|
||||
($(
|
||||
$vis:vis struct $name:ident($index:ty);
|
||||
)*) => {$(
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
$vis struct $name($index);
|
||||
|
||||
impl crate::utils::Ent for $name {
|
||||
fn new(index: usize) -> Self {
|
||||
Self(index as $index)
|
||||
}
|
||||
|
||||
fn index(self) -> usize {
|
||||
self.0 as _
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl core::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, concat!(stringify!($name), "{}"), self.0)
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
pub(crate) use decl_ent;
|
||||
|
|
|
@ -4,41 +4,44 @@ main:
|
|||
LI32 r32, 1148846080w
|
||||
CP r2, r32
|
||||
JAL r31, r0, :sin
|
||||
FMUL32 r33, r1, r32
|
||||
FTI32 r1, r33, 1b
|
||||
CP r33, r1
|
||||
FMUL32 r32, r33, r32
|
||||
FTI32 r32, r32, 1b
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
sin:
|
||||
LI32 r4, 1124073472w
|
||||
LI32 r5, 1078530011w
|
||||
FMUL32 r7, r2, r4
|
||||
FDIV32 r9, r7, r5
|
||||
FTI32 r11, r9, 1b
|
||||
ANDI r10, r11, 255d
|
||||
ITF64 r5, r11
|
||||
MULI64 r4, r10, 4d
|
||||
LRA r3, r0, :SIN_TABLE
|
||||
LI32 r7, 1086918619w
|
||||
FC64T32 r9, r5, 1b
|
||||
ADDI64 r5, r11, 64d
|
||||
ADD64 r8, r3, r4
|
||||
LI32 r1, 1132462080w
|
||||
FMUL32 r6, r9, r7
|
||||
ANDI r7, r5, 255d
|
||||
LI32 r5, 1056964608w
|
||||
LD r4, r8, 0a, 4h
|
||||
FDIV32 r8, r6, r1
|
||||
MULI64 r6, r7, 4d
|
||||
FMUL32 r10, r4, r5
|
||||
FSUB32 r11, r2, r8
|
||||
ADD64 r9, r3, r6
|
||||
FMUL32 r2, r11, r10
|
||||
LD r12, r9, 0a, 4h
|
||||
FSUB32 r5, r12, r2
|
||||
FMUL32 r7, r5, r11
|
||||
FADD32 r1, r4, r7
|
||||
CP r13, r2
|
||||
LI32 r14, 1124073472w
|
||||
LI32 r15, 1078530011w
|
||||
FMUL32 r14, r13, r14
|
||||
FDIV32 r14, r14, r15
|
||||
FTI32 r14, r14, 1b
|
||||
ANDI r15, r14, 255d
|
||||
MULI64 r15, r15, 4d
|
||||
LRA r16, r0, :sin_table
|
||||
LI32 r17, 1086918619w
|
||||
ITF32 r18, r14
|
||||
ADDI64 r14, r14, 64d
|
||||
ADD64 r15, r16, r15
|
||||
LI32 r19, 1132462080w
|
||||
FMUL32 r17, r18, r17
|
||||
ANDI r14, r14, 255d
|
||||
LI32 r18, 1056964608w
|
||||
LD r15, r15, 0a, 4h
|
||||
FDIV32 r17, r17, r19
|
||||
MULI64 r14, r14, 4d
|
||||
FMUL32 r18, r15, r18
|
||||
FSUB32 r13, r13, r17
|
||||
ADD64 r14, r16, r14
|
||||
FMUL32 r16, r13, r18
|
||||
LD r14, r14, 0a, 4h
|
||||
FSUB32 r14, r14, r16
|
||||
FMUL32 r13, r14, r13
|
||||
FADD32 r13, r15, r13
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 1303
|
||||
code size: 1311
|
||||
ret: 826
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 22
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 22
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -2,26 +2,31 @@ main:
|
|||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 24a, 32h
|
||||
LI64 r32, 1d
|
||||
ADDI64 r2, r254, 0d
|
||||
ADDI64 r33, r254, 0d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r33, 2d
|
||||
ST r33, r254, 8a, 8h
|
||||
LI64 r34, 2d
|
||||
ST r34, r254, 8a, 8h
|
||||
LI64 r34, 4d
|
||||
ST r34, r254, 16a, 8h
|
||||
CP r2, r33
|
||||
JAL r31, r0, :pass
|
||||
ADD64 r1, r1, r32
|
||||
CP r33, r1
|
||||
ADD64 r32, r33, r32
|
||||
CP r1, r32
|
||||
LD r31, r254, 24a, 32h
|
||||
ADDI64 r254, r254, 56d
|
||||
JALA r0, r31, 0a
|
||||
pass:
|
||||
LD r4, r2, 8a, 8h
|
||||
MULI64 r7, r4, 8d
|
||||
LD r5, r2, 0a, 8h
|
||||
ADD64 r10, r7, r2
|
||||
ADD64 r9, r4, r5
|
||||
LD r1, r10, 0a, 8h
|
||||
ADD64 r1, r1, r9
|
||||
CP r13, r2
|
||||
LD r14, r13, 8a, 8h
|
||||
MULI64 r15, r14, 8d
|
||||
LD r16, r13, 0a, 8h
|
||||
ADD64 r13, r15, r13
|
||||
ADD64 r14, r14, r16
|
||||
LD r13, r13, 0a, 8h
|
||||
ADD64 r13, r13, r14
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 231
|
||||
code size: 246
|
||||
ret: 8
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
main:
|
||||
LRA r1, r0, :SIN_TABLE
|
||||
LD r1, r1, 80a, 8h
|
||||
LRA r13, r0, :sin_table
|
||||
LD r13, r13, 80a, 8h
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 767
|
||||
code size: 770
|
||||
ret: 1736
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
main:
|
||||
LI64 r1, 1d
|
||||
JNE r2, r1, :0
|
||||
CP r14, r2
|
||||
LI64 r13, 1d
|
||||
JNE r14, r13, :0
|
||||
JMP :1
|
||||
0: LI64 r7, 0d
|
||||
JNE r2, r7, :2
|
||||
LI64 r1, 2d
|
||||
0: JNE r14, r0, :2
|
||||
LI64 r13, 2d
|
||||
JMP :1
|
||||
2: LI64 r1, 3d
|
||||
1: JALA r0, r31, 0a
|
||||
code size: 79
|
||||
2: LI64 r13, 3d
|
||||
1: CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 75
|
||||
ret: 2
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LRA r2, r0, :"abඞ\n\r\t56789\0"
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LRA r32, r0, :"abඞ\n\r\t56789\0"
|
||||
CP r2, r32
|
||||
JAL r31, r0, :str_len
|
||||
CP r32, r1
|
||||
LRA r2, r0, :"fff\0"
|
||||
LRA r33, r0, :"fff\0"
|
||||
CP r2, r33
|
||||
JAL r31, r0, :str_len
|
||||
ADD64 r1, r1, r32
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
CP r33, r1
|
||||
ADD64 r32, r33, r32
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
str_len:
|
||||
LI8 r6, 0b
|
||||
LI64 r1, 0d
|
||||
2: LD r8, r2, 0a, 1h
|
||||
ANDI r8, r8, 255d
|
||||
ANDI r6, r6, 255d
|
||||
JNE r8, r6, :0
|
||||
CP r13, r2
|
||||
CP r15, r0
|
||||
CP r14, r15
|
||||
2: LD r16, r13, 0a, 1h
|
||||
ANDI r16, r16, 255d
|
||||
JNE r16, r15, :0
|
||||
CP r1, r14
|
||||
JMP :1
|
||||
0: ADDI64 r2, r2, 1d
|
||||
ADDI64 r1, r1, 1d
|
||||
0: ADDI64 r13, r13, 1d
|
||||
ADDI64 r14, r14, 1d
|
||||
JMP :2
|
||||
1: JALA r0, r31, 0a
|
||||
code size: 216
|
||||
|
|
|
@ -4,10 +4,10 @@ main:
|
|||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :foo
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 95
|
||||
code size: 88
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
65
lang/tests/son_tests_comparing_floating_points.txt
Normal file
65
lang/tests/son_tests_comparing_floating_points.txt
Normal file
|
@ -0,0 +1,65 @@
|
|||
box:
|
||||
CP r13, r2
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LI32 r32, 1065353216w
|
||||
CP r2, r32
|
||||
JAL r31, r0, :box
|
||||
CP r33, r1
|
||||
CP r2, r0
|
||||
JAL r31, r0, :box
|
||||
CP r34, r1
|
||||
FCMPLT32 r33, r33, r34
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :0
|
||||
CP r2, r32
|
||||
JAL r31, r0, :box
|
||||
CP r33, r1
|
||||
CP r2, r0
|
||||
JAL r31, r0, :box
|
||||
CP r34, r1
|
||||
FCMPGT32 r33, r33, r34
|
||||
NOT r33, r33
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :1
|
||||
CP r2, r0
|
||||
JAL r31, r0, :box
|
||||
CP r33, r1
|
||||
CP r2, r32
|
||||
JAL r31, r0, :box
|
||||
CP r34, r1
|
||||
FCMPGT32 r33, r33, r34
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :2
|
||||
CP r2, r0
|
||||
JAL r31, r0, :box
|
||||
CP r33, r1
|
||||
CP r2, r32
|
||||
JAL r31, r0, :box
|
||||
CP r32, r1
|
||||
FCMPLT32 r32, r33, r32
|
||||
NOT r32, r32
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :3
|
||||
CP r1, r0
|
||||
JMP :4
|
||||
3: LI64 r32, 4d
|
||||
CP r1, r32
|
||||
JMP :4
|
||||
2: LI64 r32, 3d
|
||||
CP r1, r32
|
||||
JMP :4
|
||||
1: LI64 r32, 2d
|
||||
CP r1, r32
|
||||
JMP :4
|
||||
0: LI64 r32, 1d
|
||||
CP r1, r32
|
||||
4: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 355
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,7 +1,8 @@
|
|||
main:
|
||||
LRA r1, r0, :a
|
||||
LD r1, r1, 0a, 8h
|
||||
LRA r13, r0, :a
|
||||
LD r13, r13, 0a, 8h
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 47
|
||||
code size: 50
|
||||
ret: 50
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
main:
|
||||
LRA r1, r0, :a
|
||||
LD r1, r1, 0a, 8h
|
||||
LRA r13, r0, :a
|
||||
LD r13, r13, 0a, 8h
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 47
|
||||
code size: 50
|
||||
ret: 50
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
cond:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
JAL r31, r0, :cond
|
||||
LI64 r32, 0d
|
||||
CP r33, r32
|
||||
JNE r1, r33, :0
|
||||
CP r32, r33
|
||||
CP r1, r32
|
||||
CP r32, r0
|
||||
CP r33, r1
|
||||
JNE r33, r32, :0
|
||||
JMP :1
|
||||
0: LI64 r1, 2d
|
||||
1: LD r31, r254, 0a, 24h
|
||||
0: LI64 r32, 2d
|
||||
1: CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 134
|
||||
code size: 117
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 22
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
7
lang/tests/son_tests_constants.txt
Normal file
7
lang/tests/son_tests_constants.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
main:
|
||||
LI32 r13, 69w
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 28
|
||||
ret: 69
|
||||
status: Ok(())
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 22
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
19
lang/tests/son_tests_defer.txt
Normal file
19
lang/tests/son_tests_defer.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
main:
|
||||
LI64 r15, 3d
|
||||
LI64 r16, 10d
|
||||
CP r14, r0
|
||||
CP r13, r14
|
||||
3: JNE r13, r16, :0
|
||||
LI64 r14, -10d
|
||||
ADD64 r14, r13, r14
|
||||
CP r1, r14
|
||||
JMP :1
|
||||
0: DIRU64 r0, r17, r13, r15
|
||||
JNE r17, r14, :2
|
||||
JMP :2
|
||||
2: ADDI64 r13, r13, 1d
|
||||
JMP :3
|
||||
1: JALA r0, r31, 0a
|
||||
code size: 103
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,5 +1,11 @@
|
|||
main:
|
||||
fun:
|
||||
UN
|
||||
code size: 9
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :fun
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
code size: 64
|
||||
ret: 0
|
||||
status: Err(Unreachable)
|
||||
|
|
72
lang/tests/son_tests_different_function_destinations.txt
Normal file
72
lang/tests/son_tests_different_function_destinations.txt
Normal file
|
@ -0,0 +1,72 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -88d
|
||||
ST r31, r254, 48a, 40h
|
||||
LRA r32, r0, :glob_stru
|
||||
JAL r31, r0, :new_stru
|
||||
ST r1, r32, 0a, 16h
|
||||
LD r33, r32, 0a, 8h
|
||||
JEQ r33, r0, :0
|
||||
LI64 r32, 300d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
0: ST r0, r32, 0a, 8h
|
||||
LD r33, r32, 0a, 8h
|
||||
JEQ r33, r0, :2
|
||||
ST r0, r32, 8a, 8h
|
||||
LI64 r32, 200d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
2: LI64 r34, 1d
|
||||
ST r34, r32, 0a, 8h
|
||||
ST r34, r32, 8a, 8h
|
||||
ADDI64 r33, r254, 0d
|
||||
ST r34, r254, 0a, 8h
|
||||
ST r34, r254, 8a, 8h
|
||||
ST r34, r254, 16a, 8h
|
||||
ST r34, r254, 24a, 8h
|
||||
ST r34, r254, 32a, 8h
|
||||
ST r34, r254, 40a, 8h
|
||||
ADDI64 r35, r33, 48d
|
||||
CP r32, r33
|
||||
8: JNE r35, r32, :3
|
||||
LD r32, r254, 32a, 8h
|
||||
JEQ r32, r0, :4
|
||||
LI64 r32, 100d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
4: ST r34, r254, 0a, 8h
|
||||
ST r34, r254, 8a, 8h
|
||||
ST r34, r254, 16a, 8h
|
||||
ST r34, r254, 24a, 8h
|
||||
ST r34, r254, 32a, 8h
|
||||
ST r34, r254, 40a, 8h
|
||||
CP r32, r33
|
||||
7: LD r34, r254, 32a, 8h
|
||||
JNE r35, r32, :5
|
||||
JEQ r34, r0, :6
|
||||
LI64 r32, 10d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
6: CP r1, r0
|
||||
JMP :1
|
||||
5: ST r0, r32, 0a, 8h
|
||||
ST r0, r32, 8a, 8h
|
||||
ADDI64 r32, r32, 16d
|
||||
JMP :7
|
||||
3: JAL r31, r0, :new_stru
|
||||
ST r1, r32, 0a, 16h
|
||||
ADDI64 r32, r32, 16d
|
||||
JMP :8
|
||||
1: LD r31, r254, 48a, 40h
|
||||
ADDI64 r254, r254, 88d
|
||||
JALA r0, r31, 0a
|
||||
new_stru:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r0, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
LD r1, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 668
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,30 +1,29 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -12d
|
||||
LI8 r1, 255b
|
||||
ST r1, r254, 0a, 1h
|
||||
LI8 r4, 0b
|
||||
ST r4, r254, 1a, 1h
|
||||
ST r4, r254, 2a, 1h
|
||||
ST r1, r254, 3a, 1h
|
||||
LI32 r9, 0w
|
||||
ST r9, r254, 4a, 4h
|
||||
LI32 r12, 2w
|
||||
ST r12, r254, 8a, 4h
|
||||
LD r3, r254, 8a, 4h
|
||||
ANDI r3, r3, 4294967295d
|
||||
ANDI r12, r12, 4294967295d
|
||||
JEQ r3, r12, :0
|
||||
LI64 r1, 0d
|
||||
LI8 r13, 255b
|
||||
ST r13, r254, 0a, 1h
|
||||
ST r0, r254, 1a, 1h
|
||||
ST r0, r254, 2a, 1h
|
||||
ST r13, r254, 3a, 1h
|
||||
ST r0, r254, 4a, 4h
|
||||
LD r13, r254, 4a, 4h
|
||||
LI32 r14, 2w
|
||||
ST r14, r254, 8a, 4h
|
||||
LD r14, r254, 8a, 4h
|
||||
LI64 r15, 2d
|
||||
ANDI r14, r14, 4294967295d
|
||||
JEQ r14, r15, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LD r10, r254, 4a, 4h
|
||||
ANDI r10, r10, 4294967295d
|
||||
ANDI r9, r9, 4294967295d
|
||||
JEQ r10, r9, :2
|
||||
LI64 r1, 64d
|
||||
0: ANDI r13, r13, 4294967295d
|
||||
JEQ r13, r0, :2
|
||||
LI64 r13, 64d
|
||||
CP r1, r13
|
||||
JMP :1
|
||||
2: LI64 r1, 512d
|
||||
2: LI64 r13, 512d
|
||||
CP r1, r13
|
||||
1: ADDI64 r254, r254, 12d
|
||||
JALA r0, r31, 0a
|
||||
code size: 257
|
||||
code size: 235
|
||||
ret: 512
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
LI64 r1, 10d
|
||||
ADDI64 r4, r254, 0d
|
||||
ST r1, r254, 0a, 8h
|
||||
LI64 r7, 20d
|
||||
ST r7, r254, 8a, 8h
|
||||
LI64 r6, 6d
|
||||
LI64 r5, 5d
|
||||
LI64 r2, 1d
|
||||
CP r3, r4
|
||||
LD r3, r3, 0a, 16h
|
||||
LI64 r13, 10d
|
||||
ST r13, r254, 0a, 8h
|
||||
LI64 r13, 20d
|
||||
ST r13, r254, 8a, 8h
|
||||
LI64 r13, 6d
|
||||
LI64 r14, 5d
|
||||
LI64 r15, 1d
|
||||
CP r2, r15
|
||||
LD r3, r254, 0a, 16h
|
||||
CP r5, r14
|
||||
CP r6, r13
|
||||
ECA
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
ev: Ecall
|
||||
code size: 155
|
||||
code size: 143
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 24a, 16h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
LD r2, r254, 8a, 16h
|
||||
JAL r31, r0, :pass
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
LD r31, r254, 24a, 16h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
pass:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r2, r254, 0a, 16h
|
||||
ADDI64 r2, r254, 0d
|
||||
CP r13, r2
|
||||
LD r14, r13, 0a, 8h
|
||||
LD r13, r13, 8a, 8h
|
||||
ADD64 r13, r13, r14
|
||||
CP r1, r13
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 235
|
||||
ret: 0
|
||||
status: Ok(())
|
20
lang/tests/son_tests_enums.txt
Normal file
20
lang/tests/son_tests_enums.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
JAL r31, r0, :some_enum
|
||||
CP r32, r1
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LI64 r32, 100d
|
||||
CP r1, r32
|
||||
1: LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
some_enum:
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 128
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,105 +1,114 @@
|
|||
continue_and_state_change:
|
||||
LI64 r7, 3d
|
||||
LI64 r8, 4d
|
||||
LI64 r9, 2d
|
||||
LI64 r10, 10d
|
||||
6: JLTU r2, r10, :0
|
||||
CP r1, r2
|
||||
CP r13, r2
|
||||
LI64 r16, 3d
|
||||
LI64 r17, 2d
|
||||
LI64 r18, 10d
|
||||
CP r15, r0
|
||||
LI64 r14, 4d
|
||||
6: JLTU r13, r18, :0
|
||||
JMP :1
|
||||
0: JNE r2, r9, :2
|
||||
CP r2, r8
|
||||
0: JNE r13, r17, :2
|
||||
CP r13, r14
|
||||
JMP :3
|
||||
2: JNE r2, r7, :4
|
||||
LI64 r1, 0d
|
||||
1: JMP :5
|
||||
4: ADDI64 r2, r2, 1d
|
||||
2: JNE r13, r16, :4
|
||||
CP r13, r15
|
||||
1: CP r1, r13
|
||||
JMP :5
|
||||
4: ADDI64 r13, r13, 1d
|
||||
3: JMP :6
|
||||
5: JALA r0, r31, 0a
|
||||
infinite_loop:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r32, 1d
|
||||
LI64 r33, 0d
|
||||
CP r1, r33
|
||||
1: JNE r1, r32, :0
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LI64 r34, 1d
|
||||
CP r33, r0
|
||||
CP r32, r33
|
||||
1: JNE r32, r34, :0
|
||||
JMP :0
|
||||
0: CP r2, r33
|
||||
JAL r31, r0, :continue_and_state_change
|
||||
CP r32, r1
|
||||
JMP :1
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -64d
|
||||
ST r31, r254, 0a, 64h
|
||||
LI64 r32, 0d
|
||||
CP r2, r32
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r2, r0
|
||||
JAL r31, r0, :multiple_breaks
|
||||
LI64 r32, 3d
|
||||
CP r33, r1
|
||||
LI64 r1, 3d
|
||||
JEQ r33, r1, :0
|
||||
LI64 r1, 1d
|
||||
JEQ r33, r32, :0
|
||||
LI64 r32, 1d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
0: CP r34, r1
|
||||
LI64 r35, 4d
|
||||
CP r2, r35
|
||||
0: LI64 r33, 4d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :multiple_breaks
|
||||
CP r36, r35
|
||||
LI64 r37, 10d
|
||||
JEQ r1, r37, :2
|
||||
LI64 r1, 2d
|
||||
LI64 r34, 10d
|
||||
CP r35, r1
|
||||
JEQ r35, r34, :2
|
||||
LI64 r32, 2d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
2: CP r2, r32
|
||||
2: CP r2, r0
|
||||
JAL r31, r0, :state_change_in_break
|
||||
JEQ r1, r32, :3
|
||||
CP r1, r34
|
||||
CP r35, r1
|
||||
JEQ r35, r0, :3
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
3: CP r2, r36
|
||||
3: CP r2, r33
|
||||
JAL r31, r0, :state_change_in_break
|
||||
JEQ r1, r37, :4
|
||||
CP r1, r36
|
||||
CP r35, r1
|
||||
JEQ r35, r34, :4
|
||||
CP r1, r33
|
||||
JMP :1
|
||||
4: CP r2, r37
|
||||
4: CP r2, r34
|
||||
JAL r31, r0, :continue_and_state_change
|
||||
JEQ r1, r37, :5
|
||||
LI64 r1, 5d
|
||||
CP r33, r1
|
||||
JEQ r33, r34, :5
|
||||
LI64 r32, 5d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
5: CP r2, r34
|
||||
5: CP r2, r32
|
||||
JAL r31, r0, :continue_and_state_change
|
||||
JEQ r1, r32, :6
|
||||
LI64 r1, 6d
|
||||
CP r32, r1
|
||||
JEQ r32, r0, :6
|
||||
LI64 r32, 6d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
6: CP r38, r32
|
||||
JAL r31, r0, :infinite_loop
|
||||
CP r1, r38
|
||||
1: LD r31, r254, 0a, 64h
|
||||
ADDI64 r254, r254, 64d
|
||||
6: JAL r31, r0, :infinite_loop
|
||||
CP r1, r0
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
multiple_breaks:
|
||||
LI64 r6, 3d
|
||||
LI64 r5, 10d
|
||||
4: JLTU r2, r5, :0
|
||||
CP r1, r2
|
||||
CP r13, r2
|
||||
LI64 r14, 3d
|
||||
LI64 r15, 10d
|
||||
4: JLTU r13, r15, :0
|
||||
JMP :1
|
||||
0: ADDI64 r1, r2, 1d
|
||||
JNE r1, r6, :2
|
||||
1: JMP :3
|
||||
2: CP r2, r1
|
||||
JMP :4
|
||||
0: ADDI64 r13, r13, 1d
|
||||
JNE r13, r14, :2
|
||||
1: CP r1, r13
|
||||
JMP :3
|
||||
2: JMP :4
|
||||
3: JALA r0, r31, 0a
|
||||
state_change_in_break:
|
||||
LI64 r5, 3d
|
||||
LI64 r6, 10d
|
||||
4: JLTU r2, r6, :0
|
||||
CP r1, r2
|
||||
CP r13, r2
|
||||
LI64 r14, 3d
|
||||
LI64 r15, 10d
|
||||
4: JLTU r13, r15, :0
|
||||
JMP :1
|
||||
0: JNE r2, r5, :2
|
||||
LI64 r1, 0d
|
||||
1: JMP :3
|
||||
2: ADDI64 r2, r2, 1d
|
||||
0: JNE r13, r14, :2
|
||||
CP r13, r0
|
||||
1: CP r1, r13
|
||||
JMP :3
|
||||
2: ADDI64 r13, r13, 1d
|
||||
JMP :4
|
||||
3: JALA r0, r31, 0a
|
||||
timed out
|
||||
code size: 668
|
||||
code size: 667
|
||||
ret: 10
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,53 +1,55 @@
|
|||
check_platform:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
JAL r31, r0, :x86_fb_ptr
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -64d
|
||||
ST r31, r254, 0a, 64h
|
||||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 0a, 56h
|
||||
JAL r31, r0, :check_platform
|
||||
LI64 r32, 0d
|
||||
LI64 r33, 30d
|
||||
LI64 r34, 100d
|
||||
CP r35, r32
|
||||
CP r36, r32
|
||||
CP r37, r32
|
||||
5: JLTU r35, r33, :0
|
||||
ADDI64 r36, r36, 1d
|
||||
CP r2, r32
|
||||
CP r3, r36
|
||||
CP r4, r33
|
||||
CP r35, r0
|
||||
LI64 r36, 30d
|
||||
LI64 r37, 100d
|
||||
CP r34, r35
|
||||
CP r32, r35
|
||||
CP r33, r35
|
||||
5: JLTU r34, r36, :0
|
||||
ADDI64 r32, r32, 1d
|
||||
CP r2, r35
|
||||
CP r3, r32
|
||||
CP r4, r36
|
||||
JAL r31, r0, :set_pixel
|
||||
JEQ r1, r37, :1
|
||||
CP r1, r32
|
||||
CP r34, r1
|
||||
JEQ r34, r33, :1
|
||||
CP r1, r35
|
||||
JMP :2
|
||||
1: CP r38, r32
|
||||
JNE r36, r34, :3
|
||||
CP r1, r37
|
||||
1: JNE r32, r37, :3
|
||||
CP r1, r33
|
||||
JMP :2
|
||||
3: CP r1, r37
|
||||
CP r35, r38
|
||||
3: CP r34, r35
|
||||
JMP :4
|
||||
0: CP r1, r37
|
||||
CP r38, r32
|
||||
ADDI64 r1, r1, 1d
|
||||
ADDI64 r35, r35, 1d
|
||||
4: CP r32, r38
|
||||
CP r37, r1
|
||||
JMP :5
|
||||
2: LD r31, r254, 0a, 64h
|
||||
ADDI64 r254, r254, 64d
|
||||
0: ADDI64 r33, r33, 1d
|
||||
ADDI64 r34, r34, 1d
|
||||
4: JMP :5
|
||||
2: LD r31, r254, 0a, 56h
|
||||
ADDI64 r254, r254, 56d
|
||||
JALA r0, r31, 0a
|
||||
set_pixel:
|
||||
MUL64 r7, r3, r4
|
||||
ADD64 r1, r7, r2
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
CP r15, r4
|
||||
MUL64 r14, r14, r15
|
||||
ADD64 r13, r14, r13
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
x86_fb_ptr:
|
||||
LI64 r1, 100d
|
||||
LI64 r13, 100d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 330
|
||||
code size: 329
|
||||
ret: 3000
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
main:
|
||||
LI32 r1, 3212836864w
|
||||
LI32 r13, 3212836864w
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 25
|
||||
code size: 28
|
||||
ret: 3212836864
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
add_one:
|
||||
ADDI64 r1, r2, 1d
|
||||
CP r13, r2
|
||||
ADDI64 r13, r13, 1d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
add_two:
|
||||
ADDI64 r1, r2, 2d
|
||||
CP r13, r2
|
||||
ADDI64 r13, r13, 2d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r2, 10d
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r32, 10d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :add_one
|
||||
CP r32, r1
|
||||
LI64 r2, 20d
|
||||
LI64 r33, 20d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :add_two
|
||||
ADD64 r1, r1, r32
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
CP r33, r1
|
||||
ADD64 r32, r33, r32
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 152
|
||||
code size: 176
|
||||
ret: 33
|
||||
status: Ok(())
|
||||
|
|
21
lang/tests/son_tests_generic_function_in_struct.txt
Normal file
21
lang/tests/son_tests_generic_function_in_struct.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
b:
|
||||
CP r13, r3
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 8a, 24h
|
||||
ADDI64 r32, r254, 0d
|
||||
LI64 r33, 100d
|
||||
ST r33, r254, 0a, 8h
|
||||
CP r2, r32
|
||||
CP r3, r33
|
||||
JAL r31, r0, :b
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
LD r31, r254, 8a, 24h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 137
|
||||
ret: 100
|
||||
status: Ok(())
|
|
@ -1,24 +1,38 @@
|
|||
add:
|
||||
ADD64 r1, r2, r3
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
ADD64 r13, r13, r14
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
add:
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
ADD32 r13, r13, r14
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
add:
|
||||
ADD32 r1, r2, r3
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI32 r3, 2w
|
||||
CP r2, r3
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
JAL r31, r0, :add
|
||||
LI32 r32, 2w
|
||||
CP r2, r32
|
||||
CP r3, r32
|
||||
JAL r31, r0, :add
|
||||
CP r32, r1
|
||||
LI64 r3, 3d
|
||||
LI64 r2, 1d
|
||||
LI64 r33, 3d
|
||||
LI64 r34, 1d
|
||||
CP r2, r34
|
||||
CP r3, r33
|
||||
JAL r31, r0, :add
|
||||
ANDI r33, r32, 4294967295d
|
||||
SUB64 r1, r33, r1
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
CP r33, r1
|
||||
ANDI r32, r32, 4294967295d
|
||||
SUB64 r32, r32, r33
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 158
|
||||
code size: 209
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
32
lang/tests/son_tests_generic_type_mishap.txt
Normal file
32
lang/tests/son_tests_generic_type_mishap.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :process
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
opaque:
|
||||
JALA r0, r31, 0a
|
||||
process:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 16a, 32h
|
||||
ADDI64 r33, r254, 0d
|
||||
ST r0, r254, 0a, 1h
|
||||
LI64 r32, 1000d
|
||||
4: JGTU r32, r0, :0
|
||||
JMP :1
|
||||
0: CP r2, r33
|
||||
JAL r31, r0, :opaque
|
||||
LD r34, r254, 0a, 1h
|
||||
ANDI r34, r34, 255d
|
||||
JEQ r34, r0, :2
|
||||
JMP :3
|
||||
2: ADDI64 r32, r32, -1d
|
||||
1: JMP :4
|
||||
3: LD r31, r254, 16a, 32h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
timed out
|
||||
code size: 248
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,127 +1,219 @@
|
|||
deinit:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
LD r33, r2, 16a, 8h
|
||||
LI64 r4, 8d
|
||||
MUL64 r3, r33, r4
|
||||
CP r34, r32
|
||||
LD r2, r34, 0a, 8h
|
||||
LD r33, r32, 16a, 8h
|
||||
LI64 r34, 8d
|
||||
MUL64 r33, r33, r34
|
||||
LD r35, r32, 0a, 8h
|
||||
CP r2, r35
|
||||
CP r3, r33
|
||||
CP r4, r34
|
||||
JAL r31, r0, :free
|
||||
CP r1, r32
|
||||
JAL r31, r0, :new
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
free:
|
||||
CP r10, r2
|
||||
LRA r7, r0, :FREE_SYS_CALL
|
||||
LD r2, r7, 0a, 8h
|
||||
CP r5, r4
|
||||
CP r4, r3
|
||||
CP r3, r10
|
||||
ECA
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 24a, 32h
|
||||
ADDI64 r32, r254, 0d
|
||||
deinit:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
LI64 r33, 1d
|
||||
LD r34, r32, 16a, 8h
|
||||
LD r35, r32, 0a, 8h
|
||||
CP r2, r35
|
||||
CP r3, r34
|
||||
CP r4, r33
|
||||
JAL r31, r0, :free
|
||||
CP r1, r32
|
||||
JAL r31, r0, :new
|
||||
LI64 r3, 69d
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
free:
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
CP r15, r4
|
||||
LRA r16, r0, :free_sys_call
|
||||
LD r16, r16, 0a, 8h
|
||||
CP r2, r16
|
||||
CP r3, r13
|
||||
CP r4, r14
|
||||
CP r5, r15
|
||||
ECA
|
||||
CP r13, r1
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -88d
|
||||
ST r31, r254, 48a, 40h
|
||||
ADDI64 r32, r254, 24d
|
||||
CP r1, r32
|
||||
JAL r31, r0, :new
|
||||
LI64 r33, 35d
|
||||
CP r2, r32
|
||||
CP r3, r33
|
||||
JAL r31, r0, :push
|
||||
LD r33, r254, 0a, 8h
|
||||
LD r34, r33, 0a, 8h
|
||||
ADDI64 r33, r254, 0d
|
||||
CP r1, r33
|
||||
JAL r31, r0, :new
|
||||
LI8 r34, 34b
|
||||
CP r2, r33
|
||||
CP r3, r34
|
||||
JAL r31, r0, :push
|
||||
LD r34, r254, 0a, 8h
|
||||
LD r34, r34, 0a, 1h
|
||||
LD r35, r254, 24a, 8h
|
||||
LD r35, r35, 0a, 8h
|
||||
CP r2, r33
|
||||
JAL r31, r0, :deinit
|
||||
CP r2, r32
|
||||
JAL r31, r0, :deinit
|
||||
CP r1, r34
|
||||
LD r31, r254, 24a, 32h
|
||||
ADDI64 r254, r254, 56d
|
||||
ANDI r32, r34, 255d
|
||||
ADD64 r32, r35, r32
|
||||
CP r1, r32
|
||||
LD r31, r254, 48a, 40h
|
||||
ADDI64 r254, r254, 88d
|
||||
JALA r0, r31, 0a
|
||||
malloc:
|
||||
CP r9, r2
|
||||
LRA r5, r0, :MALLOC_SYS_CALL
|
||||
LD r2, r5, 0a, 8h
|
||||
CP r4, r3
|
||||
CP r3, r9
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
LRA r15, r0, :malloc_sys_call
|
||||
LD r15, r15, 0a, 8h
|
||||
CP r2, r15
|
||||
CP r3, r13
|
||||
CP r4, r14
|
||||
ECA
|
||||
CP r13, r1
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
new:
|
||||
ADDI64 r254, r254, -24d
|
||||
LI64 r4, 0d
|
||||
ADDI64 r5, r254, 0d
|
||||
ST r4, r254, 0a, 8h
|
||||
ST r4, r254, 8a, 8h
|
||||
ST r4, r254, 16a, 8h
|
||||
BMC r5, r1, 24h
|
||||
CP r15, r1
|
||||
LI64 r14, 8d
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r14, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
BMC r13, r15, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
new:
|
||||
ADDI64 r254, r254, -24d
|
||||
CP r15, r1
|
||||
LI64 r14, 1d
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r14, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
BMC r13, r15, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
push:
|
||||
ADDI64 r254, r254, -192d
|
||||
ST r31, r254, 0a, 192h
|
||||
CP r32, r3
|
||||
LI64 r33, 1d
|
||||
LD r34, r2, 8a, 8h
|
||||
LD r35, r2, 16a, 8h
|
||||
ADDI64 r254, r254, -80d
|
||||
ST r31, r254, 0a, 80h
|
||||
CP r36, r2
|
||||
JNE r35, r34, :0
|
||||
LI64 r37, 0d
|
||||
JNE r35, r37, :1
|
||||
CP r38, r33
|
||||
CP r37, r3
|
||||
LI64 r35, 1d
|
||||
LD r33, r36, 8a, 8h
|
||||
LD r32, r36, 16a, 8h
|
||||
JNE r32, r33, :0
|
||||
JNE r32, r0, :1
|
||||
CP r32, r35
|
||||
JMP :2
|
||||
1: MULI64 r38, r35, 2d
|
||||
2: LI64 r39, 8d
|
||||
MUL64 r2, r38, r39
|
||||
CP r3, r39
|
||||
1: MULI64 r32, r32, 2d
|
||||
2: CP r2, r32
|
||||
CP r3, r35
|
||||
JAL r31, r0, :malloc
|
||||
CP r40, r1
|
||||
LI64 r1, 0d
|
||||
CP r41, r40
|
||||
JNE r41, r1, :3
|
||||
ST r32, r36, 16a, 8h
|
||||
CP r34, r1
|
||||
JNE r34, r0, :3
|
||||
CP r1, r0
|
||||
JMP :4
|
||||
3: CP r40, r41
|
||||
CP r42, r36
|
||||
ST r38, r42, 16a, 8h
|
||||
LD r36, r42, 8a, 8h
|
||||
MULI64 r43, r36, 8d
|
||||
LD r44, r42, 0a, 8h
|
||||
ADD64 r45, r44, r43
|
||||
CP r46, r40
|
||||
9: LD r2, r42, 0a, 8h
|
||||
LD r47, r42, 8a, 8h
|
||||
JNE r45, r44, :5
|
||||
JEQ r47, r37, :6
|
||||
CP r4, r39
|
||||
MUL64 r3, r47, r4
|
||||
3: LD r32, r36, 0a, 8h
|
||||
ADD64 r38, r33, r32
|
||||
CP r33, r34
|
||||
7: LD r39, r36, 0a, 8h
|
||||
LD r40, r36, 8a, 8h
|
||||
JNE r38, r32, :5
|
||||
JEQ r40, r0, :6
|
||||
CP r2, r39
|
||||
CP r3, r40
|
||||
CP r4, r35
|
||||
JAL r31, r0, :free
|
||||
CP r1, r40
|
||||
JMP :6
|
||||
6: ST r34, r36, 0a, 8h
|
||||
JMP :0
|
||||
5: LD r39, r32, 0a, 1h
|
||||
ST r39, r33, 0a, 1h
|
||||
ADDI64 r33, r33, 1d
|
||||
ADDI64 r32, r32, 1d
|
||||
JMP :7
|
||||
6: CP r1, r40
|
||||
7: ST r1, r42, 0a, 8h
|
||||
JMP :8
|
||||
5: CP r1, r40
|
||||
CP r4, r39
|
||||
ADDI64 r41, r46, 8d
|
||||
ADDI64 r48, r44, 8d
|
||||
LD r49, r44, 0a, 8h
|
||||
ST r49, r46, 0a, 8h
|
||||
CP r44, r48
|
||||
CP r46, r41
|
||||
JMP :9
|
||||
0: CP r42, r36
|
||||
8: LD r50, r42, 8a, 8h
|
||||
MULI64 r51, r50, 8d
|
||||
LD r52, r42, 0a, 8h
|
||||
ADD64 r1, r52, r51
|
||||
CP r3, r32
|
||||
ST r3, r1, 0a, 8h
|
||||
LD r53, r42, 8a, 8h
|
||||
ADD64 r54, r53, r33
|
||||
ST r54, r42, 8a, 8h
|
||||
4: LD r31, r254, 0a, 192h
|
||||
ADDI64 r254, r254, 192d
|
||||
0: LD r32, r36, 8a, 8h
|
||||
LD r33, r36, 0a, 8h
|
||||
ADD64 r33, r32, r33
|
||||
ST r37, r33, 0a, 1h
|
||||
ADD64 r32, r32, r35
|
||||
ST r32, r36, 8a, 8h
|
||||
CP r1, r33
|
||||
4: LD r31, r254, 0a, 80h
|
||||
ADDI64 r254, r254, 80d
|
||||
JALA r0, r31, 0a
|
||||
code size: 955
|
||||
push:
|
||||
ADDI64 r254, r254, -88d
|
||||
ST r31, r254, 0a, 88h
|
||||
CP r36, r2
|
||||
CP r37, r3
|
||||
LI64 r35, 1d
|
||||
LD r33, r36, 8a, 8h
|
||||
LD r32, r36, 16a, 8h
|
||||
JNE r32, r33, :0
|
||||
JNE r32, r0, :1
|
||||
CP r32, r35
|
||||
JMP :2
|
||||
1: MULI64 r32, r32, 2d
|
||||
2: LI64 r38, 8d
|
||||
MUL64 r34, r32, r38
|
||||
CP r2, r34
|
||||
CP r3, r38
|
||||
JAL r31, r0, :malloc
|
||||
ST r32, r36, 16a, 8h
|
||||
CP r34, r1
|
||||
JNE r34, r0, :3
|
||||
CP r1, r0
|
||||
JMP :4
|
||||
3: MULI64 r33, r33, 8d
|
||||
LD r32, r36, 0a, 8h
|
||||
ADD64 r39, r32, r33
|
||||
CP r33, r34
|
||||
7: LD r40, r36, 0a, 8h
|
||||
LD r41, r36, 8a, 8h
|
||||
JNE r39, r32, :5
|
||||
JEQ r41, r0, :6
|
||||
MUL64 r32, r41, r38
|
||||
CP r2, r40
|
||||
CP r3, r32
|
||||
CP r4, r38
|
||||
JAL r31, r0, :free
|
||||
JMP :6
|
||||
6: ST r34, r36, 0a, 8h
|
||||
JMP :0
|
||||
5: LD r40, r32, 0a, 8h
|
||||
ST r40, r33, 0a, 8h
|
||||
ADDI64 r33, r33, 8d
|
||||
ADDI64 r32, r32, 8d
|
||||
JMP :7
|
||||
0: LD r32, r36, 8a, 8h
|
||||
MULI64 r33, r32, 8d
|
||||
LD r34, r36, 0a, 8h
|
||||
ADD64 r33, r34, r33
|
||||
ST r37, r33, 0a, 8h
|
||||
ADD64 r32, r32, r35
|
||||
ST r32, r36, 8a, 8h
|
||||
CP r1, r33
|
||||
4: LD r31, r254, 0a, 88h
|
||||
ADDI64 r254, r254, 88d
|
||||
JALA r0, r31, 0a
|
||||
code size: 1623
|
||||
ret: 69
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
clobber:
|
||||
LRA r1, r0, :var
|
||||
LI64 r3, 0d
|
||||
ST r3, r1, 0a, 8h
|
||||
LRA r13, r0, :var
|
||||
ST r0, r13, 0a, 8h
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
|
@ -10,10 +9,11 @@ main:
|
|||
LI64 r33, 2d
|
||||
ST r33, r32, 0a, 8h
|
||||
JAL r31, r0, :clobber
|
||||
LD r1, r32, 0a, 8h
|
||||
LD r32, r32, 0a, 8h
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 166
|
||||
code size: 159
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
23
lang/tests/son_tests_global_variable_wiredness.txt
Normal file
23
lang/tests/son_tests_global_variable_wiredness.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
inb:
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LRA r32, r0, :ports
|
||||
LD r33, r32, 0a, 1h
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :0
|
||||
JMP :1
|
||||
0: JAL r31, r0, :inb
|
||||
CP r33, r1
|
||||
CMPU r33, r33, r0
|
||||
CMPUI r33, r33, 0d
|
||||
NOT r33, r33
|
||||
ST r33, r32, 0a, 1h
|
||||
1: LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 164
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,9 +1,10 @@
|
|||
main:
|
||||
LRA r2, r0, :complex_global_var
|
||||
LD r3, r2, 0a, 8h
|
||||
ADDI64 r1, r3, 5d
|
||||
ST r1, r2, 0a, 8h
|
||||
LRA r13, r0, :complex_global_var
|
||||
LD r14, r13, 0a, 8h
|
||||
ADDI64 r14, r14, 5d
|
||||
ST r14, r13, 0a, 8h
|
||||
CP r1, r14
|
||||
JALA r0, r31, 0a
|
||||
code size: 71
|
||||
code size: 74
|
||||
ret: 55
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 22
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -128d
|
||||
LI8 r5, 69b
|
||||
LI64 r6, 128d
|
||||
LI64 r7, 0d
|
||||
ADDI64 r4, r254, 0d
|
||||
2: LD r12, r254, 42a, 1h
|
||||
JLTU r7, r6, :0
|
||||
ANDI r1, r12, 255d
|
||||
ADDI64 r14, r254, 0d
|
||||
LI8 r15, 69b
|
||||
LI64 r16, 128d
|
||||
CP r13, r0
|
||||
2: LD r17, r254, 42a, 1h
|
||||
JLTU r13, r16, :0
|
||||
ANDI r13, r17, 255d
|
||||
CP r1, r13
|
||||
JMP :1
|
||||
0: ADDI64 r3, r7, 1d
|
||||
ADD64 r7, r4, r7
|
||||
ST r5, r7, 0a, 1h
|
||||
CP r7, r3
|
||||
0: ADD64 r17, r14, r13
|
||||
ST r15, r17, 0a, 1h
|
||||
ADDI64 r13, r13, 1d
|
||||
JMP :2
|
||||
1: ADDI64 r254, r254, 128d
|
||||
JALA r0, r31, 0a
|
||||
code size: 145
|
||||
code size: 138
|
||||
ret: 69
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
fib:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
LI64 r1, 1d
|
||||
LI64 r32, 2d
|
||||
JGTU r2, r32, :0
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
LI64 r33, 1d
|
||||
LI64 r34, 2d
|
||||
JGTU r32, r34, :0
|
||||
CP r1, r33
|
||||
JMP :1
|
||||
0: CP r33, r2
|
||||
SUB64 r2, r33, r1
|
||||
CP r34, r33
|
||||
0: SUB64 r33, r32, r33
|
||||
CP r2, r33
|
||||
JAL r31, r0, :fib
|
||||
CP r2, r34
|
||||
CP r35, r1
|
||||
SUB64 r2, r2, r32
|
||||
CP r33, r1
|
||||
SUB64 r32, r32, r34
|
||||
CP r2, r32
|
||||
JAL r31, r0, :fib
|
||||
ADD64 r1, r1, r35
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
CP r32, r1
|
||||
ADD64 r32, r32, r33
|
||||
CP r1, r32
|
||||
1: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r32, 10d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 211
|
||||
code size: 229
|
||||
ret: 55
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
main:
|
||||
LI64 r2, 0d
|
||||
0: ADDI64 r2, r2, 1d
|
||||
CP r13, r0
|
||||
0: ADDI64 r13, r13, 1d
|
||||
JMP :0
|
||||
JALA r0, r31, 0a
|
||||
timed out
|
||||
code size: 45
|
||||
code size: 38
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
main:
|
||||
LI64 r7, 6d
|
||||
LRA r3, r0, :gb
|
||||
LI64 r6, 0d
|
||||
LD r8, r3, 0a, 8h
|
||||
CMPU r9, r8, r6
|
||||
CMPUI r9, r9, 0d
|
||||
ORI r11, r9, 0d
|
||||
ANDI r11, r11, 255d
|
||||
JNE r11, r0, :0
|
||||
CP r4, r7
|
||||
LI64 r13, 8d
|
||||
CP r2, r13
|
||||
ECA
|
||||
LI64 r14, 6d
|
||||
LRA r13, r0, :gb
|
||||
LD r13, r13, 0a, 8h
|
||||
CMPU r13, r13, r0
|
||||
CMPUI r13, r13, 0d
|
||||
OR r13, r13, r0
|
||||
ANDI r13, r13, 255d
|
||||
JNE r13, r0, :0
|
||||
CP r13, r14
|
||||
JMP :1
|
||||
0: LI64 r4, 1d
|
||||
1: SUB64 r1, r4, r7
|
||||
0: LI64 r13, 1d
|
||||
1: SUB64 r13, r13, r14
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 131
|
||||
ret: 0
|
||||
|
|
19
lang/tests/son_tests_inline_return_stack.txt
Normal file
19
lang/tests/son_tests_inline_return_stack.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -72d
|
||||
ADDI64 r13, r254, 24d
|
||||
ST r0, r254, 24a, 8h
|
||||
LI64 r14, 1d
|
||||
ST r14, r254, 32a, 8h
|
||||
LI64 r14, 2d
|
||||
ST r14, r254, 40a, 8h
|
||||
ADDI64 r14, r254, 0d
|
||||
BMC r13, r14, 24h
|
||||
ADDI64 r13, r254, 48d
|
||||
BMC r14, r13, 24h
|
||||
LD r13, r254, 48a, 8h
|
||||
CP r1, r13
|
||||
ADDI64 r254, r254, 72d
|
||||
JALA r0, r31, 0a
|
||||
code size: 159
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,39 +1,29 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
JAL r31, r0, :scalar_values
|
||||
LI64 r32, 0d
|
||||
CP r33, r32
|
||||
JEQ r1, r33, :0
|
||||
LI64 r1, 1d
|
||||
CP r32, r1
|
||||
JEQ r32, r0, :0
|
||||
LI64 r32, 1d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
0: JAL r31, r0, :structs
|
||||
CP r34, r33
|
||||
JEQ r1, r34, :2
|
||||
CP r32, r1
|
||||
JEQ r32, r0, :2
|
||||
JAL r31, r0, :structs
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
2: CP r1, r34
|
||||
CP r33, r34
|
||||
1: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
2: CP r1, r0
|
||||
1: LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
scalar_values:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
structs:
|
||||
ADDI64 r254, r254, -32d
|
||||
LI64 r1, 5d
|
||||
ST r1, r254, 16a, 8h
|
||||
ST r1, r254, 24a, 8h
|
||||
LD r5, r254, 16a, 8h
|
||||
ADDI64 r7, r5, 15d
|
||||
ST r7, r254, 0a, 8h
|
||||
LI64 r10, 20d
|
||||
ST r10, r254, 8a, 8h
|
||||
LD r1, r254, 0a, 8h
|
||||
SUB64 r1, r1, r10
|
||||
ADDI64 r254, r254, 32d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 307
|
||||
code size: 164
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
main:
|
||||
LI64 r1, 10d
|
||||
LI64 r13, 10d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 32
|
||||
ret: 10
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,109 +1,101 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -106d
|
||||
ST r31, r254, 58a, 48h
|
||||
ADDI64 r254, r254, -98d
|
||||
ST r31, r254, 58a, 40h
|
||||
ADDI64 r32, r254, 33d
|
||||
ADDI64 r2, r254, 34d
|
||||
ADDI64 r6, r254, 1d
|
||||
LI64 r33, 0d
|
||||
ADDI64 r4, r254, 17d
|
||||
ADDI64 r33, r254, 34d
|
||||
ST r32, r254, 34a, 8h
|
||||
LI64 r34, 100d
|
||||
ADDI64 r7, r254, 0d
|
||||
LI8 r35, 1b
|
||||
ST r33, r254, 1a, 8h
|
||||
ST r33, r254, 17a, 8h
|
||||
ST r0, r254, 1a, 8h
|
||||
ST r0, r254, 17a, 8h
|
||||
ST r34, r254, 42a, 8h
|
||||
LI8 r36, 0b
|
||||
ST r35, r254, 0a, 1h
|
||||
ST r33, r254, 9a, 8h
|
||||
ST r33, r254, 25a, 8h
|
||||
ST r0, r254, 9a, 8h
|
||||
ST r0, r254, 25a, 8h
|
||||
ST r34, r254, 50a, 8h
|
||||
ST r36, r254, 33a, 1h
|
||||
CP r3, r4
|
||||
CP r5, r6
|
||||
LD r3, r3, 0a, 16h
|
||||
LD r5, r5, 0a, 16h
|
||||
LD r7, r7, 0a, 1h
|
||||
ST r0, r254, 33a, 1h
|
||||
CP r2, r33
|
||||
LD r3, r254, 17a, 16h
|
||||
LD r5, r254, 1a, 16h
|
||||
LD r7, r254, 0a, 1h
|
||||
JAL r31, r0, :put_filled_rect
|
||||
LD r31, r254, 58a, 48h
|
||||
ADDI64 r254, r254, 106d
|
||||
LD r31, r254, 58a, 40h
|
||||
ADDI64 r254, r254, 98d
|
||||
JALA r0, r31, 0a
|
||||
put_filled_rect:
|
||||
ADDI64 r254, r254, -212d
|
||||
ST r32, r254, 108a, 104h
|
||||
ADDI64 r254, r254, -108d
|
||||
CP r14, r2
|
||||
ST r3, r254, 92a, 16h
|
||||
ADDI64 r3, r254, 92d
|
||||
CP r15, r3
|
||||
ST r5, r254, 76a, 16h
|
||||
ADDI64 r5, r254, 76d
|
||||
CP r13, r5
|
||||
ST r7, r254, 75a, 1h
|
||||
ADDI64 r7, r254, 75d
|
||||
LI64 r8, 25d
|
||||
LI64 r32, 2d
|
||||
LI64 r6, 8d
|
||||
ADDI64 r33, r254, 25d
|
||||
ADDI64 r34, r254, 50d
|
||||
LI8 r35, 5b
|
||||
ST r35, r254, 25a, 1h
|
||||
LD r36, r5, 0a, 8h
|
||||
ST r36, r254, 26a, 4h
|
||||
LI64 r37, 1d
|
||||
ST r37, r254, 30a, 4h
|
||||
ST r7, r254, 34a, 8h
|
||||
ST r35, r254, 50a, 1h
|
||||
ST r36, r254, 51a, 4h
|
||||
ST r37, r254, 55a, 4h
|
||||
ST r7, r254, 59a, 8h
|
||||
CP r38, r7
|
||||
LD r7, r3, 8a, 8h
|
||||
LD r39, r5, 8a, 8h
|
||||
ADD64 r11, r39, r7
|
||||
SUB64 r4, r11, r37
|
||||
LD r40, r2, 8a, 8h
|
||||
MUL64 r5, r40, r4
|
||||
LD r9, r2, 0a, 8h
|
||||
ADD64 r10, r9, r5
|
||||
LD r2, r3, 0a, 8h
|
||||
ADD64 r41, r2, r10
|
||||
MUL64 r3, r40, r7
|
||||
ADD64 r4, r9, r3
|
||||
ADD64 r42, r2, r4
|
||||
3: JGTU r39, r37, :0
|
||||
JNE r39, r37, :1
|
||||
ADDI64 r4, r254, 0d
|
||||
ST r35, r254, 0a, 1h
|
||||
ST r36, r254, 1a, 4h
|
||||
ST r37, r254, 5a, 4h
|
||||
ST r38, r254, 9a, 8h
|
||||
ST r42, r254, 17a, 8h
|
||||
CP r2, r6
|
||||
CP r3, r32
|
||||
CP r5, r8
|
||||
CP r16, r7
|
||||
ADDI64 r17, r254, 25d
|
||||
LI8 r18, 5b
|
||||
ST r18, r254, 25a, 1h
|
||||
LD r19, r13, 0a, 8h
|
||||
ST r19, r254, 26a, 4h
|
||||
LI64 r20, 1d
|
||||
ST r20, r254, 30a, 4h
|
||||
ST r16, r254, 34a, 8h
|
||||
LI64 r21, 25d
|
||||
ADDI64 r22, r254, 50d
|
||||
ST r18, r254, 50a, 1h
|
||||
ST r19, r254, 51a, 4h
|
||||
ST r20, r254, 55a, 4h
|
||||
ST r16, r254, 59a, 8h
|
||||
LI64 r23, 2d
|
||||
LI64 r24, 8d
|
||||
LD r25, r15, 8a, 8h
|
||||
LD r13, r13, 8a, 8h
|
||||
ADD64 r26, r13, r25
|
||||
SUB64 r26, r26, r20
|
||||
LD r27, r14, 8a, 8h
|
||||
MUL64 r26, r27, r26
|
||||
LD r14, r14, 0a, 8h
|
||||
ADD64 r26, r14, r26
|
||||
LD r28, r15, 0a, 8h
|
||||
MUL64 r15, r27, r25
|
||||
ADD64 r14, r14, r15
|
||||
ADD64 r15, r28, r26
|
||||
ADD64 r14, r28, r14
|
||||
3: JGTU r13, r20, :0
|
||||
JNE r13, r20, :1
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r18, r254, 0a, 1h
|
||||
ST r19, r254, 1a, 4h
|
||||
ST r20, r254, 5a, 4h
|
||||
ST r16, r254, 9a, 8h
|
||||
ST r14, r254, 17a, 8h
|
||||
CP r2, r24
|
||||
CP r3, r23
|
||||
CP r4, r13
|
||||
CP r5, r21
|
||||
ECA
|
||||
JMP :1
|
||||
1: JMP :2
|
||||
0: CP r3, r32
|
||||
CP r43, r6
|
||||
CP r44, r8
|
||||
ST r42, r254, 67a, 8h
|
||||
CP r2, r43
|
||||
CP r4, r34
|
||||
CP r5, r44
|
||||
0: ST r14, r254, 67a, 8h
|
||||
CP r2, r24
|
||||
CP r3, r23
|
||||
CP r4, r22
|
||||
CP r5, r21
|
||||
ECA
|
||||
ST r41, r254, 42a, 8h
|
||||
CP r2, r43
|
||||
CP r3, r32
|
||||
CP r4, r33
|
||||
CP r5, r44
|
||||
ST r15, r254, 42a, 8h
|
||||
CP r2, r24
|
||||
CP r3, r23
|
||||
CP r4, r17
|
||||
CP r5, r21
|
||||
ECA
|
||||
ADD64 r42, r40, r42
|
||||
SUB64 r41, r41, r40
|
||||
SUB64 r39, r39, r32
|
||||
CP r6, r43
|
||||
CP r8, r44
|
||||
SUB64 r13, r13, r23
|
||||
SUB64 r15, r15, r27
|
||||
ADD64 r14, r27, r14
|
||||
JMP :3
|
||||
2: LD r32, r254, 108a, 104h
|
||||
ADDI64 r254, r254, 212d
|
||||
2: ADDI64 r254, r254, 108d
|
||||
JALA r0, r31, 0a
|
||||
code size: 917
|
||||
code size: 842
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
main:
|
||||
LRA r2, r0, :x
|
||||
LI64 r1, 0d
|
||||
ST r1, r2, 0a, 8h
|
||||
JALA r0, r31, 0a
|
||||
code size: 57
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,20 +1,25 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 16a, 16h
|
||||
ADDI64 r3, r254, 0d
|
||||
ADDI64 r2, r254, 8d
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 0a, 8h
|
||||
ST r32, r254, 8a, 8h
|
||||
LI64 r4, 1024d
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 16a, 32h
|
||||
ADDI64 r32, r254, 0d
|
||||
ADDI64 r33, r254, 8d
|
||||
ST r0, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
LI64 r34, 1024d
|
||||
CP r2, r33
|
||||
CP r3, r32
|
||||
CP r4, r34
|
||||
JAL r31, r0, :set
|
||||
ANDI r1, r1, 4294967295d
|
||||
LD r31, r254, 16a, 16h
|
||||
ADDI64 r254, r254, 32d
|
||||
CP r32, r1
|
||||
ANDI r32, r32, 4294967295d
|
||||
CP r1, r32
|
||||
LD r31, r254, 16a, 32h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
set:
|
||||
CP r1, r4
|
||||
CP r13, r4
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 167
|
||||
code size: 175
|
||||
ret: 1024
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
integer_range:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r32, r254, 0a, 16h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
LI64 r3, 4d
|
||||
LI64 r2, 3d
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
LI64 r15, 4d
|
||||
LI64 r16, 3d
|
||||
CP r2, r16
|
||||
CP r3, r15
|
||||
ECA
|
||||
CP r2, r32
|
||||
CP r3, r33
|
||||
SUB64 r11, r3, r2
|
||||
ADDI64 r3, r11, 1d
|
||||
DIRU64 r0, r3, r1, r3
|
||||
ADD64 r1, r3, r2
|
||||
LD r32, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
SUB64 r14, r14, r13
|
||||
ADDI64 r14, r14, 1d
|
||||
CP r15, r1
|
||||
DIRU64 r0, r14, r15, r14
|
||||
ADD64 r13, r14, r13
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r3, 1000d
|
||||
LI64 r2, 0d
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r32, 1000d
|
||||
CP r2, r0
|
||||
CP r3, r32
|
||||
JAL r31, r0, :integer_range
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 210
|
||||
code size: 164
|
||||
ret: 42
|
||||
status: Ok(())
|
||||
|
|
49
lang/tests/son_tests_len_never_goes_down.txt
Normal file
49
lang/tests/son_tests_len_never_goes_down.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
chars:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r3, r254, 16a, 16h
|
||||
ADDI64 r3, r254, 16d
|
||||
CP r13, r3
|
||||
ADDI64 r14, r254, 0d
|
||||
BMC r13, r14, 16h
|
||||
LD r1, r14, 0a, 16h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 32a, 24h
|
||||
LRA r32, r0, :Hello, World!
|
||||
ST r32, r254, 16a, 8h
|
||||
LI64 r32, 13d
|
||||
ST r32, r254, 24a, 8h
|
||||
ADDI64 r32, r254, 0d
|
||||
LD r3, r254, 16a, 16h
|
||||
JAL r31, r0, :chars
|
||||
ST r1, r32, 0a, 16h
|
||||
2: CP r2, r32
|
||||
JAL r31, r0, :next
|
||||
CP r33, r1
|
||||
ANDI r33, r33, 65535d
|
||||
JNE r33, r0, :0
|
||||
JMP :1
|
||||
0: JMP :2
|
||||
1: LD r31, r254, 32a, 24h
|
||||
ADDI64 r254, r254, 56d
|
||||
JALA r0, r31, 0a
|
||||
next:
|
||||
CP r13, r2
|
||||
LD r14, r13, 8a, 8h
|
||||
JNE r14, r0, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LD r15, r13, 0a, 8h
|
||||
ADDI64 r15, r15, 1d
|
||||
ST r15, r13, 0a, 8h
|
||||
ADDI64 r14, r14, -1d
|
||||
LD r15, r15, 0a, 1h
|
||||
ST r14, r13, 8a, 8h
|
||||
ORI r13, r15, 32768d
|
||||
CP r1, r13
|
||||
1: JALA r0, r31, 0a
|
||||
code size: 423
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,16 +1,16 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
LI64 r3, 0d
|
||||
LI64 r2, 10d
|
||||
ST r2, r254, 0a, 8h
|
||||
2: LD r1, r254, 0a, 8h
|
||||
JNE r1, r3, :0
|
||||
LI64 r13, 10d
|
||||
ST r13, r254, 0a, 8h
|
||||
2: LD r13, r254, 0a, 8h
|
||||
JNE r13, r0, :0
|
||||
CP r1, r13
|
||||
JMP :1
|
||||
0: ADDI64 r11, r1, -1d
|
||||
ST r11, r254, 0a, 8h
|
||||
0: ADDI64 r13, r13, -1d
|
||||
ST r13, r254, 0a, 8h
|
||||
JMP :2
|
||||
1: ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 126
|
||||
code size: 119
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
fib:
|
||||
LI64 r4, 1d
|
||||
LI64 r5, 0d
|
||||
CP r1, r5
|
||||
CP r10, r4
|
||||
2: JNE r2, r5, :0
|
||||
CP r13, r2
|
||||
LI64 r17, 1d
|
||||
CP r15, r0
|
||||
CP r14, r15
|
||||
CP r16, r17
|
||||
2: JNE r13, r15, :0
|
||||
CP r1, r14
|
||||
JMP :1
|
||||
0: ADD64 r1, r10, r1
|
||||
SUB64 r2, r2, r4
|
||||
CP r3, r1
|
||||
CP r1, r10
|
||||
CP r10, r3
|
||||
0: SUB64 r13, r13, r17
|
||||
ADD64 r14, r16, r14
|
||||
SWA r14, r16
|
||||
JMP :2
|
||||
1: JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r32, 10d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
CP r32, r1
|
||||
CP r1, r32
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 153
|
||||
code size: 155
|
||||
ret: 55
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
main:
|
||||
LI64 r1, 1d
|
||||
LI64 r13, 1d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
code size: 32
|
||||
ret: 1
|
||||
status: Ok(())
|
||||
|
|
36
lang/tests/son_tests_memory_swap.txt
Normal file
36
lang/tests/son_tests_memory_swap.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
decide:
|
||||
ADDI64 r254, r254, -24d
|
||||
CP r14, r2
|
||||
CP r15, r1
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r14, r254, 0a, 8h
|
||||
ST r0, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
BMC r13, r15, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -104d
|
||||
ST r31, r254, 72a, 32h
|
||||
ADDI64 r32, r254, 48d
|
||||
CP r1, r32
|
||||
CP r2, r0
|
||||
JAL r31, r0, :decide
|
||||
ADDI64 r33, r254, 24d
|
||||
BMC r32, r33, 24h
|
||||
LI64 r34, 1d
|
||||
CP r1, r33
|
||||
CP r2, r34
|
||||
JAL r31, r0, :decide
|
||||
ADDI64 r34, r254, 0d
|
||||
BMC r32, r34, 24h
|
||||
LD r32, r254, 24a, 8h
|
||||
LD r33, r254, 0a, 8h
|
||||
ADD64 r32, r33, r32
|
||||
CP r1, r32
|
||||
LD r31, r254, 72a, 32h
|
||||
ADDI64 r254, r254, 104d
|
||||
JALA r0, r31, 0a
|
||||
code size: 273
|
||||
ret: 1
|
||||
status: Ok(())
|
59
lang/tests/son_tests_method_receiver_by_value.txt
Normal file
59
lang/tests/son_tests_method_receiver_by_value.txt
Normal file
|
@ -0,0 +1,59 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -72d
|
||||
ST r31, r254, 32a, 40h
|
||||
LRA r32, r0, :"Goodbye, World!\0"
|
||||
LRA r33, r0, :"Hello, World!\0"
|
||||
ST r32, r254, 16a, 8h
|
||||
ST r33, r254, 24a, 8h
|
||||
LD r2, r254, 24a, 8h
|
||||
LD r3, r254, 16a, 8h
|
||||
JAL r31, r0, :print
|
||||
ADDI64 r34, r254, 8d
|
||||
ADDI64 r35, r254, 0d
|
||||
ST r32, r254, 8a, 8h
|
||||
ST r33, r254, 0a, 8h
|
||||
CP r2, r35
|
||||
CP r3, r34
|
||||
JAL r31, r0, :print2
|
||||
LD r31, r254, 32a, 40h
|
||||
ADDI64 r254, r254, 72d
|
||||
JALA r0, r31, 0a
|
||||
print:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r2, r254, 8a, 8h
|
||||
ADDI64 r2, r254, 8d
|
||||
CP r13, r2
|
||||
ST r3, r254, 0a, 8h
|
||||
ADDI64 r3, r254, 0d
|
||||
CP r14, r3
|
||||
LD r13, r13, 0a, 8h
|
||||
LI64 r15, 37d
|
||||
CP r2, r15
|
||||
CP r3, r13
|
||||
ECA
|
||||
LD r13, r14, 0a, 8h
|
||||
CP r2, r15
|
||||
CP r3, r13
|
||||
ECA
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
print2:
|
||||
CP r13, r2
|
||||
CP r14, r3
|
||||
LD r13, r13, 0a, 8h
|
||||
LI64 r15, 37d
|
||||
CP r2, r15
|
||||
CP r3, r13
|
||||
ECA
|
||||
LD r13, r14, 0a, 8h
|
||||
CP r2, r15
|
||||
CP r3, r13
|
||||
ECA
|
||||
JALA r0, r31, 0a
|
||||
Hello, World!
|
||||
Goodbye, World!
|
||||
Hello, World!
|
||||
Goodbye, World!
|
||||
code size: 435
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,27 +1,23 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
JAL r31, r0, :opaque
|
||||
CP r32, r1
|
||||
CP r33, r1
|
||||
JAL r31, r0, :opaque
|
||||
LI64 r33, 0d
|
||||
CP r1, r32
|
||||
JNE r1, r33, :0
|
||||
CP r32, r1
|
||||
LI64 r1, 0d
|
||||
CP r34, r32
|
||||
JNE r33, r0, :0
|
||||
CP r32, r0
|
||||
JMP :1
|
||||
0: CP r34, r1
|
||||
LD r1, r34, 0a, 8h
|
||||
1: JEQ r34, r33, :2
|
||||
LD r1, r34, 0a, 8h
|
||||
0: LD r32, r33, 0a, 8h
|
||||
1: JEQ r33, r0, :2
|
||||
LD r32, r33, 0a, 8h
|
||||
JMP :2
|
||||
2: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
2: CP r1, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
opaque:
|
||||
LI64 r1, 0d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 183
|
||||
code size: 150
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
test.hb:4:17: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(<expr_ty>, <opt_expr>)'
|
||||
ptr := @unwrap(always_nn)
|
||||
^
|
||||
test.hb:6:16: unwrap is incorrect since the value is (provably) always null, make sure your logic is correct
|
||||
ptr = @unwrap(always_n)
|
||||
^
|
||||
test.hb:4:18: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(<expr_ty>, <opt_expr>)'
|
||||
ptr1 := @unwrap(always_nn)
|
||||
^
|
||||
test.hb:6:18: unwrap is incorrect since the value is (provably) always null, make sure your logic is correct
|
||||
ptr2 := @unwrap(always_n)
|
||||
^
|
||||
|
|
31
lang/tests/son_tests_null_check_in_the_loop.txt
Normal file
31
lang/tests/son_tests_null_check_in_the_loop.txt
Normal file
|
@ -0,0 +1,31 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -30d
|
||||
ST r31, r254, 6a, 24h
|
||||
ADDI64 r32, r254, 0d
|
||||
2: JAL r31, r0, :return_fn
|
||||
ST r1, r32, 0a, 6h
|
||||
LD r33, r254, 0a, 1h
|
||||
ANDI r33, r33, 255d
|
||||
JEQ r33, r0, :0
|
||||
LI64 r32, 1d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
0: JMP :2
|
||||
1: LD r31, r254, 6a, 24h
|
||||
ADDI64 r254, r254, 30d
|
||||
JALA r0, r31, 0a
|
||||
return_fn:
|
||||
ADDI64 r254, r254, -6d
|
||||
LI8 r13, 1b
|
||||
ST r13, r254, 0a, 1h
|
||||
ST r0, r254, 1a, 1h
|
||||
ST r0, r254, 2a, 1h
|
||||
ST r0, r254, 3a, 1h
|
||||
ST r0, r254, 4a, 1h
|
||||
ST r0, r254, 5a, 1h
|
||||
LD r1, r254, 0a, 6h
|
||||
ADDI64 r254, r254, 6d
|
||||
JALA r0, r31, 0a
|
||||
code size: 277
|
||||
ret: 1
|
||||
status: Ok(())
|
72
lang/tests/son_tests_null_check_returning_small_global.txt
Normal file
72
lang/tests/son_tests_null_check_returning_small_global.txt
Normal file
|
@ -0,0 +1,72 @@
|
|||
foo:
|
||||
ADDI64 r254, r254, -112d
|
||||
ST r31, r254, 80a, 32h
|
||||
ADDI64 r32, r254, 64d
|
||||
LRA r33, r0, :some_file
|
||||
CP r3, r33
|
||||
JAL r31, r0, :get
|
||||
ST r1, r32, 0a, 16h
|
||||
LD r33, r254, 64a, 1h
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :0
|
||||
ST r0, r254, 48a, 1h
|
||||
LD r1, r254, 48a, 16h
|
||||
JMP :1
|
||||
0: LI8 r33, 1b
|
||||
LI64 r34, 4d
|
||||
LD r32, r254, 72a, 8h
|
||||
JNE r32, r34, :2
|
||||
ST r33, r254, 32a, 1h
|
||||
LI64 r32, 2d
|
||||
ST r32, r254, 40a, 8h
|
||||
LD r1, r254, 32a, 16h
|
||||
JMP :1
|
||||
2: LRA r34, r0, :magic
|
||||
LD r34, r34, 0a, 8h
|
||||
JNE r34, r32, :3
|
||||
ST r33, r254, 16a, 1h
|
||||
ST r0, r254, 24a, 8h
|
||||
LD r1, r254, 16a, 16h
|
||||
JMP :1
|
||||
3: ST r0, r254, 0a, 1h
|
||||
LD r1, r254, 0a, 16h
|
||||
1: LD r31, r254, 80a, 32h
|
||||
ADDI64 r254, r254, 112d
|
||||
JALA r0, r31, 0a
|
||||
get:
|
||||
ADDI64 r254, r254, -32d
|
||||
CP r13, r3
|
||||
LD r13, r13, 0a, 1h
|
||||
LRA r14, r0, :magic
|
||||
ANDI r13, r13, 255d
|
||||
LD r14, r14, 0a, 8h
|
||||
JNE r14, r13, :0
|
||||
LI8 r13, 1b
|
||||
ST r13, r254, 16a, 1h
|
||||
ST r14, r254, 24a, 8h
|
||||
LD r1, r254, 16a, 16h
|
||||
JMP :1
|
||||
0: ST r0, r254, 0a, 1h
|
||||
LD r1, r254, 0a, 16h
|
||||
1: ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 16a, 24h
|
||||
ADDI64 r32, r254, 0d
|
||||
JAL r31, r0, :foo
|
||||
ST r1, r32, 0a, 16h
|
||||
LD r33, r254, 0a, 1h
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :0
|
||||
LI64 r32, 100d
|
||||
CP r1, r32
|
||||
JMP :1
|
||||
0: LD r32, r254, 8a, 8h
|
||||
CP r1, r32
|
||||
1: LD r31, r254, 16a, 24h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 673
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,26 +1,24 @@
|
|||
get_ptr:
|
||||
ADDI64 r254, r254, -8d
|
||||
ADDI64 r1, r254, 0d
|
||||
ADDI64 r254, r254, 8d
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
JAL r31, r0, :get_ptr
|
||||
LI64 r32, 0d
|
||||
JNE r1, r32, :0
|
||||
LI64 r1, 0d
|
||||
CP r32, r1
|
||||
JNE r32, r0, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LI64 r33, 10d
|
||||
CP r34, r1
|
||||
2: LD r1, r34, 0a, 8h
|
||||
JEQ r1, r33, :1
|
||||
ADDI64 r35, r1, 1d
|
||||
ST r35, r34, 0a, 8h
|
||||
JMP :2
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
3: LD r34, r32, 0a, 8h
|
||||
JEQ r34, r33, :2
|
||||
ADDI64 r34, r34, 1d
|
||||
ST r34, r32, 0a, 8h
|
||||
JMP :3
|
||||
2: CP r1, r34
|
||||
1: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 208
|
||||
ret: 10
|
||||
code size: 164
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,65 +1,57 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -122d
|
||||
ST r31, r254, 26a, 96h
|
||||
ADDI64 r254, r254, -58d
|
||||
ST r31, r254, 26a, 32h
|
||||
JAL r31, r0, :returner_fn
|
||||
CP r32, r1
|
||||
ADDI64 r1, r254, 2d
|
||||
ADDI64 r33, r254, 2d
|
||||
CP r1, r33
|
||||
JAL r31, r0, :returner_bn
|
||||
ADDI64 r33, r254, 0d
|
||||
ADDI64 r34, r254, 0d
|
||||
JAL r31, r0, :returner_cn
|
||||
ST r1, r254, 0a, 2h
|
||||
LI8 r34, 0b
|
||||
LI8 r35, 0b
|
||||
LD r36, r254, 2a, 1h
|
||||
CP r1, r32
|
||||
ANDI r37, r37, 255d
|
||||
ANDI r1, r1, 255d
|
||||
CMPU r37, r1, r34
|
||||
CMPUI r37, r37, 0d
|
||||
ANDI r38, r38, 255d
|
||||
ANDI r36, r36, 255d
|
||||
CMPU r38, r36, r35
|
||||
CMPUI r38, r38, 0d
|
||||
LD r39, r254, 0a, 1h
|
||||
AND r40, r38, r37
|
||||
ANDI r41, r41, 255d
|
||||
ANDI r39, r39, 255d
|
||||
CMPU r41, r39, r35
|
||||
CMPUI r41, r41, 0d
|
||||
AND r42, r41, r40
|
||||
ANDI r42, r42, 255d
|
||||
JNE r42, r0, :0
|
||||
LI64 r1, 0d
|
||||
ST r1, r34, 0a, 2h
|
||||
LD r33, r254, 2a, 1h
|
||||
CMPU r32, r32, r0
|
||||
CMPUI r32, r32, 0d
|
||||
CMPU r33, r33, r0
|
||||
CMPUI r33, r33, 0d
|
||||
LD r34, r254, 0a, 1h
|
||||
AND r32, r33, r32
|
||||
CMPU r33, r34, r0
|
||||
CMPUI r33, r33, 0d
|
||||
AND r32, r33, r32
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LI64 r1, 1d
|
||||
1: LD r31, r254, 26a, 96h
|
||||
ADDI64 r254, r254, 122d
|
||||
0: LI64 r32, 1d
|
||||
CP r1, r32
|
||||
1: LD r31, r254, 26a, 32h
|
||||
ADDI64 r254, r254, 58d
|
||||
JALA r0, r31, 0a
|
||||
returner_bn:
|
||||
ADDI64 r254, r254, -24d
|
||||
LI8 r6, 1b
|
||||
ADDI64 r5, r254, 0d
|
||||
ST r6, r254, 0a, 1h
|
||||
LI64 r6, 0d
|
||||
ST r6, r254, 8a, 8h
|
||||
ST r6, r254, 16a, 8h
|
||||
BMC r5, r1, 24h
|
||||
CP r15, r1
|
||||
LI8 r14, 1b
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r14, r254, 0a, 1h
|
||||
ST r0, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
BMC r13, r15, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
returner_cn:
|
||||
ADDI64 r254, r254, -2d
|
||||
LI8 r4, 1b
|
||||
ADDI64 r3, r254, 0d
|
||||
ST r4, r254, 0a, 1h
|
||||
LI8 r4, 0b
|
||||
ST r4, r254, 1a, 1h
|
||||
LD r1, r3, 0a, 2h
|
||||
LI8 r13, 1b
|
||||
ST r13, r254, 0a, 1h
|
||||
ST r0, r254, 1a, 1h
|
||||
LD r1, r254, 0a, 2h
|
||||
ADDI64 r254, r254, 2d
|
||||
JALA r0, r31, 0a
|
||||
returner_fn:
|
||||
LD r1, r254, 0a, 0h
|
||||
ORI r1, r1, 128d
|
||||
LD r13, r254, 0a, 0h
|
||||
ORI r13, r13, 128d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 546
|
||||
code size: 452
|
||||
ret: 1
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,135 +1,144 @@
|
|||
decide:
|
||||
LI8 r1, 1b
|
||||
LI8 r13, 1b
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -224d
|
||||
ST r31, r254, 80a, 144h
|
||||
ADDI64 r254, r254, -136d
|
||||
ST r31, r254, 96a, 40h
|
||||
JAL r31, r0, :decide
|
||||
LI64 r32, 0d
|
||||
ADDI64 r2, r254, 72d
|
||||
CP r33, r2
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :0
|
||||
CP r34, r32
|
||||
CP r33, r0
|
||||
ADDI64 r34, r254, 88d
|
||||
CP r32, r1
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :0
|
||||
CP r32, r33
|
||||
JMP :1
|
||||
0: CP r34, r33
|
||||
1: JNE r34, r32, :2
|
||||
LI64 r1, 9001d
|
||||
0: CP r32, r34
|
||||
1: JNE r32, r33, :2
|
||||
LI64 r32, 9001d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
2: JAL r31, r0, :decide
|
||||
LI8 r35, 0b
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :4
|
||||
LI8 r36, 1b
|
||||
ST r36, r254, 56a, 1h
|
||||
LD r36, r34, 0a, 8h
|
||||
ST r36, r254, 64a, 8h
|
||||
CP r33, r1
|
||||
ANDI r33, r33, 255d
|
||||
JNE r33, r0, :4
|
||||
LI8 r33, 1b
|
||||
ST r33, r254, 72a, 1h
|
||||
LD r32, r32, 0a, 8h
|
||||
ST r32, r254, 80a, 8h
|
||||
JMP :5
|
||||
4: ST r35, r254, 56a, 1h
|
||||
5: LD r37, r254, 56a, 1h
|
||||
ANDI r37, r37, 255d
|
||||
ANDI r35, r35, 255d
|
||||
JEQ r37, r35, :6
|
||||
LI64 r1, 42d
|
||||
4: ST r0, r254, 72a, 1h
|
||||
5: LD r32, r254, 72a, 1h
|
||||
ANDI r32, r32, 255d
|
||||
JEQ r32, r0, :6
|
||||
LI64 r32, 42d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
6: JAL r31, r0, :decide
|
||||
LI32 r38, 0w
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :7
|
||||
CP r39, r38
|
||||
CP r33, r0
|
||||
CP r32, r1
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :7
|
||||
CP r32, r33
|
||||
JMP :8
|
||||
7: LI32 r39, 2147483649w
|
||||
8: ANDI r39, r39, 4294967295d
|
||||
ANDI r38, r38, 4294967295d
|
||||
JNE r39, r38, :9
|
||||
LI64 r1, 69d
|
||||
7: LI32 r32, 2147483649w
|
||||
8: ANDI r32, r32, 4294967295d
|
||||
ANDI r33, r33, 4294967295d
|
||||
JNE r32, r33, :9
|
||||
LI64 r32, 69d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
9: ADDI64 r3, r254, 40d
|
||||
CP r40, r3
|
||||
9: ADDI64 r33, r254, 56d
|
||||
JAL r31, r0, :new_foo
|
||||
ST r1, r254, 40a, 16h
|
||||
LI64 r32, 0d
|
||||
LD r41, r254, 40a, 8h
|
||||
JNE r41, r32, :10
|
||||
LI64 r1, 999d
|
||||
ST r1, r33, 0a, 16h
|
||||
LD r35, r254, 56a, 8h
|
||||
JNE r35, r0, :10
|
||||
LI64 r32, 999d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
10: LRA r4, r0, :"foo\0"
|
||||
CP r3, r40
|
||||
CP r2, r3
|
||||
LD r2, r2, 0a, 16h
|
||||
10: LRA r35, r0, :"foo\0"
|
||||
ST r35, r254, 40a, 8h
|
||||
LI64 r35, 4d
|
||||
ST r35, r254, 48a, 8h
|
||||
LD r2, r33, 0a, 16h
|
||||
LD r4, r254, 40a, 16h
|
||||
JAL r31, r0, :use_foo
|
||||
ADDI64 r42, r254, 0d
|
||||
ADDI64 r33, r254, 0d
|
||||
JAL r31, r0, :no_foo
|
||||
ST r1, r254, 0a, 16h
|
||||
ST r1, r33, 0a, 16h
|
||||
JAL r31, r0, :decide
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :11
|
||||
CP r2, r33
|
||||
CP r35, r1
|
||||
ANDI r35, r35, 255d
|
||||
JNE r35, r0, :11
|
||||
JMP :12
|
||||
11: CP r2, r33
|
||||
ST r2, r254, 0a, 8h
|
||||
LI64 r43, 1d
|
||||
ST r43, r254, 8a, 8h
|
||||
ST r43, r254, 72a, 8h
|
||||
12: LD r44, r254, 0a, 8h
|
||||
JNE r44, r32, :13
|
||||
LI64 r1, 34d
|
||||
11: ST r34, r254, 0a, 8h
|
||||
LI64 r35, 1d
|
||||
ST r35, r254, 8a, 8h
|
||||
ST r35, r254, 88a, 8h
|
||||
12: LD r35, r254, 0a, 8h
|
||||
JNE r35, r0, :13
|
||||
LI64 r32, 34d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
13: ADDI64 r1, r254, 16d
|
||||
13: ADDI64 r35, r254, 16d
|
||||
CP r1, r35
|
||||
CP r2, r34
|
||||
JAL r31, r0, :new_bar
|
||||
JAL r31, r0, :decide
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :14
|
||||
CP r34, r1
|
||||
ANDI r34, r34, 255d
|
||||
JNE r34, r0, :14
|
||||
JMP :15
|
||||
14: ST r35, r254, 16a, 1h
|
||||
15: LD r45, r254, 16a, 1h
|
||||
ANDI r45, r45, 255d
|
||||
ANDI r35, r35, 255d
|
||||
JEQ r45, r35, :16
|
||||
LI64 r1, 420d
|
||||
14: ST r0, r254, 16a, 1h
|
||||
15: LD r34, r254, 16a, 1h
|
||||
ANDI r34, r34, 255d
|
||||
JEQ r34, r0, :16
|
||||
LI64 r32, 420d
|
||||
CP r1, r32
|
||||
JMP :3
|
||||
16: LD r46, r254, 0a, 8h
|
||||
LD r47, r46, 0a, 8h
|
||||
ANDI r48, r39, 65535d
|
||||
SUB64 r1, r48, r47
|
||||
3: LD r31, r254, 80a, 144h
|
||||
ADDI64 r254, r254, 224d
|
||||
16: LD r33, r254, 0a, 8h
|
||||
LD r33, r33, 0a, 8h
|
||||
ANDI r32, r32, 65535d
|
||||
SUB64 r32, r32, r33
|
||||
CP r1, r32
|
||||
3: LD r31, r254, 96a, 40h
|
||||
ADDI64 r254, r254, 136d
|
||||
JALA r0, r31, 0a
|
||||
new_bar:
|
||||
ADDI64 r254, r254, -24d
|
||||
LI8 r8, 1b
|
||||
ADDI64 r7, r254, 0d
|
||||
ST r8, r254, 0a, 1h
|
||||
ST r2, r254, 8a, 8h
|
||||
LI64 r9, 1d
|
||||
ST r9, r254, 16a, 8h
|
||||
BMC r7, r1, 24h
|
||||
CP r14, r2
|
||||
CP r16, r1
|
||||
LI8 r15, 1b
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r15, r254, 0a, 1h
|
||||
ST r14, r254, 8a, 8h
|
||||
LI64 r14, 1d
|
||||
ST r14, r254, 16a, 8h
|
||||
BMC r13, r16, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
new_foo:
|
||||
ADDI64 r254, r254, -24d
|
||||
ADDI64 r3, r254, 0d
|
||||
ADDI64 r2, r254, 8d
|
||||
ST r3, r254, 8a, 8h
|
||||
LI64 r5, 0d
|
||||
ST r5, r254, 16a, 8h
|
||||
LD r1, r2, 0a, 16h
|
||||
ADDI64 r13, r254, 0d
|
||||
ST r13, r254, 8a, 8h
|
||||
ST r0, r254, 16a, 8h
|
||||
LD r1, r254, 8a, 16h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
no_foo:
|
||||
ADDI64 r254, r254, -16d
|
||||
ADDI64 r1, r254, 0d
|
||||
LI64 r3, 0d
|
||||
ST r3, r254, 0a, 8h
|
||||
LD r1, r1, 0a, 16h
|
||||
ST r0, r254, 0a, 8h
|
||||
LD r1, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
use_foo:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r2, r254, 0a, 16h
|
||||
ADDI64 r2, r254, 0d
|
||||
ADDI64 r254, r254, 16d
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r2, r254, 16a, 16h
|
||||
ADDI64 r2, r254, 16d
|
||||
ST r4, r254, 0a, 16h
|
||||
ADDI64 r4, r254, 0d
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 1143
|
||||
code size: 1162
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
inb:
|
||||
CP r1, r2
|
||||
CP r13, r2
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LI64 r32, 0d
|
||||
LI64 r33, 100d
|
||||
4: CP r2, r33
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r32, 100d
|
||||
4: CP r2, r32
|
||||
JAL r31, r0, :inb
|
||||
ANDI r34, r1, 2d
|
||||
JNE r34, r32, :0
|
||||
LI64 r2, 96d
|
||||
CP r3, r32
|
||||
CP r33, r1
|
||||
ANDI r33, r33, 2d
|
||||
JNE r33, r0, :0
|
||||
LI64 r33, 96d
|
||||
CP r2, r33
|
||||
CP r3, r0
|
||||
JAL r31, r0, :outb
|
||||
3: CP r2, r33
|
||||
3: CP r2, r32
|
||||
JAL r31, r0, :inb
|
||||
JEQ r1, r32, :1
|
||||
LI64 r1, 1d
|
||||
CP r33, r1
|
||||
JEQ r33, r0, :1
|
||||
LI64 r32, 1d
|
||||
CP r1, r32
|
||||
JMP :2
|
||||
1: JMP :3
|
||||
0: JMP :4
|
||||
2: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
2: LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
outb:
|
||||
JALA r0, r31, 0a
|
||||
code size: 198
|
||||
code size: 203
|
||||
ret: 1
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ADDI64 r3, r254, 0d
|
||||
LI64 r6, 0d
|
||||
CP r3, r6
|
||||
CP r4, r6
|
||||
CP r5, r6
|
||||
ADDI64 r13, r254, 0d
|
||||
CP r3, r0
|
||||
CP r4, r0
|
||||
CP r5, r0
|
||||
CP r6, r0
|
||||
ECA
|
||||
ST r1, r254, 0a, 16h
|
||||
LI8 r8, 0b
|
||||
LD r9, r254, 0a, 1h
|
||||
ANDI r9, r9, 255d
|
||||
ANDI r8, r8, 255d
|
||||
JNE r9, r8, :0
|
||||
ST r1, r13, 0a, 16h
|
||||
LD r14, r254, 0a, 1h
|
||||
ANDI r14, r14, 255d
|
||||
JNE r14, r0, :0
|
||||
UN
|
||||
0: LD r1, r254, 8a, 8h
|
||||
0: LD r13, r254, 8a, 8h
|
||||
CP r1, r13
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
unknown ecall: 0
|
||||
code size: 142
|
||||
code size: 124
|
||||
ret: 0
|
||||
status: Err(Unreachable)
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -104d
|
||||
ST r31, r254, 40a, 64h
|
||||
LI64 r32, 4d
|
||||
ADDI64 r33, r254, 24d
|
||||
ADDI64 r34, r254, 0d
|
||||
ST r32, r254, 24a, 8h
|
||||
LI64 r35, 1d
|
||||
ST r35, r254, 32a, 8h
|
||||
ST r35, r254, 16a, 8h
|
||||
BMC r33, r34, 16h
|
||||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 24a, 32h
|
||||
ADDI64 r32, r254, 0d
|
||||
LI64 r33, 1d
|
||||
ST r33, r254, 16a, 8h
|
||||
LI64 r34, 4d
|
||||
ST r34, r254, 0a, 8h
|
||||
ST r33, r254, 8a, 8h
|
||||
JAL r31, r0, :opaque
|
||||
ST r1, r254, 0a, 16h
|
||||
LD r36, r254, 8a, 8h
|
||||
LD r37, r254, 16a, 8h
|
||||
ADD64 r38, r37, r36
|
||||
LD r37, r254, 0a, 8h
|
||||
SUB64 r1, r37, r38
|
||||
LD r31, r254, 40a, 64h
|
||||
ADDI64 r254, r254, 104d
|
||||
ST r1, r32, 0a, 16h
|
||||
LD r33, r254, 8a, 8h
|
||||
LD r34, r254, 16a, 8h
|
||||
ADD64 r33, r34, r33
|
||||
LD r32, r254, 0a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
CP r1, r32
|
||||
LD r31, r254, 24a, 32h
|
||||
ADDI64 r254, r254, 56d
|
||||
JALA r0, r31, 0a
|
||||
opaque:
|
||||
ADDI64 r254, r254, -16d
|
||||
LI64 r3, 3d
|
||||
ADDI64 r2, r254, 0d
|
||||
ST r3, r254, 0a, 8h
|
||||
LI64 r6, 2d
|
||||
ST r6, r254, 8a, 8h
|
||||
LD r1, r2, 0a, 16h
|
||||
LI64 r13, 3d
|
||||
ST r13, r254, 0a, 8h
|
||||
LI64 r13, 2d
|
||||
ST r13, r254, 8a, 8h
|
||||
LD r1, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 323
|
||||
code size: 299
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
7
lang/tests/son_tests_pointer_comparison.txt
Normal file
7
lang/tests/son_tests_pointer_comparison.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
main:
|
||||
LI64 r13, 10d
|
||||
CP r1, r13
|
||||
JALA r0, r31, 0a
|
||||
code size: 32
|
||||
ret: 10
|
||||
status: Ok(())
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue